Merge pull request #2310 from ousttrue/VRMC_springBone_extended_collider

VRMC_springBone_extended_collider
This commit is contained in:
ousttrue
2024-06-07 13:06:29 +09:00
committed by GitHub
20 changed files with 960 additions and 270 deletions

View File

@@ -79,6 +79,12 @@ namespace UniVRM10
$"{SPEC_DIR}/VRMC_vrm_animation-1.0/schema/VRMC_vrm_animation.schema.json",
"Assets/VRM10/Runtime/Format/Animation"
),
// VRMC_animation
new GenerateInfo(
$"{SPEC_DIR}/VRMC_springBone_extended_collider-1.0/schema/VRMC_springBone_extended_collider.schema.json",
"Assets/VRM10/Runtime/Format/SpringBoneExtendedCollider"
),
};
foreach (var arg in args)

View File

@@ -7,6 +7,9 @@ namespace UniVRM10
{
Sphere,
Capsule,
Plane,
SphereInside,
CapsuleInside,
}
[Serializable]
@@ -23,6 +26,10 @@ namespace UniVRM10
/// <summary>bone local position</summary>
public Vector3 Tail;
public Vector3 Normal = Vector3.up;
public Vector3 TailOrNormal => ColliderType == VRM10SpringBoneColliderTypes.Plane ? Normal : Tail;
public static int SelectedGuid;
public bool IsSelected => GetInstanceID() == SelectedGuid;
@@ -38,11 +45,107 @@ namespace UniVRM10
break;
case VRM10SpringBoneColliderTypes.Capsule:
Gizmos.color = new Color(1.0f, 0.1f, 0.1f);
Gizmos.DrawWireSphere(Offset, Radius);
Gizmos.DrawWireSphere(Tail, Radius);
Gizmos.DrawLine(Offset, Tail);
Gizmos.color = Color.magenta;
DrawCapsuleGizmo(transform.localToWorldMatrix.MultiplyPoint(Offset), transform.localToWorldMatrix.MultiplyPoint(Tail), Radius);
break;
case VRM10SpringBoneColliderTypes.Plane:
Gizmos.color = new Color(1.0f, 0.5f, 0.0f);
DrawPlane(transform.localToWorldMatrix, Offset, Normal, Radius);
break;
case VRM10SpringBoneColliderTypes.SphereInside:
Gizmos.color = new Color(0.5f, 0, 1.0f);
Gizmos.DrawWireSphere(Offset, Radius);
break;
case VRM10SpringBoneColliderTypes.CapsuleInside:
Gizmos.color = new Color(0.5f, 0, 1.0f);
DrawCapsuleGizmo(transform.localToWorldMatrix.MultiplyPoint(Offset), transform.localToWorldMatrix.MultiplyPoint(Tail), Radius);
break;
default:
throw new NotImplementedException();
}
}
public static void DrawCapsuleGizmo(Vector3 start, Vector3 end, float radius)
{
var tail = end - start;
var distance = (end - start).magnitude;
Gizmos.matrix = Matrix4x4.TRS(start, Quaternion.FromToRotation(Vector3.forward, tail), Vector3.one);
Gizmos.DrawWireSphere(Vector3.zero, radius);
Gizmos.DrawWireSphere(Vector3.forward * distance, radius);
var capsuleEnd = Vector3.forward * distance;
var offsets = new Vector3[] { new Vector3(-1.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f), new Vector3(1.0f, 0.0f, 0.0f), new Vector3(0.0f, -1.0f, 0.0f) };
for (int i = 0; i < offsets.Length; i++)
{
Gizmos.DrawLine(offsets[i] * radius, capsuleEnd + offsets[i] * radius);
}
Gizmos.matrix = Matrix4x4.identity;
}
public static void DrawPlane(in Matrix4x4 m, in Vector3 offset, in Vector3 _n, float radius)
{
var n = _n.normalized;
Gizmos.matrix = m;
Gizmos.DrawLine(offset, offset + n * radius);
// N Y-Axis として
// XZ 平面を描画する。
var z = Vector3.Cross(Vector3.right, n);
if (z.magnitude > 1e-4)
{
var zr = z * radius;
var x = Vector3.Cross(n, z);
var xr = x * radius;
Gizmos.DrawLine(-xr, xr);
{
var o = offset + zr;
Gizmos.DrawLine(o - xr, o + xr);
}
{
var o = offset - zr;
Gizmos.DrawLine(o - xr, o + xr);
}
Gizmos.DrawLine(-zr, zr);
{
var o = offset + xr;
Gizmos.DrawLine(o - zr, o + zr);
}
{
var o = offset - xr;
Gizmos.DrawLine(o - zr, o + zr);
}
}
else
{
// N と ローカル X-Axis が重なっていた場合
var x = Vector3.Cross(n, Vector3.forward);
var xr = x * radius;
var _z = Vector3.Cross(x, n);
var zr = _z * radius;
Gizmos.DrawLine(-xr, xr);
{
var o = offset + zr;
Gizmos.DrawLine(o - xr, o + xr);
}
{
var o = offset - zr;
Gizmos.DrawLine(o - xr, o + xr);
}
Gizmos.DrawLine(-zr, zr);
{
var o = offset + xr;
Gizmos.DrawLine(o - zr, o + zr);
}
{
var o = offset - xr;
Gizmos.DrawLine(o - zr, o + zr);
}
}
}

