Merge pull request #1868 from notargs/external_force

SpringBoneに対して外力を渡すインターフェースを実装する
This commit is contained in:
ousttrue
2022-10-20 15:57:07 +09:00
committed by GitHub
8 changed files with 84 additions and 25 deletions

View File

@@ -27,6 +27,7 @@ namespace UniVRM10
private readonly IReadOnlyDictionary<Transform, TransformState> m_defaultTransformStates;
private FastSpringBoneBuffer m_fastSpringBoneBuffer;
private BlittableExternalData m_externalData;
/// <summary>
/// Control Rig may be null.
@@ -38,6 +39,16 @@ namespace UniVRM10
public Vrm10RuntimeExpression Expression { get; }
public Vrm10RuntimeLookAt LookAt { get; }
public Vector3 ExternalForce
{
get => m_externalData.ExternalForce;
set
{
m_externalData.ExternalForce = value;
m_fastSpringBoneBuffer.ExternalData = m_externalData;
}
}
public Vrm10Runtime(Vrm10Instance target, ControlRigGenerationOption controlRigGenerationOption)
{
m_target = target;
@@ -131,7 +142,8 @@ namespace UniVRM10
},
DefaultLocalRotation = GetOrAddDefaultTransformState(joint.transform).LocalRotation,
}).ToArray(),
}).ToArray());
}).ToArray(),
m_externalData);
}
private TransformState GetOrAddDefaultTransformState(Transform tf)

View File

@@ -0,0 +1,12 @@
using UnityEngine;
namespace UniVRM10.FastSpringBones.Blittables
{
/// <summary>
/// 外力等の毎フレーム更新されうる外部から与えられる情報
/// </summary>
public struct BlittableExternalData
{
public Vector3 ExternalForce;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3a5066fc4e264010a58c19b781d477a0
timeCreated: 1666166451

View File

@@ -1,10 +1,10 @@
using System;
using UnityEngine.Serialization;
namespace UniVRM10.FastSpringBones.Blittables
{
/// <summary>
/// 1本の毛束を表すデータ型
/// FastSpringBoneではこれを起点として並列化し、処理を行う
/// </summary>
[Serializable]
public struct BlittableSpring
@@ -13,5 +13,6 @@ namespace UniVRM10.FastSpringBones.Blittables
public BlittableSpan logicSpan;
public int centerTransformIndex;
public int transformIndexOffset;
public unsafe BlittableExternalData* ExternalData;
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.Profiling;
using UniVRM10.FastSpringBones.Blittables;
@@ -13,6 +14,7 @@ namespace UniVRM10.FastSpringBones.System
/// </summary>
public class FastSpringBoneBuffer : IDisposable
{
// NOTE: これらはFastSpringBoneBufferCombinerによってバッチングされる
public NativeArray<BlittableSpring> Springs { get; }
public NativeArray<BlittableJoint> Joints { get; }
public NativeArray<BlittableCollider> Colliders { get; }
@@ -21,10 +23,24 @@ namespace UniVRM10.FastSpringBones.System
public Transform[] Transforms { get; }
public bool IsDisposed { get; private set; }
public FastSpringBoneBuffer(IReadOnlyList<FastSpringBoneSpring> springs, bool simulateLastBone = false)
// NOTE: これは更新頻度が高くバッチングが難しいため、ランダムアクセスを許容してメモリへ直接アクセスする
// 生のヒープ領域は扱いにくいので長さ1のNativeArrayで代用
private NativeArray<BlittableExternalData> _externalData;
public BlittableExternalData ExternalData
{
get => _externalData[0];
set => _externalData[0] = value;
}
public unsafe FastSpringBoneBuffer(IReadOnlyList<FastSpringBoneSpring> springs,
BlittableExternalData externalData,
bool simulateLastBone = false)
{
Profiler.BeginSample("FastSpringBone.ConstructBuffers");
_externalData = new NativeArray<BlittableExternalData>(1, Allocator.Persistent);
// Transformの列挙
Profiler.BeginSample("FastSpringBone.ConstructBuffers.ConstructTransformBuffer");
var transformHashSet = new HashSet<Transform>();
@@ -35,6 +51,7 @@ namespace UniVRM10.FastSpringBones.System
transformHashSet.Add(joint.Transform);
if (joint.Transform.parent != null) transformHashSet.Add(joint.Transform.parent);
}
foreach (var collider in spring.colliders)
{
transformHashSet.Add(collider.Transform);
@@ -42,6 +59,7 @@ namespace UniVRM10.FastSpringBones.System
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);
@@ -68,7 +86,8 @@ namespace UniVRM10.FastSpringBones.System
startIndex = blittableJoints.Count,
count = simulateLastBone ? spring.joints.Length : spring.joints.Length - 1,
},
centerTransformIndex = spring.center ? transformIndexDictionary[spring.center] : -1
centerTransformIndex = spring.center ? transformIndexDictionary[spring.center] : -1,
ExternalData = (BlittableExternalData*) _externalData.GetUnsafePtr()
};
blittableSprings.Add(blittableSpring);
@@ -78,17 +97,18 @@ namespace UniVRM10.FastSpringBones.System
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;
}));
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 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)
{
@@ -99,13 +119,15 @@ namespace UniVRM10.FastSpringBones.System
if (parentJoint.HasValue)
{
var delta = joint.Transform.position - parentJoint.Value.Transform.position;
localPosition = joint.Transform.worldToLocalMatrix.MultiplyPoint(joint.Transform.position + delta);
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(
@@ -131,6 +153,7 @@ namespace UniVRM10.FastSpringBones.System
});
}
}
Profiler.EndSample();
// 各種bufferの初期化
@@ -157,6 +180,7 @@ namespace UniVRM10.FastSpringBones.System
BlittableTransforms.Dispose();
Colliders.Dispose();
Logics.Dispose();
_externalData.Dispose();
}
}
}

