using System; using System.Collections.Generic; using System.Linq; using Unity.Collections; using UnityEngine; using UnityEngine.Jobs; using UnityEngine.Profiling; using UniVRM10.FastSpringBones.Blittables; namespace UniVRM10.FastSpringBones.System { /// /// FastSpringBoneの処理に利用するバッファを全て結合して持つクラス /// public sealed class FastSpringBoneBufferCombiner : IDisposable { private NativeArray _springs; private NativeArray _joints; private NativeArray _transforms; private TransformAccessArray _transformAccessArray; private NativeArray _colliders; private NativeArray _logics; private readonly LinkedList _fastSpringBoneScopes = new LinkedList(); private bool _isDirty; public NativeArray Springs => _springs; public NativeArray Joints => _joints; public NativeArray Transforms => _transforms; public TransformAccessArray TransformAccessArray => _transformAccessArray; public NativeArray Colliders => _colliders; public NativeArray Logics => _logics; public void Register(FastSpringBoneScope scope) { _fastSpringBoneScopes.AddLast(scope); _isDirty = true; } public void Unregister(FastSpringBoneScope scope) { _fastSpringBoneScopes.Remove(scope); _isDirty = true; } /// /// 変更があったならばバッファを再構築する /// public void ReconstructIfDirty() { if (_isDirty) { ReconstructBuffers(); _isDirty = false; } } /// /// バッファを再構築する /// TODO: 最適化 /// private void ReconstructBuffers() { Profiler.BeginSample("FastSpringBone.ReconstructBuffers"); DisposeAllBuffers(); var springs = _fastSpringBoneScopes.SelectMany(scope => scope.Springs).ToArray(); // Transformの列挙 var transformHashSet = new HashSet(); 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); // 各種bufferの構築 var blittableColliders = new List(); var blittableJoints = new List(); var blittableSprings = new List(); var blittableLogics = new List(); 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 = 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(spring.joints.Length - 1).Select(joint => { var blittable = joint.Joint; return blittable; })); for (var i = 0; i < spring.joints.Length - 1; ++i) { var joint = spring.joints[i]; var tailJoint = spring.joints[i + 1]; var localPosition = tailJoint.Transform.localPosition; var scale = tailJoint.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.Transform.localRotation, boneAxis = localChildPosition.normalized, length = localChildPosition.magnitude }); } } // 各種bufferの初期化 _springs = new NativeArray(blittableSprings.ToArray(), Allocator.Persistent); _joints = new NativeArray(blittableJoints.ToArray(), Allocator.Persistent); _colliders = new NativeArray(blittableColliders.ToArray(), Allocator.Persistent); _logics = new NativeArray(blittableLogics.ToArray(), Allocator.Persistent); _transforms = new NativeArray(transforms.Length, Allocator.Persistent); _transformAccessArray = new TransformAccessArray(transforms.ToArray()); Profiler.EndSample(); } private void DisposeAllBuffers() { if (_springs.IsCreated) _springs.Dispose(); if (_joints.IsCreated) _joints.Dispose(); if (_transforms.IsCreated) _transforms.Dispose(); if (_transformAccessArray.isCreated) _transformAccessArray.Dispose(); if (_colliders.IsCreated) _colliders.Dispose(); if (_logics.IsCreated) _logics.Dispose(); } public void Dispose() { DisposeAllBuffers(); } } }