//using UnityEngine; using System; using System.Collections; using System.Collections.Generic; #if !UNITY_FLASH// no static batching in flash namespace UnityEngine { internal class InternalStaticBatchingUtility { // assume 16bit indices const int MaxVerticesInBatch = 64000; // a little bit less than 64K - just in case const string CombinedMeshPrefix = "Combined Mesh"; static public void Combine (UnityEngine.GameObject staticBatchRoot) { Combine(staticBatchRoot, false); } static public void Combine (UnityEngine.GameObject staticBatchRoot, bool combineOnlyStatic) { GameObject[] gos = (GameObject[])UnityEngine.Object.FindObjectsOfType(typeof(GameObject)); List filteredGos = new List(); foreach (GameObject go in gos) { if (staticBatchRoot != null) if (!go.transform.IsChildOf(staticBatchRoot.transform)) continue; if (combineOnlyStatic && !go.isStaticBatchable ) continue; filteredGos.Add(go); } gos = filteredGos.ToArray(); // Inform user about Advanced license only feature // real license guard is baked into the native code. bool hasProOrAdvancedLicense = Application.HasProLicense() || Application.HasAdvancedLicense(); if (!hasProOrAdvancedLicense) { // Display error only if invoked from the user script if (staticBatchRoot != null && gos.Length > 0) Debug.LogError("Your Unity license is not sufficient for Static Batching."); } Combine(gos, staticBatchRoot); } static public void Combine(GameObject[] gos, UnityEngine.GameObject staticBatchRoot) { Matrix4x4 staticBatchInverseMatrix = Matrix4x4.identity; Transform staticBatchRootTransform = null; if (staticBatchRoot) { staticBatchInverseMatrix = staticBatchRoot.transform.worldToLocalMatrix; staticBatchRootTransform = staticBatchRoot.transform; } int batchIndex = 0; int verticesInBatch = 0; List meshes = new List(); List subsets = new List(); List subsetGOs = new List (); Array.Sort(gos, new SortGO()); foreach (GameObject go in gos) { MeshFilter filter = go.GetComponent(typeof(MeshFilter)) as MeshFilter; if (filter == null) continue; // reject if has no mesh if (filter.sharedMesh == null) continue; // reject if mesh not readable if (!filter.sharedMesh.canAccess) continue; // reject if has not renderer or renderer is disabled if (filter.renderer == null || !filter.renderer.enabled) continue; // reject if already combined for static batching if (filter.renderer.staticBatchIndex != 0) continue; // check if we have enough space inside the current batch if (verticesInBatch + filter.sharedMesh.vertexCount > MaxVerticesInBatch) { MakeBatch (meshes, subsets, subsetGOs, staticBatchRootTransform, batchIndex++); meshes.Clear(); subsets.Clear(); subsetGOs.Clear(); verticesInBatch = 0; } MeshSubsetCombineUtility.MeshInstance instance = new MeshSubsetCombineUtility.MeshInstance (); Mesh instanceMesh = filter.sharedMesh; instance.meshInstanceID = instanceMesh.GetInstanceID(); instance.transform = staticBatchInverseMatrix * filter.transform.localToWorldMatrix; instance.lightmapTilingOffset = filter.renderer.lightmapTilingOffset; //;;Debug.Log("New static mesh (" + go.name + ")verts: " + instance.mesh.vertexCount + // ", tris: " + instance.mesh.triangles.Length + // ", materials: " + filter.renderer.sharedMaterials.Length + // ", subs: " + instance.mesh.subMeshCount // ); meshes.Add(instance); Material[] materials = filter.renderer.sharedMaterials; if (materials.Length > instanceMesh.subMeshCount) { Debug.LogWarning("Mesh has more materials (" + materials.Length + ") than subsets (" + instanceMesh.subMeshCount + ")"); // extra materials don't have a meaning and it screws the rendering as Unity // tries to render with those extra materials. Material[] newMats = new Material[instanceMesh.subMeshCount]; for (int i = 0; i < instanceMesh.subMeshCount; ++i) newMats[i] = filter.renderer.sharedMaterials[i]; filter.renderer.sharedMaterials = newMats; materials = newMats; } for (int m = 0; m < System.Math.Min(materials.Length, instanceMesh.subMeshCount); ++m) { //;;Debug.Log(" new subset : " + m + ", tris " + instance.mesh.GetTriangles(m).Length); MeshSubsetCombineUtility.SubMeshInstance subsetInstance = new MeshSubsetCombineUtility.SubMeshInstance(); subsetInstance.meshInstanceID = filter.sharedMesh.GetInstanceID(); subsetInstance.vertexOffset = verticesInBatch; subsetInstance.subMeshIndex = m; subsetInstance.gameObjectInstanceID = go.GetInstanceID(); subsetInstance.transform = instance.transform; subsets.Add(subsetInstance); subsetGOs.Add(go); } verticesInBatch += instanceMesh.vertexCount; } MakeBatch(meshes, subsets, subsetGOs, staticBatchRootTransform, batchIndex); } static private void MakeBatch(List meshes, List subsets, List subsetGOs, Transform staticBatchRootTransform, int batchIndex) { if (meshes.Count < 2) return; MeshSubsetCombineUtility.MeshInstance[] aMeshes = meshes.ToArray(); MeshSubsetCombineUtility.SubMeshInstance[] aSubsets = subsets.ToArray(); string combinedMeshName = CombinedMeshPrefix; combinedMeshName += " (root: " + ((staticBatchRootTransform != null)? staticBatchRootTransform.name: "scene") + ")"; if (batchIndex > 0) combinedMeshName += " " + (batchIndex + 1); Mesh combinedMesh = StaticBatchingUtility.InternalCombineVertices (aMeshes, combinedMeshName); StaticBatchingUtility.InternalCombineIndices(aSubsets, combinedMesh); int subsetIndex = 0; for (int item = 0; item < aSubsets.Length; item++) { MeshSubsetCombineUtility.SubMeshInstance i = aSubsets[item]; GameObject go = subsetGOs[item]; Mesh m = combinedMesh; MeshFilter filter = (MeshFilter)go.GetComponent(typeof(MeshFilter)); filter.sharedMesh = m; go.renderer.SetSubsetIndex(i.subMeshIndex, subsetIndex); go.renderer.staticBatchRootTransform = staticBatchRootTransform; // for some reason if GOs were created dynamically // then we need to toggle renderer to avoid caching old geometry go.renderer.enabled = false; go.renderer.enabled = true; subsetIndex++; } } internal class SortGO : IComparer { int IComparer.Compare( object a, object b ) { if (a == b) return 0; Renderer aRenderer = GetRenderer(a as GameObject); Renderer bRenderer = GetRenderer(b as GameObject); int compare = GetMaterialId(aRenderer).CompareTo(GetMaterialId(bRenderer)); if (compare == 0) compare = GetLightmapIndex(aRenderer).CompareTo(GetLightmapIndex(bRenderer)); return compare; } static private int GetMaterialId(Renderer renderer) { if (renderer == null || renderer.sharedMaterial == null) return 0; return renderer.sharedMaterial.GetInstanceID(); } static private int GetLightmapIndex(Renderer renderer) { if (renderer == null) return -1; return renderer.lightmapIndex; } static private Renderer GetRenderer(GameObject go) { if (go == null) return null; MeshFilter filter = go.GetComponent(typeof(MeshFilter)) as MeshFilter; if (filter == null) return null; return filter.renderer; } } } } // namespace UnityEngine #endif