View File

@@ -131,7 +131,7 @@ namespace UniVRM10
{
offset = collider.Offset,
radius = collider.Radius,
tail = collider.Tail,
tailOrNormal = collider.TailOrNormal,
colliderType = TranslateColliderType(collider.ColliderType)
}
}).ToArray(),
@@ -172,6 +172,12 @@ namespace UniVRM10
return BlittableColliderType.Sphere;
case VRM10SpringBoneColliderTypes.Capsule:
return BlittableColliderType.Capsule;
case VRM10SpringBoneColliderTypes.Plane:
return BlittableColliderType.Plane;
case VRM10SpringBoneColliderTypes.SphereInside:
return BlittableColliderType.SphereInside;
case VRM10SpringBoneColliderTypes.CapsuleInside:
return BlittableColliderType.CapsuleInside;
default:
throw new ArgumentOutOfRangeException();
}

View File

@@ -9,11 +9,12 @@ namespace UniVRM10.FastSpringBones.Blittables
/// </summary>
[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;
}
}

View File

@@ -4,5 +4,8 @@ namespace UniVRM10.FastSpringBones.Blittables
{
Sphere,
Capsule,
Plane,
SphereInside,
CapsuleInside,
}
}

View File

@@ -22,7 +22,7 @@ namespace UniVRM10.FastSpringBones.System
[NativeDisableParallelForRestriction] public NativeArray<BlittableTransform> Transforms;
public float DeltaTime;
public unsafe void Execute(int index)
{
var spring = Springs[index];
@@ -79,16 +79,32 @@ 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, colliderTransform, ref nextTail);
break;
case BlittableColliderType.SphereInside:
ResolveSphereCollisionInside(joint, collider, colliderTransform, ref nextTail);
break;
case BlittableColliderType.CapsuleInside:
ResolveCapsuleCollisionInside(joint, collider, colliderTransform, ref nextTail);
break;
default:
throw new NotImplementedException();
}
}
@@ -203,5 +219,95 @@ namespace UniVRM10.FastSpringBones.System
nextTail = headTransform.position + (posFromCollider - headTransform.position).normalized * logic.length;
}
}
private static void ResolveSphereCollisionInside(
BlittableJoint joint,
BlittableCollider collider,
BlittableTransform colliderTransform,
ref Vector3 nextTail)
{
var transformedOffset = colliderTransform.localToWorldMatrix.MultiplyPoint(collider.offset);
var delta = nextTail - transformedOffset;
// ジョイントとコライダーの距離。負の値は衝突していることを示す
var distance = collider.radius - joint.radius - delta.magnitude;
// ジョイントとコライダーの距離の方向。衝突している場合、この方向にジョイントを押し出す
if (distance < 0)
{
var direction = -delta.normalized;
nextTail -= direction * distance;
}
}
private static void ResolveCapsuleCollisionInside(
BlittableJoint joint,
BlittableCollider collider,
BlittableTransform colliderTransform,
ref Vector3 nextTail)
{
var transformedOffset = colliderTransform.localToWorldMatrix.MultiplyPoint(collider.offset);
var transformedTail = colliderTransform.localToWorldMatrix.MultiplyPoint(collider.tailOrNormal);
var offsetToTail = transformedTail - transformedOffset;
var lengthSqCapsule = offsetToTail.sqrMagnitude;
var delta = nextTail - transformedOffset;
var dot = Vector3.Dot(offsetToTail, delta);
if (dot < 0.0)
{
// ジョイントがカプセルの始点側にある場合
// なにもしない
}
else if (dot > lengthSqCapsule)
{
// ジョイントがカプセルの終点側にある場合
delta -= offsetToTail;
}
else
{
// ジョイントがカプセルの始点と終点の間にある場合
delta -= offsetToTail * (dot / lengthSqCapsule);
}
// ジョイントとコライダーの距離。負の値は衝突していることを示す
var distance = collider.radius - joint.radius - delta.magnitude;
// ジョイントとコライダーの距離の方向。衝突している場合、この方向にジョイントを押し出す
if (distance < 0)
{
var direction = -delta.normalized;
nextTail -= direction * distance;
}
}
/// <summary>
/// Collision with SpringJoint and PlaneCollider.
/// If collide update nextTail.
/// </summary>
/// <param name="joint">joint</param>
/// <param name="collider">collier</param>
/// <param name="colliderTransform">colliderTransform.localToWorldMatrix.MultiplyPoint3x4(collider.offset);</param>
/// <param name="nextTail">result of verlet integration</param>
private static void ResolvePlaneCollision(
BlittableJoint joint,
BlittableCollider collider,
BlittableTransform colliderTransform,
ref Vector3 nextTail)
{
var transformedOffset = colliderTransform.localToWorldMatrix.MultiplyPoint(collider.offset);
var transformedNormal = colliderTransform.localToWorldMatrix.MultiplyVector(collider.tailOrNormal).normalized;
var delta = nextTail - transformedOffset;
// ジョイントとコライダーの距離。負の値は衝突していることを示す
var distance = Vector3.Dot(delta, transformedNormal) - joint.radius;
if (distance < 0)
{
// ジョイントとコライダーの距離の方向。衝突している場合、この方向にジョイントを押し出す
var direction = transformedNormal;
nextTail -= direction * distance;
}
}
}
}

