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: