diff --git a/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrationResult.cs b/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrationResult.cs index b6ec4bc57..493ca7071 100644 --- a/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrationResult.cs +++ b/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrationResult.cs @@ -46,15 +46,17 @@ namespace UniGLTF.MeshUtility throw new NotImplementedException(); } - public void AddIntegratedRendererTo(GameObject parent) + public IEnumerable AddIntegratedRendererTo(GameObject parent) { if (Integrated != null) { Integrated.AddIntegratedRendererTo(parent, Bones); + yield return Integrated.IntegratedRenderer.gameObject; } if (IntegratedNoBlendShape != null) { IntegratedNoBlendShape.AddIntegratedRendererTo(parent, Bones); + yield return IntegratedNoBlendShape.IntegratedRenderer.gameObject; } } } diff --git a/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrator.cs b/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrator.cs index 473f228f9..9dd280350 100644 --- a/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrator.cs +++ b/Assets/UniGLTF/Runtime/MeshUtility/MeshIntegrator.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Security.Cryptography.X509Certificates; +using UnityEditor; using UnityEngine; -using UnityEngine.UIElements; namespace UniGLTF.MeshUtility { @@ -25,14 +24,29 @@ namespace UniGLTF.MeshUtility public Material Material; } - class BlendShape + public class BlendShape { - public int VertexOffset; public string Name; - public float FrameWeight; - public Vector3[] Positions; - public Vector3[] Normals; - public Vector3[] Tangents; + public List Positions = new List(); + public List Normals = new List(); + public List Tangents = new List(); + + public BlendShape(string name) + { + Name = name; + } + + public void Fill(int count) + { + var size = count - Positions.Count; + if (size < 0) + { + throw new Exception(); + } + Positions.AddRange(Enumerable.Repeat(Vector3.zero, size)); + Normals.AddRange(Enumerable.Repeat(Vector3.zero, size)); + Tangents.AddRange(Enumerable.Repeat(Vector3.zero, size)); + } } MeshIntegrationResult Result { get; } = new MeshIntegrationResult(); @@ -47,37 +61,13 @@ namespace UniGLTF.MeshUtility List BlendShapes { get; } = new List(); void AddBlendShapesToMesh(Mesh mesh) { - Dictionary map = new Dictionary(); - foreach (var x in BlendShapes) - { - BlendShape bs = null; - if (!map.TryGetValue(x.Name, out bs)) - { - bs = new BlendShape(); - // arrays size must match mesh vertex count - bs.Positions = new Vector3[Positions.Count]; - bs.Normals = new Vector3[Positions.Count]; - bs.Tangents = new Vector3[Positions.Count]; - bs.Name = x.Name; - bs.FrameWeight = x.FrameWeight; - map.Add(x.Name, bs); - } - - var j = x.VertexOffset; - for (int i = 0; i < x.Positions.Length; ++i, ++j) - { - bs.Positions[j] = x.Positions[i]; - bs.Normals[j] = x.Normals[i]; - bs.Tangents[j] = x.Tangents[i]; - } - } - - foreach (var kv in map) { //Debug.LogFormat("AddBlendShapeFrame: {0}", kv.Key); - mesh.AddBlendShapeFrame(kv.Key, kv.Value.FrameWeight, - kv.Value.Positions, kv.Value.Normals, kv.Value.Tangents); + mesh.AddBlendShapeFrame(x.Name, 1, + x.Positions.ToArray(), + x.Normals.ToArray(), + x.Tangents.ToArray()); } } @@ -193,7 +183,7 @@ namespace UniGLTF.MeshUtility Result.SourceSkinnedMeshRenderers.Add(renderer); Result.Sources.Add(mesh); - var indexOffset = Positions.Count; + var vertexOffset = Positions.Count; // var boneIndexOffset = Bones.Count; Positions.AddRange(mesh.vertices); @@ -227,7 +217,7 @@ namespace UniGLTF.MeshUtility for (int i = 0; i < mesh.subMeshCount && i < renderer.sharedMaterials.Length; ++i) { - var indices = mesh.GetIndices(i).Select(x => x + indexOffset); + var indices = mesh.GetIndices(i).Select(x => x + vertexOffset); var mat = renderer.sharedMaterials[i]; var sameMaterialSubMeshIndex = SubMeshes.FindIndex(x => ReferenceEquals(x.Material, mat)); if (sameMaterialSubMeshIndex >= 0) @@ -251,16 +241,38 @@ namespace UniGLTF.MeshUtility var normals = new Vector3[mesh.vertexCount]; var tangents = new Vector3[mesh.vertexCount]; mesh.GetBlendShapeFrameVertices(i, 0, positions, normals, tangents); - BlendShapes.Add(new BlendShape - { - VertexOffset = indexOffset, - FrameWeight = mesh.GetBlendShapeFrameWeight(i, 0), - Name = mesh.GetBlendShapeName(i), - Positions = positions, - Normals = normals, - Tangents = tangents, - }); + + var blendShape = GetOrCreateBlendShape(mesh.GetBlendShapeName(i), vertexOffset); + blendShape.Positions.AddRange(positions); + blendShape.Normals.AddRange(normals); + blendShape.Tangents.AddRange(tangents); } + foreach (var blendShape in BlendShapes) + { + blendShape.Fill(Positions.Count); + } + } + + BlendShape GetOrCreateBlendShape(string name, int vertexOffset) + { + BlendShape found = null; + foreach (var blendshape in BlendShapes) + { + if (blendshape.Name == name) + { + found = blendshape; + break; + } + } + if (found == null) + { + found = new BlendShape(name); + BlendShapes.Add(found); + } + + found.Fill(vertexOffset); + + return found; } public static MeshIntegrationResult Integrate(MeshIntegrationGroup group, BlendShapeOperation op) @@ -282,6 +294,29 @@ namespace UniGLTF.MeshUtility delegate bool TriangleFilter(int i0, int i1, int i2); + static int[] GetFilteredIndices(List indices, TriangleFilter filter) + { + if (filter == null) + { + return indices.ToArray(); + } + + var filtered = new List(); + for (int i = 0; i < indices.Count; 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); + } + } + return filtered.ToArray(); + } + Mesh CreateMesh(string name, List dst, TriangleFilter filter) { var mesh = new Mesh(); @@ -296,41 +331,19 @@ namespace UniGLTF.MeshUtility mesh.uv = UV.ToArray(); mesh.tangents = Tangents.ToArray(); mesh.boneWeights = BoneWeights.ToArray(); - mesh.subMeshCount = SubMeshes.Count; - for (var submesh = 0; submesh < SubMeshes.Count; ++submesh) + + int subMeshCount = 0; + foreach (var submesh in SubMeshes) { - if (filter != null) + var indices = GetFilteredIndices(submesh.Indices, filter); + if (indices.Length > 0) { - 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); + mesh.subMeshCount = (subMeshCount + 1); + mesh.SetIndices(indices, MeshTopology.Triangles, subMeshCount++); 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, + Count = indices.Length, + Material = submesh.Material, }); } } @@ -345,37 +358,34 @@ namespace UniGLTF.MeshUtility throw new ArgumentException(); } - bool useSplit = false; - var used = new bool[Positions.Count]; + var splitter = new TriangleSeparator(Positions.Count); if (op == BlendShapeOperation.Split) { - foreach (var x in BlendShapes) + foreach (var blendShape in BlendShapes) { - for (int i = 0; i < x.Positions.Length; ++i) - { - if (x.Positions[i] != Vector3.zero) - { - used[i] = true; - } - } + splitter.CheckPositions(blendShape.Positions); } - var count = used.Count(x => x); - useSplit = count > 0 && count < used.Length; } - if (useSplit) + if (splitter.ShouldSplit) { + // + // has BlendShape + // Result.Integrated = new MeshInfo(); var mesh = CreateMesh(name, Result.Integrated.SubMeshes, - (i0, i1, i2) => used[i0] || used[i1] || used[i2]); + splitter.TriangleHasBlendShape); Result.Integrated.Mesh = mesh; AddBlendShapesToMesh(mesh); // skinning mesh.bindposes = _BindPoses.ToArray(); + // + // no BlendShape + // Result.IntegratedNoBlendShape = new MeshInfo(); var meshWithoutBlendShape = CreateMesh(name + ".no_blendshape", Result.IntegratedNoBlendShape.SubMeshes, - (i0, i1, i2) => !used[i0] && !used[i1] && !used[i2]); + splitter.TriangleHasNotBlendShape); Result.IntegratedNoBlendShape.Mesh = meshWithoutBlendShape; // skinning meshWithoutBlendShape.bindposes = _BindPoses.ToArray(); diff --git a/Assets/UniGLTF/Runtime/MeshUtility/TriangleSeparator.cs b/Assets/UniGLTF/Runtime/MeshUtility/TriangleSeparator.cs new file mode 100644 index 000000000..8ca08e604 --- /dev/null +++ b/Assets/UniGLTF/Runtime/MeshUtility/TriangleSeparator.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace UniGLTF.MeshUtility +{ + class TriangleSeparator + { + bool[] VertexHasBlendShape; + + public bool ShouldSplit + { + get + { + var count = VertexHasBlendShape.Count(x => x); + // すべて true か false の場合は分割しない + return count > 0 && count < VertexHasBlendShape.Length; + } + } + + public bool TriangleHasBlendShape(int i0, int i1, int i2) + { + return VertexHasBlendShape[i0] + || VertexHasBlendShape[i1] + || VertexHasBlendShape[i2]; + } + + public bool TriangleHasNotBlendShape(int i0, int i1, int i2) + { + return !TriangleHasBlendShape(i0, i1, i2); + } + + public TriangleSeparator(int vertexCount) + { + VertexHasBlendShape = new bool[vertexCount]; + } + + public void CheckPositions(IReadOnlyList positions) + { + for (int i = 0; i < positions.Count; ++i) + { + if (positions[i] != Vector3.zero) + { + VertexHasBlendShape[i] = true; + } + } + } + } +} \ No newline at end of file diff --git a/Assets/UniGLTF/Runtime/MeshUtility/TriangleSeparator.cs.meta b/Assets/UniGLTF/Runtime/MeshUtility/TriangleSeparator.cs.meta new file mode 100644 index 000000000..29d1e3774 --- /dev/null +++ b/Assets/UniGLTF/Runtime/MeshUtility/TriangleSeparator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b8936c9c948d10447a6899410bac3c07 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Editor/MeshUtility/Vrm10MeshUtilityDialog.cs b/Assets/VRM10/Editor/MeshUtility/Vrm10MeshUtilityDialog.cs index f382c32a3..176f5bad9 100644 --- a/Assets/VRM10/Editor/MeshUtility/Vrm10MeshUtilityDialog.cs +++ b/Assets/VRM10/Editor/MeshUtility/Vrm10MeshUtilityDialog.cs @@ -108,8 +108,11 @@ namespace UniVRM10 if (pressed) { Undo.RegisterFullObjectHierarchyUndo(exportTarget, "MeshUtility"); - _meshUtility.Process(exportTarget); - + foreach (var go in _meshUtility.Process(exportTarget)) + { + Undo.RegisterCreatedObjectUndo(go, "MeshUtility"); + } + _exportTarget = null; // Show Result ? // Close(); // GUIUtility.ExitGUI(); diff --git a/Assets/VRM10/Runtime/MeshUtility/Vrm10MeshUtility.cs b/Assets/VRM10/Runtime/MeshUtility/Vrm10MeshUtility.cs index d4915fb47..e66253d80 100644 --- a/Assets/VRM10/Runtime/MeshUtility/Vrm10MeshUtility.cs +++ b/Assets/VRM10/Runtime/MeshUtility/Vrm10MeshUtility.cs @@ -169,7 +169,7 @@ namespace UniVRM10 return empty; } - public void Process(GameObject go) + public List Process(GameObject go) { var vrmInstance = go.GetComponent(); if (vrmInstance == null) @@ -200,7 +200,8 @@ namespace UniVRM10 // TODO: update: spring // TODO: update: constraint - // TODO: update: firstPoint offset + // TODO: update: firstPerson offset + // write back normalized transform to boneMap BoneNormalizer.WriteBackResult(go, normalized, boneMap); if (Application.isPlaying) @@ -241,10 +242,10 @@ namespace UniVRM10 copy.AddRange(MeshIntegrationGroups); } + var newGo = new List(); { var empty = GetOrCreateEmpty(go, "mesh"); - // TODO: UNDO var results = new List(); foreach (var group in copy) { @@ -253,7 +254,10 @@ namespace UniVRM10 : MeshIntegrator.BlendShapeOperation.Use); results.Add(result); - result.AddIntegratedRendererTo(empty); + foreach (var created in result.AddIntegratedRendererTo(empty)) + { + newGo.Add(created); + } if (generateFirstPerson && group.Name == "auto") { @@ -283,6 +287,8 @@ namespace UniVRM10 } MeshIntegrationGroups.Clear(); + + return newGo; } void ProcessFirstPerson(Vrm10Instance vrmInstance, MeshInfo info)