From 1824d2a3b7285742ddb317ec53a838dbb461dda3 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Wed, 20 Nov 2024 14:09:37 +0900 Subject: [PATCH] basic springbone --- .../RotateParticleRuntimeProvider.cs | 2 +- .../RotateParticle/Runtime/Components/Warp.cs | 43 +-- .../Runtime/Jobs/RotateParticleJobSystem.cs | 267 +++++++++++++----- 3 files changed, 212 insertions(+), 100 deletions(-) diff --git a/Assets/VRM10_Samples/ClothSample/RotateParticle/Runtime/Components/RotateParticleRuntimeProvider.cs b/Assets/VRM10_Samples/ClothSample/RotateParticle/Runtime/Components/RotateParticleRuntimeProvider.cs index 99d3d8cf4..4acceea41 100644 --- a/Assets/VRM10_Samples/ClothSample/RotateParticle/Runtime/Components/RotateParticleRuntimeProvider.cs +++ b/Assets/VRM10_Samples/ClothSample/RotateParticle/Runtime/Components/RotateParticleRuntimeProvider.cs @@ -69,7 +69,7 @@ namespace RotateParticle.Components DragForce = joint.m_dragForce, GravityDir = joint.m_gravityDir, GravityPower = joint.m_gravityPower, - StiffnessForce = joint.m_stiffnessForce, + Stiffness = joint.m_stiffnessForce, }; if (i == 0) { diff --git a/Assets/VRM10_Samples/ClothSample/RotateParticle/Runtime/Components/Warp.cs b/Assets/VRM10_Samples/ClothSample/RotateParticle/Runtime/Components/Warp.cs index f83bfd4ae..5a3d09599 100644 --- a/Assets/VRM10_Samples/ClothSample/RotateParticle/Runtime/Components/Warp.cs +++ b/Assets/VRM10_Samples/ClothSample/RotateParticle/Runtime/Components/Warp.cs @@ -11,44 +11,31 @@ namespace RotateParticle.Components public class Warp : MonoBehaviour { [Serializable] - public class ParticleSettings : IEquatable + public struct ParticleSettings { [SerializeField] - public float StiffnessForce = 1.0f; + public float Stiffness; [SerializeField] - public float GravityPower = 0; + public float GravityPower; [SerializeField] - public Vector3 GravityDir = new Vector3(0, -1.0f, 0); + public Vector3 GravityDir; [SerializeField, Range(0, 1)] - public float DragForce = 0.4f; + public float DragForce; [SerializeField] - public float HitRadius = 0.02f; + public float HitRadius; - - public override bool Equals(object obj) // Object.Equals(Object)のオーバーライド + public static ParticleSettings Default => new ParticleSettings { - return Equals(obj as ParticleSettings); - } - - public bool Equals(ParticleSettings other) - { - return - StiffnessForce == other.StiffnessForce - && GravityPower == other.GravityPower - && GravityDir == other.GravityDir - && DragForce == other.DragForce - && HitRadius == other.HitRadius - ; - } - - public override int GetHashCode() - { - return GravityDir.GetHashCode(); - } + Stiffness = 1.0f, + GravityPower = 0, + GravityDir = new Vector3(0, -1.0f, 0), + DragForce = 0.4f, + HitRadius = 0.02f, + }; } /// @@ -58,7 +45,7 @@ namespace RotateParticle.Components public class Particle { public bool useInheritSettings = true; - public ParticleSettings OverrideSettings = new(); + public ParticleSettings OverrideSettings = ParticleSettings.Default; public Transform Transform; public ParticleSettings GetSettings(ParticleSettings baseSettings) @@ -68,7 +55,7 @@ namespace RotateParticle.Components } [SerializeField] - public ParticleSettings BaseSettings = new(); + public ParticleSettings BaseSettings = ParticleSettings.Default; /// /// null のときは world root ではなく model root で処理 diff --git a/Assets/VRM10_Samples/ClothSample/RotateParticle/Runtime/Jobs/RotateParticleJobSystem.cs b/Assets/VRM10_Samples/ClothSample/RotateParticle/Runtime/Jobs/RotateParticleJobSystem.cs index e69ae7d84..fa99ecb7d 100644 --- a/Assets/VRM10_Samples/ClothSample/RotateParticle/Runtime/Jobs/RotateParticleJobSystem.cs +++ b/Assets/VRM10_Samples/ClothSample/RotateParticle/Runtime/Jobs/RotateParticleJobSystem.cs @@ -12,30 +12,57 @@ using UniVRM10; namespace RotateParticle.Jobs { + public enum TransformType + { + Center, + WarpRootParent, + WarpRoot, + Particle, + } + + public static class TransformTypeExtensions + { + public static bool PositionInput(this TransformType t) + { + return t == TransformType.WarpRoot; + } + + public static bool Movable(this TransformType t) + { + return t == TransformType.Particle; + } + + public static bool Writable(this TransformType t) + { + switch (t) + { + case TransformType.WarpRoot: + case TransformType.Particle: + return true; + default: + return false; + } + } + } + public class RotateParticleJobSystem : IRotateParticleSystem { Vrm10Instance _vrm; - public enum TransformType - { - WarpRoot, - Particle, - Center, - } - - public struct TransformIndo + public struct TransformInfo { public TransformType TransformType; - public int? ParentIndex; + public int ParentIndex; + public Quaternion InitLocalRotation; public Vector3 InitLocalPosition; - // DragForce: 1 で即時停止 - public float DragForce; + public Warp.ParticleSettings Settings; } public struct TransformData { public Matrix4x4 ToWorld; public Vector3 Position => ToWorld.GetPosition(); + public Quaternion Rotation => ToWorld.rotation; public Matrix4x4 ToLocal; public TransformData(TransformAccess t) @@ -50,27 +77,55 @@ namespace RotateParticle.Jobs } } - // [Input] + public struct WarpInfo + { + public int StartIndex; + public int EndIndex; + } + List _transforms; - int GetTransformIndex(Transform t, - TransformIndo info, - List infos, + (int index, bool isNew) GetTransformIndex(Transform t, + TransformInfo info, + List infos, List positions) { - if (t == null) return -1; + Debug.Assert(t != null); var i = _transforms.IndexOf(t); - if (i == -1) + if (i != -1) { - i = _transforms.Count; - _transforms.Add(t); - infos.Add(info); - positions.Add(t.position); + return (i, false); } - return i; + + i = _transforms.Count; + _transforms.Add(t); + infos.Add(info); + positions.Add(t.position); + return (i, true); } + NativeArray _warps; + TransformAccessArray _transformAccessArray; NativeArray _inputData; + NativeArray _info; + NativeArray _currentPositions; + NativeArray _prevPositions; + NativeArray _nextPositions; + NativeArray _nextRotations; + + void IDisposable.Dispose() + { + if (_warps.IsCreated) _warps.Dispose(); + if (_transformAccessArray.isCreated) _transformAccessArray.Dispose(); + if (_inputData.IsCreated) _inputData.Dispose(); + if (_info.IsCreated) _info.Dispose(); + if (_currentPositions.IsCreated) _currentPositions.Dispose(); + if (_prevPositions.IsCreated) _prevPositions.Dispose(); + if (_nextPositions.IsCreated) _nextPositions.Dispose(); + if (_nextRotations.IsCreated) _nextRotations.Dispose(); + } + + // [Input] public struct InputTransformJob : IJobParallelForTransform { [WriteOnly] public NativeArray InputData; @@ -80,103 +135,161 @@ namespace RotateParticle.Jobs } } - // [Output] - NativeArray _nextPositions; - public struct OutputTransformJob : IJobParallelForTransform - { - [ReadOnly] public NativeArray Info; - [ReadOnly] public NativeArray NextPositions; - public void Execute(int index, TransformAccess transform) - { - var info = Info[index]; - if (info.TransformType == TransformType.Particle) - { - transform.position = NextPositions[index]; - } - } - } - - NativeArray _info; - - // [Init/State] - // 初期化時に値を作って毎フレーム更新していく - // output と同じ長さ index - NativeArray _currentPositions; - NativeArray _prevPositions; public struct VerletJob : IJobParallelFor { public FrameInfo Frame; - [ReadOnly] public NativeArray Info; - [ReadOnly] public NativeArray Current; - [ReadOnly] public NativeArray Prev; - [WriteOnly] public NativeArray Next; + [ReadOnly] public NativeArray Info; + [ReadOnly] public NativeArray CurrentTransforms; + [ReadOnly] public NativeArray CurrentPositions; + [ReadOnly] public NativeArray PrevPositions; + [WriteOnly] public NativeArray NextPositions; + [WriteOnly] public NativeArray NextRotations; public void Execute(int index) { var particle = Info[index]; - if (particle.ParentIndex.HasValue) + if (particle.TransformType.Movable()) { - var velocity = Current[index] - Prev[index]; - velocity *= (1 - particle.DragForce); - var newPosition = Current[index] + velocity + Frame.Force * Frame.SqDeltaTime; + var parentIndex = particle.ParentIndex; + var parentPosition = CurrentPositions[parentIndex]; + var parent = Info[parentIndex]; + var parentParentRotation = CurrentTransforms[parent.ParentIndex].Rotation; - var parentIndex = particle.ParentIndex.Value; - var parentPosition = Current[parentIndex]; + var newPosition = CurrentPositions[index] + + (CurrentPositions[index] - PrevPositions[index]) * (1.0f - particle.Settings.DragForce) + + parentParentRotation * parent.InitLocalRotation * particle.InitLocalPosition * + particle.Settings.Stiffness * Frame.DeltaTime // 親の回転による子ボーンの移動目標 + + Frame.Force * Frame.SqDeltaTime + ; // 位置を長さで拘束 newPosition = parentPosition + (newPosition - parentPosition).normalized * particle.InitLocalPosition.magnitude; - Next[index] = newPosition; + NextPositions[index] = newPosition; } else { // kinematic - Next[index] = Current[index]; + NextPositions[index] = CurrentPositions[index]; + } + + NextRotations[index] = CurrentTransforms[index].Rotation; + } + } + + public struct ApplyRotationJob : IJobParallelFor + { + [ReadOnly] public NativeArray Warps; + [ReadOnly] public NativeArray Info; + [ReadOnly] public NativeArray CurrentTransforms; + [ReadOnly] public NativeArray NextPositions; + [NativeDisableParallelForRestriction] public NativeArray NextRotations; + public void Execute(int index) + { + // warp 一本を親から子に再帰的に実行して回転を確定させる + var warp = Warps[index]; + for (int i = warp.StartIndex; i < warp.EndIndex - 1; ++i) + { + //回転を適用 + var p = Info[i]; + var rotation = NextRotations[p.ParentIndex] * Info[i].InitLocalRotation; + NextRotations[i] = Quaternion.FromToRotation( + rotation * Info[i + 1].InitLocalPosition, + NextPositions[i + 1] - NextPositions[i]) * rotation; } } } - void IDisposable.Dispose() + // [Output] + public struct OutputTransformJob : IJobParallelForTransform { + [ReadOnly] public NativeArray Info; + [ReadOnly] public NativeArray NextRotations; + public void Execute(int index, TransformAccess transform) + { + var info = Info[index]; + if (info.TransformType.Writable()) + { + transform.rotation = NextRotations[index]; + } + } } async Task IRotateParticleSystem.InitializeAsync(Vrm10Instance vrm, IAwaitCaller awaitCaller) { _vrm = vrm; _transforms = new(); - List info = new(); + List info = new(); List positions = new(); + List warps = new(); var warpSrcs = vrm.GetComponentsInChildren(); for (int warpIndex = 0; warpIndex < warpSrcs.Length; ++warpIndex) { var warp = warpSrcs[warpIndex]; - var centerTransformIndex = GetTransformIndex(warp.Center, new TransformIndo { TransformType = TransformType.Center }, info, positions); - var warpRootTransformIndex = GetTransformIndex(warp.transform, new TransformIndo { TransformType = TransformType.WarpRoot }, info, positions); - Debug.Assert(warpRootTransformIndex != -1); + var start = _transforms.Count; - var parentIndex = warpRootTransformIndex; + if (warp.Center != null) + { + GetTransformIndex(warp.Center, new TransformInfo + { + TransformType = TransformType.Center + }, info, positions); + start += 1; + } + + var warpRootParentTransformIndex = GetTransformIndex(warp.transform.parent, new TransformInfo + { + TransformType = TransformType.WarpRootParent + }, info, positions); + Debug.Assert(warpRootParentTransformIndex.index != -1); + if (warpRootParentTransformIndex.isNew) + { + start += 1; + } + + var warpRootTransformIndex = GetTransformIndex(warp.transform, new TransformInfo + { + TransformType = TransformType.WarpRoot, + ParentIndex = warpRootParentTransformIndex.index, + InitLocalPosition = vrm.DefaultTransformStates[warp.transform].LocalPosition, + InitLocalRotation = vrm.DefaultTransformStates[warp.transform].LocalRotation, + }, info, positions); + Debug.Assert(warpRootTransformIndex.index != -1); + Debug.Assert(warpRootTransformIndex.isNew); + + var parentIndex = warpRootTransformIndex.index; foreach (var particle in warp.Particles) { if (particle != null && particle.Transform != null) { - var outputParticleTransformIndex = GetTransformIndex(particle.Transform, new TransformIndo + var outputParticleTransformIndex = GetTransformIndex(particle.Transform, new TransformInfo { TransformType = TransformType.Particle, ParentIndex = parentIndex, InitLocalPosition = vrm.DefaultTransformStates[particle.Transform].LocalPosition, - DragForce = particle.GetSettings(warp.BaseSettings).DragForce, + InitLocalRotation = vrm.DefaultTransformStates[particle.Transform].LocalRotation, + Settings = particle.GetSettings(warp.BaseSettings), }, info, positions); - parentIndex = outputParticleTransformIndex; + parentIndex = outputParticleTransformIndex.index; } } + + warps.Add(new WarpInfo + { + StartIndex = start, + EndIndex = _transforms.Count, + }); + await awaitCaller.NextFrame(); } + _warps = new(warps.ToArray(), Allocator.Persistent); _transformAccessArray = new(_transforms.ToArray(), 128); _inputData = new(_transforms.Count, Allocator.Persistent); var pos = positions.ToArray(); _currentPositions = new(pos, Allocator.Persistent); _prevPositions = new(pos, Allocator.Persistent); _nextPositions = new(pos.Length, Allocator.Persistent); + _nextRotations = new(pos.Length, Allocator.Persistent); _info = new(info.ToArray(), Allocator.Persistent); } @@ -194,7 +307,7 @@ namespace RotateParticle.Jobs for (int i = 0; i < _info.Length; ++i) { var particle = _info[i]; - if (particle.TransformType == TransformType.WarpRoot) + if (particle.TransformType.PositionInput()) { _currentPositions[i] = _inputData[i].ToWorld.GetPosition(); } @@ -205,16 +318,28 @@ namespace RotateParticle.Jobs { Frame = frame, Info = _info, - Prev = _prevPositions, - Current = _currentPositions, - Next = _nextPositions, + CurrentTransforms = _inputData, + PrevPositions = _prevPositions, + CurrentPositions = _currentPositions, + NextPositions = _nextPositions, + NextRotations = _nextRotations, }.Run(_info.Length); + // NextPositions から NextRotations を作る + new ApplyRotationJob + { + Warps = _warps, + Info = _info, + CurrentTransforms = _inputData, + NextPositions = _nextPositions, + NextRotations = _nextRotations, + }.Run(_warps.Length); + // output new OutputTransformJob { Info = _info, - NextPositions = _nextPositions, + NextRotations = _nextRotations, }.Schedule(_transformAccessArray, default).Complete(); // update state