diff --git a/Assets/VRM/Editor/Format/VRMEditorExporter.cs b/Assets/VRM/Editor/Format/VRMEditorExporter.cs
index 6e48f7d886..de37f4dad9 100644
--- a/Assets/VRM/Editor/Format/VRMEditorExporter.cs
+++ b/Assets/VRM/Editor/Format/VRMEditorExporter.cs
@@ -208,8 +208,11 @@ private static byte[] Export(
if (settings.PoseFreeze)
{
- // 正規化
- VRMBoneNormalizer.Execute(target, settings.ForceTPose);
+ using (var backup = new VrmGeometryBackup(target))
+ {
+ // 正規化
+ VRMBoneNormalizer.Execute(target, settings.ForceTPose);
+ }
}
// 元のBlendShapeClipに変更を加えないように複製
diff --git a/Assets/VRM/Runtime/IO/VrmGeometryBackup.cs b/Assets/VRM/Runtime/IO/VrmGeometryBackup.cs
new file mode 100644
index 0000000000..a1d62feba9
--- /dev/null
+++ b/Assets/VRM/Runtime/IO/VrmGeometryBackup.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using UniGLTF;
+
+namespace VRM
+{
+ ///
+ /// SpringBone, LookAt の座標を記録しておき、Dispose で復帰する。
+ ///
+ public class VrmGeometryBackup : IDisposable
+ {
+ struct VrmSpringBoneColliderBackup
+ {
+ struct Collider
+ {
+ Vector3 _offsetInWorld;
+ float _radiusMultLossyScale;
+
+ public Collider(Transform transform, VRMSpringBoneColliderGroup.SphereCollider collider)
+ {
+ _offsetInWorld = transform.TransformPoint(collider.Offset);
+ _radiusMultLossyScale = transform.UniformedLossyScale() * collider.Radius;
+ }
+
+ public void Restore(Transform transform, VRMSpringBoneColliderGroup.SphereCollider collider)
+ {
+ collider.Offset = transform.worldToLocalMatrix.MultiplyPoint(_offsetInWorld);
+ collider.Radius = _radiusMultLossyScale / transform.UniformedLossyScale();
+ }
+ }
+ Collider[] _colliers;
+
+ public VrmSpringBoneColliderBackup(VRMSpringBoneColliderGroup colliderGroup)
+ {
+ _colliers = colliderGroup.Colliders.Select(x => new Collider(colliderGroup.transform, x)).ToArray();
+ }
+
+ public void Restore(VRMSpringBoneColliderGroup colliderGroup)
+ {
+ for (int i = 0; i < colliderGroup.Colliders.Length; ++i)
+ {
+ _colliers[i].Restore(colliderGroup.transform, colliderGroup.Colliders[i]);
+ }
+ }
+ }
+
+ struct VrmSpringBoneBackup
+ {
+ Vector3 _gravityDirInWorld;
+ float _radiusMultLossyScale;
+
+ public VrmSpringBoneBackup(VRMSpringBone springBone)
+ {
+ _gravityDirInWorld = springBone.transform.TransformDirection(springBone.m_gravityDir);
+ _radiusMultLossyScale = springBone.transform.UniformedLossyScale() * springBone.m_hitRadius;
+ }
+ public void Restore(VRMSpringBone springBone)
+ {
+ springBone.m_gravityDir = springBone.transform.worldToLocalMatrix.MultiplyVector(_gravityDirInWorld);
+ springBone.m_hitRadius = _radiusMultLossyScale / springBone.transform.UniformedLossyScale();
+ }
+ }
+
+ Dictionary _springBoneColliders;
+ Dictionary _springBones;
+ VRMFirstPerson _firstPerson;
+ Vector3 _firstPersonOffsetInWorld;
+
+ public VrmGeometryBackup(GameObject root)
+ {
+ _springBoneColliders = root.GetComponentsInChildren().ToDictionary(x => x, x => new VrmSpringBoneColliderBackup(x));
+ _springBones = root.GetComponentsInChildren().ToDictionary(x => x, x => new VrmSpringBoneBackup(x));
+ _firstPerson = root.GetComponent();
+ _firstPersonOffsetInWorld = _firstPerson.transform.TransformPoint(_firstPerson.FirstPersonOffset);
+ }
+
+ public void Dispose()
+ {
+ foreach (var (k, v) in _springBoneColliders)
+ {
+ v.Restore(k);
+ }
+ foreach (var (k, v) in _springBones)
+ {
+ v.Restore(k);
+ }
+ _firstPerson.FirstPersonOffset = _firstPerson.transform.worldToLocalMatrix.MultiplyPoint(_firstPersonOffsetInWorld);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/VRM/Runtime/IO/VrmGeometryBackup.cs.meta b/Assets/VRM/Runtime/IO/VrmGeometryBackup.cs.meta
new file mode 100644
index 0000000000..5d98f29482
--- /dev/null
+++ b/Assets/VRM/Runtime/IO/VrmGeometryBackup.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 349a17edf9cddac469f1bd6a26cd0610
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/VRM10/Editor/Vrm10ExportDialog.cs b/Assets/VRM10/Editor/Vrm10ExportDialog.cs
index a2c9195aa8..0dadef5050 100644
--- a/Assets/VRM10/Editor/Vrm10ExportDialog.cs
+++ b/Assets/VRM10/Editor/Vrm10ExportDialog.cs
@@ -293,14 +293,17 @@ protected override void ExportPath(string path)
disposer.Push(copy);
root = copy;
- // Transform の回転とスケールを Mesh に適用します。
- // - BlendShape は現状がbakeされます
- // - 回転とスケールが反映された新しい Mesh が作成されます
- // - Transform の回転とスケールはクリアされます。world position を維持します
- var newMeshMap = BoneNormalizer.NormalizeHierarchyFreezeMesh(root);
-
- // SkinnedMeshRenderer.sharedMesh と MeshFilter.sharedMesh を新しいMeshで置き換える
- BoneNormalizer.Replace(root, newMeshMap, true, true);
+ using (var backup = new Vrm10GeometryBackup(root))
+ {
+ // Transform の回転とスケールを Mesh に適用します。
+ // - BlendShape は現状がbakeされます
+ // - 回転とスケールが反映された新しい Mesh が作成されます
+ // - Transform の回転とスケールはクリアされます。world position を維持します
+ var newMeshMap = BoneNormalizer.NormalizeHierarchyFreezeMesh(root);
+
+ // SkinnedMeshRenderer.sharedMesh と MeshFilter.sharedMesh を新しいMeshで置き換える
+ BoneNormalizer.Replace(root, newMeshMap, true, true);
+ }
}
var converter = new UniVRM10.ModelExporter();
diff --git a/Assets/VRM10/Runtime/IO/Vrm10GeometryBackup.cs b/Assets/VRM10/Runtime/IO/Vrm10GeometryBackup.cs
new file mode 100644
index 0000000000..ea614f4247
--- /dev/null
+++ b/Assets/VRM10/Runtime/IO/Vrm10GeometryBackup.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using UniGLTF;
+
+namespace UniVRM10
+{
+ ///
+ /// SpringBone, LookAt の座標を記録しておき、Dispose で復帰する。
+ ///
+ public class Vrm10GeometryBackup : IDisposable
+ {
+ struct Vrm10SpringBoneColliderBackup
+ {
+ Vector3 _offsetInWorld;
+ float _radiusMultLossyScale;
+
+ public Vrm10SpringBoneColliderBackup(VRM10SpringBoneCollider collider)
+ {
+ _offsetInWorld = collider.transform.TransformPoint(collider.Offset);
+ _radiusMultLossyScale = collider.transform.UniformedLossyScale() * collider.Radius;
+ }
+
+ public void Restore(VRM10SpringBoneCollider collider)
+ {
+ collider.Offset = collider.transform.worldToLocalMatrix.MultiplyPoint(_offsetInWorld);
+ collider.Radius = _radiusMultLossyScale / collider.transform.UniformedLossyScale();
+ }
+ }
+
+ struct Vrm10SpringBoneJointBackup
+ {
+ Vector3 _gravityDirInWorld;
+ float _radiusMultLossyScale;
+
+ public Vrm10SpringBoneJointBackup(VRM10SpringBoneJoint joint)
+ {
+ _gravityDirInWorld = joint.transform.TransformDirection(joint.m_gravityDir);
+ _radiusMultLossyScale = joint.transform.UniformedLossyScale() * joint.m_jointRadius;
+ }
+ public void Restore(VRM10SpringBoneJoint joint)
+ {
+ joint.m_gravityDir = joint.transform.worldToLocalMatrix.MultiplyVector(_gravityDirInWorld);
+ joint.m_jointRadius = _radiusMultLossyScale / joint.transform.UniformedLossyScale();
+ }
+ }
+
+ Dictionary _springBoneColliders;
+ Dictionary _springBoneJoints;
+ Vrm10Instance _vrm;
+ Vector3 _lookAtOffsetInWorld;
+
+ public Vrm10GeometryBackup(GameObject root)
+ {
+ _springBoneColliders = root.GetComponentsInChildren().ToDictionary(x => x, x => new Vrm10SpringBoneColliderBackup(x));
+ _springBoneJoints = root.GetComponentsInChildren().ToDictionary(x => x, x => new Vrm10SpringBoneJointBackup(x));
+ _vrm = root.GetComponent();
+ _lookAtOffsetInWorld = _vrm.transform.TransformPoint(_vrm.Vrm.LookAt.OffsetFromHead);
+ }
+
+ public void Dispose()
+ {
+ foreach (var (k, v) in _springBoneColliders)
+ {
+ v.Restore(k);
+ }
+ foreach (var (k, v) in _springBoneJoints)
+ {
+ v.Restore(k);
+ }
+ _vrm.Vrm.LookAt.OffsetFromHead = _vrm.transform.worldToLocalMatrix.MultiplyPoint(_lookAtOffsetInWorld);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/VRM10/Runtime/IO/Vrm10GeometryBackup.cs.meta b/Assets/VRM10/Runtime/IO/Vrm10GeometryBackup.cs.meta
new file mode 100644
index 0000000000..6a14f7a37b
--- /dev/null
+++ b/Assets/VRM10/Runtime/IO/Vrm10GeometryBackup.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 38fed05921b6c5c428e73312b6f18891
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: