separate FastSpringBoneBufferBuilder

This commit is contained in:
ousttrue
2024-07-17 13:38:40 +09:00
parent 3a37f35883
commit 7f856bf945
4 changed files with 229 additions and 188 deletions

View File

@@ -14,27 +14,17 @@ namespace UniVRM10
private readonly Vrm10Instance m_instance;
private readonly IReadOnlyDictionary<Transform, TransformState> m_defaultTransformStates;
private readonly FastSpringBoneService m_fastSpringBoneService;
private FastSpringBoneBuffer m_fastSpringBoneBuffer;
private BlittableExternalData m_externalData;
private FastSpringBoneBuffer m_fastSpringBoneBuffer;
public Vector3 ExternalForce
{
get => m_externalData.ExternalForce;
set
{
m_externalData.ExternalForce = value;
m_fastSpringBoneBuffer.ExternalData = m_externalData;
}
get => m_fastSpringBoneBuffer.ExternalForce;
set { m_fastSpringBoneBuffer.ExternalForce = value; }
}
public bool IsSpringBoneEnabled
{
get => m_externalData.IsSpringBoneEnabled;
set
{
m_externalData.IsSpringBoneEnabled = value;
m_fastSpringBoneBuffer.ExternalData = m_externalData;
}
get => m_fastSpringBoneBuffer.IsSpringBoneEnabled;
set { m_fastSpringBoneBuffer.IsSpringBoneEnabled = value; }
}
public Vrm10RuntimeSpringBone(Vrm10Instance instance)
@@ -86,39 +76,39 @@ namespace UniVRM10
private FastSpringBoneBuffer CreateFastSpringBoneBuffer(Vrm10InstanceSpringBone springBone)
{
return new FastSpringBoneBuffer(
springBone.Springs.Select(spring => new FastSpringBoneSpring
{
center = spring.Center,
colliders = spring.ColliderGroups
.SelectMany(group => group.Colliders)
.Select(collider => new FastSpringBoneCollider
{
Transform = collider.transform,
Collider = new BlittableCollider
{
offset = collider.Offset,
radius = collider.Radius,
tailOrNormal = collider.TailOrNormal,
colliderType = TranslateColliderType(collider.ColliderType)
}
}).ToArray(),
joints = spring.Joints
.Select(joint => new FastSpringBoneJoint
{
Transform = joint.transform,
Joint = new BlittableJoint
{
radius = joint.m_jointRadius,
dragForce = joint.m_dragForce,
gravityDir = joint.m_gravityDir,
gravityPower = joint.m_gravityPower,
stiffnessForce = joint.m_stiffnessForce
},
DefaultLocalRotation = GetOrAddDefaultTransformState(joint.transform).LocalRotation,
}).ToArray(),
}).ToArray(),
m_externalData);
var b = new FastSpringBoneBufferBuilder(springBone.Springs.Select(spring => new FastSpringBoneSpring
{
center = spring.Center,
colliders = spring.ColliderGroups
.SelectMany(group => group.Colliders)
.Select(collider => new FastSpringBoneCollider
{
Transform = collider.transform,
Collider = new BlittableCollider
{
offset = collider.Offset,
radius = collider.Radius,
tailOrNormal = collider.TailOrNormal,
colliderType = TranslateColliderType(collider.ColliderType)
}
}).ToArray(),
joints = spring.Joints
.Select(joint => new FastSpringBoneJoint
{
Transform = joint.transform,
Joint = new BlittableJoint
{
radius = joint.m_jointRadius,
dragForce = joint.m_dragForce,
gravityDir = joint.m_gravityDir,
gravityPower = joint.m_gravityPower,
stiffnessForce = joint.m_stiffnessForce
},
DefaultLocalRotation = GetOrAddDefaultTransformState(joint.transform).LocalRotation,
}).ToArray(),
}));
return new FastSpringBoneBuffer(b);
}
private TransformState GetOrAddDefaultTransformState(Transform tf)

View File

