private bool CompareLODs(LODGroup a, LODGroup b) { if (a.GetLODs() == null || b.GetLODs() == null || a.GetLODs().Length != b.GetLODs().Length) { return false; } MeshFilter[] componentsInChildren = a.GetComponentsInChildren<MeshFilter>(); MeshFilter[] componentsInChildren2 = b.GetComponentsInChildren<MeshFilter>(); if (componentsInChildren.Length != componentsInChildren2.Length) { return false; } for (int i = 0; i < componentsInChildren.Length; i++) { if (componentsInChildren[i].sharedMesh != componentsInChildren2[i].sharedMesh) { return false; } } return true; }
public static void GenerateLODs(LODGroup lodGroup, HashSet <Mesh> newMeshes = null) { LOD[] lods = lodGroup.GetLODs(); // Cleanup for (int i = 1; i < lods.Length; i++) { foreach (Renderer renderer in lods[i].renderers) { if (renderer != null) { Object.DestroyImmediate(renderer.gameObject); } } } if (newMeshes == null) { newMeshes = new HashSet <Mesh>(); } // Assign LOD0 Renderer[] renderers = lodGroup.GetComponentsInChildren <Renderer>(); lods[0].renderers = renderers; Dictionary <Mesh, ConnectedMesh> uniqueMeshes = new Dictionary <Mesh, ConnectedMesh>(); foreach (Renderer renderer in renderers) { if (renderer is MeshRenderer meshRenderer) { MeshFilter meshFilter = renderer.gameObject.GetComponent <MeshFilter>(); if (meshFilter) { Mesh mesh = meshFilter.sharedMesh; if (!uniqueMeshes.ContainsKey(mesh)) { uniqueMeshes.TryAdd(mesh, m => UnityConverter.ToSharedMesh(m).ToConnectedMesh()); } } } else if (renderer is SkinnedMeshRenderer skinnedMeshRenderer) { Mesh mesh = skinnedMeshRenderer.sharedMesh; if (!uniqueMeshes.ContainsKey(mesh)) { uniqueMeshes.TryAdd(mesh, m => UnityConverter.ToSharedMesh(m).ToConnectedMesh()); } } } foreach (KeyValuePair <Mesh, ConnectedMesh> uniqueMesh in uniqueMeshes) { uniqueMesh.Value.MergePositions(0.0001); uniqueMesh.Value.MergeAttributes(); uniqueMesh.Value.Compact(); } SceneDecimator sceneDecimator = new SceneDecimator(); sceneDecimator.Initialize(uniqueMeshes.Values); // Build LODs for (int i = 1; i < lods.Length; i++) { // Decimates gradually sceneDecimator.DecimateToRatio(lods[i - 1].screenRelativeTransitionHeight); Dictionary <Mesh, Mesh> optimizedMeshes = uniqueMeshes.ToDictionary(x => x.Key, x => x.Value.ToSharedMesh().ToUnityMesh()); List <Renderer> lodRenderers = new List <Renderer>(); foreach (Renderer renderer in renderers) { if (renderer is MeshRenderer meshRenderer) { MeshFilter meshFilter = renderer.gameObject.GetComponent <MeshFilter>(); if (meshFilter) { GameObject gameObject = new GameObject(renderer.gameObject.name + "_LOD" + i); gameObject.transform.parent = renderer.transform; gameObject.transform.localPosition = UnityEngine.Vector3.zero; gameObject.transform.localRotation = UnityEngine.Quaternion.identity; gameObject.transform.localScale = UnityEngine.Vector3.one; MeshRenderer mr = gameObject.AddComponent <MeshRenderer>(); MeshFilter mf = gameObject.AddComponent <MeshFilter>(); Mesh originalMesh = meshFilter.sharedMesh; Mesh optimizedMesh = optimizedMeshes[originalMesh]; // Todo : Don't create new mesh if it's the same (tri count); optimizedMesh.name = originalMesh.name + "_LOD" + i; mr.sharedMaterials = meshRenderer.sharedMaterials; mf.sharedMesh = optimizedMesh; newMeshes.Add(optimizedMesh); lodRenderers.Add(mr); } } else if (renderer is SkinnedMeshRenderer skinnedMeshRenderer) { GameObject gameObject = new GameObject(renderer.gameObject.name + "_LOD" + i); gameObject.transform.parent = renderer.transform; gameObject.transform.localPosition = UnityEngine.Vector3.zero; gameObject.transform.localRotation = UnityEngine.Quaternion.identity; gameObject.transform.localScale = UnityEngine.Vector3.one; SkinnedMeshRenderer smr = gameObject.AddComponent <SkinnedMeshRenderer>(); smr.bones = skinnedMeshRenderer.bones; smr.rootBone = skinnedMeshRenderer.rootBone; Mesh originalMesh = skinnedMeshRenderer.sharedMesh; Mesh optimizedMesh = optimizedMeshes[originalMesh]; // Todo : Don't create new mesh if it's the same (tri count); optimizedMesh.name = originalMesh.name + "_LOD" + i; smr.sharedMaterials = skinnedMeshRenderer.sharedMaterials; smr.sharedMesh = optimizedMesh; optimizedMesh.bindposes = originalMesh.bindposes; // Copy poses newMeshes.Add(optimizedMesh); lodRenderers.Add(smr); } } ///Debug.Log($"LOD{i} created with {lodRenderers.Count} renderers at {100f * lods[i - 1].screenRelativeTransitionHeight}% poly ratio"); lods[i].renderers = lodRenderers.ToArray(); } lodGroup.SetLODs(lods); }