private void SetDecimation(float value, Dictionary <string, float> variables) { ConnectedMesh[] connectedMeshes = _originalMeshes.Select(x => UnityConverter.ToSharedMesh(x).ToConnectedMesh()).ToArray(); foreach (ConnectedMesh connectedMesh in connectedMeshes) { for (int i = 0; i < connectedMesh.attributeDefinitions.Length; i++) { switch (connectedMesh.attributeDefinitions[i].type) { case AttributeType.Normals: connectedMesh.attributeDefinitions[i].weight = variables["NormalWeight"]; break; case AttributeType.UVs: connectedMesh.attributeDefinitions[i].weight = variables["UVsWeight"]; break; } } } foreach (ConnectedMesh connectedMesh in connectedMeshes) { // Important step : // We merge positions to increase chances of having correct topology information // We merge attributes in order to make interpolation properly operate on every face connectedMesh.MergePositions(0.0001f /*variables["MergeThreshold"]*/); connectedMesh.MergeAttributes(); connectedMesh.Compact(); } DecimateModifier.MergeNormalsThresholdDegrees = variables["MergeNormalsThreshold"]; //DecimateModifier.UpdateFarNeighbors = variables["UpdateFarNeighbors"] > 0.5; //DecimateModifier.UpdateMinsOnCollapse = variables["UpdateMinsOnCollapse"] > 0.5; //DecimateModifier.UseEdgeLength = variables["UseEdgeLength"] > 0.5; DecimateModifier.CollapseToMidpointPenalty = variables["CollapseToMidpointPenalty"]; ConnectedMesh.EdgeBorderPenalty = variables["EdgeBorderPenalty"]; SceneDecimator sceneDecimator = new SceneDecimator(); sceneDecimator.Initialize(connectedMeshes); sceneDecimator.DecimateToRatio(value); for (int i = 0; i < connectedMeshes.Length; i++) { _meshes[i].Clear(); connectedMeshes[i].ToSharedMesh().ToUnityMesh(_meshes[i]); _meshes[i].bindposes = _originalMeshes[i].bindposes; } }
private void OnValueChanged(float value) { polycountLabel.text = $"{Math.Round(100 * value)}% ({Math.Round(value * _polycount)}/{_polycount} triangles)"; Profiling.Start("Convert"); var connectedMeshes = _originalMeshes.Select(x => UnityConverter.ToSharedMesh(x).ToConnectedMesh()).ToArray(); Debug.Log(Profiling.End("Convert")); Profiling.Start("Clean"); foreach (var connectedMesh in connectedMeshes) { // Important step : // We merge positions to increase chances of having correct topology information // We merge attributes in order to make interpolation properly operate on every face connectedMesh.MergePositions(0.0001f); connectedMesh.MergeAttributes(); connectedMesh.Compact(); } Debug.Log(Profiling.End("Clean")); Profiling.Start("Decimate"); SceneDecimator sceneDecimator = new SceneDecimator(); sceneDecimator.Initialize(connectedMeshes); sceneDecimator.DecimateToRatio(value); Debug.Log(Profiling.End("Decimate")); Profiling.Start("Convert back"); for (int i = 0; i < connectedMeshes.Length; i++) { _meshes[i].Clear(); connectedMeshes[i].ToSharedMesh().ToUnityMesh(_meshes[i]); _meshes[i].bindposes = _originalMeshes[i].bindposes; } Debug.Log(Profiling.End("Convert back")); }
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); }