@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
@@ -26,149 +24,48 @@ namespace UniVRM10.FastSpringBones.System
// NOTE: これは更新頻度が高くバッチングが難しいため、ランダムアクセスを許容してメモリへ直接アクセスする
// 生のヒープ領域は扱いにくいので長さ1のNativeArrayで代用
private NativeArray<BlittableExternalData> _externalData;
public BlittableExternalData ExternalData
public Vector3 ExternalForce
{
get => _externalData[0];
set => _externalData[0] = value;
get => _externalData[0].ExternalForce;
set
{
_externalData[0] = new BlittableExternalData
{
ExternalForce = value,
IsSpringBoneEnabled = _externalData[0].IsSpringBoneEnabled,
};
}
}
public bool IsSpringBoneEnabled
{
get => _externalData[0].IsSpringBoneEnabled;
set
{
_externalData[0] = new BlittableExternalData
{
ExternalForce = _externalData[0].ExternalForce,
IsSpringBoneEnabled = value,
};
}
}
public unsafe FastSpringBoneBuffer(IReadOnlyList<FastSpringBoneSpring> springs,
BlittableExternalData externalData,
bool simulateLastBone = false)
public unsafe FastSpringBoneBuffer(FastSpringBoneBufferBuilder b)
{
Profiler.BeginSample("FastSpringBone.ConstructBuffers");
_externalData = new NativeArray<BlittableExternalData>(1, Allocator.Persistent);
ExternalData = externalData;
// Transformの列挙
Profiler.BeginSample("FastSpringBone.ConstructBuffers.ConstructTransformBuffer");
var transformHashSet = new HashSet<Transform>();
foreach (var spring in springs)
{
foreach (var joint in spring.joints)
{
transformHashSet.Add(joint.Transform);
if (joint.Transform.parent != null) transformHashSet.Add(joint.Transform.parent);
}
foreach (var collider in spring.colliders)
{
transformHashSet.Add(collider.Transform);
}
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);
Profiler.EndSample();
// 各種bufferの構築
Profiler.BeginSample("FastSpringBone.ConstructBuffers.ConstructBuffers");
var blittableColliders = new List<BlittableCollider>();
var blittableJoints = new List<BlittableJoint>();
var blittableSprings = new List<BlittableSpring>();
var blittableLogics = new List<BlittableLogic>();
foreach (var spring in springs)
{
var blittableSpring = new BlittableSpring
{
colliderSpan = new BlittableSpan
{
startIndex = blittableColliders.Count,
count = spring.colliders.Length,
},
logicSpan = new BlittableSpan
{
startIndex = blittableJoints.Count,
count = simulateLastBone ? spring.joints.Length : spring.joints.Length - 1,
},
centerTransformIndex = spring.center ? transformIndexDictionary[spring.center] : -1,
ExternalData = (BlittableExternalData*) _externalData.GetUnsafePtr()
};
blittableSprings.Add(blittableSpring);
blittableColliders.AddRange(spring.colliders.Select(collider =>
{
var blittable = collider.Collider;
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;
}));
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 localPosition = Vector3.zero;
if (tailJoint.HasValue)
{
localPosition = tailJoint.Value.Transform.localPosition;
}
else
{
if (parentJoint.HasValue)
{
var delta = joint.Transform.position - parentJoint.Value.Transform.position;
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(
localPosition.x * scale.x,
localPosition.y * scale.y,
localPosition.z * scale.z
);
var worldChildPosition = joint.Transform.TransformPoint(localChildPosition);
var currentTail = spring.center != null
? spring.center.InverseTransformPoint(worldChildPosition)
: worldChildPosition;
var parent = joint.Transform.parent;
blittableLogics.Add(new BlittableLogic
{
headTransformIndex = transformIndexDictionary[joint.Transform],
parentTransformIndex = parent != null ? transformIndexDictionary[parent] : -1,
currentTail = currentTail,
prevTail = currentTail,
localRotation = joint.DefaultLocalRotation,
boneAxis = localChildPosition.normalized,
length = localChildPosition.magnitude
});
}
}
Profiler.EndSample();
// 各種bufferの初期化
Profiler.BeginSample("FastSpringBone.ConstructBuffers.ConstructNativeArrays");
Springs = new NativeArray<BlittableSpring>(blittableSprings.ToArray(), Allocator.Persistent);
Joints = new NativeArray<BlittableJoint>(blittableJoints.ToArray(), Allocator.Persistent);
Colliders = new NativeArray<BlittableCollider>(blittableColliders.ToArray(), Allocator.Persistent);
Logics = new NativeArray<BlittableLogic>(blittableLogics.ToArray(), Allocator.Persistent);
BlittableTransforms = new NativeArray<BlittableTransform>(transforms.Length, Allocator.Persistent);
Transforms = transforms.ToArray();
Profiler.EndSample();
_externalData = new NativeArray<BlittableExternalData>(1, Allocator.Persistent);
_externalData[0] = new BlittableExternalData
{
ExternalForce = Vector3.zero,
IsSpringBoneEnabled = true,
};
b.SetExternalDataPtr((BlittableExternalData*)_externalData.GetUnsafePtr());
Springs = new NativeArray<BlittableSpring>(b.BlittableSprings.ToArray(), Allocator.Persistent);
Joints = new NativeArray<BlittableJoint>(b.BlittableJoints.ToArray(), Allocator.Persistent);
Colliders = new NativeArray<BlittableCollider>(b.BlittableColliders.ToArray(), Allocator.Persistent);
Logics = new NativeArray<BlittableLogic>(b.BlittableLogics.ToArray(), Allocator.Persistent);
BlittableTransforms = new NativeArray<BlittableTransform>(b.Transforms.Length, Allocator.Persistent);
Transforms = b.Transforms;
Profiler.EndSample();
}

View File

@@ -0,0 +1,143 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Profiling;
using UniVRM10.FastSpringBones.Blittables;
namespace UniVRM10.FastSpringBones.System
{
public class FastSpringBoneBufferBuilder
{
public readonly List<BlittableSpring> BlittableSprings = new();
public readonly List<BlittableJoint> BlittableJoints = new();
public readonly List<BlittableCollider> BlittableColliders = new();
public readonly List<BlittableLogic> BlittableLogics = new();
public readonly Transform[] Transforms;
public FastSpringBoneBufferBuilder(IEnumerable<FastSpringBoneSpring> springs, bool simulateLastBone = false)
{
Profiler.BeginSample("FastSpringBone.ConstructBuffers.BufferBuilder");
var transformHashSet = new HashSet<Transform>();
foreach (var spring in springs)
{
foreach (var joint in spring.joints)
{
transformHashSet.Add(joint.Transform);
if (joint.Transform.parent != null) transformHashSet.Add(joint.Transform.parent);
}
foreach (var collider in spring.colliders)
{
transformHashSet.Add(collider.Transform);
}
if (spring.center != null) transformHashSet.Add(spring.center);
}
Transforms = transformHashSet.ToArray();
var transformIndexDictionary = Transforms.Select((trs, index) => (trs, index))
.ToDictionary(tuple => tuple.trs, tuple => tuple.index);
foreach (var spring in springs)
{
var blittableSpring = new BlittableSpring
{
colliderSpan = new BlittableSpan
{
startIndex = BlittableColliders.Count,
count = spring.colliders.Length,
},
logicSpan = new BlittableSpan
{
startIndex = BlittableJoints.Count,
count = simulateLastBone ? spring.joints.Length : spring.joints.Length - 1,
},
centerTransformIndex = spring.center ? transformIndexDictionary[spring.center] : -1,
};
BlittableSprings.Add(blittableSpring);
BlittableColliders.AddRange(spring.colliders.Select(collider =>
{
var blittable = collider.Collider;
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;
}));
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 localPosition = Vector3.zero;
if (tailJoint.HasValue)
{
localPosition = tailJoint.Value.Transform.localPosition;
}
else
{
if (parentJoint.HasValue)
{
var delta = joint.Transform.position - parentJoint.Value.Transform.position;
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(
localPosition.x * scale.x,
localPosition.y * scale.y,
localPosition.z * scale.z
);
var worldChildPosition = joint.Transform.TransformPoint(localChildPosition);
var currentTail = spring.center != null
? spring.center.InverseTransformPoint(worldChildPosition)
: worldChildPosition;
var parent = joint.Transform.parent;
BlittableLogics.Add(new BlittableLogic
{
headTransformIndex = transformIndexDictionary[joint.Transform],
parentTransformIndex = parent != null ? transformIndexDictionary[parent] : -1,
currentTail = currentTail,
prevTail = currentTail,
localRotation = joint.DefaultLocalRotation,
boneAxis = localChildPosition.normalized,
length = localChildPosition.magnitude
});
}
}
Profiler.EndSample();
}
public unsafe void SetExternalDataPtr(BlittableExternalData* externalData)
{
for (int i = 0; i < BlittableSprings.Count; ++i)
{
// blittableSprings[i] = blittableSprings[i] with
// {
// ExternalData = externalData,
// };
var b = BlittableSprings[i];
BlittableSprings[i] = new BlittableSpring
{
centerTransformIndex = b.centerTransformIndex,
colliderSpan = b.colliderSpan,
logicSpan = b.logicSpan,
transformIndexOffset = b.transformIndexOffset,
ExternalData = externalData,
};
}
}
}
}

View File

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