Skip to content

Commit

Permalink
Merge pull request #2507 from ousttrue/fix/spring_reconstruct_logic
Browse files Browse the repository at this point in the history
[fastspringbone] InitCurrentTailsJob use spring.transformIndexOffset
  • Loading branch information
ousttrue authored Nov 19, 2024
2 parents fbe9342 + 0c6b99d commit 8bbe080
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 128 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using UnityEngine.Profiling;
using UniGLTF.SpringBoneJobs.InputPorts;
using UnityEngine;
using Unity.Collections;
using System.Linq;

namespace UniGLTF.SpringBoneJobs
{
Expand All @@ -20,34 +22,63 @@ public sealed class FastSpringBoneBufferCombiner : IDisposable
private FastSpringBoneCombinedBuffer _combinedBuffer;
public FastSpringBoneCombinedBuffer Combined => _combinedBuffer;
private readonly LinkedList<FastSpringBoneBuffer> _buffers = new LinkedList<FastSpringBoneBuffer>();
private bool _isDirty;
private Queue<(bool isAdd, FastSpringBoneBuffer buffer)> _request = new();
public bool HasBuffer => _buffers.Count > 0 && _combinedBuffer != null;

public void Register(FastSpringBoneBuffer buffer)
{
_buffers.AddLast(buffer);
_isDirty = true;
_request.Enqueue((true, buffer));
}

public void Unregister(FastSpringBoneBuffer buffer)
{
_buffers.Remove(buffer);
_isDirty = true;
_request.Enqueue((false, buffer));
}

/// <summary>
/// 変更があったならばバッファを再構築する
/// </summary>
public JobHandle ReconstructIfDirty(JobHandle handle)
{
if (_isDirty)
if (_request.Count == 0)
{
var result = ReconstructBuffers(handle);
_isDirty = false;
return result;
return handle;
}

return handle;
if (_combinedBuffer is FastSpringBoneCombinedBuffer combined)
{
// index が変わる前に シミュレーションの状態を保存する。
// 状態の保存場所が BlittableJoint から CurrentTails に移動しているのでここでやる。
var logicsIndex = 0;
foreach (var buffer in _buffers)
{
if (_request.Any(x => !x.isAdd && x.buffer == buffer))
{
// 削除するので skip
continue;
}
buffer.BackupCurrentTails(combined.CurrentTails, combined.NextTails, logicsIndex);
logicsIndex += buffer.Logics.Length;
}
}

// buffer 増減
while (_request.Count > 0)
{
var (isAdd, buffer) = _request.Dequeue();
if (isAdd)
{
// 速度 0 にする
_buffers.AddLast(buffer);
}
else
{
_buffers.Remove(buffer);
}
}

// 再構築
return ReconstructBuffers(handle);
}

/// <summary>
Expand All @@ -60,10 +91,6 @@ private JobHandle ReconstructBuffers(JobHandle handle)
Profiler.BeginSample("FastSpringBone.ReconstructBuffers.DisposeBuffers");
if (_combinedBuffer is FastSpringBoneCombinedBuffer combined)
{
Profiler.BeginSample("FastSpringBone.ReconstructBuffers.SaveToSourceBuffer");
combined.SaveToSourceBuffer();
Profiler.EndSample();

// TODO: Dispose せずに再利用?
combined.Dispose();
}
Expand Down
170 changes: 76 additions & 94 deletions Assets/UniGLTF/Runtime/SpringBoneJobs/FastSpringBoneConbinedBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ internal static JobHandle Create(JobHandle handle,
springsCount += buffer.Springs.Length;
collidersCount += buffer.Colliders.Length;
logicsCount += buffer.Logics.Length;
transformsCount += buffer.BlittableTransforms.Length;
transformsCount += buffer.Transforms.Length;
}
Profiler.EndSample();

Expand All @@ -112,6 +112,24 @@ internal static JobHandle Create(JobHandle handle,

private JobHandle Batching(JobHandle handle)
{
// TransformAccessArrayの構築
Profiler.BeginSample("FastSpringBone.ReconstructBuffers.LoadTransformAccessArray");
var transforms = new Transform[_transforms.Length];
var transformAccessArrayOffset = 0;
foreach (var buffer in _batchedBuffers)
{
Array.Copy(buffer.Transforms, 0, transforms, transformAccessArrayOffset, buffer.Transforms.Length);
transformAccessArrayOffset += buffer.Transforms.Length;
}
_transformAccessArray = new TransformAccessArray(transforms);
Profiler.EndSample();

// Transforms を更新。後続の InitCurrentTails で使う
handle = new PullTransformJob
{
Transforms = Transforms
}.Schedule(TransformAccessArray, handle);

Profiler.BeginSample("FastSpringBone.ReconstructBuffers.ScheduleLoadBufferJobs");
var springsOffset = 0;
var collidersOffset = 0;
Expand All @@ -129,13 +147,8 @@ private JobHandle Batching(JobHandle handle)
_jointMap.Add(buffer.Transforms[head], logicsOffset + j);
}

// バッファの読み込みをスケジュール
handle = new LoadTransformsJob
{
SrcTransforms = buffer.BlittableTransforms,
DestTransforms = new NativeSlice<BlittableTransform>(_transforms, transformOffset,
buffer.BlittableTransforms.Length)
}.Schedule(buffer.BlittableTransforms.Length, 1, handle);
// 速度の維持
buffer.RestoreCurrentTails(_currentTails, _nextTails, logicsOffset);

handle = new LoadSpringsJob
{
Expand Down Expand Up @@ -165,26 +178,10 @@ private JobHandle Batching(JobHandle handle)
springsOffset += buffer.Springs.Length;
collidersOffset += buffer.Colliders.Length;
logicsOffset += buffer.Logics.Length;
transformOffset += buffer.BlittableTransforms.Length;
transformOffset += buffer.Transforms.Length;
}

handle = InitCurrentTails(handle);

// TransformAccessArrayの構築と並行してJobを行うため、この時点で走らせておく
JobHandle.ScheduleBatchedJobs();
Profiler.EndSample();

// TransformAccessArrayの構築
Profiler.BeginSample("FastSpringBone.ReconstructBuffers.LoadTransformAccessArray");
var transforms = new Transform[_transforms.Length];
var transformAccessArrayOffset = 0;
foreach (var buffer in _batchedBuffers)
{
Array.Copy(buffer.Transforms, 0, transforms, transformAccessArrayOffset, buffer.Transforms.Length);
transformAccessArrayOffset += buffer.BlittableTransforms.Length;
}

_transformAccessArray = new TransformAccessArray(transforms);
Profiler.EndSample();

return handle;
Expand All @@ -208,25 +205,6 @@ public void Dispose()
if (_transformAccessArray.isCreated) _transformAccessArray.Dispose();
}

/// <summary>
/// バッチングされたバッファから、個々のバッファへと値を戻す
/// Logics to _batchedBuffers[].Logics
/// バッファの再構築前にこの処理を行わないと、揺れの状態がリセットされてしまい、不自然な挙動になる
/// </summary>
internal void SaveToSourceBuffer()
{
var logicsIndex = 0;
for (var i = 0; i < _batchedBuffers.Length; ++i)
{
var length = _batchedBufferLogicSizes[i];
if (!_batchedBuffers[i].IsDisposed && length > 0)
{
NativeArray<BlittableJointImmutable>.Copy(Logics, logicsIndex, _batchedBuffers[i].Logics, 0, length);
}
logicsIndex += length;
}
}

public void FlipBuffer()
{
var tmp = _prevTails;
Expand All @@ -251,23 +229,6 @@ public void SetModelLevel(Transform model, BlittableModelLevel modelSetting)
}
}

