diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs index 314a539ab..49a0f602b 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs @@ -27,6 +27,7 @@ namespace UniVRM10 private readonly IReadOnlyDictionary m_defaultTransformStates; private FastSpringBoneBuffer m_fastSpringBoneBuffer; + private BlittableExternalData m_externalData; /// /// Control Rig may be null. @@ -38,6 +39,16 @@ namespace UniVRM10 public Vrm10RuntimeExpression Expression { get; } public Vrm10RuntimeLookAt LookAt { get; } + public Vector3 ExternalForce + { + get => m_externalData.ExternalForce; + set + { + m_externalData.ExternalForce = value; + m_fastSpringBoneBuffer.ExternalData = m_externalData; + } + } + public Vrm10Runtime(Vrm10Instance target, ControlRigGenerationOption controlRigGenerationOption) { m_target = target; @@ -131,7 +142,8 @@ namespace UniVRM10 }, DefaultLocalRotation = GetOrAddDefaultTransformState(joint.transform).LocalRotation, }).ToArray(), - }).ToArray()); + }).ToArray(), + m_externalData); } private TransformState GetOrAddDefaultTransformState(Transform tf) diff --git a/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableExternalData.cs b/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableExternalData.cs new file mode 100644 index 000000000..f55dd5d97 --- /dev/null +++ b/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableExternalData.cs @@ -0,0 +1,12 @@ +using UnityEngine; + +namespace UniVRM10.FastSpringBones.Blittables +{ + /// + /// 外力等の毎フレーム更新されうる外部から与えられる情報 + /// + public struct BlittableExternalData + { + public Vector3 ExternalForce; + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableExternalData.cs.meta b/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableExternalData.cs.meta new file mode 100644 index 000000000..173858d95 --- /dev/null +++ b/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableExternalData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3a5066fc4e264010a58c19b781d477a0 +timeCreated: 1666166451 \ No newline at end of file diff --git a/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableSpring.cs b/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableSpring.cs index 36f6d7401..bd145fa04 100644 --- a/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableSpring.cs +++ b/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableSpring.cs @@ -1,10 +1,10 @@ using System; -using UnityEngine.Serialization; namespace UniVRM10.FastSpringBones.Blittables { /// /// 1本の毛束を表すデータ型 + /// FastSpringBoneではこれを起点として並列化し、処理を行う /// [Serializable] public struct BlittableSpring @@ -13,5 +13,6 @@ namespace UniVRM10.FastSpringBones.Blittables public BlittableSpan logicSpan; public int centerTransformIndex; public int transformIndexOffset; + public unsafe BlittableExternalData* ExternalData; } } \ No newline at end of file diff --git a/Assets/VRM10/Runtime/FastSpringBone/InputPorts/FastSpringBoneBuffer.cs b/Assets/VRM10/Runtime/FastSpringBone/InputPorts/FastSpringBoneBuffer.cs index 5d71e537e..e86452f8c 100644 --- a/Assets/VRM10/Runtime/FastSpringBone/InputPorts/FastSpringBoneBuffer.cs +++ b/Assets/VRM10/Runtime/FastSpringBone/InputPorts/FastSpringBoneBuffer.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine; using UnityEngine.Profiling; using UniVRM10.FastSpringBones.Blittables; @@ -13,6 +14,7 @@ namespace UniVRM10.FastSpringBones.System /// public class FastSpringBoneBuffer : IDisposable { + // NOTE: これらはFastSpringBoneBufferCombinerによってバッチングされる public NativeArray Springs { get; } public NativeArray Joints { get; } public NativeArray Colliders { get; } @@ -21,10 +23,24 @@ namespace UniVRM10.FastSpringBones.System public Transform[] Transforms { get; } public bool IsDisposed { get; private set; } - public FastSpringBoneBuffer(IReadOnlyList springs, bool simulateLastBone = false) + // NOTE: これは更新頻度が高くバッチングが難しいため、ランダムアクセスを許容してメモリへ直接アクセスする + // 生のヒープ領域は扱いにくいので長さ1のNativeArrayで代用 + private NativeArray _externalData; + + public BlittableExternalData ExternalData + { + get => _externalData[0]; + set => _externalData[0] = value; + } + + public unsafe FastSpringBoneBuffer(IReadOnlyList springs, + BlittableExternalData externalData, + bool simulateLastBone = false) { Profiler.BeginSample("FastSpringBone.ConstructBuffers"); + _externalData = new NativeArray(1, Allocator.Persistent); + // Transformの列挙 Profiler.BeginSample("FastSpringBone.ConstructBuffers.ConstructTransformBuffer"); var transformHashSet = new HashSet(); @@ -35,6 +51,7 @@ namespace UniVRM10.FastSpringBones.System transformHashSet.Add(joint.Transform); if (joint.Transform.parent != null) transformHashSet.Add(joint.Transform.parent); } + foreach (var collider in spring.colliders) { transformHashSet.Add(collider.Transform); @@ -42,6 +59,7 @@ namespace UniVRM10.FastSpringBones.System if (spring.center != null) transformHashSet.Add(spring.center); } + var transforms = transformHashSet.ToArray(); var transformIndexDictionary = transforms.Select((trs, index) => (trs, index)) .ToDictionary(tuple => tuple.trs, tuple => tuple.index); @@ -68,7 +86,8 @@ namespace UniVRM10.FastSpringBones.System startIndex = blittableJoints.Count, count = simulateLastBone ? spring.joints.Length : spring.joints.Length - 1, }, - centerTransformIndex = spring.center ? transformIndexDictionary[spring.center] : -1 + centerTransformIndex = spring.center ? transformIndexDictionary[spring.center] : -1, + ExternalData = (BlittableExternalData*) _externalData.GetUnsafePtr() }; blittableSprings.Add(blittableSpring); @@ -78,17 +97,18 @@ namespace UniVRM10.FastSpringBones.System blittable.transformIndex = transformIndexDictionary[collider.Transform]; return blittable; })); - blittableJoints.AddRange(spring.joints.Take(simulateLastBone ? spring.joints.Length : spring.joints.Length - 1).Select(joint => - { - var blittable = joint.Joint; - return blittable; - })); + blittableJoints.AddRange(spring.joints + .Take(simulateLastBone ? spring.joints.Length : spring.joints.Length - 1).Select(joint => + { + var blittable = joint.Joint; + return blittable; + })); for (var i = 0; i < (simulateLastBone ? spring.joints.Length : spring.joints.Length - 1); ++i) { var joint = spring.joints[i]; - var tailJoint = i + 1 < spring.joints.Length ? spring.joints[i + 1] : (FastSpringBoneJoint?)null; - var parentJoint = i - 1 >= 0 ? spring.joints[i - 1] : (FastSpringBoneJoint?)null; + var tailJoint = i + 1 < spring.joints.Length ? spring.joints[i + 1] : (FastSpringBoneJoint?) null; + var parentJoint = i - 1 >= 0 ? spring.joints[i - 1] : (FastSpringBoneJoint?) null; var localPosition = Vector3.zero; if (tailJoint.HasValue) { @@ -99,13 +119,15 @@ namespace UniVRM10.FastSpringBones.System if (parentJoint.HasValue) { var delta = joint.Transform.position - parentJoint.Value.Transform.position; - localPosition = joint.Transform.worldToLocalMatrix.MultiplyPoint(joint.Transform.position + delta); + localPosition = + joint.Transform.worldToLocalMatrix.MultiplyPoint(joint.Transform.position + delta); } else { localPosition = Vector3.down; } } + var scale = tailJoint.HasValue ? tailJoint.Value.Transform.lossyScale : joint.Transform.lossyScale; var localChildPosition = new Vector3( @@ -131,6 +153,7 @@ namespace UniVRM10.FastSpringBones.System }); } } + Profiler.EndSample(); // 各種bufferの初期化 @@ -157,6 +180,7 @@ namespace UniVRM10.FastSpringBones.System BlittableTransforms.Dispose(); Colliders.Dispose(); Logics.Dispose(); + _externalData.Dispose(); } } } \ No newline at end of file diff --git a/Assets/VRM10/Runtime/FastSpringBone/System/FastSpringBoneBufferCombiner.cs b/Assets/VRM10/Runtime/FastSpringBone/System/FastSpringBoneBufferCombiner.cs index b1b513c69..ce2fb53a0 100644 --- a/Assets/VRM10/Runtime/FastSpringBone/System/FastSpringBoneBufferCombiner.cs +++ b/Assets/VRM10/Runtime/FastSpringBone/System/FastSpringBoneBufferCombiner.cs @@ -38,6 +38,8 @@ namespace UniVRM10.FastSpringBones.System public NativeArray Colliders => _colliders; public NativeArray Logics => _logics; + public bool HasBuffer => _batchedBuffers != null && _batchedBuffers.Length > 0; + public void Register(FastSpringBoneBuffer buffer) { _buffers.AddLast(buffer); @@ -81,6 +83,7 @@ namespace UniVRM10.FastSpringBones.System { NativeArray.Copy(_logics, logicsIndex, _batchedBuffers[i].Logics, 0, length); } + logicsIndex += length; } } @@ -107,7 +110,7 @@ namespace UniVRM10.FastSpringBones.System Profiler.BeginSample("FastSpringBone.ReconstructBuffers.CopyToBatchedBuffers"); _batchedBuffers = _buffers.ToArray(); - _batchedBufferLogicSizes = _buffers.Select(buffer => buffer.Logics.Length).ToArray(); + _batchedBufferLogicSizes = _batchedBuffers.Select(buffer => buffer.Logics.Length).ToArray(); Profiler.EndSample(); // バッファを数える @@ -139,8 +142,10 @@ namespace UniVRM10.FastSpringBones.System var transformOffset = 0; Profiler.BeginSample("FastSpringBone.ReconstructBuffers.ScheduleLoadBufferJobs"); - foreach (var buffer in _buffers) + for (var i = 0; i < _batchedBuffers.Length; i++) { + var buffer = _batchedBuffers[i]; + // バッファの読み込みをスケジュール handle = new LoadTransformsJob { @@ -155,15 +160,15 @@ namespace UniVRM10.FastSpringBones.System DestSprings = new NativeSlice(_springs, springsOffset, buffer.Springs.Length), CollidersOffset = collidersOffset, LogicsOffset = logicsOffset, - TransformOffset = transformOffset + TransformOffset = transformOffset, }.Schedule(buffer.Springs.Length, 1, handle); - handle = new LoadCollidersJob() + handle = new LoadCollidersJob { SrcColliders = buffer.Colliders, DestColliders = new NativeSlice(_colliders, collidersOffset, buffer.Colliders.Length) }.Schedule(buffer.Colliders.Length, 1, handle); - handle = new OffsetLogicsJob() + handle = new OffsetLogicsJob { SrcLogics = buffer.Logics, SrcJoints = buffer.Joints, @@ -185,7 +190,7 @@ namespace UniVRM10.FastSpringBones.System Profiler.BeginSample("FastSpringBone.ReconstructBuffers.LoadTransformAccessArray"); var transforms = new Transform[transformsCount]; var transformAccessArrayOffset = 0; - foreach (var buffer in _buffers) + foreach (var buffer in _batchedBuffers) { Array.Copy(buffer.Transforms, 0, transforms, transformAccessArrayOffset, buffer.Transforms.Length); transformAccessArrayOffset += buffer.BlittableTransforms.Length; @@ -260,7 +265,7 @@ namespace UniVRM10.FastSpringBones.System public void Execute(int index) { - DestColliders[index] = SrcColliders[index]; + DestColliders[index] = SrcColliders[index]; } } @@ -281,4 +286,4 @@ namespace UniVRM10.FastSpringBones.System } } } -} +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/FastSpringBone/System/FastSpringBoneScheduler.cs b/Assets/VRM10/Runtime/FastSpringBone/System/FastSpringBoneScheduler.cs index 064d37360..c5079cfc0 100644 --- a/Assets/VRM10/Runtime/FastSpringBone/System/FastSpringBoneScheduler.cs +++ b/Assets/VRM10/Runtime/FastSpringBone/System/FastSpringBoneScheduler.cs @@ -8,7 +8,6 @@ namespace UniVRM10.FastSpringBones.System public sealed class FastSpringBoneScheduler : IDisposable { private readonly FastSpringBoneBufferCombiner _bufferCombiner; - public FastSpringBoneScheduler(FastSpringBoneBufferCombiner bufferCombiner) { _bufferCombiner = bufferCombiner; @@ -18,6 +17,10 @@ namespace UniVRM10.FastSpringBones.System { var handle = default(JobHandle); handle = _bufferCombiner.ReconstructIfDirty(handle); + if (!_bufferCombiner.HasBuffer) + { + return handle; + } handle = new PullTransformJob { diff --git a/Assets/VRM10/Runtime/FastSpringBone/System/UpdateFastSpringBoneJob.cs b/Assets/VRM10/Runtime/FastSpringBone/System/UpdateFastSpringBoneJob.cs index 6c2315861..7e7352a16 100644 --- a/Assets/VRM10/Runtime/FastSpringBone/System/UpdateFastSpringBoneJob.cs +++ b/Assets/VRM10/Runtime/FastSpringBone/System/UpdateFastSpringBoneJob.cs @@ -22,8 +22,8 @@ namespace UniVRM10.FastSpringBones.System [NativeDisableParallelForRestriction] public NativeArray Transforms; public float DeltaTime; - - public void Execute(int index) + + public unsafe void Execute(int index) { var spring = Springs[index]; var transformIndexOffset = spring.transformIndexOffset; @@ -43,7 +43,6 @@ namespace UniVRM10.FastSpringBones.System ? Transforms[spring.centerTransformIndex + transformIndexOffset] : (BlittableTransform?)null; - // 親があったら、親に依存するTransformを再計算 if (parentTransform.HasValue) { @@ -62,7 +61,7 @@ namespace UniVRM10.FastSpringBones.System var parentRotation = parentTransform?.rotation ?? Quaternion.identity; // verlet積分で次の位置を計算 - var external = joint.gravityDir * joint.gravityPower * DeltaTime; + var external = (joint.gravityDir * joint.gravityPower + spring.ExternalData->ExternalForce) * DeltaTime; var nextTail = currentTail + (currentTail - prevTail) * (1.0f - joint.dragForce) // 前フレームの移動を継続する(減衰もあるよ) + parentRotation * logic.localRotation * logic.boneAxis *