View File

@@ -38,6 +38,8 @@ namespace UniVRM10.FastSpringBones.System
public NativeArray<BlittableCollider> Colliders => _colliders;
public NativeArray<BlittableLogic> Logics => _logics;
public bool HasBuffer => _batchedBuffers != null && _batchedBuffers.Length > 0;
public void Register(FastSpringBoneBuffer buffer)
{
_buffers.AddLast(buffer);
@@ -81,6 +83,7 @@ namespace UniVRM10.FastSpringBones.System
{
NativeArray<BlittableLogic>.Copy(_logics, logicsIndex, _batchedBuffers[i].Logics, 0, length);
}
logicsIndex += length;
}
}
@@ -107,7 +110,7 @@ namespace UniVRM10.FastSpringBones.System
Profiler.BeginSample("FastSpringBone.ReconstructBuffers.CopyToBatchedBuffers");
_batchedBuffers = _buffers.ToArray();
_batchedBufferLogicSizes = _buffers.Select(buffer => buffer.Logics.Length).ToArray();
_batchedBufferLogicSizes = _batchedBuffers.Select(buffer => buffer.Logics.Length).ToArray();
Profiler.EndSample();
// バッファを数える
@@ -139,8 +142,10 @@ namespace UniVRM10.FastSpringBones.System
var transformOffset = 0;
Profiler.BeginSample("FastSpringBone.ReconstructBuffers.ScheduleLoadBufferJobs");
foreach (var buffer in _buffers)
for (var i = 0; i < _batchedBuffers.Length; i++)
{
var buffer = _batchedBuffers[i];
// バッファの読み込みをスケジュール
handle = new LoadTransformsJob
{
@@ -155,15 +160,15 @@ namespace UniVRM10.FastSpringBones.System
DestSprings = new NativeSlice<BlittableSpring>(_springs, springsOffset, buffer.Springs.Length),
CollidersOffset = collidersOffset,
LogicsOffset = logicsOffset,
TransformOffset = transformOffset
TransformOffset = transformOffset,
}.Schedule(buffer.Springs.Length, 1, handle);
handle = new LoadCollidersJob()
handle = new LoadCollidersJob
{
SrcColliders = buffer.Colliders,
DestColliders =
new NativeSlice<BlittableCollider>(_colliders, collidersOffset, buffer.Colliders.Length)
}.Schedule(buffer.Colliders.Length, 1, handle);
handle = new OffsetLogicsJob()
handle = new OffsetLogicsJob
{
SrcLogics = buffer.Logics,
SrcJoints = buffer.Joints,
@@ -185,7 +190,7 @@ namespace UniVRM10.FastSpringBones.System
Profiler.BeginSample("FastSpringBone.ReconstructBuffers.LoadTransformAccessArray");
var transforms = new Transform[transformsCount];
var transformAccessArrayOffset = 0;
foreach (var buffer in _buffers)
foreach (var buffer in _batchedBuffers)
{
Array.Copy(buffer.Transforms, 0, transforms, transformAccessArrayOffset, buffer.Transforms.Length);
transformAccessArrayOffset += buffer.BlittableTransforms.Length;
@@ -260,7 +265,7 @@ namespace UniVRM10.FastSpringBones.System
public void Execute(int index)
{
DestColliders[index] = SrcColliders[index];
DestColliders[index] = SrcColliders[index];
}
}
@@ -281,4 +286,4 @@ namespace UniVRM10.FastSpringBones.System
}
}
}
}
}

View File

@@ -8,7 +8,6 @@ namespace UniVRM10.FastSpringBones.System
public sealed class FastSpringBoneScheduler : IDisposable
{
private readonly FastSpringBoneBufferCombiner _bufferCombiner;
public FastSpringBoneScheduler(FastSpringBoneBufferCombiner bufferCombiner)
{
_bufferCombiner = bufferCombiner;
@@ -18,6 +17,10 @@ namespace UniVRM10.FastSpringBones.System
{
var handle = default(JobHandle);
handle = _bufferCombiner.ReconstructIfDirty(handle);
if (!_bufferCombiner.HasBuffer)
{
return handle;
}
handle = new PullTransformJob
{

View File

@@ -22,8 +22,8 @@ namespace UniVRM10.FastSpringBones.System
[NativeDisableParallelForRestriction] public NativeArray<BlittableTransform> Transforms;
public float DeltaTime;
public void Execute(int index)
public unsafe void Execute(int index)
{
var spring = Springs[index];
var transformIndexOffset = spring.transformIndexOffset;
@@ -43,7 +43,6 @@ namespace UniVRM10.FastSpringBones.System
? Transforms[spring.centerTransformIndex + transformIndexOffset]
: (BlittableTransform?)null;
// 親があったら、親に依存するTransformを再計算
if (parentTransform.HasValue)
{
@@ -62,7 +61,7 @@ namespace UniVRM10.FastSpringBones.System
var parentRotation = parentTransform?.rotation ?? Quaternion.identity;
// verlet積分で次の位置を計算
var external = joint.gravityDir * joint.gravityPower * DeltaTime;
var external = (joint.gravityDir * joint.gravityPower + spring.ExternalData->ExternalForce) * DeltaTime;
var nextTail = currentTail
+ (currentTail - prevTail) * (1.0f - joint.dragForce) // 前フレームの移動を継続する(減衰もあるよ)
+ parentRotation * logic.localRotation * logic.boneAxis *