BlittableLogic を immutable にするべく currentTail と prevTail を外出し

This commit is contained in:
ousttrue 2024-09-11 20:35:57 +09:00
parent 14f3add22f
commit 0e738ae3d2
11 changed files with 168 additions and 95 deletions

View File

@ -4,16 +4,15 @@ using UnityEngine;
namespace UniGLTF.SpringBoneJobs.Blittables
{
/// <summary>
/// SpringBoneの各関節に紐付いた計算情報を表すデータ型
/// 初期状態から計算できる Joint のパラメーター
/// </summary>
[Serializable]
public struct BlittableLogic
public struct BlittableJointInit
{
public int parentTransformIndex;
public int headTransformIndex;
public int tailTransformIndex;
public float length;
public Vector3 currentTail;
public Vector3 prevTail;
public Quaternion localRotation;
public Vector3 boneAxis;
}

View File

@ -4,10 +4,10 @@ using UnityEngine;
namespace UniGLTF.SpringBoneJobs.Blittables
{
/// <summary>
/// SpringBoneの各関節を表すデータ型
/// - 毎フレームの変化を許可する
/// </summary>
[Serializable]
public struct BlittableJoint
public struct BlittableJointSettings
{
public float stiffnessForce;
public float gravityPower;
@ -15,4 +15,4 @@ namespace UniGLTF.SpringBoneJobs.Blittables
public float dragForce;
public float radius;
}
}
}

View File

