using System.Collections.Generic; using UnityEngine; using UnityEngine.ProBuilder; using UnityEngine.Rendering; namespace Unity.FPS.Game { public static class MeshCombineUtility { public class RenderBatchData { public class MeshAndTrs { public Mesh Mesh; public Matrix4x4 Trs; public MeshAndTrs(Mesh m, Matrix4x4 t) { Mesh = m; Trs = t; } } public Material Material; public int SubmeshIndex = 0; public ShadowCastingMode ShadowMode; public bool ReceiveShadows; public MotionVectorGenerationMode MotionVectors; public List MeshesWithTrs = new List(); } public enum RendererDisposeMethod { DestroyGameObject, DestroyRendererAndFilter, DisableGameObject, DisableRenderer, } public static void Combine(List renderers, RendererDisposeMethod disposeMethod, string newObjectName) { int renderersCount = renderers.Count; List renderBatches = new List(); // Build render batches for all unique material + submeshIndex combinations for (int i = 0; i < renderersCount; i++) { MeshRenderer meshRenderer = renderers[i]; if (meshRenderer == null) continue; MeshFilter meshFilter = meshRenderer.GetComponent(); if (meshFilter == null) continue; Mesh mesh = meshFilter.sharedMesh; if (mesh == null) continue; Transform t = meshRenderer.GetComponent(); Material[] materials = meshRenderer.sharedMaterials; for (int s = 0; s < mesh.subMeshCount; s++) { if (materials[s] == null) continue; int batchIndex = GetExistingRenderBatch(renderBatches, materials[s], meshRenderer, s); if (batchIndex >= 0) { renderBatches[batchIndex].MeshesWithTrs .Add(new RenderBatchData.MeshAndTrs(mesh, Matrix4x4.TRS(t.position, t.rotation, t.lossyScale))); } else { RenderBatchData newBatchData = new RenderBatchData(); newBatchData.Material = materials[s]; newBatchData.SubmeshIndex = s; newBatchData.ShadowMode = meshRenderer.shadowCastingMode; newBatchData.ReceiveShadows = meshRenderer.receiveShadows; newBatchData.MeshesWithTrs.Add(new RenderBatchData.MeshAndTrs(mesh, Matrix4x4.TRS(t.position, t.rotation, t.lossyScale))); renderBatches.Add(newBatchData); } } // Destroy probuilder component if present ProBuilderMesh pbm = meshRenderer.GetComponent(); if (pbm) { GameObject.Destroy(pbm); } switch (disposeMethod) { case RendererDisposeMethod.DestroyGameObject: if (Application.isPlaying) { GameObject.Destroy(meshRenderer.gameObject); } else { GameObject.DestroyImmediate(meshRenderer.gameObject); } break; case RendererDisposeMethod.DestroyRendererAndFilter: if (Application.isPlaying) { GameObject.Destroy(meshRenderer); GameObject.Destroy(meshFilter); } else { GameObject.DestroyImmediate(meshRenderer); GameObject.DestroyImmediate(meshFilter); } break; case RendererDisposeMethod.DisableGameObject: meshRenderer.gameObject.SetActive(false); break; case RendererDisposeMethod.DisableRenderer: meshRenderer.enabled = false; break; } } // Combine each unique render batch for (int i = 0; i < renderBatches.Count; i++) { RenderBatchData rbd = renderBatches[i]; Mesh newMesh = new Mesh(); newMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; CombineInstance[] combineInstances = new CombineInstance[rbd.MeshesWithTrs.Count]; for (int j = 0; j < rbd.MeshesWithTrs.Count; j++) { combineInstances[j].subMeshIndex = rbd.SubmeshIndex; combineInstances[j].mesh = rbd.MeshesWithTrs[j].Mesh; combineInstances[j].transform = rbd.MeshesWithTrs[j].Trs; } // Create mesh newMesh.CombineMeshes(combineInstances); newMesh.RecalculateBounds(); // Create the gameObject GameObject combinedObject = new GameObject(newObjectName); MeshFilter mf = combinedObject.AddComponent(); mf.sharedMesh = newMesh; MeshRenderer mr = combinedObject.AddComponent(); mr.sharedMaterial = rbd.Material; mr.shadowCastingMode = rbd.ShadowMode; } } static int GetExistingRenderBatch(List renderBatches, Material mat, MeshRenderer ren, int submeshIndex) { for (int i = 0; i < renderBatches.Count; i++) { RenderBatchData data = renderBatches[i]; if (data.Material == mat && data.SubmeshIndex == submeshIndex && data.ShadowMode == ren.shadowCastingMode && data.ReceiveShadows == ren.receiveShadows) { return i; } } return -1; } } }