diff --git a/Assets/UniGLTF/Editor/MeshUtility/MeshUtilityDialog.cs b/Assets/UniGLTF/Editor/MeshUtility/MeshUtilityDialog.cs index 65bfb6dc5..6207d98c8 100644 --- a/Assets/UniGLTF/Editor/MeshUtility/MeshUtilityDialog.cs +++ b/Assets/UniGLTF/Editor/MeshUtility/MeshUtilityDialog.cs @@ -300,7 +300,10 @@ namespace UniGLTF.MeshUtility bool MeshFreezeGui() { - return ToggleIsModified("BlendShapeRotationScaling", ref MeshUtility.FreezeBlendShapeRotationAndScaling); + var meshFreeze = ToggleIsModified("FreezeMesh", ref MeshUtility.FreezeMesh); + var meshFreezeKeepRotation = ToggleIsModified("FreezeMeshKeeprotation", ref MeshUtility.FreezeMeshKeepRotation); + var meshFreezeUseCurrentWeight = ToggleIsModified("FreezeMeshCurrentBlendShapeWeight", ref MeshUtility.FreezeMeshCurrentBlendShapeWeight); + return meshFreeze || meshFreezeKeepRotation || meshFreezeUseCurrentWeight; } protected virtual bool MeshIntegrateGui() diff --git a/Assets/UniGLTF/Runtime/MeshUtility/BoneNormalizer.cs b/Assets/UniGLTF/Runtime/MeshUtility/BoneNormalizer.cs index 39a4e96e4..d9f7def75 100644 --- a/Assets/UniGLTF/Runtime/MeshUtility/BoneNormalizer.cs +++ b/Assets/UniGLTF/Runtime/MeshUtility/BoneNormalizer.cs @@ -9,12 +9,12 @@ namespace UniGLTF.MeshUtility { public static class BoneNormalizer { - private static MeshAttachInfo CreateMeshInfo(Transform src) + private static MeshAttachInfo CreateMeshInfo(Transform src, bool bakeCurrentBlendShape) { // SkinnedMeshRenderer if (src.TryGetComponent(out var smr)) { - var mesh = MeshFreezer.NormalizeSkinnedMesh(smr); + var mesh = MeshFreezer.NormalizeSkinnedMesh(smr, bakeCurrentBlendShape); if (mesh != null) { return new MeshAttachInfo @@ -50,12 +50,12 @@ namespace UniGLTF.MeshUtility /// 回転とスケールを除去し、BlendShape の現状を焼き付けた版を作成する(まだ、アタッチしない) /// public static Dictionary NormalizeHierarchyFreezeMesh( - GameObject go) + GameObject go, bool bakeCurrentBlendShape) { var result = new Dictionary(); foreach (var src in go.transform.Traverse()) { - var info = CreateMeshInfo(src); + var info = CreateMeshInfo(src, bakeCurrentBlendShape); if (info != null) { result.Add(src, info); @@ -65,7 +65,7 @@ namespace UniGLTF.MeshUtility } public static void Replace(GameObject go, Dictionary meshMap, - bool FreezeRotation, bool FreezeScaling) + bool KeepRotation) { var boneMap = go.transform.Traverse().ToDictionary(x => x, x => new EuclideanTransform(x.rotation, x.position)); @@ -73,23 +73,16 @@ namespace UniGLTF.MeshUtility foreach (var src in go.transform.Traverse()) { var tr = boneMap[src]; - if (FreezeScaling) - { - src.localScale = Vector3.one; - } - else - { - throw new NotImplementedException(); - } + src.localScale = Vector3.one; - if (FreezeRotation) - { - src.rotation = Quaternion.identity; - } - else + if (KeepRotation) { src.rotation = tr.Rotation; } + else + { + src.rotation = Quaternion.identity; + } src.position = tr.Translation; } diff --git a/Assets/UniGLTF/Runtime/MeshUtility/GltfMeshUtility.cs b/Assets/UniGLTF/Runtime/MeshUtility/GltfMeshUtility.cs index 71c1eb368..25057218e 100644 --- a/Assets/UniGLTF/Runtime/MeshUtility/GltfMeshUtility.cs +++ b/Assets/UniGLTF/Runtime/MeshUtility/GltfMeshUtility.cs @@ -18,12 +18,22 @@ namespace UniGLTF.MeshUtility public class GltfMeshUtility { /// - /// Same as VRM-0 normalization - /// - Mesh - /// - Node - /// - InverseBindMatrices + /// v0.127.2 + /// Mesh の bake。Hierarhcy の改変(scale/rotationの除去)。Binding行列の再作成 /// - public bool FreezeBlendShapeRotationAndScaling = false; + public bool FreezeMesh = false; + + /// + /// v0.127.2 + /// if false Same as VRM-0 normalization + /// + public bool FreezeMeshKeepRotation = false; + + /// + /// v0.127.2 + /// BlendShape の現状を base にする + /// + public bool FreezeMeshCurrentBlendShapeWeight = false; public List MeshIntegrationGroups = new List(); @@ -156,15 +166,15 @@ namespace UniGLTF.MeshUtility public virtual (List, List) Process( GameObject target, IEnumerable groupCopy) { - if (FreezeBlendShapeRotationAndScaling) + if (FreezeMesh) { // MeshをBakeする - var meshMap = BoneNormalizer.NormalizeHierarchyFreezeMesh(target); + var meshMap = BoneNormalizer.NormalizeHierarchyFreezeMesh(target, FreezeMeshCurrentBlendShapeWeight); // - ヒエラルキーから回転・拡縮を除去する // - BakeされたMeshで置き換える // - bindPoses を再計算する - BoneNormalizer.Replace(target, meshMap, true, true); + BoneNormalizer.Replace(target, meshMap, FreezeMeshKeepRotation); } var newList = new List(); diff --git a/Assets/UniGLTF/Runtime/MeshUtility/MeshFreezer.cs b/Assets/UniGLTF/Runtime/MeshUtility/MeshFreezer.cs index 56b1a01fe..61ca9a091 100644 --- a/Assets/UniGLTF/Runtime/MeshUtility/MeshFreezer.cs +++ b/Assets/UniGLTF/Runtime/MeshUtility/MeshFreezer.cs @@ -120,7 +120,7 @@ namespace UniGLTF.MeshUtility return newBoneWeights; } - public static Mesh NormalizeSkinnedMesh(SkinnedMeshRenderer src) + public static Mesh NormalizeSkinnedMesh(SkinnedMeshRenderer src, bool bakeCurrentBlendShape) { if (src == null || !src.enabled @@ -140,14 +140,41 @@ namespace UniGLTF.MeshUtility AssignSingleBoneWeight(src); } + var backcup = new List(); + for (int i = 0; i < src.sharedMesh.blendShapeCount; ++i) + { + backcup.Add(src.GetBlendShapeWeight(i)); + } + // BakeMesh var mesh = new Mesh { name = src.sharedMesh.name + ".baked" }; - // memo: BakeMesh(mesh, useScale) という第2引数がある - src.BakeMesh(mesh); - BakeBlendShapes(src, mesh); + + if (bakeCurrentBlendShape) + { + // blendShape weight の現状を使用する + src.BakeMesh(mesh); + BakeBlendShapes(src, mesh, backcup); + } + else + { + // blendShape weight をすべて 0 clear する + for (int i = 0; i < src.sharedMesh.blendShapeCount; ++i) + { + src.SetBlendShapeWeight(i, 0.0f); + } + src.BakeMesh(mesh); + BakeBlendShapes(src, mesh, null); + } + + // restore blendshape weights + for (int i = 0; i < backcup.Count; ++i) + { + src.SetBlendShapeWeight(i, backcup[i]); + } + mesh.boneWeights = src.sharedMesh.boneWeights; // apply SkinnedMesh.transform rotation @@ -185,18 +212,11 @@ namespace UniGLTF.MeshUtility src.sharedMesh = srcMesh; } - private static void BakeBlendShapes(SkinnedMeshRenderer src, Mesh mesh) + /// basemesh の blendshape 状態 + private static void BakeBlendShapes(SkinnedMeshRenderer src, Mesh mesh, List blendShapeBase) { var srcMesh = src.sharedMesh; - // clear blendShape always - var backcup = new List(); - for (int i = 0; i < srcMesh.blendShapeCount; ++i) - { - backcup.Add(src.GetBlendShapeWeight(i)); - src.SetBlendShapeWeight(i, 0); - } - var meshVertices = mesh.vertices; var meshNormals = mesh.normals; var meshTangents = Array.Empty(); @@ -237,7 +257,7 @@ namespace UniGLTF.MeshUtility throw new Exception("different vertex count"); } - src.SetBlendShapeWeight(i, backcup[i]); + src.SetBlendShapeWeight(i, blendShapeBase == null ? 0 : blendShapeBase[i]); Vector3[] vertices = blendShapeMesh.vertices; @@ -286,12 +306,6 @@ namespace UniGLTF.MeshUtility } } } - - // restore blendshape weights - for (int i = 0; i < backcup.Count; ++i) - { - src.SetBlendShapeWeight(i, backcup[i]); - } } public static Mesh NormalizeNoneSkinnedMesh(MeshRenderer srcRenderer, bool freezeRotation) diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/GltfExportSettings.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/GltfExportSettings.cs index 41468f154..e9534bf0c 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/GltfExportSettings.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/GltfExportSettings.cs @@ -54,10 +54,16 @@ namespace UniGLTF /// public bool ExportUvSecondary; + [Tooltip("freeze mesh")] + public bool FreezeMesh = false; + + [Tooltip("when freeze mesh, keep rotation")] + public bool FreezeMeshKeepRotation = false; + /// /// FreezeBlendShape /// - [Tooltip("freeze rotation and scale and blendshape")] - public bool FreezeMesh = false; + [Tooltip("when freeze mesh, blendShpae base use current weight")] + public bool FreezeMeshUseCurrentBlendShapeWeight = false; } } diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs index 57d423dd1..db93b8017 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs @@ -237,10 +237,10 @@ namespace UniGLTF // - BlendShape は現状がbakeされます // - 回転とスケールが反映された新しい Mesh が作成されます // - Transform の回転とスケールはクリアされます。world position を維持します - var newMeshMap = BoneNormalizer.NormalizeHierarchyFreezeMesh(Copy); + var newMeshMap = BoneNormalizer.NormalizeHierarchyFreezeMesh(Copy, m_settings.FreezeMeshUseCurrentBlendShapeWeight); // SkinnedMeshRenderer.sharedMesh と MeshFilter.sharedMesh を新しいMeshで置き換える - BoneNormalizer.Replace(Copy, newMeshMap, true, true); + BoneNormalizer.Replace(Copy, newMeshMap, m_settings.FreezeMeshKeepRotation); } Nodes = Copy.transform.Traverse() diff --git a/Assets/VRM/Editor/Format/VRMEditorExporter.cs b/Assets/VRM/Editor/Format/VRMEditorExporter.cs index 6e48f7d88..b109f98b6 100644 --- a/Assets/VRM/Editor/Format/VRMEditorExporter.cs +++ b/Assets/VRM/Editor/Format/VRMEditorExporter.cs @@ -209,7 +209,7 @@ namespace VRM if (settings.PoseFreeze) { // 正規化 - VRMBoneNormalizer.Execute(target, settings.ForceTPose); + VRMBoneNormalizer.Execute(target, settings.ForceTPose, settings.FreezeMeshUseCurrentBlendShapeWeight); } // 元のBlendShapeClipに変更を加えないように複製 diff --git a/Assets/VRM/Editor/Format/VRMExportSettings.cs b/Assets/VRM/Editor/Format/VRMExportSettings.cs index ff9e5067e..cc634263a 100644 --- a/Assets/VRM/Editor/Format/VRMExportSettings.cs +++ b/Assets/VRM/Editor/Format/VRMExportSettings.cs @@ -19,6 +19,12 @@ namespace VRM [Tooltip("Require only first time")] public bool PoseFreeze = true; + /// + /// FreezeBlendShape + /// + [Tooltip("when freeze mesh, blendShpae base use current weight")] + public bool FreezeMeshUseCurrentBlendShapeWeight = false; + /// /// BlendShapeのシリアライズにSparseAccessorを使う /// diff --git a/Assets/VRM/Editor/Format/VRMHumanoidNormalizerMenu.cs b/Assets/VRM/Editor/Format/VRMHumanoidNormalizerMenu.cs index 274dd48e5..b10b88e8d 100644 --- a/Assets/VRM/Editor/Format/VRMHumanoidNormalizerMenu.cs +++ b/Assets/VRM/Editor/Format/VRMHumanoidNormalizerMenu.cs @@ -43,11 +43,11 @@ namespace VRM } } - public static void Normalize() + public static void Normalize(bool bakeCurrentBlendShape) { var go = Selection.activeObject as GameObject; - VRMBoneNormalizer.Execute(go, true); + VRMBoneNormalizer.Execute(go, true, bakeCurrentBlendShape); } } } diff --git a/Assets/VRM/Editor/VrmTopMenu.cs b/Assets/VRM/Editor/VrmTopMenu.cs index bcf9f937a..f1e910074 100644 --- a/Assets/VRM/Editor/VrmTopMenu.cs +++ b/Assets/VRM/Editor/VrmTopMenu.cs @@ -31,7 +31,7 @@ namespace VRM [MenuItem(UserMenuPrefix + "/" + VRMHumanoidNormalizerMenu.MENU_NAME, true, 52)] private static bool FreezeTPoseValidation() => VRMHumanoidNormalizerMenu.NormalizeValidation(); [MenuItem(UserMenuPrefix + "/" + VRMHumanoidNormalizerMenu.MENU_NAME, false, 52)] - private static void FreezeTPose() => VRMHumanoidNormalizerMenu.Normalize(); + private static void FreezeTPose() => VRMHumanoidNormalizerMenu.Normalize(bakeCurrentBlendShape: true); [MenuItem(UserMenuPrefix + "/" + VRMSpringBoneUtilityEditor.SAVE_MENU_NAME, true, 53)] diff --git a/Assets/VRM/Runtime/SkinnedMeshUtility/VRMBoneNormalizer.cs b/Assets/VRM/Runtime/SkinnedMeshUtility/VRMBoneNormalizer.cs index 9df426565..3e53c3cba 100644 --- a/Assets/VRM/Runtime/SkinnedMeshUtility/VRMBoneNormalizer.cs +++ b/Assets/VRM/Runtime/SkinnedMeshUtility/VRMBoneNormalizer.cs @@ -52,7 +52,8 @@ namespace VRM /// /// 対象モデルのルート /// 強制的にT-Pose化するか - public static void Execute(GameObject go, bool forceTPose) + /// BlendShape の現状をbakeするか + public static void Execute(GameObject go, bool forceTPose, bool useCurrentBlendShapeWeight) { if (forceTPose) { @@ -72,13 +73,12 @@ namespace VRM } // Transform の回転とスケールを Mesh に適用します。 - // - BlendShape は現状がbakeされます // - 回転とスケールが反映された新しい Mesh が作成されます // - Transform の回転とスケールはクリアされます。world position を維持します - var newMeshMap = BoneNormalizer.NormalizeHierarchyFreezeMesh(go); + var newMeshMap = BoneNormalizer.NormalizeHierarchyFreezeMesh(go, useCurrentBlendShapeWeight); // SkinnedMeshRenderer.sharedMesh と MeshFilter.sharedMesh を新しいMeshで置き換える - BoneNormalizer.Replace(go, newMeshMap, true, true); + BoneNormalizer.Replace(go, newMeshMap, false); // 回転とスケールが除去された新しいヒエラルキーからAvatarを作る Avatar newAvatar = default; diff --git a/Assets/VRM/Runtime/SkinnedMeshUtility/VrmMeshUtility.cs b/Assets/VRM/Runtime/SkinnedMeshUtility/VrmMeshUtility.cs index 375214498..424b41234 100644 --- a/Assets/VRM/Runtime/SkinnedMeshUtility/VrmMeshUtility.cs +++ b/Assets/VRM/Runtime/SkinnedMeshUtility/VrmMeshUtility.cs @@ -105,7 +105,7 @@ namespace VRM // TODO: update: firstPerson offset var (list, newList) = base.Process(target, copyGroup); - if (FreezeBlendShapeRotationAndScaling) + if (FreezeMesh) { Avatar newAvatar = null; if (target.TryGetComponent(out var animator)) diff --git a/Assets/VRM10/Editor/VRM10ExportSettings.cs b/Assets/VRM10/Editor/VRM10ExportSettings.cs index f191598d5..28175946a 100644 --- a/Assets/VRM10/Editor/VRM10ExportSettings.cs +++ b/Assets/VRM10/Editor/VRM10ExportSettings.cs @@ -30,6 +30,15 @@ namespace UniVRM10 [Tooltip("freeze rotation and scale and blendshape")] public bool FreezeMesh = false; + [Tooltip("when freeze mesh, keep rotation")] + public bool FreezeMeshKeepRotation = false; + + /// + /// FreezeBlendShape + /// + [Tooltip("when freeze mesh, blendShpae base use current weight")] + public bool FreezeMeshUseCurrentBlendShapeWeight = false; + public GltfExportSettings MeshExportSettings => new GltfExportSettings { UseSparseAccessorForMorphTarget = MorphTargetUseSparse, diff --git a/Assets/VRM10/Editor/Vrm10ExportDialog.cs b/Assets/VRM10/Editor/Vrm10ExportDialog.cs index a2c9195aa..8bdd2b39e 100644 --- a/Assets/VRM10/Editor/Vrm10ExportDialog.cs +++ b/Assets/VRM10/Editor/Vrm10ExportDialog.cs @@ -297,10 +297,10 @@ namespace UniVRM10 // - BlendShape は現状がbakeされます // - 回転とスケールが反映された新しい Mesh が作成されます // - Transform の回転とスケールはクリアされます。world position を維持します - var newMeshMap = BoneNormalizer.NormalizeHierarchyFreezeMesh(root); + var newMeshMap = BoneNormalizer.NormalizeHierarchyFreezeMesh(root, m_settings.FreezeMeshUseCurrentBlendShapeWeight); // SkinnedMeshRenderer.sharedMesh と MeshFilter.sharedMesh を新しいMeshで置き換える - BoneNormalizer.Replace(root, newMeshMap, true, true); + BoneNormalizer.Replace(root, newMeshMap, m_settings.FreezeMeshKeepRotation); } var converter = new UniVRM10.ModelExporter(); diff --git a/Assets/VRM10/Runtime/MeshUtility/Vrm10MeshUtility.cs b/Assets/VRM10/Runtime/MeshUtility/Vrm10MeshUtility.cs index f61c93e6f..2ad8c59e6 100644 --- a/Assets/VRM10/Runtime/MeshUtility/Vrm10MeshUtility.cs +++ b/Assets/VRM10/Runtime/MeshUtility/Vrm10MeshUtility.cs @@ -12,7 +12,9 @@ namespace UniVRM10 { public Vrm10MeshUtility() { - FreezeBlendShapeRotationAndScaling = true; + FreezeMesh = true; + FreezeMeshKeepRotation = true; + FreezeMeshCurrentBlendShapeWeight = true; } bool _generateFirstPerson = false; @@ -112,7 +114,7 @@ namespace UniVRM10 // TODO: update: firstPerson offset var (list, newList) = base.Process(target, groupCopy); - if (FreezeBlendShapeRotationAndScaling) + if (FreezeMesh) { Avatar newAvatar = default; if (target.TryGetComponent(out var animator)) diff --git a/Assets/VRM_Samples/RuntimeExporterSample/VRMRuntimeExporter.cs b/Assets/VRM_Samples/RuntimeExporterSample/VRMRuntimeExporter.cs index 091800880..ad237600c 100644 --- a/Assets/VRM_Samples/RuntimeExporterSample/VRMRuntimeExporter.cs +++ b/Assets/VRM_Samples/RuntimeExporterSample/VRMRuntimeExporter.cs @@ -10,6 +10,9 @@ namespace VRM.RuntimeExporterSample [SerializeField] public bool UseNormalize = true; + [SerializeField] + public bool BakeBlendShapes = false; + GameObject m_model; void OnGUI() @@ -29,7 +32,7 @@ namespace VRM.RuntimeExporterSample if (GUILayout.Button("Export")) { - Export(m_model, UseNormalize); + Export(m_model, UseNormalize, BakeBlendShapes); } } @@ -112,7 +115,7 @@ namespace VRM.RuntimeExporterSample } - static void Export(GameObject model, bool useNormalize) + static void Export(GameObject model, bool useNormalize, bool bakeBlendShape) { //#if UNITY_STANDALONE_WIN #if false @@ -125,7 +128,7 @@ namespace VRM.RuntimeExporterSample return; } - var bytes = useNormalize ? ExportCustom(model) : ExportSimple(model); + var bytes = useNormalize ? ExportCustom(model, false, bakeBlendShape) : ExportSimple(model); File.WriteAllBytes(path, bytes); Debug.LogFormat("export to {0}", path); @@ -138,10 +141,10 @@ namespace VRM.RuntimeExporterSample return bytes; } - static byte[] ExportCustom(GameObject exportRoot, bool forceTPose = false) + static byte[] ExportCustom(GameObject exportRoot, bool forceTPose, bool bakeBlendShape) { // normalize - VRMBoneNormalizer.Execute(exportRoot, forceTPose); + VRMBoneNormalizer.Execute(exportRoot, forceTPose, bakeBlendShape); return ExportSimple(exportRoot); }