@ -19,11 +19,19 @@ namespace UniGLTF.SpringBoneJobs
/// </summary>
public sealed class FastSpringBoneBufferCombiner : IDisposable
{
// 長さと index 同じ
private NativeArray<BlittableJointInit> _logics;
private NativeArray<BlittableJointSettings> _joints;
private NativeArray<Vector3> _prevTails;
private NativeArray<Vector3> _currentTails;
private NativeArray<Vector3> _nextTails;
private NativeArray<BlittableSpring> _springs;
private NativeArray<BlittableTransform> _transforms;
private NativeArray<BlittableCollider> _colliders;
private NativeArray<BlittableJoint> _joints;
private NativeArray<BlittableLogic> _logics;
private NativeArray<BlittableTransform> _transforms;
private TransformAccessArray _transformAccessArray;
private readonly LinkedList<FastSpringBoneBuffer> _buffers = new LinkedList<FastSpringBoneBuffer>();
@ -32,15 +40,39 @@ namespace UniGLTF.SpringBoneJobs
private bool _isDirty;
public NativeArray<BlittableJointInit> Logics => _logics;
public NativeArray<BlittableJointSettings> Joints => _joints;
public NativeArray<Vector3> PrevTails => _prevTails;
public NativeArray<Vector3> CurrentTails => _currentTails;
public NativeArray<Vector3> NextTails => _nextTails;
public NativeArray<BlittableSpring> Springs => _springs;
public NativeArray<BlittableJoint> Joints => _joints;
public NativeArray<BlittableCollider> Colliders => _colliders;
public NativeArray<BlittableTransform> Transforms => _transforms;
public TransformAccessArray TransformAccessArray => _transformAccessArray;
public NativeArray<BlittableCollider> Colliders => _colliders;
public NativeArray<BlittableLogic> Logics => _logics;
public bool HasBuffer => _batchedBuffers != null && _batchedBuffers.Length > 0;
int flipPhase = 0;
public (NativeArray<Vector3> current, NativeArray<Vector3> prev, NativeArray<Vector3> next) FlipBuffer()
{
switch (flipPhase++ % 3)
{
case 0: return (_currentTails, _prevTails, _nextTails);
case 1: return (_nextTails, _currentTails, _prevTails);
case 2: return (_prevTails, _nextTails, _currentTails);
default:
throw new Exception();
}
// dispose が狂う??
// var tmp = _prevTails;
// _currentTails = _nextTails;
// _prevTails = _currentTails;
// _nextTails = tmp;
}
public void Register(FastSpringBoneBuffer buffer)
{
_buffers.AddLast(buffer);
@ -82,7 +114,7 @@ namespace UniGLTF.SpringBoneJobs
var length = _batchedBufferLogicSizes[i];
if (!_batchedBuffers[i].IsDisposed && length > 0)
{
NativeArray<BlittableLogic>.Copy(_logics, logicsIndex, _batchedBuffers[i].Logics, 0, length);
NativeArray<BlittableJointInit>.Copy(_logics, logicsIndex, _batchedBuffers[i].Logics, 0, length);
}
logicsIndex += length;
@ -123,26 +155,29 @@ namespace UniGLTF.SpringBoneJobs
logicsCount += buffer.Logics.Length;
transformsCount += buffer.BlittableTransforms.Length;
}
Profiler.EndSample();
// バッファの構築
Profiler.BeginSample("FastSpringBone.ReconstructBuffers.CreateBuffers");
_logics = new NativeArray<BlittableJointInit>(logicsCount, Allocator.Persistent);
_joints = new NativeArray<BlittableJointSettings>(logicsCount, Allocator.Persistent);
_prevTails = new NativeArray<Vector3>(logicsCount, Allocator.Persistent);
_currentTails = new NativeArray<Vector3>(logicsCount, Allocator.Persistent);
_nextTails = new NativeArray<Vector3>(logicsCount, Allocator.Persistent);
_springs = new NativeArray<BlittableSpring>(springsCount, Allocator.Persistent);
_joints = new NativeArray<BlittableJoint>(logicsCount, Allocator.Persistent);
_logics = new NativeArray<BlittableLogic>(logicsCount, Allocator.Persistent);
_colliders = new NativeArray<BlittableCollider>(collidersCount, Allocator.Persistent);
_transforms = new NativeArray<BlittableTransform>(transformsCount, Allocator.Persistent);
Profiler.EndSample();
Profiler.BeginSample("FastSpringBone.ReconstructBuffers.ScheduleLoadBufferJobs");
var springsOffset = 0;
var collidersOffset = 0;
var logicsOffset = 0;
var transformOffset = 0;
Profiler.BeginSample("FastSpringBone.ReconstructBuffers.ScheduleLoadBufferJobs");
for (var i = 0; i < _batchedBuffers.Length; i++)
{
var buffer = _batchedBuffers[i];
@ -151,10 +186,10 @@ namespace UniGLTF.SpringBoneJobs
handle = new LoadTransformsJob
{
SrcTransforms = buffer.BlittableTransforms,
DestTransforms =
new NativeSlice<BlittableTransform>(_transforms, transformOffset,
DestTransforms = new NativeSlice<BlittableTransform>(_transforms, transformOffset,
buffer.BlittableTransforms.Length)
}.Schedule(buffer.BlittableTransforms.Length, 1, handle);
handle = new LoadSpringsJob
{
SrcSprings = buffer.Springs,
@ -163,18 +198,20 @@ namespace UniGLTF.SpringBoneJobs
LogicsOffset = logicsOffset,
TransformOffset = transformOffset,
}.Schedule(buffer.Springs.Length, 1, handle);
handle = new LoadCollidersJob
{
SrcColliders = buffer.Colliders,
DestColliders =
new NativeSlice<BlittableCollider>(_colliders, collidersOffset, buffer.Colliders.Length)
DestColliders = new NativeSlice<BlittableCollider>(_colliders, collidersOffset, buffer.Colliders.Length)
}.Schedule(buffer.Colliders.Length, 1, handle);
handle = new OffsetLogicsJob
{
SrcLogics = buffer.Logics,
SrcJoints = buffer.Joints,
DestLogics = new NativeSlice<BlittableLogic>(_logics, logicsOffset, buffer.Logics.Length),
DestJoints = new NativeSlice<BlittableJoint>(_joints, logicsOffset, buffer.Logics.Length),
DestLogics = new NativeSlice<BlittableJointInit>(_logics, logicsOffset, buffer.Logics.Length),
DestJoints = new NativeSlice<BlittableJointSettings>(_joints, logicsOffset, buffer.Logics.Length),
}.Schedule(buffer.Logics.Length, 1, handle);
springsOffset += buffer.Springs.Length;
@ -183,6 +220,8 @@ namespace UniGLTF.SpringBoneJobs
transformOffset += buffer.BlittableTransforms.Length;
}
handle = InitCurrentTails(handle);
// TransformAccessArrayの構築と並行してJobを行うため、この時点で走らせておく
JobHandle.ScheduleBatchedJobs();
Profiler.EndSample();
@ -205,14 +244,35 @@ namespace UniGLTF.SpringBoneJobs
return handle;
}
/// <summary>
/// Transform から currentTail を更新。
/// prevTail も同じ内容にする(速度0)。
/// </summary>
/// <param name="handle"></param>
/// <returns></returns>
public JobHandle InitCurrentTails(JobHandle handle)
{
return new InitCurrentTailsJob
{
Logics = Logics,
Transforms = Transforms,
CurrentTails = CurrentTails,
PrevTails = PrevTails,
NextTails = NextTails,
}.Schedule(Logics.Length, 1, handle);
}
private void DisposeAllBuffers()
{
if (_springs.IsCreated) _springs.Dispose();
if (_logics.IsCreated) _logics.Dispose();
if (_joints.IsCreated) _joints.Dispose();
if (_prevTails.IsCreated) _prevTails.Dispose();
if (_currentTails.IsCreated) _currentTails.Dispose();
if (_nextTails.IsCreated) _nextTails.Dispose();
if (_springs.IsCreated) _springs.Dispose();
if (_colliders.IsCreated) _colliders.Dispose();
if (_transforms.IsCreated) _transforms.Dispose();
if (_transformAccessArray.isCreated) _transformAccessArray.Dispose();
if (_colliders.IsCreated) _colliders.Dispose();
if (_logics.IsCreated) _logics.Dispose();
}
public void Dispose()
@ -275,10 +335,10 @@ namespace UniGLTF.SpringBoneJobs
#endif
private struct OffsetLogicsJob : IJobParallelFor
{
[ReadOnly] public NativeSlice<BlittableLogic> SrcLogics;
[ReadOnly] public NativeSlice<BlittableJoint> SrcJoints;
[WriteOnly] public NativeSlice<BlittableLogic> DestLogics;
[WriteOnly] public NativeSlice<BlittableJoint> DestJoints;
[ReadOnly] public NativeSlice<BlittableJointInit> SrcLogics;
[ReadOnly] public NativeSlice<BlittableJointSettings> SrcJoints;
[WriteOnly] public NativeSlice<BlittableJointInit> DestLogics;
[WriteOnly] public NativeSlice<BlittableJointSettings> DestJoints;
public void Execute(int index)
{
@ -286,5 +346,37 @@ namespace UniGLTF.SpringBoneJobs
DestJoints[index] = SrcJoints[index];
}
}
#if ENABLE_SPRINGBONE_BURST
[BurstCompile]
#endif
private struct InitCurrentTailsJob : IJobParallelFor
{
[ReadOnly] public NativeArray<BlittableJointInit> Logics;
[ReadOnly] public NativeArray<BlittableTransform> Transforms;
[WriteOnly] public NativeSlice<Vector3> CurrentTails;
[WriteOnly] public NativeSlice<Vector3> PrevTails;
[WriteOnly] public NativeSlice<Vector3> NextTails;
public void Execute(int jointIndex)
{
var tailIndex = Logics[jointIndex].tailTransformIndex;
if (tailIndex == -1)
{
// tail 無い
var tail = Transforms[Logics[jointIndex].headTransformIndex];
CurrentTails[jointIndex] = tail.position;
PrevTails[jointIndex] = tail.position;
NextTails[jointIndex] = tail.position;
}
else
{
var tail = Transforms[tailIndex];
CurrentTails[jointIndex] = tail.position;
PrevTails[jointIndex] = tail.position;
NextTails[jointIndex] = tail.position;
}
}
}
}
}

