diff --git a/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneCollider.cs b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneCollider.cs index 5cea5a9c7..c33c12dd0 100644 --- a/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneCollider.cs +++ b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneCollider.cs @@ -27,6 +27,8 @@ namespace UniVRM10 public Vector3 Normal = Vector3.up; + public Vector3 TailOrNormal => ColliderType == VRM10SpringBoneColliderTypes.Plane ? Normal : Tail; + public static int SelectedGuid; public bool IsSelected => GetInstanceID() == SelectedGuid; diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs index 8a40ad9e5..abaa213c4 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs @@ -131,7 +131,7 @@ namespace UniVRM10 { offset = collider.Offset, radius = collider.Radius, - tail = collider.Tail, + tailOrNormal = collider.TailOrNormal, colliderType = TranslateColliderType(collider.ColliderType) } }).ToArray(), diff --git a/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableCollider.cs b/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableCollider.cs index 334887503..1f88e7fee 100644 --- a/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableCollider.cs +++ b/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableCollider.cs @@ -9,11 +9,12 @@ namespace UniVRM10.FastSpringBones.Blittables /// [Serializable] public struct BlittableCollider - { + { public BlittableColliderType colliderType; public Vector3 offset; public float radius; - public Vector3 tail; + // capsule tail or plane normal + public Vector3 tailOrNormal; public int transformIndex; } } \ No newline at end of file diff --git a/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableColliderType.cs b/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableColliderType.cs index 04f0b9f25..93ddef69a 100644 --- a/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableColliderType.cs +++ b/Assets/VRM10/Runtime/FastSpringBone/Blittables/BlittableColliderType.cs @@ -4,5 +4,7 @@ namespace UniVRM10.FastSpringBones.Blittables { Sphere, Capsule, + Plane, + SphereInside, } } \ No newline at end of file diff --git a/Assets/VRM10/Runtime/FastSpringBone/System/UpdateFastSpringBoneJob.cs b/Assets/VRM10/Runtime/FastSpringBone/System/UpdateFastSpringBoneJob.cs index 87e5e16d4..495f7eecb 100644 --- a/Assets/VRM10/Runtime/FastSpringBone/System/UpdateFastSpringBoneJob.cs +++ b/Assets/VRM10/Runtime/FastSpringBone/System/UpdateFastSpringBoneJob.cs @@ -22,7 +22,7 @@ namespace UniVRM10.FastSpringBones.System [NativeDisableParallelForRestriction] public NativeArray Transforms; public float DeltaTime; - + public unsafe void Execute(int index) { var spring = Springs[index]; @@ -79,16 +79,28 @@ namespace UniVRM10.FastSpringBones.System var colliderScale = colliderTransform.localToWorldMatrix.lossyScale; var maxColliderScale = Mathf.Max(Mathf.Max(Mathf.Abs(colliderScale.x), Mathf.Abs(colliderScale.y)), Mathf.Abs(colliderScale.z)); var worldPosition = colliderTransform.localToWorldMatrix.MultiplyPoint3x4(collider.offset); - var worldTail = colliderTransform.localToWorldMatrix.MultiplyPoint3x4(collider.tail); - + var worldTail = colliderTransform.localToWorldMatrix.MultiplyPoint3x4(collider.tailOrNormal); + switch (collider.colliderType) { case BlittableColliderType.Sphere: - ResolveSphereCollision(joint, collider, worldPosition, headTransform, maxColliderScale, logic, ref nextTail); + ResolveSphereCollision(joint, collider, worldPosition, headTransform, maxColliderScale, logic, ref nextTail); break; + case BlittableColliderType.Capsule: ResolveCapsuleCollision(worldTail, worldPosition, headTransform, joint, collider, maxColliderScale, logic, ref nextTail); break; + + case BlittableColliderType.Plane: + ResolvePlaneCollision(joint, collider, worldPosition, headTransform, maxColliderScale, logic, ref nextTail); + break; + + case BlittableColliderType.SphereInside: + ResolveSphereCollisionInside(joint, collider, worldPosition, headTransform, maxColliderScale, logic, ref nextTail); + break; + + default: + throw new NotImplementedException(); } } @@ -203,5 +215,71 @@ namespace UniVRM10.FastSpringBones.System nextTail = headTransform.position + (posFromCollider - headTransform.position).normalized * logic.length; } } + + private static void ResolveSphereCollisionInside( + BlittableJoint joint, + BlittableCollider collider, + Vector3 worldPosition, + BlittableTransform headTransform, + float maxColliderScale, + BlittableLogic logic, + ref Vector3 nextTail) + { + var r = joint.radius + collider.radius * maxColliderScale; + if (Vector3.SqrMagnitude(nextTail - worldPosition) <= (r * r)) + { + // ヒット。Colliderの半径方向に押し出す + var normal = (nextTail - worldPosition).normalized; + var posFromCollider = worldPosition + normal * r; + // 長さをboneLengthに強制 + nextTail = headTransform.position + (posFromCollider - headTransform.position).normalized * logic.length; + } + + // let transformedOffset = colliderOffset * colliderTransform; + // let transformedTail = colliderTail * colliderTransform; + // let offsetToTail = transformedTail - transformedOffset; + // let lengthSqCapsule = lengthSq(offsetToTail); + + // let dot = dot(offsetToTail, delta); + + // var delta = jointPosition - transformedOffset; + + // if (dot < 0.0) { + // // ジョイントがカプセルの始点側にある場合 + // // なにもしない + // } else if (dot > lengthSqCapsule) { + // // ジョイントがカプセルの終点側にある場合 + // delta -= offsetToTail; + // } else { + // // ジョイントがカプセルの始点と終点の間にある場合 + // delta -= offsetToTail * (dot / lengthSqCapsule); + // } + + // // ジョイントとコライダーの距離。負の値は衝突していることを示す + // let distance = colliderRadius - jointRadius - length(delta); + + // // ジョイントとコライダーの距離の方向。衝突している場合、この方向にジョイントを押し出す + // let direction = -normalize(delta); + } + + private static void ResolvePlaneCollision( + BlittableJoint joint, + BlittableCollider collider, + Vector3 worldPosition, + BlittableTransform headTransform, + float maxColliderScale, + BlittableLogic logic, + ref Vector3 nextTail) + { + // let transformedOffset = colliderOffset * colliderTransform; + // let transformedNormal = normalize(colliderNormal * normalMatrixFrom(colliderTransform)); + // let delta = jointPosition - transformedOffset; + + // // ジョイントとコライダーの距離。負の値は衝突していることを示す + // let distance = dot(delta, transformedNormal) - jointRadius; + + // // ジョイントとコライダーの距離の方向。衝突している場合、この方向にジョイントを押し出す + // let direction = transformedNormal; + } } } \ No newline at end of file diff --git a/Assets/VRM10/Runtime/IO/Vrm10Importer.cs b/Assets/VRM10/Runtime/IO/Vrm10Importer.cs index 65b201ae4..8f66a9c37 100644 --- a/Assets/VRM10/Runtime/IO/Vrm10Importer.cs +++ b/Assets/VRM10/Runtime/IO/Vrm10Importer.cs @@ -538,6 +538,41 @@ namespace UniVRM10 { throw new Vrm10Exception("unknown shape"); } + + // https://github.com/vrm-c/vrm-specification/tree/master/specification/VRMC_springBone_extended_collider-1.0 + // VRMC_springBone_extended_collider + if (UniGLTF.Extensions.VRMC_springBone_extended_collider.GltfDeserializer.TryGet(c.Extensions as glTFExtension, + out UniGLTF.Extensions.VRMC_springBone_extended_collider.VRMC_springBone_extended_collider exCollider)) + { + if (exCollider.Shape.Sphere is UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShapeSphere exSphere) + { + collider.ColliderType = VRM10SpringBoneColliderTypes.Sphere; + collider.Offset = Vector3InvertX(exSphere.Offset); + collider.Radius = exSphere.Radius.Value; + if (exSphere.Inside.HasValue && exSphere.Inside.Value) + { + collider.ColliderType = VRM10SpringBoneColliderTypes.SphereIside; + } + } + else if (exCollider.Shape.Capsule is UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShapeCapsule exCapsule) + { + collider.ColliderType = VRM10SpringBoneColliderTypes.Capsule; + collider.Offset = Vector3InvertX(exCapsule.Offset); + collider.Tail = Vector3InvertX(exCapsule.Tail); + collider.Radius = exCapsule.Radius.Value; + } + else if (exCollider.Shape.Plane is UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShapePlane exPlane) + { + collider.ColliderType = VRM10SpringBoneColliderTypes.Plane; + collider.Offset = Vector3InvertX(exPlane.Offset); + collider.Normal = Vector3InvertX(exPlane.Normal); + collider.Radius = 0.1f; // gizmo visualize. 10cm + } + else + { + throw new Vrm10Exception("VRMC_springBone_extended_collider: unknown shape"); + } + } } }