Skip to content

Commit

Permalink
Merge pull request #2457 from ousttrue/fix/springbone0x_runtime_inter…
Browse files Browse the repository at this point in the history
…face

[0.x] IVrm0XSpringBoneRuntime の実装
  • Loading branch information
ousttrue authored Oct 8, 2024
2 parents b45b514 + 60a226a commit 83b63ea
Show file tree
Hide file tree
Showing 12 changed files with 2,740 additions and 303 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace UniGLTF.SpringBoneJobs.Blittables
public struct BlittableModelLevel
{
/// <summary>
/// 風など。
/// World 座標系の追加の力。風など。
/// </summary>
public Vector3 ExternalForce;

Expand Down
23 changes: 23 additions & 0 deletions Assets/VRM/Runtime/SpringBone/IVrm0XSpringBoneRuntime.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
using System.Threading.Tasks;
using UniGLTF;
using UniGLTF.SpringBoneJobs.Blittables;
using UnityEngine;

namespace VRM
{
public interface IVrm0XSpringBoneRuntime
{
public Task InitializeAsync(GameObject vrm, IAwaitCaller awaitCaller);

/// <summary>
/// SpringBone の構成変更を反映して再構築する。
/// </summary>
public void ReconstructSpringBone();

/// <summary>
/// initialTransform 状態に復帰。verlet の速度 も 0 に。
/// </summary>
public void RestoreInitialTransform();

/// <summary>
/// Joint レベルの可変情報をセットする
/// stiffness,
/// </summary>
public void SetJointLevel(Transform joint, BlittableJointMutable jointSettings);

/// <summary>
/// Model レベルの可変情報をセットする
/// 風, pause, scaling
/// </summary>
public void SetModelLevel(Transform modelRoot, BlittableModelLevel modelSettings);
}
}
24 changes: 10 additions & 14 deletions Assets/VRM/Runtime/SpringBone/Jobs/FastSpringBoneReplacer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,17 @@
using UniGLTF;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using UniGLTF.Utils;


namespace VRM.SpringBoneJobs
{
public static class FastSpringBoneReplacer
{
/// <summary>
/// - 指定された GameObject 内にある SpringBone を停止させる
/// - FastSpringBoneBuffer に変換する
/// </summary>
public static async Task<FastSpringBoneBuffer> MakeBufferAsync(GameObject root, IAwaitCaller awaitCaller = null, CancellationToken token = default)
{
var components = root.GetComponentsInChildren<VRMSpringBone>();
foreach (var sb in components)
{
// 停止させて FastSpringBoneService から実行させる
sb.m_updateType = VRMSpringBone.SpringBoneUpdateType.Manual;
}

var springs = new FastSpringBoneSpring[components.Length];
for (int i = 0; i < components.Length; ++i)
Expand Down Expand Up @@ -57,6 +50,9 @@ public static async Task<FastSpringBoneBuffer> MakeBufferAsync(GameObject root,
}
}

var runtime = root.GetComponent<RuntimeGltfInstance>();
var initMap = runtime != null ? runtime.InitialTransformStates : root.GetComponentsInChildren<Transform>().ToDictionary(x => x, x => new TransformState(x));

var joints = new List<FastSpringBoneJoint>();
foreach (var springRoot in component.RootBones)
{
Expand All @@ -67,7 +63,7 @@ public static async Task<FastSpringBoneBuffer> MakeBufferAsync(GameObject root,
token.ThrowIfCancellationRequested();
}

Traverse(joints, component, springRoot);
Traverse(joints, component, springRoot, initMap);
}

var spring = new FastSpringBoneSpring
Expand All @@ -82,11 +78,11 @@ public static async Task<FastSpringBoneBuffer> MakeBufferAsync(GameObject root,
return new FastSpringBoneBuffer(root.transform, springs);
}

static void Traverse(List<FastSpringBoneJoint> joints, VRMSpringBone spring, Transform joint)
static void Traverse(List<FastSpringBoneJoint> joints, VRMSpringBone spring, Transform joint, IReadOnlyDictionary<Transform, TransformState> initMap)
{
joints.Add(new FastSpringBoneJoint
{
Transform = joint.transform,
Transform = joint,
Joint = new BlittableJointMutable
{
radius = spring.m_hitRadius,
Expand All @@ -95,11 +91,11 @@ static void Traverse(List<FastSpringBoneJoint> joints, VRMSpringBone spring, Tra
gravityPower = spring.m_gravityPower,
stiffnessForce = spring.m_stiffnessForce
},
DefaultLocalRotation = joint.transform.localRotation,
DefaultLocalRotation = initMap[joint.transform].LocalRotation,
});
foreach (Transform child in joint)
{
Traverse(joints, spring, child);
Traverse(joints, spring, child, initMap);
}
}
}
Expand Down
68 changes: 61 additions & 7 deletions Assets/VRM/Runtime/SpringBone/Jobs/Vrm0XFastSpringBoneRuntime.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Threading.Tasks;
using UniGLTF;
using UniGLTF.SpringBoneJobs;
using UniGLTF.SpringBoneJobs.Blittables;
using UniGLTF.SpringBoneJobs.InputPorts;
using UnityEngine;

namespace VRM
Expand All @@ -16,26 +18,78 @@ namespace VRM
/// </summary>
public class Vrm0XFastSpringboneRuntime : IVrm0XSpringBoneRuntime
{
GameObject m_vrm;
SpringBoneJobs.FastSpringBoneService m_service;
FastSpringBoneBuffer m_buffer;

public async Task InitializeAsync(GameObject vrm, IAwaitCaller awaitCaller)
{
m_vrm = vrm;
m_service = SpringBoneJobs.FastSpringBoneService.Instance;

// default update の停止
foreach (VRMSpringBone sb in vrm.GetComponentsInChildren<VRMSpringBone>())
{
sb.m_updateType = VRMSpringBone.SpringBoneUpdateType.Manual;
}

// create
var buffer = await SpringBoneJobs.FastSpringBoneReplacer.MakeBufferAsync(vrm, awaitCaller);
SpringBoneJobs.FastSpringBoneService.Instance.BufferCombiner.Register(buffer);

// disposer
var disposer = vrm.AddComponent<FastSpringBoneDisposer>()
var disposer = m_vrm.AddComponent<FastSpringBoneDisposer>()
.AddAction(() =>
{
SpringBoneJobs.FastSpringBoneService.Instance.BufferCombiner.Unregister(buffer);
buffer.Dispose();
Unregister();
})
;

// create
await RegisterAsync(awaitCaller);
}

void Unregister()
{
Debug.Log("Vrm0XFastSpringboneRuntime.Unregister");
if (m_buffer == null)
{
return;
}

m_service.BufferCombiner.Unregister(m_buffer);
m_buffer.Dispose();
m_buffer = null;
}

async Task RegisterAsync(IAwaitCaller awaitCaller)
{
Debug.Assert(m_buffer == null);
var buffer = await SpringBoneJobs.FastSpringBoneReplacer.MakeBufferAsync(m_vrm, awaitCaller);
m_buffer = buffer;
SpringBoneJobs.FastSpringBoneService.Instance.BufferCombiner.Register(buffer);

}

public void ReconstructSpringBone()
{
var disposer = m_vrm.gameObject.GetComponent<FastSpringBoneDisposer>();
Unregister();
var task = RegisterAsync(new ImmediateCaller());
}

public void RestoreInitialTransform()
{
if (m_buffer != null)
{
m_service.BufferCombiner.InitializeJointsLocalRotation(m_buffer);
}
}

public void SetJointLevel(Transform joint, BlittableJointMutable jointSettings)
{
m_service.BufferCombiner.Combined.SetJointLevel(joint, jointSettings);
}

public void SetModelLevel(Transform modelRoot, BlittableModelLevel modelSettings)
{
m_service.BufferCombiner.Combined?.SetModelLevel(modelRoot, modelSettings);
}
}
}
7 changes: 5 additions & 2 deletions Assets/VRM/Runtime/SpringBone/Logic/SceneInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ readonly struct SceneInfo
public readonly IReadOnlyList<Transform> RootBones;
public readonly Transform Center;
public readonly VRMSpringBoneColliderGroup[] ColliderGroups;
public readonly Vector3 ExternalForce;

public SceneInfo(
IReadOnlyList<Transform> rootBones,
Transform center,
VRMSpringBoneColliderGroup[] colliderGroups) =>
(RootBones, Center, ColliderGroups) = (rootBones, center, colliderGroups);
VRMSpringBoneColliderGroup[] colliderGroups,
Vector3 externalForce) =>
(RootBones, Center, ColliderGroups, ExternalForce)
= (rootBones, center, colliderGroups, externalForce);
}
}
4 changes: 2 additions & 2 deletions Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointInit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ public Quaternion WorldRotationFromTailPosition(Transform m_transform, Vector3 n
/// Verlet積分で次の位置を計算する
/// </summary>
public Vector3 VerletIntegration(float deltaTime, Transform center, Quaternion parentRotation,
SpringBoneSettings settings, SpringBoneJointState _state, float scalingFactor)
SpringBoneSettings settings, SpringBoneJointState _state, float scalingFactor, Vector3 externalForce)
{
var state = _state.ToWorld(center);

var nextTail = state.CurrentTail
+ (state.CurrentTail - state.PrevTail) * (1.0f - settings.DragForce) // 前フレームの移動を継続する(減衰もあるよ)
+ parentRotation * LocalRotation * BoneAxis * settings.StiffnessForce * deltaTime * scalingFactor // 親の回転による子ボーンの移動目標
+ settings.GravityDir * (settings.GravityPower * deltaTime) * scalingFactor; // 外力による移動量
+ (settings.GravityDir * settings.GravityPower + externalForce) * deltaTime * scalingFactor; // 外力による移動量
return nextTail;
}