View File

@@ -1921,26 +1921,6 @@ public static Preset __expressions_Deserialize_Preset(JsonNode parsed)
continue;
}
if(key=="lookUp"){
value.LookUp = __expressions__preset_Deserialize_LookUp(kv.Value);
continue;
}
if(key=="lookDown"){
value.LookDown = __expressions__preset_Deserialize_LookDown(kv.Value);
continue;
}
if(key=="lookLeft"){
value.LookLeft = __expressions__preset_Deserialize_LookLeft(kv.Value);
continue;
}
if(key=="lookRight"){
value.LookRight = __expressions__preset_Deserialize_LookRight(kv.Value);
continue;
}
if(key=="neutral"){
value.Neutral = __expressions__preset_Deserialize_Neutral(kv.Value);
continue;
@@ -2301,114 +2281,6 @@ public static Expression __expressions__preset_Deserialize_BlinkRight(JsonNode p
return value;
}
public static Expression __expressions__preset_Deserialize_LookUp(JsonNode parsed)
{
var value = new Expression();
foreach(var kv in parsed.ObjectItems())
{
var key = kv.Key.GetString();
if(key=="extensions"){
value.Extensions = new glTFExtensionImport(kv.Value);
continue;
}
if(key=="extras"){
value.Extras = new glTFExtensionImport(kv.Value);
continue;
}
if(key=="node"){
value.Node = kv.Value.GetInt32();
continue;
}
}
return value;
}
public static Expression __expressions__preset_Deserialize_LookDown(JsonNode parsed)
{
var value = new Expression();
foreach(var kv in parsed.ObjectItems())
{
var key = kv.Key.GetString();
if(key=="extensions"){
value.Extensions = new glTFExtensionImport(kv.Value);
continue;
}
if(key=="extras"){
value.Extras = new glTFExtensionImport(kv.Value);
continue;
}
if(key=="node"){
value.Node = kv.Value.GetInt32();
continue;
}
}
return value;
}
public static Expression __expressions__preset_Deserialize_LookLeft(JsonNode parsed)
{
var value = new Expression();
foreach(var kv in parsed.ObjectItems())
{
var key = kv.Key.GetString();
if(key=="extensions"){
value.Extensions = new glTFExtensionImport(kv.Value);
continue;
}
if(key=="extras"){
value.Extras = new glTFExtensionImport(kv.Value);
continue;
}
if(key=="node"){
value.Node = kv.Value.GetInt32();
continue;
}
}
return value;
}
public static Expression __expressions__preset_Deserialize_LookRight(JsonNode parsed)
{
var value = new Expression();
foreach(var kv in parsed.ObjectItems())
{
var key = kv.Key.GetString();
if(key=="extensions"){
value.Extensions = new glTFExtensionImport(kv.Value);
continue;
}
if(key=="extras"){
value.Extras = new glTFExtensionImport(kv.Value);
continue;
}
if(key=="node"){
value.Node = kv.Value.GetInt32();
continue;
}
}
return value;
}
public static Expression __expressions__preset_Deserialize_Neutral(JsonNode parsed)
{
var value = new Expression();
@@ -2496,9 +2368,25 @@ public static LookAt Deserialize_LookAt(JsonNode parsed)
continue;
}
if(key=="offsetFromHeadBone"){
value.OffsetFromHeadBone = __lookAt_Deserialize_OffsetFromHeadBone(kv.Value);
continue;
}
}
return value;
}
public static float[] __lookAt_Deserialize_OffsetFromHeadBone(JsonNode parsed)
{
var value = new float[parsed.GetArrayCount()];
int i=0;
foreach(var x in parsed.ArrayItems())
{
value[i++] = x.GetSingle();
}
return value;
}
} // GltfDeserializer
} // UniGLTF

