mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-09-30 06:43:21 -05:00
basic springbone
This commit is contained in:
@@ -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)
|
||||
{
|
||||
|
||||
@@ -11,44 +11,31 @@ namespace RotateParticle.Components
|
||||
public class Warp : MonoBehaviour
|
||||
{
|
||||
[Serializable]
|
||||
public class ParticleSettings : IEquatable<ParticleSettings>
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// null のときは world root ではなく model root で処理
|
||||
|
||||
@@ -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<Transform> _transforms;
|
||||
int GetTransformIndex(Transform t,
|
||||
TransformIndo info,
|
||||
List<TransformIndo> infos,
|
||||
(int index, bool isNew) GetTransformIndex(Transform t,
|
||||
TransformInfo info,
|
||||
List<TransformInfo> infos,
|
||||
List<Vector3> 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<WarpInfo> _warps;
|
||||
|
||||
TransformAccessArray _transformAccessArray;
|
||||
NativeArray<TransformData> _inputData;
|
||||
NativeArray<TransformInfo> _info;
|
||||
NativeArray<Vector3> _currentPositions;
|
||||
NativeArray<Vector3> _prevPositions;
|
||||
NativeArray<Vector3> _nextPositions;
|
||||
NativeArray<Quaternion> _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<TransformData> InputData;
|
||||
@@ -80,103 +135,161 @@ namespace RotateParticle.Jobs
|
||||
}
|
||||
}
|
||||
|
||||
// [Output]
|
||||
NativeArray<Vector3> _nextPositions;
|
||||
public struct OutputTransformJob : IJobParallelForTransform
|
||||
{
|
||||
[ReadOnly] public NativeArray<TransformIndo> Info;
|
||||
[ReadOnly] public NativeArray<Vector3> NextPositions;
|
||||
public void Execute(int index, TransformAccess transform)
|
||||
{
|
||||
var info = Info[index];
|
||||
if (info.TransformType == TransformType.Particle)
|
||||
{
|
||||
transform.position = NextPositions[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NativeArray<TransformIndo> _info;
|
||||
|
||||
// [Init/State]
|
||||
// 初期化時に値を作って毎フレーム更新していく
|
||||
// output と同じ長さ index
|
||||
NativeArray<Vector3> _currentPositions;
|
||||
NativeArray<Vector3> _prevPositions;
|
||||
public struct VerletJob : IJobParallelFor
|
||||
{
|
||||
public FrameInfo Frame;
|
||||
[ReadOnly] public NativeArray<TransformIndo> Info;
|
||||
[ReadOnly] public NativeArray<Vector3> Current;
|
||||
[ReadOnly] public NativeArray<Vector3> Prev;
|
||||
[WriteOnly] public NativeArray<Vector3> Next;
|
||||
[ReadOnly] public NativeArray<TransformInfo> Info;
|
||||
[ReadOnly] public NativeArray<TransformData> CurrentTransforms;
|
||||
[ReadOnly] public NativeArray<Vector3> CurrentPositions;
|
||||
[ReadOnly] public NativeArray<Vector3> PrevPositions;
|
||||
[WriteOnly] public NativeArray<Vector3> NextPositions;
|
||||
[WriteOnly] public NativeArray<Quaternion> 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<WarpInfo> Warps;
|
||||
[ReadOnly] public NativeArray<TransformInfo> Info;
|
||||
[ReadOnly] public NativeArray<TransformData> CurrentTransforms;
|
||||
[ReadOnly] public NativeArray<Vector3> NextPositions;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<Quaternion> 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<TransformInfo> Info;
|
||||
[ReadOnly] public NativeArray<Quaternion> 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<TransformIndo> info = new();
|
||||
List<TransformInfo> info = new();
|
||||
List<Vector3> positions = new();
|
||||
List<WarpInfo> warps = new();
|
||||
var warpSrcs = vrm.GetComponentsInChildren<Warp>();
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user