From 9f4384f5d297e57114aaa43a185e072478bcb0b1 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Mon, 30 Oct 2023 16:43:01 +0900 Subject: [PATCH] chore: improve support for multi pass rendering with multiple materials --- Editor/Processors/SkinnedMeshes/MeshInfo2.cs | 104 ++++++++++++++----- 1 file changed, 76 insertions(+), 28 deletions(-) diff --git a/Editor/Processors/SkinnedMeshes/MeshInfo2.cs b/Editor/Processors/SkinnedMeshes/MeshInfo2.cs index 68d9dd759..b1d4defbb 100644 --- a/Editor/Processors/SkinnedMeshes/MeshInfo2.cs +++ b/Editor/Processors/SkinnedMeshes/MeshInfo2.cs @@ -94,17 +94,30 @@ public MeshInfo2(MeshRenderer renderer) private void SetMaterials(Renderer renderer) { + if (SubMeshes.Count == 0) return; + var sourceMaterials = renderer.sharedMaterials; if (sourceMaterials.Length < SubMeshes.Count) SubMeshes.RemoveRange(sourceMaterials.Length, SubMeshes.Count - sourceMaterials.Length); - for (var i = 0; i < SubMeshes.Count; i++) - SubMeshes[i].SharedMaterial = sourceMaterials[i]; - var verticesForLastSubMesh = - SubMeshes.Count == 0 ? new List() : SubMeshes[SubMeshes.Count - 1].Triangles; - for (var i = SubMeshes.Count; i < sourceMaterials.Length; i++) - SubMeshes.Add(new SubMesh(verticesForLastSubMesh.ToList(), sourceMaterials[i])); + if (SubMeshes.Count == sourceMaterials.Length) + { + for (var i = 0; i < SubMeshes.Count; i++) + SubMeshes[i].SharedMaterial = sourceMaterials[i]; + } + else + { + // there are multi pass rendering + for (var i = 0; i < SubMeshes.Count - 1; i++) + SubMeshes[i].SharedMaterial = sourceMaterials[i]; + + var lastMeshMaterials = new Material[sourceMaterials.Length - SubMeshes.Count + 1]; + + for (int i = SubMeshes.Count - 1, j = 0; i < sourceMaterials.Length; i++, j++) + lastMeshMaterials[j] = sourceMaterials[i]; + SubMeshes[SubMeshes.Count - 1].SharedMaterials = lastMeshMaterials; + } } [Conditional("UNITY_ASSERTIONS")] @@ -391,6 +404,9 @@ public void WriteToMesh(Mesh destMesh) Optimize(); destMesh.Clear(); + // if mesh is empty, clearing mesh is enough! + if (SubMeshes.Count == 0) return; + Profiler.BeginSample("Write to Mesh"); Profiler.BeginSample("Vertices and Normals"); @@ -480,33 +496,58 @@ public void WriteToMesh(Mesh destMesh) for (var i = 0; i < Vertices.Count; i++) vertexIndices.Add(Vertices[i], i); - var triangles = new int[SubMeshes.Sum(x => x.Triangles.Count)]; - var subMeshDescriptors = new SubMeshDescriptor[SubMeshes.Count]; - var trianglesIndex = 0; - for (var i = 0; i < SubMeshes.Count; i++) + var totalTriangles = 0; + var totalSubMeshes = 0; + for (var i = 0; i < SubMeshes.Count - 1; i++) { - var subMesh = SubMeshes[i]; - var existingIndex = SubMeshes.FindIndex(0, i, sm => sm.Triangles.SequenceEqual(subMesh.Triangles)); - if (existingIndex != -1) - { - subMeshDescriptors[i] = subMeshDescriptors[existingIndex]; - } - else + // for non-last submesh, we have to duplicate submesh for multi pass rendering + for (var j = 0; j < SubMeshes[i].SharedMaterials.Length; j++) { - subMeshDescriptors[i] = new SubMeshDescriptor(trianglesIndex, SubMeshes[i].Triangles.Count); - foreach (var triangle in SubMeshes[i].Triangles) - triangles[trianglesIndex++] = vertexIndices[triangle]; + totalTriangles += SubMeshes[i].Triangles.Count; + totalSubMeshes++; } } + { + // for last submesh, we can use single submesh for multi pass reendering + totalTriangles += SubMeshes[SubMeshes.Count - 1].Triangles.Count; + totalSubMeshes++; + } - triangles = triangles.Length == trianglesIndex - ? triangles - : triangles.AsSpan().Slice(0, trianglesIndex).ToArray(); + var triangles = new int[totalTriangles]; + var subMeshDescriptors = new SubMeshDescriptor[totalSubMeshes]; + var trianglesIndex = 0; + var submeshIndex = 0; + + for (var i = 0; i < SubMeshes.Count - 1; i++) + { + var subMesh = SubMeshes[i]; + var descriptor = new SubMeshDescriptor(trianglesIndex, subMesh.Triangles.Count); + foreach (var triangle in subMesh.Triangles) + triangles[trianglesIndex++] = vertexIndices[triangle]; + + // general case: for non-last submesh, we have to duplicate submesh for multi pass rendering + for (var j = 0; j < subMesh.SharedMaterials.Length; j++) + subMeshDescriptors[submeshIndex++] = descriptor; + } + + { + var subMesh = SubMeshes[SubMeshes.Count - 1]; + + var descriptor = new SubMeshDescriptor(trianglesIndex, subMesh.Triangles.Count); + foreach (var triangle in subMesh.Triangles) + triangles[trianglesIndex++] = vertexIndices[triangle]; + + // for last submesh, we can use single submesh for multi pass reendering + subMeshDescriptors[submeshIndex++] = descriptor; + } + + Debug.Assert(subMeshDescriptors.Length == submeshIndex); + Debug.Assert(triangles.Length == trianglesIndex); destMesh.indexFormat = Vertices.Count <= ushort.MaxValue ? IndexFormat.UInt16 : IndexFormat.UInt32; destMesh.triangles = triangles; - destMesh.subMeshCount = SubMeshes.Count; - for (var i = 0; i < SubMeshes.Count; i++) + destMesh.subMeshCount = submeshIndex; + for (var i = 0; i < subMeshDescriptors.Length; i++) destMesh.SetSubMesh(i, subMeshDescriptors[i]); } Profiler.EndSample(); @@ -593,7 +634,7 @@ public void WriteToSkinnedMeshRenderer(SkinnedMeshRenderer targetRenderer) targetRenderer.sharedMesh = mesh; for (var i = 0; i < BlendShapes.Count; i++) targetRenderer.SetBlendShapeWeight(i, BlendShapes[i].weight); - targetRenderer.sharedMaterials = SubMeshes.Select(x => x.SharedMaterial).ToArray(); + targetRenderer.sharedMaterials = SubMeshes.SelectMany(x => x.SharedMaterials).ToArray(); targetRenderer.bones = Bones.Select(x => x.Transform).ToArray(); targetRenderer.rootBone = RootBone; @@ -610,7 +651,7 @@ public void WriteToMeshRenderer(MeshRenderer targetRenderer) var meshFilter = targetRenderer.GetComponent(); WriteToMesh(mesh); meshFilter.sharedMesh = mesh; - targetRenderer.sharedMaterials = SubMeshes.Select(x => x.SharedMaterial).ToArray(); + targetRenderer.sharedMaterials = SubMeshes.SelectMany(x => x.SharedMaterials).ToArray(); }); } } @@ -619,7 +660,14 @@ internal class SubMesh { // size of this must be 3 * n public readonly List Triangles = new List(); - public Material SharedMaterial; + + public Material SharedMaterial + { + get => SharedMaterials[0]; + set => SharedMaterials[0] = value; + } + + public Material[] SharedMaterials = { null }; public SubMesh() {