diff --git a/Assets/UniGLTF/Editor/MeshUtility/TabMeshIntegrator.cs b/Assets/UniGLTF/Editor/MeshUtility/TabMeshIntegrator.cs index 923980b26..035c1a6f2 100644 --- a/Assets/UniGLTF/Editor/MeshUtility/TabMeshIntegrator.cs +++ b/Assets/UniGLTF/Editor/MeshUtility/TabMeshIntegrator.cs @@ -90,7 +90,7 @@ namespace UniGLTF.MeshUtility // write mesh asset. foreach (var result in results) { - var mesh = result.MeshMap.Integrated; + var mesh = result.Integrated.Mesh; var assetPath = GetMeshWritePath(mesh); Debug.LogFormat("CreateAsset: {0}", assetPath); AssetDatabase.CreateAsset(mesh, assetPath); diff --git a/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrationResult.cs b/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrationResult.cs index e5f8652c5..b6ec4bc57 100644 --- a/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrationResult.cs +++ b/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrationResult.cs @@ -1,40 +1,61 @@ using System; using System.Collections.Generic; +using System.Linq; using UnityEngine; namespace UniGLTF.MeshUtility { - [System.Serializable] - public class MeshMap + public struct DrawCount { - public List Sources = new List(); - public Mesh Integrated; - public Material[] SharedMaterials; - public Transform[] Bones; + public int Count; + public Material Material; + } + + [Serializable] + public class MeshInfo + { + public Mesh Mesh; + public List SubMeshes = new List(); + + public SkinnedMeshRenderer IntegratedRenderer; + + public void AddIntegratedRendererTo(GameObject parent, Transform[] bones) + { + var go = new GameObject(Mesh.name); + go.transform.SetParent(parent.transform, false); + var smr = go.AddComponent(); + smr.sharedMesh = Mesh; + smr.sharedMaterials = SubMeshes.Where(x => x.Count > 0).Select(x => x.Material).ToArray(); + smr.bones = bones; + IntegratedRenderer = smr; + } } public class MeshIntegrationResult { public List SourceSkinnedMeshRenderers = new List(); public List SourceMeshRenderers = new List(); - public MeshMap MeshMap = new MeshMap(); - public SkinnedMeshRenderer IntegratedRenderer; - public void AddIntegratedRendererTo(GameObject parent) - { - var go = new GameObject(MeshMap.Integrated.name); - go.transform.SetParent(parent.transform, false); - var smr = go.AddComponent(); - smr.sharedMesh = MeshMap.Integrated; - smr.sharedMaterials = MeshMap.SharedMaterials; - smr.bones = MeshMap.Bones; - - IntegratedRenderer = smr; - } + public List Sources = new List(); + public MeshInfo Integrated; + public MeshInfo IntegratedNoBlendShape; + public Transform[] Bones; public void DestroySourceRenderer() { throw new NotImplementedException(); } + + public void AddIntegratedRendererTo(GameObject parent) + { + if (Integrated != null) + { + Integrated.AddIntegratedRendererTo(parent, Bones); + } + if (IntegratedNoBlendShape != null) + { + IntegratedNoBlendShape.AddIntegratedRendererTo(parent, Bones); + } + } } } diff --git a/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrator.cs b/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrator.cs index 1f48e0932..473f228f9 100644 --- a/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrator.cs +++ b/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrator.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Cryptography.X509Certificates; using UnityEngine; using UnityEngine.UIElements; @@ -118,7 +119,7 @@ namespace UniGLTF.MeshUtility return; } Result.SourceMeshRenderers.Add(renderer); - Result.MeshMap.Sources.Add(mesh); + Result.Sources.Add(mesh); var indexOffset = Positions.Count; @@ -190,7 +191,7 @@ namespace UniGLTF.MeshUtility return; } Result.SourceSkinnedMeshRenderers.Add(renderer); - Result.MeshMap.Sources.Add(mesh); + Result.Sources.Add(mesh); var indexOffset = Positions.Count; // var boneIndexOffset = Bones.Count; @@ -279,48 +280,129 @@ namespace UniGLTF.MeshUtility return integrator.Integrate(group.Name, op); } - public MeshIntegrationResult Integrate(string name, BlendShapeOperation op) + delegate bool TriangleFilter(int i0, int i1, int i2); + + Mesh CreateMesh(string name, List dst, TriangleFilter filter) { - if (op == BlendShapeOperation.Split) - { - throw new NotImplementedException(); - } - var mesh = new Mesh(); - + mesh.name = name; if (Positions.Count > ushort.MaxValue) { Debug.LogFormat("exceed 65535 vertices: {0}", Positions.Count); mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; } - mesh.vertices = Positions.ToArray(); mesh.normals = Normals.ToArray(); mesh.uv = UV.ToArray(); mesh.tangents = Tangents.ToArray(); mesh.boneWeights = BoneWeights.ToArray(); mesh.subMeshCount = SubMeshes.Count; - for (var i = 0; i < SubMeshes.Count; ++i) + for (var submesh = 0; submesh < SubMeshes.Count; ++submesh) { - mesh.SetIndices(SubMeshes[i].Indices.ToArray(), MeshTopology.Triangles, i); + if (filter != null) + { + var indices = SubMeshes[submesh].Indices.ToArray(); + var filtered = new List(); + for (int i = 0; i < indices.Length; i += 3) + { + var i0 = indices[i]; + var i1 = indices[i + 1]; + var i2 = indices[i + 2]; + if (filter(i0, i1, i2)) + { + filtered.Add(i0); + filtered.Add(i1); + filtered.Add(i2); + } + } + mesh.SetIndices(filtered.ToArray(), MeshTopology.Triangles, submesh); + dst.Add(new DrawCount + { + Count = filtered.Count, + Material = SubMeshes[submesh].Material, + }); + } + else + { + // use all triangle + var indices = SubMeshes[submesh].Indices; + mesh.SetIndices(indices.ToArray(), MeshTopology.Triangles, submesh); + dst.Add(new DrawCount + { + Count = indices.Count, + Material = SubMeshes[submesh].Material, + }); + } } + return mesh; + } + + public MeshIntegrationResult Integrate(string name, BlendShapeOperation op) + { if (_Bones.Count != _BindPoses.Count) { throw new ArgumentException(); } - Debug.Log($"Bones: {_Bones.Count}"); - mesh.bindposes = _BindPoses.ToArray(); - if (op == BlendShapeOperation.Use) + bool useSplit = false; + var used = new bool[Positions.Count]; + if (op == BlendShapeOperation.Split) { - AddBlendShapesToMesh(mesh); + foreach (var x in BlendShapes) + { + for (int i = 0; i < x.Positions.Length; ++i) + { + if (x.Positions[i] != Vector3.zero) + { + used[i] = true; + } + } + } + var count = used.Count(x => x); + useSplit = count > 0 && count < used.Length; } - mesh.name = name; - Result.MeshMap.SharedMaterials = SubMeshes.Select(x => x.Material).ToArray(); - Result.MeshMap.Bones = _Bones.ToArray(); - Result.MeshMap.Integrated = mesh; + if (useSplit) + { + Result.Integrated = new MeshInfo(); + var mesh = CreateMesh(name, Result.Integrated.SubMeshes, + (i0, i1, i2) => used[i0] || used[i1] || used[i2]); + Result.Integrated.Mesh = mesh; + AddBlendShapesToMesh(mesh); + // skinning + mesh.bindposes = _BindPoses.ToArray(); + + Result.IntegratedNoBlendShape = new MeshInfo(); + var meshWithoutBlendShape = CreateMesh(name + ".no_blendshape", Result.IntegratedNoBlendShape.SubMeshes, + (i0, i1, i2) => !used[i0] && !used[i1] && !used[i2]); + Result.IntegratedNoBlendShape.Mesh = meshWithoutBlendShape; + // skinning + meshWithoutBlendShape.bindposes = _BindPoses.ToArray(); + } + else + { + var useBlendShape = op == BlendShapeOperation.Use && BlendShapes.Count > 0; + if (useBlendShape) + { + Result.Integrated = new MeshInfo(); + var mesh = CreateMesh(name, Result.Integrated.SubMeshes, null); + Result.Integrated.Mesh = mesh; + AddBlendShapesToMesh(mesh); + // skinning + mesh.bindposes = _BindPoses.ToArray(); + } + else + { + Result.IntegratedNoBlendShape = new MeshInfo(); + var mesh = CreateMesh(name, Result.IntegratedNoBlendShape.SubMeshes, null); + Result.IntegratedNoBlendShape.Mesh = mesh; + // skinning + mesh.bindposes = _BindPoses.ToArray(); + } + } + Result.Bones = _Bones.ToArray(); + return Result; } } diff --git a/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegratorUtility.cs b/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegratorUtility.cs index cee998e1a..283fe3fc8 100644 --- a/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegratorUtility.cs +++ b/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegratorUtility.cs @@ -191,7 +191,7 @@ namespace UniGLTF.MeshUtility // Add integrated foreach (var result in results) - { + { result.AddIntegratedRendererTo(copy); } } diff --git a/Assets/VRM/Editor/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs b/Assets/VRM/Editor/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs index 480f8b04c..33faa8359 100644 --- a/Assets/VRM/Editor/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs +++ b/Assets/VRM/Editor/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs @@ -21,7 +21,7 @@ namespace VRM { return clips; } - var result = results.FirstOrDefault(x => x.MeshMap.Integrated.blendShapeCount > 0); + var result = results.FirstOrDefault(x => x.Integrated.Mesh.blendShapeCount > 0); if (result == null) { return clips; @@ -32,7 +32,7 @@ namespace VRM { rendererDict.Add(x.transform.RelativePathFrom(root.transform), x); } - var dstPath = result.IntegratedRenderer.transform.RelativePathFrom(root.transform); + var dstPath = result.Integrated.IntegratedRenderer.transform.RelativePathFrom(root.transform); // copy modify and write var clipAssetPathList = new List(); @@ -54,7 +54,7 @@ namespace VRM { var srcRenderer = rendererDict[val.RelativePath]; var name = srcRenderer.sharedMesh.GetBlendShapeName(val.Index); - var newIndex = result.IntegratedRenderer.sharedMesh.GetBlendShapeIndex(name); + var newIndex = result.Integrated.IntegratedRenderer.sharedMesh.GetBlendShapeIndex(name); if (newIndex == -1) { throw new KeyNotFoundException($"blendshape:{name} not found"); diff --git a/Assets/VRM/Editor/SkinnedMeshUtility/VrmMeshIntegratorWizard.cs b/Assets/VRM/Editor/SkinnedMeshUtility/VrmMeshIntegratorWizard.cs index 1d43b2103..1cf40e9b5 100644 --- a/Assets/VRM/Editor/SkinnedMeshUtility/VrmMeshIntegratorWizard.cs +++ b/Assets/VRM/Editor/SkinnedMeshUtility/VrmMeshIntegratorWizard.cs @@ -91,7 +91,7 @@ namespace VRM [Header("Result")] [SerializeField] - MeshMap[] integrationResults; + MeshInfo[] integrationResults; public static void CreateWizard() { @@ -279,17 +279,17 @@ namespace VRM // write mesh asset foreach (var result in results) { - var childAssetPath = $"{assetFolder}/{result.IntegratedRenderer.gameObject.name}{ASSET_SUFFIX}"; + var childAssetPath = $"{assetFolder}/{result.Integrated.IntegratedRenderer.gameObject.name}{ASSET_SUFFIX}"; Debug.LogFormat("CreateAsset: {0}", childAssetPath); - AssetDatabase.CreateAsset(result.IntegratedRenderer.sharedMesh, childAssetPath); + AssetDatabase.CreateAsset(result.Integrated.IntegratedRenderer.sharedMesh, childAssetPath); } // 統合した結果をヒエラルキーに追加する foreach (var result in results) { - if (result.IntegratedRenderer != null) + if (result.Integrated.IntegratedRenderer != null) { - result.IntegratedRenderer.transform.SetParent(copy.transform, false); + result.Integrated.IntegratedRenderer.transform.SetParent(copy.transform, false); } } diff --git a/Assets/VRM10/Runtime/MeshUtility/Vrm10MeshUtility.cs b/Assets/VRM10/Runtime/MeshUtility/Vrm10MeshUtility.cs index 755625344..ab6494771 100644 --- a/Assets/VRM10/Runtime/MeshUtility/Vrm10MeshUtility.cs +++ b/Assets/VRM10/Runtime/MeshUtility/Vrm10MeshUtility.cs @@ -251,7 +251,7 @@ namespace UniVRM10 { var result = MeshIntegrator.Integrate(group, SplitByBlendShape ? MeshIntegrator.BlendShapeOperation.Split - : MeshIntegrator.BlendShapeOperation.None); + : MeshIntegrator.BlendShapeOperation.Use); results.Add(result); result.AddIntegratedRendererTo(empty); @@ -259,21 +259,13 @@ namespace UniVRM10 if (generateFirstPerson && group.Name == "auto") { Debug.Log("generateFirstPerson"); - var firstPersonBone = vrmInstance.Humanoid.Head; - var task = VRM10ObjectFirstPerson.CreateErasedMeshAsync( - result.IntegratedRenderer, - firstPersonBone, - new ImmediateCaller()); - task.Wait(); - - if (task.Result != null) + if (result.Integrated.Mesh != null) { - result.IntegratedRenderer.sharedMesh = task.Result; - result.IntegratedRenderer.name = "auto.headless"; + ProcessFirstPerson(vrmInstance, result.Integrated); } - else + if (result.IntegratedNoBlendShape.Mesh != null) { - Debug.LogWarning("no result"); + ProcessFirstPerson(vrmInstance, result.IntegratedNoBlendShape); } } } @@ -293,5 +285,25 @@ namespace UniVRM10 MeshIntegrationGroups.Clear(); } + + void ProcessFirstPerson(Vrm10Instance vrmInstance, MeshInfo info) + { + var firstPersonBone = vrmInstance.Humanoid.Head; + var task = VRM10ObjectFirstPerson.CreateErasedMeshAsync( + info.IntegratedRenderer, + firstPersonBone, + new ImmediateCaller()); + task.Wait(); + + if (task.Result != null) + { + info.IntegratedRenderer.sharedMesh = task.Result; + info.IntegratedRenderer.name = "auto.headless"; + } + else + { + Debug.LogWarning("no result"); + } + } } }