WIP parent length constraint

This commit is contained in:
ousttrue
2024-11-19 22:15:01 +09:00
parent aba3114c9d
commit 23ddea2ebe
4 changed files with 165 additions and 320 deletions

View File

@@ -16,170 +16,224 @@ namespace RotateParticle.Jobs
{
Vrm10Instance _vrm;
NativeArray<WarpImmutable> _warpImmutables;
NativeArray<WarpMutable> _warpMutables;
// [Input]
// center
// rootParent
// root
//
// の入力(jobにわたす)
List<Transform> _inputTransforms;
TransformAccessArray _inputTransformAccessArray;
public struct InputTransformData
{
public Matrix4x4 ToWorld;
public Matrix4x4 TOLocal;
TransformAccessArray _transformAccessArray;
NativeArray<ParticleImmutable> _particleImmutables;
NativeArray<ParticleMutable> _particleMutables;
public InputTransformData(TransformAccess t)
{
ToWorld = t.localToWorldMatrix;
TOLocal = t.worldToLocalMatrix;
}
}
NativeArray<InputTransformData> _inputData;
public struct InputTransformJob : IJobParallelForTransform
{
[WriteOnly] public NativeArray<InputTransformData> InputData;
public void Execute(int index, TransformAccess transform)
{
InputData[index] = new InputTransformData(transform);
}
}
NativeArray<Vector3> _prevPositionsOnCenter;
NativeArray<Vector3> _currentPositions;
// [Output]
// root 回転のみ
// particle 回転 + 移動
// を出力(jobから反映する)
List<Transform> _outputTransforms;
List<Vector3> _positions;
NativeArray<Vector3> _nextPositions;
// NativeArray<Quaternion> _currentRotations;
NativeArray<Quaternion> _nextRotations;
TransformAccessArray _outputTransformAccessArray;
public struct OutputTransformJob : IJobParallelForTransform
{
[ReadOnly] public NativeArray<Vector3> NextPositions;
public void Execute(int index, TransformAccess transform)
{
transform.position = NextPositions[index];
}
}
bool _zeroVelocity = true;
public struct ParticleIndo
{
public int InputTransformIndex;
public int? ParentIndex;
public Vector3 InitLocalPosition;
// DragForce: 1 で即時停止
public float DragForce;
}
NativeArray<ParticleIndo> _particles;
// [Init/State]
// 初期化時に値を作って毎フレーム更新していく
// output と同じ長さ index
NativeArray<Vector3> _currentPositions;
NativeArray<Vector3> _prevPositions;
public struct VerletJob : IJobParallelFor
{
public FrameInfo Frame;
[WriteOnly] public NativeArray<Vector3> NextPositions;
[ReadOnly] public NativeArray<Vector3> CurrentPositions;
[ReadOnly] public NativeArray<Vector3> PrevPositions;
[ReadOnly] public NativeArray<ParticleIndo> Particles;
public void Execute(int index)
{
var particle = Particles[index];
if (particle.ParentIndex.HasValue)
{
var velocity = CurrentPositions[index] - PrevPositions[index];
velocity *= (1 - particle.DragForce);
var newPosition = CurrentPositions[index] + velocity + Frame.Force * Frame.SqDeltaTime;
var parentIndex = particle.ParentIndex.Value;
var parentPosition = CurrentPositions[parentIndex];
// 位置を長さで拘束
newPosition = parentPosition + (newPosition - parentPosition).normalized * particle.InitLocalPosition.magnitude;
NextPositions[index] = newPosition;
}
else
{
// kinematic
NextPositions[index] = CurrentPositions[index];
}
}
}
void IDisposable.Dispose()
{
if (_transformAccessArray.isCreated) _transformAccessArray.Dispose();
if (_warpImmutables.IsCreated) _warpImmutables.Dispose();
if (_warpMutables.IsCreated) _warpMutables.Dispose();
if (_particleImmutables.IsCreated) _particleImmutables.Dispose();
if (_particleMutables.IsCreated) _particleMutables.Dispose();
if (_prevPositionsOnCenter.IsCreated) _prevPositionsOnCenter.Dispose();
if (_currentPositions.IsCreated) _currentPositions.Dispose();
if (_nextPositions.IsCreated) _nextPositions.Dispose();
// if (_currentRotations.IsCreated) _currentRotations.Dispose();
if (_nextRotations.IsCreated) _nextRotations.Dispose();
}
async Task IRotateParticleSystem.InitializeAsync(Vrm10Instance vrm, IAwaitCaller awaitCaller)
{
_vrm = vrm;
List<WarpImmutable> warpImmutables = new();
List<WarpMutable> warpMutables = new();
_inputTransforms = new();
_outputTransforms = new();
_positions = new();
List<Transform> transforms = new();
List<ParticleImmutable> particleImmutables = new();
// List<ParticleMutable> particleMutables = new();
List<ParticleIndo> particles = new();
var warpSrcs = vrm.GetComponentsInChildren<Warp>();
for (int warpIndex = 0; warpIndex < warpSrcs.Length; ++warpIndex)
{
var warp = warpSrcs[warpIndex];
warpImmutables.Add(new WarpImmutable
{
var inputCenterTransformIndex = GetInputTransformIndex(warp.Center);
var inputWarpRootParentTransformIndex = GetInputTransformIndex(warp.transform.parent);
Debug.Assert(inputWarpRootParentTransformIndex != -1);
var inputWarpRootTransformIndex = GetInputTransformIndex(warp.transform);
Debug.Assert(inputWarpRootParentTransformIndex != -1);
});
warpMutables.Add(new WarpMutable
var ouputWarpRootTransformIndex = GetOutputTransformIndex(warp.transform);
particles.Add(new ParticleIndo
{
});
transforms.Add(warp.transform);
particleImmutables.Add(new ParticleImmutable
{
ParticleType = ParticleType.Root,
WarpIndex = warpIndex,
InputTransformIndex = inputWarpRootTransformIndex,
});
var parentIndex = ouputWarpRootTransformIndex;
foreach (var particle in warp.Particles)
{
if (particle != null && particle.Transform != null)
{
transforms.Add(particle.Transform);
particleImmutables.Add(new ParticleImmutable
var outputParticleTransformIndex = GetOutputTransformIndex(particle.Transform);
particles.Add(new ParticleIndo
{
// TODO: ParticleType.Branch
ParticleType = ParticleType.Verlet,
WarpIndex = warpIndex,
ParentIndex = parentIndex,
InitLocalPosition = vrm.DefaultTransformStates[particle.Transform].LocalPosition,
DragForce = particle.GetSettings(warp.BaseSettings).DragForce,
});
parentIndex = outputParticleTransformIndex;
}
}
await awaitCaller.NextFrame();
}
_inputTransformAccessArray = new(_inputTransforms.ToArray(), 128);
_inputData = new(_inputTransforms.Count, Allocator.Persistent);
_warpImmutables = new(warpImmutables.ToArray(), Allocator.Persistent);
_warpMutables = new(warpMutables.ToArray(), Allocator.Persistent);
_outputTransformAccessArray = new(_outputTransforms.ToArray(), 128);
var positions = _positions.ToArray();
_currentPositions = new(positions, Allocator.Persistent);
_prevPositions = new(positions, Allocator.Persistent);
_nextPositions = new(positions, Allocator.Persistent);
_particles = new(particles.ToArray(), Allocator.Persistent);
}
_transformAccessArray = new TransformAccessArray(transforms.ToArray());
_particleImmutables = new(particleImmutables.ToArray(), Allocator.Persistent);
_particleMutables = new(transforms.Count, Allocator.Persistent);
int GetInputTransformIndex(Transform t)
{
if (t == null) return -1;
var i = _inputTransforms.IndexOf(t);
if (i == -1)
{
i = _inputTransforms.Count;
_inputTransforms.Add(t);
}
return i;
}
_currentPositions = new(transforms.Count, Allocator.Persistent);
_prevPositionsOnCenter = new(transforms.Count, Allocator.Persistent);
_nextPositions = new(transforms.Count, Allocator.Persistent);
// _currentRotations = new(transforms.Count, Allocator.Persistent);
_nextRotations = new(transforms.Count, Allocator.Persistent);
int GetOutputTransformIndex(Transform t)
{
if (t == null) return -1;
var i = _outputTransforms.IndexOf(t);
if (i == -1)
{
i = _outputTransforms.Count;
_outputTransforms.Add(t);
_positions.Add(t.position);
}
return i;
}
void IRotateParticleSystem.Process(float deltaTime)
{
Schedule(
new FrameInfo(deltaTime, Vector3.zero),
_transformAccessArray
).Complete();
}
var frame = new FrameInfo(deltaTime, Vector3.zero);
/// <summary>
/// INIT PROCESS
/// _transformAccessArray
/// 👇
/// _currentPositions
/// _currentRotations
/// </summary>
private JobHandle Schedule(
in FrameInfo frame,
TransformAccessArray transformAccessArray
)
{
JobHandle handle = default;
// load transform
handle = new ReadTransformJob
// input
new InputTransformJob
{
CurrentPositions = _currentPositions,
NextRotations = _nextRotations,
}.Schedule(_transformAccessArray, handle);
InputData = _inputData,
}.Schedule(_inputTransformAccessArray, default).Complete();
// update root position
for (int i = 0; i < _particles.Length; ++i)
{
var particle = _particles[i];
if (!particle.ParentIndex.HasValue)
{
_currentPositions[i] = _inputData[particle.InputTransformIndex].ToWorld.GetPosition();
}
}
// verlet
handle = new VerletJob
new VerletJob
{
ZeroVelocity = _zeroVelocity,
Frame = frame,
WarpMutables = _warpMutables,
ParticleImmutables = _particleImmutables,
ParticleMutables = _particleMutables,
PrevPositionsOnCenter = _prevPositionsOnCenter,
Particles = _particles,
PrevPositions = _prevPositions,
CurrentPositions = _currentPositions,
NextPositions = _nextPositions,
}.Schedule(_currentPositions.Length, 128, handle);
_zeroVelocity = false;
}.Run(_particles.Length);
// local rotation constraint
handle = new LocalRotationConstraintJob
{
WarpImmutables = _warpImmutables,
ParticleImmutables = _particleImmutables,
Positions = _nextPositions,
Rotations = _nextRotations,
}.Schedule(_warpImmutables.Length, 1, handle);
// update verlet
handle = new VerletStatusJob
{
WarpImmutables = _warpImmutables,
WarpMutables = _warpMutables,
CurrentPositions = _currentPositions,
PrevPositionsOnCenter = _prevPositionsOnCenter,
}.Schedule(_warpImmutables.Length, 1, handle);
// writeback transform
handle = new WritebackTransformJob
// output
new OutputTransformJob
{
NextPositions = _nextPositions,
NextRotations = _nextRotations,
}.Schedule(_transformAccessArray, handle);
}.Schedule(_outputTransformAccessArray, default).Complete();
return handle;
// update state
NativeArray<Vector3>.Copy(_currentPositions, _prevPositions);
NativeArray<Vector3>.Copy(_nextPositions, _currentPositions);
}
void IRotateParticleSystem.ResetInitialRotation()
{
throw new NotImplementedException();
}
void IRotateParticleSystem.DrawGizmos()

