If there are a lot of objects in a scene that share the same material but do not share the same meshes you can lower the number of draw calls by combining them into one single mesh.
Unity has a "MeshCombineUtility" in the standard assets that is supposed to do exactly that, but several of our combined meshes were missing parts in an unpredictable way. It uses a hashtable to index the used materials. I replaced it with a typed dictionary and that fixed it. Somehow we must have had hash collisions.
In the documentation of Mesh.Combinemeshes there is a script that intends to do the same, with simpler code. Oddly, that script does combine multiple meshes into one but it generates an object without materials.
So I wrote my own version of the CombineChildren script that combines both into one, working script:
using UnityEngine; using System.Collections.Generic; [AddComponentMenu("Mesh/Combine Children")] public class CombineChildren : MonoBehaviour { void Start() { Matrix4x4 myTransform = transform.worldToLocalMatrix; Dictionary<string, List<CombineInstance>> combines = new Dictionary<string, List<CombineInstance>>(); Dictionary<string , Material> namedMaterials = new Dictionary<string, Material>(); MeshRenderer[] meshRenderers = GetComponentsInChildren<MeshRenderer>(); foreach (var meshRenderer in meshRenderers) { foreach (var material in meshRenderer.sharedMaterials) if (material != null && !combines.ContainsKey(material.name)) { combines.Add(material.name, new List<CombineInstance>()); namedMaterials.Add(material.name, material); } } MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>(); foreach(var filter in meshFilters) { if (filter.sharedMesh == null) continue; var filterRenderer = filter.GetComponent<Renderer>(); if (filterRenderer.sharedMaterial == null) continue; if (filterRenderer.sharedMaterials.Length > 1) continue; CombineInstance ci = new CombineInstance { mesh = filter.sharedMesh, transform = myTransform*filter.transform.localToWorldMatrix }; combines[filterRenderer.sharedMaterial.name].Add(ci); Destroy(filterRenderer); } foreach (Material m in namedMaterials.Values) { var go = new GameObject("Combined mesh"); go.transform.parent = transform; go.transform.localPosition = Vector3.zero; go.transform.localRotation = Quaternion.identity; go.transform.localScale = Vector3.one; var filter = go.AddComponent<MeshFilter>(); filter.mesh.CombineMeshes(combines[m.name].ToArray(), true, true); var arenderer = go.AddComponent<MeshRenderer>(); arenderer.material = m; } }}