vrm-0.x の fastspringbone の処理は、 vrm-1.0 版と同じものを使う

This commit is contained in:
ousttrue
2024-09-05 15:27:29 +09:00
parent da9869f8c5
commit 0f191e5e8f
72 changed files with 204 additions and 1760 deletions

View File

@@ -2,7 +2,8 @@ using System;
using System.Collections.Generic;
using UnityEngine;
namespace VRM
namespace UniGLTF.SpringBoneJobs
{
/// <summary>
/// FastSpringBoneに関連して、特定のGameObjectと紐付いたIDisposableの破棄を担当するクラス
@@ -15,7 +16,7 @@ namespace VRM
{
_disposables.Add(disposable);
}
private void OnDestroy()
{
foreach (var disposable in _disposables)

View File

@@ -1,10 +1,9 @@
using System;
using UniGLTF.SpringBoneJobs;
using System;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.Jobs;
namespace UniVRM10.FastSpringBones
namespace UniGLTF.SpringBoneJobs
{
public sealed class FastSpringBoneScheduler : IDisposable
{
@@ -14,20 +13,24 @@ namespace UniVRM10.FastSpringBones
_bufferCombiner = bufferCombiner;
}
public void Dispose()
{
_bufferCombiner.Dispose();
}
public JobHandle Schedule(float deltaTime)
{
var handle = default(JobHandle);
handle = _bufferCombiner.ReconstructIfDirty(handle);
var handle = _bufferCombiner.ReconstructIfDirty(default);
if (!_bufferCombiner.HasBuffer)
{
return handle;
}
handle = new PullTransformJob
{
Transforms = _bufferCombiner.Transforms
}.Schedule(_bufferCombiner.TransformAccessArray, handle);
{
Transforms = _bufferCombiner.Transforms
}.Schedule(_bufferCombiner.TransformAccessArray, handle);
handle = new UpdateFastSpringBoneJob
{
Colliders = _bufferCombiner.Colliders,
@@ -39,16 +42,11 @@ namespace UniVRM10.FastSpringBones
}.Schedule(_bufferCombiner.Springs.Length, 1, handle);
handle = new PushTransformJob
{
Transforms = _bufferCombiner.Transforms
}.Schedule(_bufferCombiner.TransformAccessArray, handle);
{
Transforms = _bufferCombiner.Transforms
}.Schedule(_bufferCombiner.TransformAccessArray, handle);
return handle;
}
public void Dispose()
{
_bufferCombiner.Dispose();
}
}
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: b2c4f2f05fcb4fb49be96d3b99236983
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,20 +0,0 @@
using UnityEngine;
namespace VRM.FastSpringBones.Blittables
{
/// <summary>
/// VRMSpringBoneのSphereColliderをBlittableにしたもの
/// 位置情報は親であるColliderGroupが持つ
/// </summary>
public readonly struct BlittableCollider
{
public Vector3 Offset { get; }
public float Radius { get; }
public BlittableCollider(Vector3 offset, float radius)
{
Offset = offset;
Radius = radius;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: cc94117758e24aca9cf44d3f930fdcb0
timeCreated: 1550209189

View File

@@ -1,20 +0,0 @@
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace VRM.FastSpringBones.Blittables
{
/// <summary>
/// VRMSpringBoneのColliderGroupをBlittableにしたもの
/// </summary>
public readonly struct BlittableColliderGroup
{
public BlittableColliders Colliders { get; }
public unsafe BlittableTransform* Transform { get; }
public unsafe BlittableColliderGroup(NativeArray<BlittableCollider> colliders, BlittableTransform* transform)
{
Colliders = new BlittableColliders((BlittableCollider*)colliders.GetUnsafePtr(), colliders.Length);
Transform = transform;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 9bceae7ad8eb4021912778484d1b2ebc
timeCreated: 1550223602

View File

@@ -1,34 +0,0 @@
using UnityEngine;
namespace VRM.FastSpringBones.Blittables
{
/// <summary>
/// BlittableColliderGroupのポインタの配列
/// </summary>
public readonly unsafe struct BlittableColliderGroups
{
private readonly BlittableColliderGroup* _data;
public int Length { get; }
public BlittableColliderGroup this[int i] => _data[i];
public void DrawGizmos()
{
for (var i = 0; i < Length; i++)
{
var group = this[i];
var colliders = group.Colliders;
for (var j = 0; j < colliders.Count; ++j)
{
Gizmos.DrawWireSphere(group.Transform->WorldPosition, colliders[j].Radius);
}
}
}
public BlittableColliderGroups(BlittableColliderGroup* data, int length)
{
_data = data;
Length = length;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 61f56040664345a8aba5309e7714f8f2
timeCreated: 1550563011

View File

@@ -1,19 +0,0 @@
namespace VRM.FastSpringBones.Blittables
{
/// <summary>
/// BlittableColliderのポインタの配列
/// </summary>
public unsafe struct BlittableColliders
{
private readonly BlittableCollider* _colliders;
public int Count { get; }
public BlittableCollider this[int i] => _colliders[i];
public BlittableColliders(BlittableCollider* colliders, int count)
{
_colliders = colliders;
Count = count;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 07087baebec34286ac230d58cd0163d2
timeCreated: 1550562383

View File

@@ -1,149 +0,0 @@
using UnityEngine;
namespace VRM.FastSpringBones.Blittables
{
/// <summary>
/// VRMSpringBoneLogicをBlittableにしたもの
/// ベルレ積分など、コアな計算を行う
/// </summary>
public unsafe struct BlittablePoint
{
private readonly float _length;
private readonly Quaternion _localRotation;
private readonly Vector3 _boneAxis;
private readonly float _radius;
private Vector3 _prevPosition;
private readonly BlittableColliderGroups* _blittableColliderGroups;
private readonly BlittableTransform* _center;
public Vector3 CurrentPosition { get; private set; }
private readonly BlittableTransform* _transform;
public BlittablePoint(
Transform transform,
float radius,
BlittableTransform* center,
BlittableColliderGroups* blittableColliderGroups,
BlittableTransform* blittableTransform)
{
Vector3 localPosition;
if (transform.childCount == 0)
{
var delta = transform.position - transform.parent.position;
var childPosition = transform.position + delta.normalized * 0.07f;
localPosition = transform.worldToLocalMatrix.MultiplyPoint(childPosition);
}
else
{
var firstChild = transform.GetChild(0);
var scale = firstChild.lossyScale;
localPosition = firstChild.localPosition;
localPosition.x *= scale.x;
localPosition.y *= scale.y;
localPosition.z *= scale.z;
}
var worldChildPosition = (Vector3)transform.TransformPoint(localPosition);
_prevPosition = CurrentPosition = center != null ? center->LocalToWorld.inverse.MultiplyPoint3x4(worldChildPosition) : worldChildPosition;
_localRotation = transform.localRotation;
_boneAxis = localPosition.normalized;
_length = localPosition.magnitude;
_radius = radius;
_blittableColliderGroups = blittableColliderGroups;
_transform = blittableTransform;
_center = center;
}
public void Update(float stiffnessForce, float dragForce, Vector3 external)
{
// 親のRotationが変わっている可能性があるので更新する
_transform->UpdateLocalToWorldMatrix();
Vector3 currentPosition;
Vector3 prevPosition;
if (_center == null)
{
currentPosition = CurrentPosition;
prevPosition = _prevPosition;
}
else
{
var centerLocalToWorld = _center->LocalToWorld;
currentPosition = centerLocalToWorld.MultiplyPoint3x4(CurrentPosition);
prevPosition = centerLocalToWorld.MultiplyPoint3x4(_prevPosition);
}
// verlet積分で次の位置を計算
var nextPosition = currentPosition
+ (currentPosition - prevPosition) * (1.0f - dragForce) // 前フレームの移動を継続する(減衰もあるよ)
+ _transform->ParentWorldRotation * _localRotation * _boneAxis * stiffnessForce // 親の回転による子ボーンの移動目標
+ external; // 外力による移動量
// 長さをboneLengthに強制
var position = _transform->WorldPosition;
nextPosition = position + (nextPosition - position).normalized * _length;
nextPosition = Collision(nextPosition, position);
if (_center == null)
{
_prevPosition = currentPosition;
CurrentPosition = nextPosition;
}
else
{
var centerWorldToLocal = _center->LocalToWorld.inverse;
_prevPosition = centerWorldToLocal.MultiplyPoint3x4(currentPosition);
CurrentPosition = centerWorldToLocal.MultiplyPoint3x4(nextPosition);
}
//回転を適用
_transform->SetWorldRotation(ApplyRotation(nextPosition));
}
private Vector3 Collision(Vector3 nextPosition, Vector3 position)
{
for (var i = 0; i < _blittableColliderGroups->Length; ++i)
{
var colliderGroup = (*_blittableColliderGroups)[i];
for (var j = 0; j < colliderGroup.Colliders.Count; ++j)
{
var collider = colliderGroup.Colliders[j];
var colliderPosition = colliderGroup.Transform->TransformPoint(collider.Offset);
var r = _radius + collider.Radius;
if (!((nextPosition - colliderPosition).sqrMagnitude <= (r * r))) continue;
// ヒット。Colliderの半径方向に押し出す
var normal = (nextPosition - colliderPosition).normalized;
var posFromCollider = colliderPosition + normal * (_radius + collider.Radius);
// 長さをboneLengthに強制
nextPosition = position + (posFromCollider - position).normalized * _length;
}
}
return nextPosition;
}
private static Quaternion FromToRotation(Vector3 from, Vector3 to)
=> Quaternion.AngleAxis(
angle: Mathf.Acos(Mathf.Clamp(Vector3.Dot(from.normalized, to.normalized), -1f, 1f)) * Mathf.Rad2Deg,
axis: Vector3.Cross(from, to).normalized
);
private Quaternion ApplyRotation(Vector3 nextTail)
{
var rotation = _transform->ParentWorldRotation * _localRotation;
return
FromToRotation(
rotation * _boneAxis,
nextTail - _transform->WorldPosition) *
rotation;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: f3f0fc54e34944c892cec2b1e86de7d7
timeCreated: 1549430605

View File

@@ -1,23 +0,0 @@
namespace VRM.FastSpringBones.Blittables
{
/// <summary>
/// BlittablePointのポインタの配列
/// </summary>
public unsafe struct BlittablePoints
{
private readonly BlittablePoint* _points;
public int Count { get; }
public BlittablePoint this[int i]
{
get => _points[i];
set => _points[i] = value;
}
public BlittablePoints(BlittablePoint* points, int count)
{
_points = points;
Count = count;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 0c39cdb389d141bbb084030dd8ebfe8b
timeCreated: 1550721598

View File

@@ -1,64 +0,0 @@
using UnityEngine;
namespace VRM.FastSpringBones.Blittables
{
/// <summary>
/// 1本の毛束を示すBlittable型
/// </summary>
public unsafe readonly struct BlittableRootBone
{
private readonly float _gravityPower;
private readonly Vector3 _gravityDir;
private readonly float _dragForce;
private readonly float _stiffnessForce;
private readonly BlittablePoints* _blittablePoints;
public void DrawGizmos()
{
for (var i = 0; i < _blittablePoints->Count; i++)
{
var point = (*_blittablePoints)[i];
Gizmos.DrawWireSphere(point.CurrentPosition, 0.05f);
}
for (var i = 0; i < _blittablePoints->Count - 1; i++)
{
var point1 = (*_blittablePoints)[i];
var point2 = (*_blittablePoints)[i + 1];
Gizmos.DrawLine(point1.CurrentPosition, point2.CurrentPosition);
}
}
public BlittableRootBone(
float gravityPower,
Vector3 gravityDir,
float dragForce,
float stiffnessForce,
BlittablePoints* blittablePoints)
{
_gravityPower = gravityPower;
_gravityDir = gravityDir;
_dragForce = dragForce;
_stiffnessForce = stiffnessForce;
_blittablePoints = blittablePoints;
}
public void Update(float deltaTime)
{
var stiffness = _stiffnessForce * deltaTime;
var external = _gravityDir * (_gravityPower * deltaTime);
for (var i = 0; i < _blittablePoints->Count; i++)
{
var point = (*_blittablePoints)[i];
// Pointを更新
point.Update(
stiffness,
_dragForce,
external
);
(*_blittablePoints)[i] = point;
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: bc470487e0a546f087709dbab3c7a6f1
timeCreated: 1549952425

View File

@@ -1,72 +0,0 @@
using UnityEngine;
using UnityEngine.Jobs;
namespace VRM.FastSpringBones.Blittables
{
/// <summary>
/// Transformの必要な機能だけを絞り、Blittableに対応させたクラス
/// </summary>
public unsafe struct BlittableTransform
{
private readonly BlittableTransform* _parent;
private Quaternion _worldRotation;
private Vector3 _localPosition;
private Vector3 _localScale;
private Quaternion _localRotation;
private Matrix4x4 _localToWorld;
public Vector3 WorldPosition { get; private set; }
public void SetWorldRotation(Quaternion rotation)
{
var parentWorldRotation = ParentWorldRotation;
_localRotation = Quaternion.Inverse(parentWorldRotation) * rotation;
UpdateLocalToWorldMatrix();
}
public Matrix4x4 LocalToWorld => _localToWorld;
private Matrix4x4 LocalTransform => Matrix4x4.TRS(_localPosition, _localRotation, _localScale);
public Quaternion ParentWorldRotation => _parent != null ? _parent->_worldRotation : Quaternion.identity;
public BlittableTransform(BlittableTransform* parent, Transform transform)
{
_parent = parent;
WorldPosition = transform.position;
_worldRotation = transform.rotation;
_localPosition = transform.localPosition;
_localRotation = transform.localRotation;
_localScale = transform.localScale;
_localToWorld = transform.localToWorldMatrix;
}
public void PullFrom(TransformAccess transform)
{
WorldPosition = transform.position;
_worldRotation = transform.rotation;
_localPosition = transform.localPosition;
_localRotation = transform.localRotation;
_localScale = transform.localScale;
_localToWorld = transform.localToWorldMatrix;
}
public void PushTo(TransformAccess transform)
{
transform.localPosition = _localPosition;
transform.localRotation = _localRotation;
}
public Vector3 TransformPoint(Vector3 offset) => _localToWorld.MultiplyPoint3x4(offset);
public void UpdateLocalToWorldMatrix()
{
_localToWorld = _parent == null ? LocalTransform : _parent->_localToWorld * LocalTransform;
WorldPosition = _localToWorld.MultiplyPoint3x4(Vector3.zero);
_worldRotation = _localToWorld.rotation;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: bffbbf422e984d028cd82c11f2ca00b1
timeCreated: 1550544819

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 89d63c6c8787b9a4c80df0fb18aa3d43
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,31 +0,0 @@
using UnityEngine;
using VRM.FastSpringBones.Blittables;
using VRM.FastSpringBones.NativeWrappers;
using VRM.FastSpringBones.Registries;
namespace VRM.FastSpringBones.Components
{
/// <summary>
/// VRMSpringBoneColliderGroupに対応したクラス
/// バッファの作成も行う
/// </summary>
public sealed unsafe class FastSpringBoneColliderGroup : MonoBehaviour
{
private NativeTransform _nativeTransform;
private NativeColliderGroup _nativeColliderGroup;
public BlittableColliderGroup* ColliderGroupPtr => _nativeColliderGroup.GetUnsafePtr();
public void Initialize(TransformRegistry transformRegistry, BlittableCollider[] colliders)
{
_nativeTransform = new NativeTransform(transformRegistry, TransformSynchronizationType.PullOnly, transform);
_nativeColliderGroup = new NativeColliderGroup(colliders, _nativeTransform);
}
private void OnDestroy()
{
_nativeTransform?.Dispose();
_nativeColliderGroup?.Dispose();
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 2c6ab135b4b945ee9d75c5476ea6bcc0
timeCreated: 1550213674

View File

@@ -1,100 +0,0 @@
using Unity.Jobs;
using UnityEngine;
using UnityEngine.Profiling;
using VRM.FastSpringBones.Registries;
using VRM.FastSpringBones.Schedulers;
namespace VRM.FastSpringBones.Components
{
/// <summary>
/// Jobを連続して発火させるComponent
/// シーンに1つだけあればいい
/// </summary>
[DefaultExecutionOrder(11000)]
public sealed class FastSpringBoneScheduler : MonoBehaviour
{
[SerializeField] private bool showGizmos;
private CustomSampler _updateSampler;
private PullTransformJobScheduler _pullTransformJobScheduler;
private PushTransformJobScheduler _pushTransformJobScheduler;
private UpdateSpringBoneJobScheduler _updateSpringBoneJobScheduler;
private RootBoneRegistry _rootBoneRegistry;
private ColliderGroupRegistry _colliderGroupRegistry;
private JobHandle _prevJobHandle;
public bool ShowGizmos { get => showGizmos; set => showGizmos = value; }
public void Initialize(
RootBoneRegistry rootBoneRegistry,
TransformRegistry transformRegistry,
ColliderGroupRegistry colliderGroupRegistry)
{
_rootBoneRegistry = rootBoneRegistry;
_colliderGroupRegistry = colliderGroupRegistry;
_updateSampler = CustomSampler.Create("FastSpringBone(Update)");
_pullTransformJobScheduler = new PullTransformJobScheduler(transformRegistry);
_pushTransformJobScheduler = new PushTransformJobScheduler(transformRegistry);
_updateSpringBoneJobScheduler = new UpdateSpringBoneJobScheduler(_rootBoneRegistry);
_rootBoneRegistry.SubscribeOnValueChanged(OnRootBoneChanged);
}
private void OnDestroy()
{
_rootBoneRegistry.UnSubscribeOnValueChanged(OnRootBoneChanged);
_prevJobHandle.Complete();
_pullTransformJobScheduler.Dispose();
_pushTransformJobScheduler.Dispose();
_updateSpringBoneJobScheduler.Dispose();
}
private void OnRootBoneChanged()
{
_prevJobHandle.Complete();
}
#if UNITY_EDITOR
private void OnDrawGizmos()
{
if (!ShowGizmos) return;
_prevJobHandle.Complete();
Gizmos.color = Color.blue;
foreach (var rootBoneWrapper in _rootBoneRegistry.Items)
{
rootBoneWrapper.Value.DrawGizmos();
}
Gizmos.color = Color.yellow;
foreach (var colliderGroup in _colliderGroupRegistry.Items)
{
colliderGroup.DrawGizmos();
}
}
#endif
private void LateUpdate()
{
_updateSampler.Begin();
_prevJobHandle.Complete();
var tempJobHandle = default(JobHandle);
tempJobHandle = _pullTransformJobScheduler.Schedule(tempJobHandle);
tempJobHandle = _updateSpringBoneJobScheduler.Schedule(tempJobHandle);
tempJobHandle = _pushTransformJobScheduler.Schedule(tempJobHandle);
_prevJobHandle = tempJobHandle;
_updateSampler.End();
}
}
}

View File

@@ -1,124 +0,0 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using VRM.FastSpringBones.Blittables;
using VRM.FastSpringBones.NativeWrappers;
using VRM.FastSpringBones.Registries;
namespace VRM.FastSpringBones.Components
{
/// <summary>
/// SpringBoneの1本の毛束の処理を担当するクラス
/// </summary>
public sealed class FastSpringRootBone : IDisposable
{
private readonly TransformRegistry _transformRegistry;
private readonly RootBoneRegistry _rootBoneRegistry;
private readonly ColliderGroupRegistry _colliderGroupRegistry;
private readonly Transform _transform;
private float _radius;
private NativeTransform _center;
private IReadOnlyDictionary<Transform, int> _transformIndexMap;
private NativeColliderGroups _nativeColliderGroups;
private NativePoints _nativePoints;
private NativePointer<BlittableRootBone> _rootBoneWrapper;
private readonly IList<NativeTransform> _transformWrappers = new List<NativeTransform>();
private readonly IList<NativePointer<BlittablePoint>> _points = new List<NativePointer<BlittablePoint>>();
public FastSpringRootBone(
TransformRegistry transformRegistry,
Transform transform,
RootBoneRegistry rootBoneRegistry,
ColliderGroupRegistry colliderGroupRegistry
)
{
_transformRegistry = transformRegistry;
_transform = transform;
_rootBoneRegistry = rootBoneRegistry;
_colliderGroupRegistry = colliderGroupRegistry;
}
public IReadOnlyList<FastSpringBoneColliderGroup> ColliderGroups
{
get => _nativeColliderGroups.ColliderGroups;
set => _nativeColliderGroups.ColliderGroups = value;
}
public unsafe void Initialize(
float gravityPower,
Vector3 gravityDir,
float dragForce,
float stiffnessForce,
IReadOnlyList<FastSpringBoneColliderGroup> colliderGroups,
float radius,
Transform center)
{
_radius = radius;
if (center != null)
{
_center = new NativeTransform(_transformRegistry, TransformSynchronizationType.PullOnly, center);
}
_nativeColliderGroups = new NativeColliderGroups(colliderGroups);
NativeTransform parent = null;
if (_transform.parent)
{
parent = new NativeTransform(_transformRegistry, TransformSynchronizationType.PullOnly, _transform.parent);
}
SetupRecursive(_transform, parent);
_nativePoints = new NativePoints(_points);
_rootBoneWrapper = new NativePointer<BlittableRootBone>(new BlittableRootBone(gravityPower, gravityDir, dragForce, stiffnessForce, _nativePoints.GetUnsafePtr()));
_rootBoneRegistry.Register(_rootBoneWrapper);
_colliderGroupRegistry.Register(_nativeColliderGroups);
}
public void Dispose()
{
_colliderGroupRegistry.Unregister(_nativeColliderGroups);
_rootBoneRegistry.Unregister(_rootBoneWrapper);
foreach (var transformWrapper in _transformWrappers)
{
transformWrapper.Dispose();
}
foreach (var point in _points)
{
point.Dispose();
}
_center?.Dispose();
_nativeColliderGroups?.Dispose();
_nativePoints.Dispose();
_rootBoneWrapper.Dispose();
}
private unsafe void SetupRecursive(Transform trs, NativeTransform parent = null)
{
var transformWrapper = new NativeTransform(_transformRegistry, TransformSynchronizationType.PushOnly, trs, parent);
_transformWrappers.Add(transformWrapper);
var point = new NativePointer<BlittablePoint>(
new BlittablePoint(
trs,
_radius,
_center != null ? _center.GetUnsafePtr() : null,
_nativeColliderGroups.GetUnsafePtr(),
transformWrapper.GetUnsafePtr())
);
_points.Add(point);
for (var i = 0; i < trs.childCount; ++i)
{
SetupRecursive(trs.GetChild(i), transformWrapper);
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 24cdd60af2d54c908e84b17802bfb5bc
timeCreated: 1550036996

View File

@@ -1,21 +0,0 @@
{
"name": "FastSpringBone",
"references": [
"GUID:2665a8d13d1b3f18800f46e256720795"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.burst",
"expression": "0.0.1",
"define": "ENABLE_SPRINGBONE_BURST"
}
],
"noEngineReferences": false
}

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: ac229b552c3025545b074203f857547c
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 96634b0bb2d12d044a21f3fdff7a2c43
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,31 +0,0 @@
using System;
using Unity.Collections;
using VRM.FastSpringBones.Blittables;
namespace VRM.FastSpringBones.NativeWrappers
{
/// <summary>
/// BlittableColliderGroupのライフサイクルを管理するWrapper
/// </summary>
public sealed unsafe class NativeColliderGroup : IDisposable
{
private readonly NativePointer<BlittableColliderGroup> _nativePointer;
private NativeArray<BlittableCollider> Colliders { get; }
public BlittableColliderGroup* GetUnsafePtr() => _nativePointer.GetUnsafePtr();
public NativeColliderGroup(BlittableCollider[] colliders, NativeTransform nativeTransform)
{
Colliders = new NativeArray<BlittableCollider>(colliders, Allocator.Persistent);
_nativePointer = new NativePointer<BlittableColliderGroup>(new BlittableColliderGroup(Colliders, nativeTransform.GetUnsafePtr()));
}
public void Dispose()
{
if (Colliders.IsCreated) Colliders.Dispose();
_nativePointer.Dispose();
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 4cf2baba01384c31a1fdcca7489007f4
timeCreated: 1550640666

View File

@@ -1,78 +0,0 @@
using System;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using VRM.FastSpringBones.Blittables;
using VRM.FastSpringBones.Components;
namespace VRM.FastSpringBones.NativeWrappers
{
/// <summary>
/// BlittableColliderGroupsのライフサイクルを管理するWrapper
/// </summary>
public sealed unsafe class NativeColliderGroups : IDisposable
{
private readonly NativePointer<BlittableColliderGroups> _nativePointer = new NativePointer<BlittableColliderGroups>();
private NativeArray<BlittableColliderGroup> _colliderGroupArray;
private IReadOnlyList<FastSpringBoneColliderGroup> _colliderGroups;
//Disposeされた後にUpdateColliderGroupsが呼ばれるのを防ぐためのフラグ
private bool _isDisposed;
public BlittableColliderGroups* GetUnsafePtr() => _nativePointer.GetUnsafePtr();
public void DrawGizmos() => _nativePointer.Value.DrawGizmos();
public IReadOnlyList<FastSpringBoneColliderGroup> ColliderGroups
{
get => _colliderGroups;
set
{
_colliderGroups = value;
UpdateColliderGroups();
}
}
private void UpdateColliderGroups()
{
if (_isDisposed) return;
if (_colliderGroupArray.IsCreated)
{
_colliderGroupArray.Dispose();
}
CreateColliderGroupArray(_colliderGroups);
UpdateData();
}
public NativeColliderGroups(IReadOnlyList<FastSpringBoneColliderGroup> colliderGroups)
{
_colliderGroups = colliderGroups;
UpdateColliderGroups();
}
public void Dispose()
{
if (_colliderGroupArray.IsCreated)
{
_colliderGroupArray.Dispose();
_isDisposed = true;
}
_nativePointer.Dispose();
}
private void CreateColliderGroupArray(IReadOnlyList<FastSpringBoneColliderGroup> colliderGroups)
{
_colliderGroupArray = new NativeArray<BlittableColliderGroup>(colliderGroups.Count, Allocator.Persistent);
for (var i = 0; i < _colliderGroupArray.Length; ++i)
{
_colliderGroupArray[i] = *colliderGroups[i].ColliderGroupPtr;
}
}
private void UpdateData()
{
_nativePointer.Value = new BlittableColliderGroups(
(BlittableColliderGroup*)_colliderGroupArray.GetUnsafePtr(),
_colliderGroupArray.Length);
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 3944dc9ee4a94814bc1d333fd0b7ba82
timeCreated: 1550645184

View File

@@ -1,35 +0,0 @@
using System;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace VRM.FastSpringBones.NativeWrappers
{
public sealed class NativePointer<T> : IDisposable where T : unmanaged
{
private readonly unsafe T* _unsafePtr;
public unsafe T* GetUnsafePtr() => _unsafePtr;
public unsafe T Value
{
get => *_unsafePtr;
set => *_unsafePtr = value;
}
public unsafe NativePointer()
{
_unsafePtr = (T*)UnsafeUtility.Malloc(sizeof(T), 16, Allocator.Persistent);
}
public unsafe NativePointer(T value)
{
_unsafePtr = (T*)UnsafeUtility.Malloc(sizeof(T), 16, Allocator.Persistent);
Value = value;
}
public unsafe void Dispose()
{
UnsafeUtility.Free(_unsafePtr, Allocator.Persistent);
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 70f8b67ae2644eb999cfb45ea5bf25a3
timeCreated: 1595316733

View File

@@ -1,39 +0,0 @@
using System;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using VRM.FastSpringBones.Blittables;
namespace VRM.FastSpringBones.NativeWrappers
{
/// <summary>
/// BlittablePointsGroupのライフサイクルを管理するWrapper
/// </summary>
public sealed unsafe class NativePoints : IDisposable
{
private readonly NativePointer<BlittablePoints> _nativePointer;
private NativeArray<BlittablePoint> _buffer;
public BlittablePoints* GetUnsafePtr() => _nativePointer.GetUnsafePtr();
public NativePoints(IList<NativePointer<BlittablePoint>> points)
{
_buffer = new NativeArray<BlittablePoint>(points.Count, Allocator.Persistent);
for (var i = 0; i < _buffer.Length; ++i)
{
_buffer[i] = points[i].Value;
}
_nativePointer = new NativePointer<BlittablePoints>(new BlittablePoints((BlittablePoint*) _buffer.GetUnsafePtr(), _buffer.Length));
}
public void Dispose()
{
if (_buffer.IsCreated)
{
_buffer.Dispose();
}
_nativePointer.Dispose();
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 7a8763ad7e8243d3b4001a2beb37bf9d
timeCreated: 1550723674

View File

@@ -1,43 +0,0 @@
using System;
using UnityEngine;
using VRM.FastSpringBones.Blittables;
using VRM.FastSpringBones.Registries;
namespace VRM.FastSpringBones.NativeWrappers
{
/// <summary>
/// BlittableTransformのライフサイクルを管理するWrapper
/// </summary>
public sealed class NativeTransform : IDisposable
{
private readonly NativePointer<BlittableTransform> _nativePointer;
public Transform Transform { get; }
private readonly TransformRegistry _transformRegistry;
public unsafe BlittableTransform* GetUnsafePtr() => _nativePointer.GetUnsafePtr();
public BlittableTransform Value => _nativePointer.Value;
public unsafe NativeTransform(
TransformRegistry transformRegistry,
TransformSynchronizationType transformSynchronizationType,
Transform transform,
NativeTransform parent = null
)
{
_nativePointer = new NativePointer<BlittableTransform>(new BlittableTransform(parent != null ? parent.GetUnsafePtr() : null, transform));
Transform = transform;
_transformRegistry = transformRegistry;
_transformRegistry.Register(this, transformSynchronizationType);
}
public void Dispose()
{
_transformRegistry.Unregister(this);
_nativePointer.Dispose();
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 2acc8862ffb44f4fbd4e1e03e9a9d46e
timeCreated: 1550545201

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: daa8d50bee84b0c4387d22784cce66ff
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,8 +0,0 @@
using VRM.FastSpringBones.NativeWrappers;
namespace VRM.FastSpringBones.Registries
{
public sealed class ColliderGroupRegistry : Registry<NativeColliderGroups>
{
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: eec0fe3cd3694a379885fa62a9994bd5
timeCreated: 1594963749

View File

@@ -1,28 +0,0 @@
using System;
using System.Collections.Generic;
namespace VRM.FastSpringBones.Registries
{
public class Registry<T>
{
private readonly List<T> _items = new List<T>();
private Action _onValueChanged;
public IReadOnlyList<T> Items => _items;
public void Register(T value)
{
_items.Add(value);
_onValueChanged?.Invoke();
}
public void Unregister(T value)
{
_items.Remove(value);
_onValueChanged?.Invoke();
}
public void SubscribeOnValueChanged(Action action) => _onValueChanged += action;
public void UnSubscribeOnValueChanged(Action action) => _onValueChanged -= action;
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 0d3b558d03dd42b184512fe19797ff07
timeCreated: 1595407071

View File

@@ -1,12 +0,0 @@
using VRM.FastSpringBones.Blittables;
using VRM.FastSpringBones.NativeWrappers;
namespace VRM.FastSpringBones.Registries
{
/// <summary>
/// 今生きているRootBoneの一覧を返すクラス
/// </summary>
public sealed class RootBoneRegistry : Registry<NativePointer<BlittableRootBone>>
{
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 4609c8e2a4c64fb7b3bfa587f475e965
timeCreated: 1550734399

View File

@@ -1,60 +0,0 @@
using System;
using System.Collections.Generic;
using VRM.FastSpringBones.NativeWrappers;
namespace VRM.FastSpringBones.Registries
{
/// <summary>
/// 今生きているTransformの一覧を返すクラス
/// </summary>
public sealed class TransformRegistry
{
private readonly List<NativeTransform> _transforms = new List<NativeTransform>();
public IReadOnlyList<NativeTransform> Transforms => _transforms;
private readonly List<NativeTransform> _pullTargets = new List<NativeTransform>();
public IReadOnlyList<NativeTransform> PullTargets => _pullTargets;
private readonly List<NativeTransform> _pushTargets = new List<NativeTransform>();
public IReadOnlyList<NativeTransform> PushTargets => _pushTargets;
private Action _onValueChanged;
public void SubscribeOnValueChanged(Action action) => _onValueChanged += action;
public void UnSubscribeOnValueChanged(Action action) => _onValueChanged -= action;
public void Register(NativeTransform nativeTransform, TransformSynchronizationType synchronizationType)
{
_transforms.Add(nativeTransform);
switch (synchronizationType)
{
case TransformSynchronizationType.PullOnly:
_pullTargets.Add(nativeTransform);
break;
case TransformSynchronizationType.PushOnly:
_pushTargets.Add(nativeTransform);
break;
default:
throw new ArgumentOutOfRangeException(nameof(synchronizationType), synchronizationType, null);
}
_onValueChanged?.Invoke();
}
public void Unregister(NativeTransform nativeTransform)
{
_transforms.Remove(nativeTransform);
if (_pullTargets.Contains(nativeTransform))
{
_pullTargets.Remove(nativeTransform);
}
if (_pushTargets.Contains(nativeTransform))
{
_pushTargets.Remove(nativeTransform);
}
_onValueChanged?.Invoke();
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 102dd7059fb34c00a7d1d6d4f2c38729
timeCreated: 1550666913

View File

@@ -1,8 +0,0 @@
namespace VRM.FastSpringBones.Registries
{
public enum TransformSynchronizationType
{
PullOnly,
PushOnly,
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 6d3911eb7ee54861b51bccf4ed32aec2
timeCreated: 1551169872

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 802ad91bcfdacbd4bbce16eff061cd68
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,119 +0,0 @@
using System;
using System.Collections.Generic;
#if ENABLE_SPRINGBONE_BURST
using Unity.Burst;
#endif
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using UnityEngine.Jobs;
using UnityEngine.Profiling;
using VRM.FastSpringBones.Blittables;
using VRM.FastSpringBones.NativeWrappers;
using VRM.FastSpringBones.Registries;
namespace VRM.FastSpringBones.Schedulers
{
/// <summary>
/// GameObjectの世界からBlittableな世界へTransformを複製する処理を行うクラス
/// </summary>
public sealed unsafe class PullTransformJobScheduler : IDisposable
{
private BlittableTransform** _transformPointers;
private TransformAccessArray _transformAccessArray;
private readonly CustomSampler _sampler = CustomSampler.Create("Schedule CopyFromTransformJob");
private readonly TransformRegistry _transformRegistry;
private bool _dirty = true;
private IReadOnlyList<NativeTransform> Targets => _transformRegistry.PullTargets;
public PullTransformJobScheduler(TransformRegistry transformRegistry)
{
_transformRegistry = transformRegistry;
_transformRegistry.SubscribeOnValueChanged(OnTransformChanged);
}
private void OnTransformChanged()
{
_dirty = true;
}
public JobHandle Schedule(JobHandle dependOn = default(JobHandle))
{
if (Targets.Count == 0)
{
return dependOn;
}
_sampler.Begin();
// リストが変更されていたらバッファを再構築
if (_dirty)
{
ReconstructBuffers();
_dirty = false;
}
// ジョブを発火
var job = new Job { TransformPointers = _transformPointers };
var jobHandle = job.Schedule(_transformAccessArray, dependOn);
_sampler.End();
return jobHandle;
}
private void ReconstructBuffers()
{
ReleaseBuffers();
var transforms = Targets;
_transformPointers = (BlittableTransform**)UnsafeUtility.Malloc(
sizeof(BlittableTransform*) * transforms.Count,
16,
Allocator.Persistent
);
_transformAccessArray = new TransformAccessArray(transforms.Count);
for (var i = 0; i < transforms.Count; i++)
{
_transformPointers[i] = transforms[i].GetUnsafePtr();
_transformAccessArray.Add(transforms[i].Transform);
}
}
public void Dispose()
{
ReleaseBuffers();
_transformRegistry.UnSubscribeOnValueChanged(OnTransformChanged);
}
private void ReleaseBuffers()
{
if (_transformAccessArray.isCreated) _transformAccessArray.Dispose();
if (_transformPointers != null)
{
UnsafeUtility.Free(_transformPointers, Allocator.Persistent);
_transformPointers = null;
}
}
#if ENABLE_SPRINGBONE_BURST
[BurstCompile]
#endif
private struct Job : IJobParallelForTransform
{
[NativeDisableUnsafePtrRestriction] public BlittableTransform** TransformPointers;
public void Execute(int index, TransformAccess transform)
{
TransformPointers[index]->PullFrom(transform);
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 83ca6a73284247c0a809eb9b83b6296b
timeCreated: 1550547066

View File

@@ -1,119 +0,0 @@
using System;
using System.Collections.Generic;
#if ENABLE_SPRINGBONE_BURST
using Unity.Burst;
#endif
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using UnityEngine.Jobs;
using UnityEngine.Profiling;
using VRM.FastSpringBones.Blittables;
using VRM.FastSpringBones.NativeWrappers;
using VRM.FastSpringBones.Registries;
namespace VRM.FastSpringBones.Schedulers
{
/// <summary>
/// Blittableな世界からGameObjectの世界へTransformを送り込む処理を行うクラス
/// </summary>
public sealed unsafe class PushTransformJobScheduler : IDisposable
{
private BlittableTransform** _transformPointers;
private TransformAccessArray _transformAccessArray;
private readonly CustomSampler _sampler = CustomSampler.Create("Schedule CopyFromTransformJob");
private readonly TransformRegistry _transformRegistry;
private bool _dirty = true;
private IReadOnlyList<NativeTransform> Targets => _transformRegistry.PushTargets;
public PushTransformJobScheduler(TransformRegistry transformRegistry)
{
_transformRegistry = transformRegistry;
_transformRegistry.SubscribeOnValueChanged(OnTransformChanged);
}
private void OnTransformChanged()
{
_dirty = true;
}
public JobHandle Schedule(JobHandle dependOn = default(JobHandle))
{
if (Targets.Count == 0)
{
return dependOn;
}
_sampler.Begin();
// リストが変更されていたらバッファを再構築
if (_dirty)
{
ReconstructBuffers();
_dirty = false;
}
// Jobを発火
var job = new Job { TransformPointers = _transformPointers };
var jobHandle = job.Schedule(_transformAccessArray, dependOn);
_sampler.End();
return jobHandle;
}
private void ReconstructBuffers()
{
ReleaseBuffers();
var transforms = Targets;
_transformPointers = (BlittableTransform**)UnsafeUtility.Malloc(
sizeof(BlittableTransform*) * transforms.Count,
16,
Allocator.Persistent
);
_transformAccessArray = new TransformAccessArray(transforms.Count);
for (var i = 0; i < transforms.Count; i++)
{
_transformPointers[i] = transforms[i].GetUnsafePtr();
_transformAccessArray.Add(transforms[i].Transform);
}
}
public void Dispose()
{
ReleaseBuffers();
_transformRegistry.UnSubscribeOnValueChanged(OnTransformChanged);
}
private void ReleaseBuffers()
{
if (_transformAccessArray.isCreated) _transformAccessArray.Dispose();
if (_transformPointers != null)
{
UnsafeUtility.Free(_transformPointers, Allocator.Persistent);
_transformPointers = null;
}
}
#if ENABLE_SPRINGBONE_BURST
[BurstCompile]
#endif
private struct Job : IJobParallelForTransform
{
[NativeDisableUnsafePtrRestriction] public BlittableTransform** TransformPointers;
public void Execute(int index, TransformAccess transform)
{
TransformPointers[index]->PushTo(transform);
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 5a12fcea984d4387a5f3d0f5291df34e
timeCreated: 1550717407

View File

@@ -1,113 +0,0 @@
using System;
using System.Collections.Generic;
#if ENABLE_SPRINGBONE_BURST
using Unity.Burst;
#endif
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.Profiling;
using VRM.FastSpringBones.Blittables;
using VRM.FastSpringBones.NativeWrappers;
using VRM.FastSpringBones.Registries;
namespace VRM.FastSpringBones.Schedulers
{
/// <summary>
/// SpringBoneを更新する処理を行うクラス
/// </summary>
public sealed unsafe class UpdateSpringBoneJobScheduler : IDisposable
{
private BlittableRootBone** _rootBonePointers;
private readonly RootBoneRegistry _rootBoneRegistry;
private readonly CustomSampler _sampler = CustomSampler.Create("Schedule CopyFromTransformJob");
private bool _dirty = true;
private IReadOnlyList<NativePointer<BlittableRootBone>> Targets => _rootBoneRegistry.Items;
public UpdateSpringBoneJobScheduler(RootBoneRegistry rootBoneRegistry)
{
_rootBoneRegistry = rootBoneRegistry;
_rootBoneRegistry.SubscribeOnValueChanged(OnRootBoneChanged);
}
public void Dispose()
{
ReleaseBuffer();
_rootBoneRegistry.UnSubscribeOnValueChanged(OnRootBoneChanged);
}
private void OnRootBoneChanged()
{
_dirty = true;
}
public JobHandle Schedule(JobHandle dependOn = default(JobHandle))
{
if (Targets.Count == 0)
{
return dependOn;
}
_sampler.Begin();
// リストが変更されていたらバッファを再構築
if (_dirty)
{
ReconstructBuffers();
_dirty = false;
}
// Jobを発火
var job = new Job { RootBonePointers = _rootBonePointers, DeltaTime = Time.deltaTime };
var jobHandle = job.Schedule(Targets.Count, 0, dependOn);
_sampler.End();
return jobHandle;
}
private void ReconstructBuffers()
{
ReleaseBuffer();
_rootBonePointers = (BlittableRootBone**)UnsafeUtility.Malloc(
sizeof(BlittableTransform*) * Targets.Count,
16,
Allocator.Persistent
);
for (var i = 0; i < Targets.Count; i++)
{
_rootBonePointers[i] = Targets[i].GetUnsafePtr();
}
}
private void ReleaseBuffer()
{
if (_rootBonePointers == null) return;
UnsafeUtility.Free(_rootBonePointers, Allocator.Persistent);
_rootBonePointers = null;
}
#if ENABLE_SPRINGBONE_BURST
[BurstCompile]
#endif
private struct Job : IJobParallelFor
{
[NativeDisableUnsafePtrRestriction] public BlittableRootBone** RootBonePointers;
public float DeltaTime;
public void Execute(int index)
{
// 各点を更新する
RootBonePointers[index]->Update(DeltaTime);
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: a6b730c674ce4484862ddee8459c8598
timeCreated: 1549516763

View File

@@ -1,130 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using UniGLTF;
using UnityEngine;
using VRM.FastSpringBones.Blittables;
using VRM.FastSpringBones.Components;
using Object = UnityEngine.Object;
namespace VRM
{
/// <summary>
/// 指定されたGameObject内にあるSpringBoneをFastSpringBoneに差し替えるユーティリティ
/// </summary>
public static class FastSpringBoneReplacer
{
public static async Task ReplaceAsync(GameObject gameObject, IAwaitCaller awaitCaller = null, CancellationToken token = default)
{
var service = FastSpringBoneService.Instance;
var springBones = gameObject.GetComponentsInChildren<VRMSpringBone>();
var disposer = gameObject.AddComponent<FastSpringBoneDisposer>();
// VRMSpringBoneで動いた後の状態がFastSpringBoneの初期状態にならないようにするためawait UniTask.Yield()する前にVRMSpringBoneをdisableにしておく
foreach (var springBone in springBones)
{
springBone.enabled = false;
};
if (awaitCaller != null)
{
await awaitCaller.NextFrame();
token.ThrowIfCancellationRequested();
}
var vrmColliderGroups = gameObject.GetComponentsInChildren<VRMSpringBoneColliderGroup>();
var colliderGroupDictionary = new Dictionary<VRMSpringBoneColliderGroup, FastSpringBoneColliderGroup>();
// Colliderを差し替える
foreach (var vrmColliderGroup in vrmColliderGroups)
{
if (awaitCaller != null)
{
await awaitCaller.NextFrame();
token.ThrowIfCancellationRequested();
}
var fastSpringBoneCollider = vrmColliderGroup.gameObject.AddComponent<FastSpringBoneColliderGroup>();
fastSpringBoneCollider.Initialize(
service.TransformRegistry,
vrmColliderGroup.Colliders
.Select(data => new BlittableCollider(data.Offset, data.Radius))
.ToArray()
);
colliderGroupDictionary[vrmColliderGroup] = fastSpringBoneCollider;
}
var springRootBones =
(
from springBone in springBones
from rootBone in springBone.RootBones
select (springBone, rootBone)
).ToList();
for (var i = 0; i < springRootBones.Count; i++)
{
var current = springRootBones[i];
// 他のRootBoneのどれかが、自分の親もしくは同じTransformなら自分自身を削除する
if (springRootBones
.Where(other => other != current)
.Any(other => current.rootBone.IsChildOf(other.rootBone)))
{
springRootBones.RemoveAt(i);
--i;
}
}
if (awaitCaller != null)
{
await awaitCaller.NextFrame();
token.ThrowIfCancellationRequested();
}
token.ThrowIfCancellationRequested();
foreach (var (vrmSpringBone, rootBoneTransform) in springRootBones)
{
// FastSpringRootBoneに差し替える
var fastSpringRootBone =
new FastSpringRootBone(
service.TransformRegistry,
rootBoneTransform,
service.RootBoneRegistry,
service.ColliderGroupRegistry);
disposer.Add(fastSpringRootBone);
var colliderGroups =
vrmSpringBone.ColliderGroups != null
? vrmSpringBone.ColliderGroups.Select(group => colliderGroupDictionary[@group]).ToArray()
: Array.Empty<FastSpringBoneColliderGroup>();
fastSpringRootBone.Initialize(
vrmSpringBone.m_gravityPower,
vrmSpringBone.m_gravityDir,
vrmSpringBone.m_dragForce,
vrmSpringBone.m_stiffnessForce,
colliderGroups,
vrmSpringBone.m_hitRadius,
vrmSpringBone.m_center
);
Object.Destroy(vrmSpringBone);
if (awaitCaller != null)
{
await awaitCaller.NextFrame();
token.ThrowIfCancellationRequested();
}
token.ThrowIfCancellationRequested();
}
// Colliderを削除
foreach (var vrmSpringBoneColliderGroup in vrmColliderGroups)
{
Object.Destroy(vrmSpringBoneColliderGroup);
}
}
}
}

View File

@@ -1,54 +0,0 @@
using UnityEngine;
using VRM.FastSpringBones.Components;
using VRM.FastSpringBones.Registries;
namespace VRM
{
/// <summary>
/// Scene 上に単一で存在する、FastSpringBone の ServiceLocator 兼 EntryPoint
/// </summary>
public class FastSpringBoneService : MonoBehaviour
{
public RootBoneRegistry RootBoneRegistry { get; private set; }
public TransformRegistry TransformRegistry { get; private set; }
public ColliderGroupRegistry ColliderGroupRegistry { get; private set; }
public FastSpringBoneScheduler FastSpringBoneScheduler { get; private set; }
private static FastSpringBoneService _instance;
public static FastSpringBoneService Instance
{
get
{
if (!_instance)
{
var gameObject = new GameObject("FastSpringBone Service");
DontDestroyOnLoad(gameObject);
_instance = gameObject.AddComponent<FastSpringBoneService>();
}
return _instance;
}
}
/// <summary>
/// 専有しているインスタンスを破棄する
/// </summary>
public static void Free()
{
Destroy(_instance.gameObject);
_instance = null;
}
private void Awake()
{
RootBoneRegistry = new RootBoneRegistry();
TransformRegistry = new TransformRegistry();
ColliderGroupRegistry = new ColliderGroupRegistry();
FastSpringBoneScheduler = gameObject.AddComponent<FastSpringBoneScheduler>();
FastSpringBoneScheduler.Initialize(
RootBoneRegistry,
TransformRegistry,
ColliderGroupRegistry);
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: cf09adfffebbff24eb2185e1d9b5b0a9
guid: ec0b831d5a861c2479ab473c4ee62a5d
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -0,0 +1,97 @@
using System;
using UnityEngine;
using UniGLTF.SpringBoneJobs.InputPorts;
using System.Linq;
using UniGLTF.SpringBoneJobs.Blittables;
using System.Collections.Generic;
namespace VRM.SpringBoneJobs
{
public static class FastSpringBoneReplacer
{
/// <summary>
/// - 指定された GameObject 内にある SpringBone を停止させる
/// - FastSpringBoneBuffer に変換する
/// </summary>
public static FastSpringBoneBuffer MakeBuffer(GameObject root)
{
var components = root.GetComponentsInChildren<VRMSpringBone>();
foreach (var sb in components)
{
// 停止させて FastSpringBoneService から実行させる
sb.m_updateType = VRMSpringBone.SpringBoneUpdateType.Manual;
}
// create(Spring情報の再収集。設定変更の反映)
var springs = components.Select(spring => new FastSpringBoneSpring
{
center = spring.m_center,
colliders = spring.ColliderGroups
.SelectMany(group => group.Colliders.Select(collider => new FastSpringBoneCollider
{
Transform = group.transform,
Collider = new BlittableCollider
{
offset = collider.Offset,
radius = collider.Radius,
tailOrNormal = default,
colliderType = BlittableColliderType.Sphere
}
})).ToArray(),
joints = spring.RootBones.Select(x => Traverse(spring, x)).SelectMany(x => x).ToArray(),
}).ToArray();
return new FastSpringBoneBuffer(springs);
}
static IEnumerable<FastSpringBoneJoint> Traverse(VRMSpringBone spring, Transform joint)
{
yield return new FastSpringBoneJoint
{
Transform = joint.transform,
Joint = new BlittableJoint
{
radius = spring.m_hitRadius,
dragForce = spring.m_dragForce,
gravityDir = spring.m_gravityDir,
gravityPower = spring.m_gravityPower,
stiffnessForce = spring.m_stiffnessForce
},
DefaultLocalRotation = joint.transform.localRotation,
};
foreach (Transform child in joint)
{
foreach (var x in Traverse(spring, child))
{
yield return x;
}
}
// TODO: 詳細分岐
// 7cm tail
// var springRootBones =
// (
// from springBone in springBones
// from rootBone in springBone.RootBones
// select (springBone, rootBone)
// ).ToList();
// for (var i = 0; i < springRootBones.Count; i++)
// {
// var current = springRootBones[i];
// // 他のRootBoneのどれかが、自分の親もしくは同じTransformなら自分自身を削除する
// if (springRootBones
// .Where(other => other != current)
// .Any(other => current.rootBone.IsChildOf(other.rootBone)))
// {
// springRootBones.RemoveAt(i);
// --i;
// }
// }
}
}
}

View File

@@ -0,0 +1,73 @@
using UniGLTF.SpringBoneJobs;
using UnityEngine;
using UnityEngine.Profiling;
namespace VRM.SpringBoneJobs
{
/// <summary>
/// シングルトン。FastSpringBone の ServiceLocator 兼 EntryPoint。
/// シーンすべてのVRMのSpringBoneをバッチングしてまとめて実行する。
/// </summary>
[DefaultExecutionOrder(11000)]
public class FastSpringBoneService : MonoBehaviour
{
private CustomSampler _updateSampler = CustomSampler.Create("FastSpringBone(Update)");
public FastSpringBoneBufferCombiner BufferCombiner { get; private set; }
private FastSpringBoneScheduler _fastSpringBoneScheduler;
private static FastSpringBoneService _instance;
public static FastSpringBoneService Instance
{
get
{
if (_instance) return _instance;
#if UNITY_2022_3_OR_NEWER
_instance = FindFirstObjectByType<FastSpringBoneService>();
#else
_instance = FindObjectOfType<FastSpringBoneService>();
#endif
if (_instance) return _instance;
var gameObject = new GameObject("FastSpringBone Service");
DontDestroyOnLoad(gameObject);
_instance = gameObject.AddComponent<FastSpringBoneService>();
return _instance;
}
}
/// <summary>
/// 専有しているインスタンスを破棄する
/// </summary>
public static void Free()
{
Destroy(_instance.gameObject);
_instance = null;
}
private void OnEnable()
{
BufferCombiner = new FastSpringBoneBufferCombiner();
_fastSpringBoneScheduler = new FastSpringBoneScheduler(BufferCombiner);
}
private void OnDisable()
{
BufferCombiner.Dispose();
_fastSpringBoneScheduler.Dispose();
}
private void LateUpdate()
{
_updateSampler.Begin();
_fastSpringBoneScheduler.Schedule(Time.deltaTime).Complete();
_updateSampler.End();
}
}
}

View File

@@ -4,11 +4,10 @@
"references": [
"GUID:b7aa47b240b57de44a4b2021c143c9bf",
"GUID:8d76e605759c3f64a957d63ef96ada7c",
"GUID:da3e51d19d51a544fa14d43fee843098",
"GUID:a9bc101fb0471f94a8f99fd242fdd934",
"GUID:ac229b552c3025545b074203f857547c",
"GUID:1cd941934d098654fa21a13f28346412",
"GUID:60c8346e00a8ddd4cafc5a02eceeec57"
"GUID:60c8346e00a8ddd4cafc5a02eceeec57",
"GUID:3e5d614bc16b50d41bd94c8d7444ca46"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

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

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: af417d77d895bc84ba5d2c8f0542bdd8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,33 +0,0 @@
using Unity.Collections;
using UnityEngine.Jobs;
using UniGLTF.SpringBoneJobs.Blittables;
#if ENABLE_SPRINGBONE_BURST
using Unity.Burst;
#endif
namespace UniVRM10.FastSpringBones.System
{
#if ENABLE_SPRINGBONE_BURST
[BurstCompile]
#endif
public struct PullTransformJob : IJobParallelForTransform
{
[WriteOnly] public NativeArray<BlittableTransform> Transforms;
public void Execute(int index, TransformAccess transform)
{
Transforms[index] = new BlittableTransform
{
position = transform.position,
rotation = transform.rotation,
localPosition = transform.localPosition,
localRotation = transform.localRotation,
localScale = transform.localScale,
localToWorldMatrix = transform.localToWorldMatrix,
worldToLocalMatrix = transform.worldToLocalMatrix
};
}
}
}

View File

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

View File

@@ -5,7 +5,8 @@
"GUID:da3e51d19d51a544fa14d43fee843098",
"GUID:b7aa47b240b57de44a4b2021c143c9bf",
"GUID:8d76e605759c3f64a957d63ef96ada7c",
"GUID:05dd262a0c0a2f841b8252c8c3815582"
"GUID:05dd262a0c0a2f841b8252c8c3815582",
"GUID:3e5d614bc16b50d41bd94c8d7444ca46"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UniGLTF;
using UniGLTF.SpringBoneJobs;
using UniHumanoid;
using UnityEngine;
using UnityEngine.UI;
@@ -387,7 +388,14 @@ namespace VRM.SimpleViewer
if (m_useFastSpringBone.isOn)
{
var _ = FastSpringBoneReplacer.ReplaceAsync(instance.Root);
// job用バッファ作成
var buffer = SpringBoneJobs.FastSpringBoneReplacer.MakeBuffer(instance.Root);
// 登録
SpringBoneJobs.FastSpringBoneService.Instance.BufferCombiner.Register(buffer);
// 削除登録
instance.Root.AddComponent<FastSpringBoneDisposer>().Add(buffer);
}
instance.EnableUpdateWhenOffscreen();