View File

@@ -1,199 +0,0 @@
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.Jobs;
namespace RotateParticle.Jobs
{
public enum ParticleType
{
// kinematic. not verlet move
Root,
// particle. verlet move
Verlet,
// branch. move same as 1st branch sibling
Branch,
}
/// <summary>
/// Reconstruct されるまで不変
/// </summary>
public struct ParticleImmutable
{
public ParticleType ParticleType;
public int WarpIndex;
public int ParentIndex;
public float DistanceFromParent;
public Quaternion InitRotation;
public Vector3 InitTailLocalPosition;
}
/// <summary>
/// 毎フレーム変化するかもしれない
/// </summary>
public struct ParticleMutable
{
public float DragForce;
}
/// <summary>
/// Reconstruct されるまで不変
/// </summary>
public struct WarpImmutable
{
public int ParentIndex;
public int BranchRootIndex;
public int Start;
public int End;
}
/// <summary>
/// 毎フレーム変化するかもしれない
/// </summary>
public struct WarpMutable
{
/// <summary>
/// center が未指定ならば Vector3.zero
/// </summary>
public Vector3 Center;
}
public struct ReadTransformJob : IJobParallelForTransform
{
[WriteOnly] public NativeArray<Vector3> CurrentPositions;
[WriteOnly] public NativeArray<Quaternion> NextRotations;
public void Execute(int index, TransformAccess transform)
{
// TODO: ModelSpace
CurrentPositions[index] = transform.position;
NextRotations[index] = transform.rotation;
}
}
public struct WritebackTransformJob : IJobParallelForTransform
{
[ReadOnly] public NativeArray<Vector3> NextPositions;
[ReadOnly] public NativeArray<Quaternion> NextRotations;
public void Execute(int index, TransformAccess transform)
{
// TODO: ModelSpace
transform.position = NextPositions[index];
transform.rotation = NextRotations[index];
}
}
struct VerletJob : IJobParallelFor
{
public bool ZeroVelocity;
[ReadOnly] public FrameInfo Frame;
// [ReadOnly] public NativeArray<WarpImmutable> WarpImmutables;
[ReadOnly] public NativeArray<WarpMutable> WarpMutables;
[ReadOnly] public NativeArray<ParticleImmutable> ParticleImmutables;
[ReadOnly] public NativeArray<ParticleMutable> ParticleMutables;
[ReadOnly] public NativeArray<Vector3> PrevPositionsOnCenter;
[ReadOnly] public NativeArray<Vector3> CurrentPositions;
[WriteOnly] public NativeArray<Vector3> NextPositions;
public void Execute(int index)
{
var particle = ParticleImmutables[index];
var particleMutable = ParticleMutables[index];
if (particle.ParticleType == ParticleType.Root)
{
// kinematic. not move
NextPositions[index] = CurrentPositions[index];
}
else
{
var warp = WarpMutables[particle.WarpIndex];
var velocity = ZeroVelocity
? Vector3.zero
: CurrentPositions[index] - warp.Center - PrevPositionsOnCenter[index]
;
velocity *= 1 - particleMutable.DragForce;
NextPositions[index] = CurrentPositions[index] +
velocity + Frame.Force * Frame.SqDeltaTime;
}
}
}
/// <summary>
/// Warp毎に verlet の結果を拘束する
/// 1. 親子間の距離を一定に保つ
/// 2. 移動を親の回転に変換する
/// </summary>
struct LocalRotationConstraintJob : IJobParallelFor
{
[ReadOnly] public NativeArray<WarpImmutable> WarpImmutables;
[ReadOnly] public NativeArray<ParticleImmutable> ParticleImmutables;
[NativeDisableParallelForRestriction] public NativeArray<Vector3> Positions;
[NativeDisableParallelForRestriction] public NativeArray<Quaternion> Rotations;
public void Execute(int warpIndex)
{
var warp = WarpImmutables[warpIndex];
for (int i = warp.Start; i < warp.End; ++i)
{
var particle = ParticleImmutables[i];
switch (particle.ParticleType)
{
case ParticleType.Root:
break;
case ParticleType.Verlet:
if (i + 1 < warp.End)
{
var next = ParticleImmutables[i + 1];
if (next.ParentIndex == i)
{
/// 1. 親子間の距離を一定に保つ
Positions[i + 1] = CalcPosition(Positions[i + 1],
Positions[i], next.DistanceFromParent);
// 2. 移動を親の回転に変換する
var rest = Rotations[i] * next.InitRotation;
Rotations[i] = rest * Quaternion.FromToRotation(
// 初期状態
rest * particle.InitTailLocalPosition,
// 現状
Positions[i + 1] - Positions[i]
);
}
}
break;
case ParticleType.Branch:
{
// TODO
break;
}
}
}
}
static Vector3 CalcPosition(in Vector3 pos, in Vector3 parent, float distance)
{
return pos + (pos - parent).normalized * distance;
}
}
struct VerletStatusJob : IJobParallelFor
{
[ReadOnly] public NativeArray<WarpImmutable> WarpImmutables;
[ReadOnly] public NativeArray<WarpMutable> WarpMutables;
[ReadOnly] public NativeArray<Vector3> CurrentPositions;
[WriteOnly] public NativeArray<Vector3> PrevPositionsOnCenter;
public void Execute(int warpIndex)
{
var warp = WarpImmutables[warpIndex];
var warpMutable = WarpMutables[warpIndex];
for (int i = warp.Start; i < warp.End; ++i)
{
PrevPositionsOnCenter[i] = CurrentPositions[i] - warpMutable.Center;
}
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 139cb8a384ad8c64abc4dcfab37bcaca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -26,6 +26,7 @@ CustomRenderTexture:
m_UseDynamicScale: 0
m_BindMS: 0
m_EnableCompatibleFormat: 1
m_EnableRandomWrite: 0
m_TextureSettings:
serializedVersion: 2
m_FilterMode: 1