View File

@@ -251,18 +251,6 @@ namespace UniGLTF.Extensions.VRMC_vrm_animation
// Represents a single expression.
public Expression BlinkRight;
// Represents a single expression.
public Expression LookUp;
// Represents a single expression.
public Expression LookDown;
// Represents a single expression.
public Expression LookLeft;
// Represents a single expression.
public Expression LookRight;
// Represents a single expression.
public Expression Neutral;
}
@@ -292,6 +280,9 @@ namespace UniGLTF.Extensions.VRMC_vrm_animation
// Represents a single glTF node represents the eye gaze point.
public int? Node;
// The position offset of the origin of the LookAt from the humanoid head bone
public float[] OffsetFromHeadBone;
}
public class VRMC_vrm_animation

View File

@@ -1689,26 +1689,6 @@ public static void __expressions_Serialize_Preset(JsonFormatter f, Preset value)
__expressions__preset_Serialize_BlinkRight(f, value.BlinkRight);
}
if(value.LookUp!=null){
f.Key("lookUp");
__expressions__preset_Serialize_LookUp(f, value.LookUp);
}
if(value.LookDown!=null){
f.Key("lookDown");
__expressions__preset_Serialize_LookDown(f, value.LookDown);
}
if(value.LookLeft!=null){
f.Key("lookLeft");
__expressions__preset_Serialize_LookLeft(f, value.LookLeft);
}
if(value.LookRight!=null){
f.Key("lookRight");
__expressions__preset_Serialize_LookRight(f, value.LookRight);
}
if(value.Neutral!=null){
f.Key("neutral");
__expressions__preset_Serialize_Neutral(f, value.Neutral);
@@ -2016,98 +1996,6 @@ public static void __expressions__preset_Serialize_BlinkRight(JsonFormatter f, E
f.EndMap();
}
public static void __expressions__preset_Serialize_LookUp(JsonFormatter f, Expression value)
{
f.BeginMap();
if(value.Extensions!=null){
f.Key("extensions");
(value.Extensions as glTFExtension).Serialize(f);
}
if(value.Extras!=null){
f.Key("extras");
(value.Extras as glTFExtension).Serialize(f);
}
if(value.Node.HasValue){
f.Key("node");
f.Value(value.Node.GetValueOrDefault());
}
f.EndMap();
}
public static void __expressions__preset_Serialize_LookDown(JsonFormatter f, Expression value)
{
f.BeginMap();
if(value.Extensions!=null){
f.Key("extensions");
(value.Extensions as glTFExtension).Serialize(f);
}
if(value.Extras!=null){
f.Key("extras");
(value.Extras as glTFExtension).Serialize(f);
}
if(value.Node.HasValue){
f.Key("node");
f.Value(value.Node.GetValueOrDefault());
}
f.EndMap();
}
public static void __expressions__preset_Serialize_LookLeft(JsonFormatter f, Expression value)
{
f.BeginMap();
if(value.Extensions!=null){
f.Key("extensions");
(value.Extensions as glTFExtension).Serialize(f);
}
if(value.Extras!=null){
f.Key("extras");
(value.Extras as glTFExtension).Serialize(f);
}
if(value.Node.HasValue){
f.Key("node");
f.Value(value.Node.GetValueOrDefault());
}
f.EndMap();
}
public static void __expressions__preset_Serialize_LookRight(JsonFormatter f, Expression value)
{
f.BeginMap();
if(value.Extensions!=null){
f.Key("extensions");
(value.Extensions as glTFExtension).Serialize(f);
}
if(value.Extras!=null){
f.Key("extras");
(value.Extras as glTFExtension).Serialize(f);
}
if(value.Node.HasValue){
f.Key("node");
f.Value(value.Node.GetValueOrDefault());
}
f.EndMap();
}
public static void __expressions__preset_Serialize_Neutral(JsonFormatter f, Expression value)
{
f.BeginMap();
@@ -2187,8 +2075,25 @@ public static void Serialize_LookAt(JsonFormatter f, LookAt value)
f.Value(value.Node.GetValueOrDefault());
}
if(value.OffsetFromHeadBone!=null&&value.OffsetFromHeadBone.Count()>=3){
f.Key("offsetFromHeadBone");
__lookAt_Serialize_OffsetFromHeadBone(f, value.OffsetFromHeadBone);
}
f.EndMap();
}
public static void __lookAt_Serialize_OffsetFromHeadBone(JsonFormatter f, float[] value)
{
f.BeginList();
foreach(var item in value)
{
f.Value(item);
}
f.EndList();
}
} // class
} // namespace