#if ENABLE_SPRINGBONE_BURST
[BurstCompile]
#endif
/// <summary>
///
/// </summary>
private struct LoadTransformsJob : IJobParallelFor
{
[ReadOnly] public NativeArray<BlittableTransform> SrcTransforms;
[WriteOnly] public NativeSlice<BlittableTransform> DestTransforms;

public void Execute(int index)
{
DestTransforms[index] = SrcTransforms[index];
}
}

#if ENABLE_SPRINGBONE_BURST
[BurstCompile]
#endif
Expand Down Expand Up @@ -328,49 +289,69 @@ public void Execute(int index)
#endif
private struct InitCurrentTailsJob : IJobParallelFor
{
[ReadOnly] public NativeArray<BlittableSpring> Springs;

[ReadOnly] public NativeArray<BlittableJointImmutable> Logics;
[ReadOnly] public NativeArray<BlittableTransform> Transforms;
[WriteOnly] public NativeSlice<Vector3> CurrentTails;
[WriteOnly] public NativeSlice<Vector3> PrevTails;
[WriteOnly] public NativeSlice<Vector3> NextTails;
[NativeDisableParallelForRestriction] public NativeSlice<Vector3> CurrentTails;
[NativeDisableParallelForRestriction] public NativeSlice<Vector3> PrevTails;
[NativeDisableParallelForRestriction] public NativeSlice<Vector3> NextTails;

public void Execute(int jointIndex)
public void Execute(int springIndex)
{
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 spring = Springs[springIndex];
for (int jointIndex = spring.logicSpan.startIndex; jointIndex < spring.logicSpan.EndIndex; ++jointIndex)
{
var tail = Transforms[tailIndex];
CurrentTails[jointIndex] = tail.position;
PrevTails[jointIndex] = tail.position;
NextTails[jointIndex] = tail.position;
if (float.IsNaN(CurrentTails[jointIndex].x))
{
// Transsform の現状を使う。velocity を zero にする
int tailIndex;
if (Logics[jointIndex].tailTransformIndex == -1)
{
// tail 無い
tailIndex = spring.transformIndexOffset + Logics[jointIndex].headTransformIndex;
}
else
{
tailIndex = spring.transformIndexOffset + Logics[jointIndex].tailTransformIndex;
}

var tail = Transforms[tailIndex];
CurrentTails[jointIndex] = tail.position;
PrevTails[jointIndex] = tail.position;
NextTails[jointIndex] = tail.position;
}
}
}
}