Expand Down
54 changes: 53 additions & 1 deletion Assets/VRM/Runtime/SpringBone/Logic/SpringBoneSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class SpringBoneSystem
{
Dictionary<Transform, Quaternion> m_initialLocalRotationMap;
List<(Transform, SpringBoneJointInit, SpringBoneJointState)> m_joints = new();
Dictionary<Transform, int> m_jointIndexMap = new();
List<SphereCollider> m_colliders = new();

public void Setup(SceneInfo scene, bool force)
Expand All @@ -23,6 +24,7 @@ public void Setup(SceneInfo scene, bool force)
m_initialLocalRotationMap.Clear();
}
m_joints.Clear();
m_jointIndexMap.Clear();

foreach (var go in scene.RootBones)
{
Expand All @@ -33,6 +35,55 @@ public void Setup(SceneInfo scene, bool force)
SetupRecursive(scene.Center, go);
}
}

for (int i = 0; i < m_joints.Count; ++i)
{
m_jointIndexMap.Add(m_joints[i].Item1, i);
}
}

public void ReinitializeRotation(SceneInfo scene)
{
foreach (var go in scene.RootBones)
{
if (go != null)
{
ReinitializeRotationRecursive(scene.Center, go);
}
}
}

public void ReinitializeRotationRecursive(Transform center, Transform parent)
{
var index = m_jointIndexMap[parent];
var (t, init, state) = m_joints[index];
t.localRotation = m_initialLocalRotationMap[t];

Vector3 localPosition;
Vector3 scale;
if (parent.childCount == 0)
{
// 子ノードが無い。7cm 固定
var delta = parent.position - parent.parent.position;
var childPosition = parent.position + delta.normalized * 0.07f * parent.UniformedLossyScale();
localPosition = parent.worldToLocalMatrix.MultiplyPoint(childPosition); // cancel scale
scale = parent.lossyScale;
}
else
{
var firstChild = GetChildren(parent).First();
localPosition = firstChild.localPosition;
scale = firstChild.lossyScale;
}
var localChildPosition = new Vector3(
localPosition.x * scale.x,
localPosition.y * scale.y,
localPosition.z * scale.z
);

m_joints[index] = (t, init, new SpringBoneJointState(localChildPosition, localChildPosition));

foreach (Transform child in parent) SetupRecursive(center, child);
}