View File

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

View File

@@ -0,0 +1,238 @@
// This file is generated from JsonSchema. Don't modify this source code.
using UniJSON;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UniGLTF.Extensions.VRMC_springBone_extended_collider {
public static class GltfDeserializer
{
public static readonly Utf8String ExtensionNameUtf8 = Utf8String.From(VRMC_springBone_extended_collider.ExtensionName);
public static bool TryGet(UniGLTF.glTFExtension src, out VRMC_springBone_extended_collider extension)
{
if(src is UniGLTF.glTFExtensionImport extensions)
{
foreach(var kv in extensions.ObjectItems())
{
if(kv.Key.GetUtf8String() == ExtensionNameUtf8)
{
extension = Deserialize(kv.Value);
return true;
}
}
}
extension = default;
return false;
}
public static VRMC_springBone_extended_collider Deserialize(JsonNode parsed)
{
var value = new VRMC_springBone_extended_collider();
foreach(var kv in parsed.ObjectItems())
{
var key = kv.Key.GetString();
if(key=="extensions"){
value.Extensions = new glTFExtensionImport(kv.Value);
continue;
}
if(key=="extras"){
value.Extras = new glTFExtensionImport(kv.Value);
continue;
}
if(key=="specVersion"){
value.SpecVersion = kv.Value.GetString();
continue;
}
if(key=="shape"){
value.Shape = Deserialize_Shape(kv.Value);
continue;
}
}
return value;
}
public static ExtendedColliderShape Deserialize_Shape(JsonNode parsed)
{
var value = new ExtendedColliderShape();
foreach(var kv in parsed.ObjectItems())
{
var key = kv.Key.GetString();
if(key=="extensions"){
value.Extensions = new glTFExtensionImport(kv.Value);
continue;
}
if(key=="extras"){
value.Extras = new glTFExtensionImport(kv.Value);
continue;
}
if(key=="sphere"){
value.Sphere = __shape_Deserialize_Sphere(kv.Value);
continue;
}
if(key=="capsule"){
value.Capsule = __shape_Deserialize_Capsule(kv.Value);
continue;
}
if(key=="plane"){
value.Plane = __shape_Deserialize_Plane(kv.Value);
continue;
}
}
return value;
}
public static ExtendedColliderShapeSphere __shape_Deserialize_Sphere(JsonNode parsed)
{
var value = new ExtendedColliderShapeSphere();
foreach(var kv in parsed.ObjectItems())
{
var key = kv.Key.GetString();
if(key=="offset"){
value.Offset = __shape__sphere_Deserialize_Offset(kv.Value);
continue;
}
if(key=="radius"){
value.Radius = kv.Value.GetSingle();
continue;
}
if(key=="inside"){
value.Inside = kv.Value.GetBoolean();
continue;
}
}
return value;
}
public static float[] __shape__sphere_Deserialize_Offset(JsonNode parsed)
{
var value = new float[parsed.GetArrayCount()];
int i=0;
foreach(var x in parsed.ArrayItems())
{
value[i++] = x.GetSingle();
}
return value;
}
public static ExtendedColliderShapeCapsule __shape_Deserialize_Capsule(JsonNode parsed)
{
var value = new ExtendedColliderShapeCapsule();
foreach(var kv in parsed.ObjectItems())
{
var key = kv.Key.GetString();
if(key=="offset"){
value.Offset = __shape__capsule_Deserialize_Offset(kv.Value);
continue;
}
if(key=="radius"){
value.Radius = kv.Value.GetSingle();
continue;
}
if(key=="tail"){
value.Tail = __shape__capsule_Deserialize_Tail(kv.Value);
continue;
}
if(key=="inside"){
value.Inside = kv.Value.GetBoolean();
continue;
}
}
return value;
}
public static float[] __shape__capsule_Deserialize_Offset(JsonNode parsed)
{
var value = new float[parsed.GetArrayCount()];
int i=0;
foreach(var x in parsed.ArrayItems())
{
value[i++] = x.GetSingle();
}
return value;
}
public static float[] __shape__capsule_Deserialize_Tail(JsonNode parsed)
{
var value = new float[parsed.GetArrayCount()];
int i=0;
foreach(var x in parsed.ArrayItems())
{
value[i++] = x.GetSingle();
}
return value;
}
public static ExtendedColliderShapePlane __shape_Deserialize_Plane(JsonNode parsed)
{
var value = new ExtendedColliderShapePlane();
foreach(var kv in parsed.ObjectItems())
{
var key = kv.Key.GetString();
if(key=="offset"){
value.Offset = __shape__plane_Deserialize_Offset(kv.Value);
continue;
}
if(key=="normal"){
value.Normal = __shape__plane_Deserialize_Normal(kv.Value);
continue;
}
}
return value;
}
public static float[] __shape__plane_Deserialize_Offset(JsonNode parsed)
{
var value = new float[parsed.GetArrayCount()];
int i=0;
foreach(var x in parsed.ArrayItems())
{
value[i++] = x.GetSingle();
}
return value;
}
public static float[] __shape__plane_Deserialize_Normal(JsonNode parsed)
{
var value = new float[parsed.GetArrayCount()];
int i=0;
foreach(var x in parsed.ArrayItems())
{
value[i++] = x.GetSingle();
}
return value;
}
} // GltfDeserializer
} // UniGLTF