View File

@ -31,12 +31,17 @@ namespace UniGLTF.SpringBoneJobs
Transforms = _bufferCombiner.Transforms
}.Schedule(_bufferCombiner.TransformAccessArray, handle);
var (current, prev, next) = _bufferCombiner.FlipBuffer();
handle = new UpdateFastSpringBoneJob
{
Colliders = _bufferCombiner.Colliders,
Joints = _bufferCombiner.Joints,
Logics = _bufferCombiner.Logics,
CurrentTail = current,
PrevTail = prev,
NextTail = next,
Springs = _bufferCombiner.Springs,
Colliders = _bufferCombiner.Colliders,
Transforms = _bufferCombiner.Transforms,
DeltaTime = deltaTime,
}.Schedule(_bufferCombiner.Springs.Length, 1, handle);

View File

@ -16,9 +16,9 @@ namespace UniGLTF.SpringBoneJobs.InputPorts
{
// NOTE: これらはFastSpringBoneBufferCombinerによってバッチングされる
public NativeArray<BlittableSpring> Springs { get; }
public NativeArray<BlittableJoint> Joints { get; }
public NativeArray<BlittableJointSettings> Joints { get; }
public NativeArray<BlittableCollider> Colliders { get; }
public NativeArray<BlittableLogic> Logics { get; }
public NativeArray<BlittableJointInit> Logics { get; }
public NativeArray<BlittableTransform> BlittableTransforms { get; }
public Transform[] Transforms { get; }
public bool IsDisposed { get; private set; }
@ -92,9 +92,9 @@ namespace UniGLTF.SpringBoneJobs.InputPorts
var externalDataPtr = (BlittableExternalData*)_externalData.GetUnsafePtr();
List<BlittableSpring> blittableSprings = new();
List<BlittableJoint> blittableJoints = new();
List<BlittableJointSettings> blittableJoints = new();
List<BlittableCollider> blittableColliders = new();
List<BlittableLogic> blittableLogics = new();
List<BlittableJointInit> blittableLogics = new();
foreach (var spring in springs)
{
var blittableSpring = new BlittableSpring
@ -131,9 +131,9 @@ namespace UniGLTF.SpringBoneJobs.InputPorts
}
Springs = new NativeArray<BlittableSpring>(blittableSprings.ToArray(), Allocator.Persistent);
Joints = new NativeArray<BlittableJoint>(blittableJoints.ToArray(), Allocator.Persistent);
Joints = new NativeArray<BlittableJointSettings>(blittableJoints.ToArray(), Allocator.Persistent);
Colliders = new NativeArray<BlittableCollider>(blittableColliders.ToArray(), Allocator.Persistent);
Logics = new NativeArray<BlittableLogic>(blittableLogics.ToArray(), Allocator.Persistent);
Logics = new NativeArray<BlittableJointInit>(blittableLogics.ToArray(), Allocator.Persistent);
BlittableTransforms = new NativeArray<BlittableTransform>(Transforms.Length, Allocator.Persistent);
Profiler.EndSample();
}
@ -145,15 +145,17 @@ namespace UniGLTF.SpringBoneJobs.InputPorts
/// <param name="spring"></param>
/// <param name="i">joint index</param>
/// <returns></returns>
public static IEnumerable<BlittableLogic> LogicFromTransform(Transform[] Transforms, FastSpringBoneSpring spring)
public static IEnumerable<BlittableJointInit> LogicFromTransform(Transform[] Transforms, FastSpringBoneSpring spring)
{
// vrm-1.0 では末端の joint は tail で処理対象でないのに注意!
for (int i = 0; i < spring.joints.Length - 1; ++i)
{
var joint = spring.joints[i];
var tailJoint = i + 1 < spring.joints.Length ? spring.joints[i + 1] : (FastSpringBoneJoint?)null;
Debug.Assert(i + 1 < spring.joints.Length);
var tailJoint = (i + 1 < spring.joints.Length) ? spring.joints[i + 1] : (FastSpringBoneJoint?)null;
Debug.Assert(tailJoint.HasValue);
var parentJoint = i - 1 >= 0 ? spring.joints[i - 1] : (FastSpringBoneJoint?)null;
var localPosition = Vector3.zero;
Vector3 localPosition;
if (tailJoint.HasValue)
{
localPosition = tailJoint.Value.Transform.localPosition;
@ -178,19 +180,13 @@ namespace UniGLTF.SpringBoneJobs.InputPorts
localPosition.y * scale.y,
localPosition.z * scale.z
);
var worldChildPosition = joint.Transform.TransformPoint(localChildPosition);
var currentTail = spring.center != null
? spring.center.InverseTransformPoint(worldChildPosition)
: worldChildPosition;
var parent = joint.Transform.parent;
yield return new BlittableLogic
yield return new BlittableJointInit
{
headTransformIndex = Array.IndexOf(Transforms, joint.Transform),
parentTransformIndex = Array.IndexOf(Transforms, parent),
currentTail = currentTail,
prevTail = currentTail, // same with currentTail. velocity zero.
tailTransformIndex = Array.IndexOf(Transforms, tailJoint.Value),
localRotation = joint.DefaultLocalRotation,
boneAxis = localChildPosition.normalized,
length = localChildPosition.magnitude
@ -209,16 +205,5 @@ namespace UniGLTF.SpringBoneJobs.InputPorts
Logics.Dispose();
_externalData.Dispose();
}
public void SyncAndZeroVelocity(IReadOnlyList<BlittableLogic> logics)
{
var dst = Logics;
for (int i = 0; i < logics.Count; ++i)
{
var l = logics[i];
l.prevTail = l.currentTail;
dst[i] = l;
}
}
}
}