private static IEnumerable<Transform> GetChildren(Transform parent)
Expand Down Expand Up @@ -115,7 +166,8 @@ SpringBoneSettings settings
// false の場合
// 拡大すると移動速度はだいたい同じ => SpringBone の角速度が遅くなる
var scalingFactor = settings.UseRuntimeScalingSupport ? transform.UniformedLossyScale() : 1.0f;
var nextTail = init.VerletIntegration(deltaTime, scene.Center, parentRotation, settings, state, scalingFactor);
var nextTail = init.VerletIntegration(deltaTime, scene.Center, parentRotation, settings, state,
scalingFactor, scene.ExternalForce);

// 長さをboneLengthに強制
nextTail = transform.position + (nextTail - transform.position).normalized * init.Length;
Expand Down
25 changes: 23 additions & 2 deletions Assets/VRM/Runtime/SpringBone/VRMSpringBone.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ public sealed class VRMSpringBone : MonoBehaviour
/// </summary>
public bool UseRuntimeScalingSupport { get; set; }

/// <summary>
/// VRM-1.0 からのバックポート。
/// - Runtime 制御用のパラメタである
/// - シリアライズ対象でない
/// - World座標系
/// </summary>
public Vector3 ExternalForce { get; set; }

public enum SpringBoneUpdateType
{
LateUpdate,
Expand All @@ -49,7 +57,9 @@ void Awake()
SpringBone.SceneInfo Scene => new(
rootBones: RootBones,
center: m_center,
colliderGroups: ColliderGroups);
colliderGroups: ColliderGroups,
externalForce: ExternalForce);

SpringBone.SpringBoneSettings Settings => new
(
stiffnessForce: m_stiffnessForce,
Expand All @@ -68,6 +78,17 @@ public void Setup(bool force = false)
}
}

public void ReinitializeRotation()
{
m_system.ReinitializeRotation(Scene);
}

public void SetModelLevel(UniGLTF.SpringBoneJobs.Blittables.BlittableModelLevel modelSettings)
{
UseRuntimeScalingSupport = modelSettings.SupportsScalingAtRuntime;
ExternalForce = modelSettings.ExternalForce;
}

void LateUpdate()
{
if (m_updateType == SpringBoneUpdateType.LateUpdate)
Expand Down Expand Up @@ -95,7 +116,7 @@ public void ManualUpdate(float deltaTime)

private void OnDrawGizmosSelected()
{
if (Application.isPlaying && m_updateType!=SpringBoneUpdateType.Manual)
if (Application.isPlaying && m_updateType != SpringBoneUpdateType.Manual)
{
m_system.PlayingGizmo(m_center, Settings, m_gizmoColor);
}
Expand Down
Loading

0 comments on commit 83b63ea

Please sign in to comment.