View File

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

View File

@@ -0,0 +1,76 @@
// This file is generated from JsonSchema. Don't modify this source code.
using System;
using System.Collections.Generic;
namespace UniGLTF.Extensions.VRMC_springBone_extended_collider
{
public class ExtendedColliderShapeSphere
{
// The offset of the sphere from the origin in the local space.
public float[] Offset;
// The radius of the sphere.
public float? Radius;
// If true, the collider prevents spring bones from going outside of the sphere instead.
public bool? Inside;
}
public class ExtendedColliderShapeCapsule
{
// The offset of the capsule head from the origin in the local space.
public float[] Offset;
// The radius of the capsule.
public float? Radius;
// The offset of the capsule tail from the origin in the local space.
public float[] Tail;
// If true, the collider prevents spring bones from going outside of the capsule instead.
public bool? Inside;
}
public class ExtendedColliderShapePlane
{
// The offset of the plane from the origin in the local space.
public float[] Offset;
// The normal of the plane in the local space. Must be normalized.
public float[] Normal;
}
public class ExtendedColliderShape
{
// Dictionary object with extension-specific objects.
public object Extensions;
// Application-specific data.
public object Extras;
public ExtendedColliderShapeSphere Sphere;
public ExtendedColliderShapeCapsule Capsule;
public ExtendedColliderShapePlane Plane;
}
public class VRMC_springBone_extended_collider
{
public const string ExtensionName = "VRMC_springBone_extended_collider";
// Dictionary object with extension-specific objects.
public object Extensions;
// Application-specific data.
public object Extras;
// Specification version of VRMC_springBone_extended_collider.
public string SpecVersion;
// The shape of the collider.
public ExtendedColliderShape Shape;
}
}

View File

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

View File