View File

@ -8,7 +8,7 @@ namespace UniGLTF.SpringBoneJobs.InputPorts
public struct FastSpringBoneJoint
{
public Transform Transform;
public BlittableJoint Joint;
public BlittableJointSettings Joint;
public Quaternion DefaultLocalRotation;
}
}

View File

@ -14,13 +14,18 @@ namespace UniGLTF.SpringBoneJobs
#endif
public struct UpdateFastSpringBoneJob : IJobParallelFor
{
[ReadOnly] public NativeArray<BlittableSpring> Springs;
[ReadOnly] public NativeArray<BlittableJoint> Joints;
[ReadOnly] public NativeArray<BlittableCollider> Colliders;
[NativeDisableParallelForRestriction] public NativeArray<BlittableLogic> Logics;
[NativeDisableParallelForRestriction] public NativeArray<BlittableTransform> Transforms;
[ReadOnly] public NativeArray<BlittableSpring> Springs;
[ReadOnly] public NativeArray<BlittableJointSettings> Joints;
[ReadOnly] public NativeArray<BlittableJointInit> Logics;
[ReadOnly] public NativeArray<Vector3> PrevTail;
[ReadOnly] public NativeArray<Vector3> CurrentTail;
[NativeDisableParallelForRestriction] public NativeArray<Vector3> NextTail;
[ReadOnly] public NativeArray<BlittableCollider> Colliders;
public float DeltaTime;
public unsafe void Execute(int index)
@ -52,11 +57,11 @@ namespace UniGLTF.SpringBoneJobs
}
var currentTail = centerTransform.HasValue
? centerTransform.Value.localToWorldMatrix.MultiplyPoint3x4(logic.currentTail)
: logic.currentTail;
? centerTransform.Value.localToWorldMatrix.MultiplyPoint3x4(CurrentTail[logicIndex])
: CurrentTail[logicIndex];
var prevTail = centerTransform.HasValue
? centerTransform.Value.localToWorldMatrix.MultiplyPoint3x4(logic.prevTail)
: logic.prevTail;
? centerTransform.Value.localToWorldMatrix.MultiplyPoint3x4(PrevTail[logicIndex])
: PrevTail[logicIndex];
var parentRotation = parentTransform?.rotation ?? Quaternion.identity;
@ -108,14 +113,10 @@ namespace UniGLTF.SpringBoneJobs
}
}
logic.prevTail = centerTransform.HasValue
? centerTransform.Value.worldToLocalMatrix.MultiplyPoint3x4(currentTail)
: currentTail;
logic.currentTail = centerTransform.HasValue
NextTail[logicIndex] = centerTransform.HasValue
? centerTransform.Value.worldToLocalMatrix.MultiplyPoint3x4(nextTail)
: nextTail;
//回転を適用
var rotation = parentRotation * logic.localRotation;
headTransform.rotation = Quaternion.FromToRotation(rotation * logic.boneAxis,
@ -157,7 +158,6 @@ namespace UniGLTF.SpringBoneJobs
// SpringBone の結果を Transform に反映しないが logic の更新は継続する。
// 再開したときに暴れない。
}
Logics[logicIndex] = logic;
}
}
@ -174,10 +174,10 @@ namespace UniGLTF.SpringBoneJobs
Vector3 worldTail,
Vector3 worldPosition,
BlittableTransform headTransform,
BlittableJoint joint,
BlittableJointSettings joint,
BlittableCollider collider,
float maxColliderScale,
BlittableLogic logic,
BlittableJointInit logic,
ref Vector3 nextTail)
{
var direction = worldTail - worldPosition;
@ -209,12 +209,12 @@ namespace UniGLTF.SpringBoneJobs
}
private static void ResolveSphereCollision(
BlittableJoint joint,
BlittableJointSettings joint,
BlittableCollider collider,
Vector3 worldPosition,
BlittableTransform headTransform,
float maxColliderScale,
BlittableLogic logic,
BlittableJointInit logic,
ref Vector3 nextTail)
{
var r = joint.radius + collider.radius * maxColliderScale;
@ -229,7 +229,7 @@ namespace UniGLTF.SpringBoneJobs
}
private static void ResolveSphereCollisionInside(
BlittableJoint joint,
BlittableJointSettings joint,
BlittableCollider collider,
BlittableTransform colliderTransform,
ref Vector3 nextTail)
@ -249,7 +249,7 @@ namespace UniGLTF.SpringBoneJobs
}
private static void ResolveCapsuleCollisionInside(
BlittableJoint joint,
BlittableJointSettings joint,
BlittableCollider collider,
BlittableTransform colliderTransform,
ref Vector3 nextTail)
@ -298,7 +298,7 @@ namespace UniGLTF.SpringBoneJobs
/// <param name="colliderTransform">colliderTransform.localToWorldMatrix.MultiplyPoint3x4(collider.offset);</param>
/// <param name="nextTail">result of verlet integration</param>
private static void ResolvePlaneCollision(
BlittableJoint joint,
BlittableJointSettings joint,
BlittableCollider collider,
BlittableTransform colliderTransform,
ref Vector3 nextTail)

View File

@ -87,7 +87,7 @@ namespace VRM.SpringBoneJobs
joints.Add(new FastSpringBoneJoint
{
Transform = joint.transform,
Joint = new BlittableJoint
Joint = new BlittableJointSettings
{
radius = spring.m_hitRadius,
dragForce = spring.m_dragForce,

View File

@ -92,7 +92,7 @@ namespace UniVRM10
.Select(joint => new FastSpringBoneJoint
{
Transform = joint.transform,
Joint = new BlittableJoint
Joint = new BlittableJointSettings
{
radius = joint.m_jointRadius,
dragForce = joint.m_dragForce,
@ -150,15 +150,7 @@ namespace UniVRM10
transform.localRotation = m_initialLocalRotations[i];
}
// 初期状態にしたtransformを使って spring logic を構築
List<BlittableLogic> blittableLogics = new();
foreach (var spring in m_springs)
{
blittableLogics.AddRange(
FastSpringBoneBuffer.LogicFromTransform(m_fastSpringBoneBuffer.Transforms, spring));
}
// DOTS バッファーを更新
m_fastSpringBoneBuffer.SyncAndZeroVelocity(blittableLogics);
// TODO:
}
}
}