From a9e9ec7b3b26944973b348f9ee0d8bd202f5202e Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 9 Sep 2025 15:01:21 +0900 Subject: [PATCH 1/3] generate --- .../VRM10/Editor/Vrm10SerializerGenerator.cs | 8 + .../VRM10/Runtime/Format/SpringBoneLimit.meta | 8 + .../Format/SpringBoneLimit/Deserializer.g.cs | 236 ++++++++++++++++++ .../SpringBoneLimit/Deserializer.g.cs.meta | 11 + .../Format/SpringBoneLimit/Format.g.cs | 91 +++++++ .../Format/SpringBoneLimit/Format.g.cs.meta | 11 + .../Format/SpringBoneLimit/Serializer.g.cs | 218 ++++++++++++++++ .../SpringBoneLimit/Serializer.g.cs.meta | 11 + 8 files changed, 594 insertions(+) create mode 100644 Assets/VRM10/Runtime/Format/SpringBoneLimit.meta create mode 100644 Assets/VRM10/Runtime/Format/SpringBoneLimit/Deserializer.g.cs create mode 100644 Assets/VRM10/Runtime/Format/SpringBoneLimit/Deserializer.g.cs.meta create mode 100644 Assets/VRM10/Runtime/Format/SpringBoneLimit/Format.g.cs create mode 100644 Assets/VRM10/Runtime/Format/SpringBoneLimit/Format.g.cs.meta create mode 100644 Assets/VRM10/Runtime/Format/SpringBoneLimit/Serializer.g.cs create mode 100644 Assets/VRM10/Runtime/Format/SpringBoneLimit/Serializer.g.cs.meta diff --git a/Assets/VRM10/Editor/Vrm10SerializerGenerator.cs b/Assets/VRM10/Editor/Vrm10SerializerGenerator.cs index 0229699b6..5cb7d8a34 100644 --- a/Assets/VRM10/Editor/Vrm10SerializerGenerator.cs +++ b/Assets/VRM10/Editor/Vrm10SerializerGenerator.cs @@ -86,6 +86,14 @@ namespace UniVRM10 $"{Vrm10SpecDir}/VRMC_springBone_extended_collider-1.0/schema/VRMC_springBone_extended_collider.schema.json", $"{Vrm10FormatGeneratedDir}/SpringBoneExtendedCollider" ), + + // VRMC_springBone_limit-1.0 + // (experimental) + // https://github.com/0b5vr/vrm-specification/tree/springBone-limit + new GenerateInfo( + $"{Vrm10SpecDir}/VRMC_springBone_limit-1.0/schema/VRMC_springBone_limit.schema.json", + $"{Vrm10FormatGeneratedDir}/SpringBoneLimit" + ), }; foreach (var arg in args) diff --git a/Assets/VRM10/Runtime/Format/SpringBoneLimit.meta b/Assets/VRM10/Runtime/Format/SpringBoneLimit.meta new file mode 100644 index 000000000..8b9e26e5e --- /dev/null +++ b/Assets/VRM10/Runtime/Format/SpringBoneLimit.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6b6c07a37ce7d2348beb488d3c2fad01 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Format/SpringBoneLimit/Deserializer.g.cs b/Assets/VRM10/Runtime/Format/SpringBoneLimit/Deserializer.g.cs new file mode 100644 index 000000000..16fb7981a --- /dev/null +++ b/Assets/VRM10/Runtime/Format/SpringBoneLimit/Deserializer.g.cs @@ -0,0 +1,236 @@ +// 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_limit { + +public static class GltfDeserializer +{ + public static readonly Utf8String ExtensionNameUtf8 = Utf8String.From(VRMC_springBone_limit.ExtensionName); + +public static bool TryGet(UniGLTF.glTFExtension src, out VRMC_springBone_limit 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_limit Deserialize(JsonNode parsed) +{ + var value = new VRMC_springBone_limit(); + + 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=="limit"){ + value.Limit = Deserialize_Limit(kv.Value); + continue; + } + + } + return value; +} + +public static Limit Deserialize_Limit(JsonNode parsed) +{ + var value = new Limit(); + + 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=="cone"){ + value.Cone = __limit_Deserialize_Cone(kv.Value); + continue; + } + + if(key=="hinge"){ + value.Hinge = __limit_Deserialize_Hinge(kv.Value); + continue; + } + + if(key=="spherical"){ + value.Spherical = __limit_Deserialize_Spherical(kv.Value); + continue; + } + + } + return value; +} + +public static ConeLimit __limit_Deserialize_Cone(JsonNode parsed) +{ + var value = new ConeLimit(); + + 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=="angle"){ + value.Angle = kv.Value.GetSingle(); + continue; + } + + if(key=="rotation"){ + value.Rotation = __limit__cone_Deserialize_Rotation(kv.Value); + continue; + } + + } + return value; +} + +public static float[] __limit__cone_Deserialize_Rotation(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 HingeLimit __limit_Deserialize_Hinge(JsonNode parsed) +{ + var value = new HingeLimit(); + + 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=="angle"){ + value.Angle = kv.Value.GetSingle(); + continue; + } + + if(key=="rotation"){ + value.Rotation = __limit__hinge_Deserialize_Rotation(kv.Value); + continue; + } + + } + return value; +} + +public static float[] __limit__hinge_Deserialize_Rotation(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 SphericalLimit __limit_Deserialize_Spherical(JsonNode parsed) +{ + var value = new SphericalLimit(); + + 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=="phi"){ + value.Phi = kv.Value.GetSingle(); + continue; + } + + if(key=="theta"){ + value.Theta = kv.Value.GetSingle(); + continue; + } + + if(key=="rotation"){ + value.Rotation = __limit__spherical_Deserialize_Rotation(kv.Value); + continue; + } + + } + return value; +} + +public static float[] __limit__spherical_Deserialize_Rotation(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 diff --git a/Assets/VRM10/Runtime/Format/SpringBoneLimit/Deserializer.g.cs.meta b/Assets/VRM10/Runtime/Format/SpringBoneLimit/Deserializer.g.cs.meta new file mode 100644 index 000000000..ce3c9d1bd --- /dev/null +++ b/Assets/VRM10/Runtime/Format/SpringBoneLimit/Deserializer.g.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91feea663efc32c40bc07c67bdc8b868 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Format/SpringBoneLimit/Format.g.cs b/Assets/VRM10/Runtime/Format/SpringBoneLimit/Format.g.cs new file mode 100644 index 000000000..069ed2edd --- /dev/null +++ b/Assets/VRM10/Runtime/Format/SpringBoneLimit/Format.g.cs @@ -0,0 +1,91 @@ +// This file is generated from JsonSchema. Don't modify this source code. +using System; +using System.Collections.Generic; + + +namespace UniGLTF.Extensions.VRMC_springBone_limit +{ + + public class ConeLimit + { + // Dictionary object with extension-specific objects. + public object Extensions; + + // Application-specific data. + public object Extras; + + // The angle of the cone limit in radians. If the angle is set to π or greater, the angle will be interpreted as π by the implementation. When the angle is set to π, the cone shape becomes a sphere. + public float? Angle; + + // The rotation from the default orientation of the cone limit. The rotation is represented as a quaternion (x, y, z, w), where w is the scalar. + public float[] Rotation; + } + + public class HingeLimit + { + // Dictionary object with extension-specific objects. + public object Extensions; + + // Application-specific data. + public object Extras; + + // The angle of the hinge limit in radians. If the angle is set to π or greater, the angle will be interpreted as π by the implementation. When the angle is set to π, the hinge shape becomes a full disc. + public float? Angle; + + // The rotation from the default orientation of the hinge limit. The rotation is represented as a quaternion (x, y, z, w), where w is the scalar. + public float[] Rotation; + } + + public class SphericalLimit + { + // Dictionary object with extension-specific objects. + public object Extensions; + + // Application-specific data. + public object Extras; + + // The phi angle of the spherical limit in radians. If the phi angle is set to π or greater, the angle will be interpreted as π by the implementation. + public float? Phi; + + // The theta angle of the spherical limit in radians. If the theta angle is set to π/2 or greater, the angle will be interpreted as π/2 by the implementation. + public float? Theta; + + // The rotation from the default orientation of the spherical limit. The rotation is represented as a quaternion (x, y, z, w), where w is the scalar. + public float[] Rotation; + } + + public class Limit + { + // Dictionary object with extension-specific objects. + public object Extensions; + + // Application-specific data. + public object Extras; + + // A limit that restricts the orientation of the spring in a cone shape. + public ConeLimit Cone; + + // A limit that restricts the orientation of the spring in a hinge shape. + public HingeLimit Hinge; + + // A limit that restricts the orientation of the spring in a spherical coordinate shape. + public SphericalLimit Spherical; + } + + public class VRMC_springBone_limit + { + public const string ExtensionName = "VRMC_springBone_limit"; + + // Dictionary object with extension-specific objects. + public object Extensions; + + // Application-specific data. + public object Extras; + + // Specification version of VRMC_springBone_limit. + public string SpecVersion; + + // Describes a limit apply to the spring. Either cone, hinge, or spherical must be present. + public Limit Limit; + } +} diff --git a/Assets/VRM10/Runtime/Format/SpringBoneLimit/Format.g.cs.meta b/Assets/VRM10/Runtime/Format/SpringBoneLimit/Format.g.cs.meta new file mode 100644 index 000000000..524099a2a --- /dev/null +++ b/Assets/VRM10/Runtime/Format/SpringBoneLimit/Format.g.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 40be47ec456e1cb46a3ce8a3ecb70910 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Format/SpringBoneLimit/Serializer.g.cs b/Assets/VRM10/Runtime/Format/SpringBoneLimit/Serializer.g.cs new file mode 100644 index 000000000..1056a8989 --- /dev/null +++ b/Assets/VRM10/Runtime/Format/SpringBoneLimit/Serializer.g.cs @@ -0,0 +1,218 @@ +// 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_limit { + + static public class GltfSerializer + { + + public static void SerializeTo(ref UniGLTF.glTFExtension dst, VRMC_springBone_limit 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_limit.ExtensionName, f.GetStoreBytes()); + } + + +public static void Serialize(JsonFormatter f, VRMC_springBone_limit 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.Limit!=null){ + f.Key("limit"); + Serialize_Limit(f, value.Limit); + } + + f.EndMap(); +} + +public static void Serialize_Limit(JsonFormatter f, Limit 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.Cone!=null){ + f.Key("cone"); + __limit_Serialize_Cone(f, value.Cone); + } + + if(value.Hinge!=null){ + f.Key("hinge"); + __limit_Serialize_Hinge(f, value.Hinge); + } + + if(value.Spherical!=null){ + f.Key("spherical"); + __limit_Serialize_Spherical(f, value.Spherical); + } + + f.EndMap(); +} + +public static void __limit_Serialize_Cone(JsonFormatter f, ConeLimit 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.Angle.HasValue){ + f.Key("angle"); + f.Value(value.Angle.GetValueOrDefault()); + } + + if(value.Rotation!=null&&value.Rotation.Count()>=4){ + f.Key("rotation"); + __limit__cone_Serialize_Rotation(f, value.Rotation); + } + + f.EndMap(); +} + +public static void __limit__cone_Serialize_Rotation(JsonFormatter f, float[] value) +{ + f.BeginList(); + + foreach(var item in value) + { + f.Value(item); + + } + f.EndList(); +} + +public static void __limit_Serialize_Hinge(JsonFormatter f, HingeLimit 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.Angle.HasValue){ + f.Key("angle"); + f.Value(value.Angle.GetValueOrDefault()); + } + + if(value.Rotation!=null&&value.Rotation.Count()>=4){ + f.Key("rotation"); + __limit__hinge_Serialize_Rotation(f, value.Rotation); + } + + f.EndMap(); +} + +public static void __limit__hinge_Serialize_Rotation(JsonFormatter f, float[] value) +{ + f.BeginList(); + + foreach(var item in value) + { + f.Value(item); + + } + f.EndList(); +} + +public static void __limit_Serialize_Spherical(JsonFormatter f, SphericalLimit 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.Phi.HasValue){ + f.Key("phi"); + f.Value(value.Phi.GetValueOrDefault()); + } + + if(value.Theta.HasValue){ + f.Key("theta"); + f.Value(value.Theta.GetValueOrDefault()); + } + + if(value.Rotation!=null&&value.Rotation.Count()>=4){ + f.Key("rotation"); + __limit__spherical_Serialize_Rotation(f, value.Rotation); + } + + f.EndMap(); +} + +public static void __limit__spherical_Serialize_Rotation(JsonFormatter f, float[] value) +{ + f.BeginList(); + + foreach(var item in value) + { + f.Value(item); + + } + f.EndList(); +} + + } // class +} // namespace diff --git a/Assets/VRM10/Runtime/Format/SpringBoneLimit/Serializer.g.cs.meta b/Assets/VRM10/Runtime/Format/SpringBoneLimit/Serializer.g.cs.meta new file mode 100644 index 000000000..1ed147337 --- /dev/null +++ b/Assets/VRM10/Runtime/Format/SpringBoneLimit/Serializer.g.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 02602b2f45f722f47a872ca0d1d8262d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 6c5b02fb15473f3d46e0660fc5ee175c42f6e45e Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 9 Sep 2025 15:42:29 +0900 Subject: [PATCH 2/3] importer --- Assets/VRM10/Runtime/IO/Vrm10Importer.cs | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Assets/VRM10/Runtime/IO/Vrm10Importer.cs b/Assets/VRM10/Runtime/IO/Vrm10Importer.cs index af6bdc9c1..2c285f069 100644 --- a/Assets/VRM10/Runtime/IO/Vrm10Importer.cs +++ b/Assets/VRM10/Runtime/IO/Vrm10Importer.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Threading.Tasks; using UniGLTF; +using UniGLTF.Extensions.VRMC_springBone_limit; using UniGLTF.Utils; using UnityEngine; @@ -626,6 +628,31 @@ namespace UniVRM10 joint.m_gravityDir = gltfJoint.GravityDir != null ? Vector3InvertX(gltfJoint.GravityDir) : Vector3.down; joint.m_gravityPower = gltfJoint.GravityPower.GetValueOrDefault(0.0f); joint.m_stiffnessForce = gltfJoint.Stiffness.GetValueOrDefault(1.0f); + + if (UniGLTF.Extensions.VRMC_springBone_limit.GltfDeserializer.TryGet(gltfJoint.Extensions as glTFExtension, + out var extensionSpringBoneLimit)) + { + if (extensionSpringBoneLimit.Limit.Cone is UniGLTF.Extensions.VRMC_springBone_limit.ConeLimit cone) + { + joint.m_anglelimitType = UniGLTF.SpringBoneJobs.AnglelimitTypes.Cone; + joint.m_angleLimitRotation = QuaternionFromFloat4(cone.Rotation); + joint.m_angleLimitAngle1 = cone.Angle.GetValueOrDefault(Mathf.PI * 0.5f); + } + else if (extensionSpringBoneLimit.Limit.Hinge is UniGLTF.Extensions.VRMC_springBone_limit.HingeLimit hinge) + { + joint.m_anglelimitType = UniGLTF.SpringBoneJobs.AnglelimitTypes.Hinge; + joint.m_angleLimitRotation = QuaternionFromFloat4(hinge.Rotation); + joint.m_angleLimitAngle1 = hinge.Angle.GetValueOrDefault(Mathf.PI * 0.5f); + } + else if (extensionSpringBoneLimit.Limit.Spherical is UniGLTF.Extensions.VRMC_springBone_limit.SphericalLimit spherical) + { + joint.m_anglelimitType = UniGLTF.SpringBoneJobs.AnglelimitTypes.Spherical; + joint.m_angleLimitRotation = QuaternionFromFloat4(spherical.Rotation); + joint.m_angleLimitAngle1 = spherical.Theta.GetValueOrDefault(Mathf.PI * 0.5f); + joint.m_angleLimitAngle2 = spherical.Phi.GetValueOrDefault(Mathf.PI * 0.5f); + } + } + spring.Joints.Add(joint); } } @@ -633,6 +660,16 @@ namespace UniVRM10 } } + private static Quaternion QuaternionFromFloat4(float[] xyzw) + { + var q = (xyzw != null && xyzw.Length == 4) + ? new Quaternion(xyzw[0], xyzw[1], xyzw[2], xyzw[3]) + : Quaternion.identity + ; + // vrm-1.0 is x inverted + return Axes.X.Create().InvertQuaternion(q); + } + static AxisMask ConstraintAxes(bool[] flags) { var mask = default(AxisMask); From c14b32645f043149ac67344ccd7fe3f5006cf244 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 9 Sep 2025 16:34:48 +0900 Subject: [PATCH 3/3] exporter --- Assets/VRM10/Runtime/IO/Vrm10Exporter.cs | 65 ++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/Assets/VRM10/Runtime/IO/Vrm10Exporter.cs b/Assets/VRM10/Runtime/IO/Vrm10Exporter.cs index 34b51f3ca..1cad2eb9b 100644 --- a/Assets/VRM10/Runtime/IO/Vrm10Exporter.cs +++ b/Assets/VRM10/Runtime/IO/Vrm10Exporter.cs @@ -140,6 +140,12 @@ namespace UniVRM10 return new float[] { -v.x, v.y, v.z }; } + static float[] ReverseX(Quaternion q) + { + q = UniGLTF.Axes.X.Create().InvertQuaternion(q); + return new float[] { q.x, q.y, q.z, q.w }; + } + /// /// 必要な容量を計算 /// (sparseは考慮してないので大きめ) @@ -444,6 +450,65 @@ namespace UniVRM10 GravityDir = ReverseX(y.m_gravityDir), GravityPower = y.m_gravityPower, }; + + switch (y.m_anglelimitType) + { + case UniGLTF.SpringBoneJobs.AnglelimitTypes.Cone: + { + var limit = new UniGLTF.Extensions.VRMC_springBone_limit.VRMC_springBone_limit + { + Limit = new UniGLTF.Extensions.VRMC_springBone_limit.Limit + { + Cone = new UniGLTF.Extensions.VRMC_springBone_limit.ConeLimit + { + Rotation = ReverseX(y.m_angleLimitRotation), + Angle = y.m_angleLimitAngle1, + } + } + }; + glTFExtension extensions = default; + UniGLTF.Extensions.VRMC_springBone_limit.GltfSerializer.SerializeTo(ref extensions, limit); + joint.Extensions = extensions; + break; + } + case UniGLTF.SpringBoneJobs.AnglelimitTypes.Hinge: + { + var limit = new UniGLTF.Extensions.VRMC_springBone_limit.VRMC_springBone_limit + { + Limit = new UniGLTF.Extensions.VRMC_springBone_limit.Limit + { + Hinge = new UniGLTF.Extensions.VRMC_springBone_limit.HingeLimit + { + Rotation = ReverseX(y.m_angleLimitRotation), + Angle = y.m_angleLimitAngle1, + } + } + }; + glTFExtension extensions = default; + UniGLTF.Extensions.VRMC_springBone_limit.GltfSerializer.SerializeTo(ref extensions, limit); + joint.Extensions = extensions; + break; + } + case UniGLTF.SpringBoneJobs.AnglelimitTypes.Spherical: + { + var limit = new UniGLTF.Extensions.VRMC_springBone_limit.VRMC_springBone_limit + { + Limit = new UniGLTF.Extensions.VRMC_springBone_limit.Limit + { + Spherical = new UniGLTF.Extensions.VRMC_springBone_limit.SphericalLimit + { + Rotation = ReverseX(y.m_angleLimitRotation), + Theta = y.m_angleLimitAngle1, + Phi = y.m_angleLimitAngle2, + } + } + }; + glTFExtension extensions = default; + UniGLTF.Extensions.VRMC_springBone_limit.GltfSerializer.SerializeTo(ref extensions, limit); + joint.Extensions = extensions; + break; + } + } return joint; }