@@ -0,0 +1,222 @@
// This file is generated from JsonSchema. Don't modify this source code.
using System;
using System.Collections.Generic;
using System.Linq;
using UniJSON;
namespace UniGLTF.Extensions.VRMC_springBone_extended_collider {
static public class GltfSerializer
{
public static void SerializeTo(ref UniGLTF.glTFExtension dst, VRMC_springBone_extended_collider extension)
{
if (dst is glTFExtensionImport)
{
throw new NotImplementedException();
}
if (!(dst is glTFExtensionExport extensions))
{
extensions = new glTFExtensionExport();
dst = extensions;
}
var f = new JsonFormatter();
Serialize(f, extension);
extensions.Add(VRMC_springBone_extended_collider.ExtensionName, f.GetStoreBytes());
}
public static void Serialize(JsonFormatter f, VRMC_springBone_extended_collider value)
{
f.BeginMap();
if(value.Extensions!=null){
f.Key("extensions");
(value.Extensions as glTFExtension).Serialize(f);
}
if(value.Extras!=null){
f.Key("extras");
(value.Extras as glTFExtension).Serialize(f);
}
if(!string.IsNullOrEmpty(value.SpecVersion)){
f.Key("specVersion");
f.Value(value.SpecVersion);
}
if(value.Shape!=null){
f.Key("shape");
Serialize_Shape(f, value.Shape);
}
f.EndMap();
}
public static void Serialize_Shape(JsonFormatter f, ExtendedColliderShape value)
{
f.BeginMap();
if(value.Extensions!=null){
f.Key("extensions");
(value.Extensions as glTFExtension).Serialize(f);
}
if(value.Extras!=null){
f.Key("extras");
(value.Extras as glTFExtension).Serialize(f);
}
if(value.Sphere!=null){
f.Key("sphere");
__shape_Serialize_Sphere(f, value.Sphere);
}
if(value.Capsule!=null){
f.Key("capsule");
__shape_Serialize_Capsule(f, value.Capsule);
}
if(value.Plane!=null){
f.Key("plane");
__shape_Serialize_Plane(f, value.Plane);
}
f.EndMap();
}
public static void __shape_Serialize_Sphere(JsonFormatter f, ExtendedColliderShapeSphere value)
{
f.BeginMap();
if(value.Offset!=null&&value.Offset.Count()>=3){
f.Key("offset");
__shape__sphere_Serialize_Offset(f, value.Offset);
}
if(value.Radius.HasValue){
f.Key("radius");
f.Value(value.Radius.GetValueOrDefault());
}
if(value.Inside.HasValue){
f.Key("inside");
f.Value(value.Inside.GetValueOrDefault());
}
f.EndMap();
}
public static void __shape__sphere_Serialize_Offset(JsonFormatter f, float[] value)
{
f.BeginList();
foreach(var item in value)
{
f.Value(item);
}
f.EndList();
}
public static void __shape_Serialize_Capsule(JsonFormatter f, ExtendedColliderShapeCapsule value)
{
f.BeginMap();
if(value.Offset!=null&&value.Offset.Count()>=3){
f.Key("offset");
__shape__capsule_Serialize_Offset(f, value.Offset);
}
if(value.Radius.HasValue){
f.Key("radius");
f.Value(value.Radius.GetValueOrDefault());
}
if(value.Tail!=null&&value.Tail.Count()>=3){
f.Key("tail");
__shape__capsule_Serialize_Tail(f, value.Tail);
}
if(value.Inside.HasValue){
f.Key("inside");
f.Value(value.Inside.GetValueOrDefault());
}
f.EndMap();
}
public static void __shape__capsule_Serialize_Offset(JsonFormatter f, float[] value)
{
f.BeginList();
foreach(var item in value)
{
f.Value(item);
}
f.EndList();
}
public static void __shape__capsule_Serialize_Tail(JsonFormatter f, float[] value)
{
f.BeginList();
foreach(var item in value)
{
f.Value(item);
}
f.EndList();
}
public static void __shape_Serialize_Plane(JsonFormatter f, ExtendedColliderShapePlane value)
{
f.BeginMap();
if(value.Offset!=null&&value.Offset.Count()>=3){
f.Key("offset");
__shape__plane_Serialize_Offset(f, value.Offset);
}
if(value.Normal!=null&&value.Normal.Count()>=3){
f.Key("normal");
__shape__plane_Serialize_Normal(f, value.Normal);
}
f.EndMap();
}
public static void __shape__plane_Serialize_Offset(JsonFormatter f, float[] value)
{
f.BeginList();
foreach(var item in value)
{
f.Value(item);
}
f.EndList();
}
public static void __shape__plane_Serialize_Normal(JsonFormatter f, float[] value)
{
f.BeginList();
foreach(var item in value)
{
f.Value(item);
}
f.EndList();
}
} // class
} // namespace

View File

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

View File