/// <summary>
/// Transform から currentTail を更新。
/// prevTail も同じ内容にする(速度0)。
/// <summary>
/// # CurrentTails[i] == NAN
///
/// Transforms から Current, Prev, Next を代入する。
/// 速度 0 で初期化することになる。
///
/// # CurrentTails[i] != NAN
///
/// 本処理はスキップされて Current, Next の利用が継続されます。
///
/// # NAN
///
/// Batching 関数内の FastSpringBoneBuffer.RestoreCurrentTails にて backup の Current, Next が無かったときに
/// 目印として NAN が代入されます。
/// </summary>
/// <param name="handle"></param>
/// <returns></returns>
public JobHandle InitCurrentTails(JobHandle handle)
{
return new InitCurrentTailsJob
{
Springs = Springs,

Logics = Logics,
Transforms = Transforms,
CurrentTails = CurrentTails,
PrevTails = PrevTails,
NextTails = NextTails,
}.Schedule(Logics.Length, 1, handle);
}.Schedule(Springs.Length, 1, handle);
}

public void InitializeJointsLocalRotation(FastSpringBoneBuffer buffer)
Expand All @@ -379,9 +360,9 @@ public void InitializeJointsLocalRotation(FastSpringBoneBuffer buffer)
for (var i = 0; i < _batchedBuffers.Length; ++i)
{
var length = _batchedBufferLogicSizes[i];
Debug.Assert(length == buffer.Logics.Length);
if (_batchedBuffers[i] == buffer)
{
Debug.Assert(length == buffer.Logics.Length);
for (var j = 0; j < length; ++j)
{
var logic = buffer.Logics[j];
Expand All @@ -401,22 +382,23 @@ public void InitializeJointsLocalRotation(FastSpringBoneBuffer buffer)

public void DrawGizmos()
{
foreach (var collider in _colliders)
{
collider.DrawGizmo(_transforms[collider.transformIndex]);
}

foreach (var spring in _springs)
{
for (int i = spring.colliderSpan.startIndex; i < spring.colliderSpan.EndIndex; ++i)
{
var collider = _colliders[i];
collider.DrawGizmo(_transforms[spring.transformIndexOffset + collider.transformIndex]);
}

for (int i = spring.logicSpan.startIndex; i < spring.logicSpan.EndIndex; ++i)
{
var joint = _logics[i];
joint.DrawGizmo(_transforms[joint.tailTransformIndex], _joints[i]);
joint.DrawGizmo(_transforms[spring.transformIndexOffset + joint.tailTransformIndex], _joints[i]);

Gizmos.matrix = Matrix4x4.identity;
Gizmos.DrawLine(
_transforms[joint.tailTransformIndex].position,
_transforms[joint.headTransformIndex].position);
_transforms[spring.transformIndexOffset + joint.tailTransformIndex].position,
_transforms[spring.transformIndexOffset + joint.headTransformIndex].position);
}
}
}
Expand Down
Loading

0 comments on commit 8bbe080

Please sign in to comment.