@@ -324,6 +324,68 @@ namespace UniVRM10
return shape;
}
static UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShape ExportShapeExtended(VRM10SpringBoneCollider z)
{
var shape = new UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShape();
switch (z.ColliderType)
{
case VRM10SpringBoneColliderTypes.Sphere:
{
shape.Sphere = new UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShapeSphere
{
Radius = z.Radius,
Offset = ReverseX(z.Offset),
};
break;
}
case VRM10SpringBoneColliderTypes.Capsule:
{
shape.Capsule = new UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShapeCapsule
{
Radius = z.Radius,
Offset = ReverseX(z.Offset),
Tail = ReverseX(z.Tail),
};
break;
}
case VRM10SpringBoneColliderTypes.SphereInside:
{
shape.Sphere = new UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShapeSphere
{
Radius = z.Radius,
Offset = ReverseX(z.Offset),
Inside = true,
};
break;
}
case VRM10SpringBoneColliderTypes.CapsuleInside:
{
shape.Capsule = new UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShapeCapsule
{
Radius = z.Radius,
Offset = ReverseX(z.Offset),
Tail = ReverseX(z.Tail),
};
break;
}
case VRM10SpringBoneColliderTypes.Plane:
{
shape.Plane = new UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShapePlane
{
Offset = ReverseX(z.Offset),
Normal = ReverseX(z.Normal),
};
break;
}
break;
}
return shape;
}
static UniGLTF.Extensions.VRMC_springBone.SpringBoneJoint ExportJoint(VRM10SpringBoneJoint y, Func<Transform, int> getIndexFromTransform)
{
var joint = new UniGLTF.Extensions.VRMC_springBone.SpringBoneJoint
@@ -369,11 +431,22 @@ namespace UniVRM10
foreach (var c in colliders)
{
springBone.Colliders.Add(new UniGLTF.Extensions.VRMC_springBone.Collider
var exportCollider = new UniGLTF.Extensions.VRMC_springBone.Collider
{
Node = getNodeIndexFromTransform(c.transform),
Shape = ExportShape(c),
});
};
if (c.ColliderType == VRM10SpringBoneColliderTypes.SphereInside
|| c.ColliderType == VRM10SpringBoneColliderTypes.CapsuleInside
|| c.ColliderType == VRM10SpringBoneColliderTypes.Plane
)
{
exportCollider.Extensions = new UniGLTF.Extensions.VRMC_springBone_extended_collider.VRMC_springBone_extended_collider
{
Shape = ExportShapeExtended(c),
};
}
springBone.Colliders.Add(exportCollider);
}
// colliderGroups

View File

@@ -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 = exSphere.Inside.GetValueOrDefault() ? VRM10SpringBoneColliderTypes.SphereInside : VRM10SpringBoneColliderTypes.Sphere;
collider.Offset = Vector3InvertX(exSphere.Offset);
collider.Radius = exSphere.Radius.Value;
if (exSphere.Inside.HasValue && exSphere.Inside.Value)
{
collider.ColliderType = VRM10SpringBoneColliderTypes.SphereInside;
}
}
else if (exCollider.Shape.Capsule is UniGLTF.Extensions.VRMC_springBone_extended_collider.ExtendedColliderShapeCapsule exCapsule)
{
collider.ColliderType = exCapsule.Inside.GetValueOrDefault() ? VRM10SpringBoneColliderTypes.CapsuleInside : 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");
}
}
}
}

View File

@@ -181,10 +181,6 @@ namespace UniVRM10
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.blink), nameof(Vrm10AnimationInstance.preset_blink), preset.Blink) is ExpressionInfo info) yield return info; }
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.blinkLeft), nameof(Vrm10AnimationInstance.preset_blinkleft), preset.BlinkLeft) is ExpressionInfo info) yield return info; }
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.blinkRight), nameof(Vrm10AnimationInstance.preset_blinkright), preset.BlinkRight) is ExpressionInfo info) yield return info; }
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.lookUp), "preset_lookup", preset.LookUp) is ExpressionInfo info) yield return info; }
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.lookDown), "preset_lookdown", preset.LookDown) is ExpressionInfo info) yield return info; }
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.lookLeft), "preset_lookleft", preset.LookLeft) is ExpressionInfo info) yield return info; }
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.lookRight), "preset_lookright", preset.LookRight) is ExpressionInfo info) yield return info; }
{ if (GetExpression(gltfAnimation, ExpressionKey.CreateFromPreset(ExpressionPreset.neutral), nameof(Vrm10AnimationInstance.preset_neutral), preset.Neutral) is ExpressionInfo info) yield return info; }
}
if (animation.Expressions.Custom != null)