static void PersistHLODs(LODVolume lodVolume, string scenePath) { var hlodRoot = lodVolume.hlodRoot; if (hlodRoot) { var mf = hlodRoot.GetComponent <MeshFilter>(); if (mf) { var sharedMesh = mf.sharedMesh; if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(sharedMesh))) { SaveUniqueHLODAsset(sharedMesh, scenePath); } } } foreach (Transform child in lodVolume.transform) { var childLODVolume = child.GetComponent <LODVolume>(); if (childLODVolume) { PersistHLODs(childLODVolume, scenePath); } } }
public void Create(Action finishAction) { MonoBehaviourHelper.maxSharedExecutionTimeMS = 50.0f; //Assets should be saved which changed. AssetDatabase.SaveAssets(); var lodGroups = FindAllGroups(); if (lodGroups.Count == 0) { return; } //find biggest bounds including all groups. var lodGroupList = lodGroups.Values.ToList(); Bounds bounds = CalcBounds(lodGroupList[0]); for (int i = 1; i < lodGroupList.Count; ++i) { bounds.Encapsulate(CalcBounds(lodGroupList[i])); } var rootVolume = LODVolume.Create(); rootVolume.SetLODGroups(lodGroups); rootVolume.Bounds = bounds; m_HLODRootContainer = new GameObject(k_HLODRootContainer); m_HLODRootContainer.AddComponent <SceneLODUpdater>(); m_GroupHLODRootContainer = new Dictionary <string, GameObject>(); StartCustomCoroutine(BuildOctree(rootVolume), CoroutineOrder.BuildTree); StartCustomCoroutine(BuildBatch(), CoroutineOrder.Batch); StartCustomCoroutine(EnqueueAction(finishAction), CoroutineOrder.Finish); StartCustomCoroutine(EnqueueAction(() => { if (rootVolume != null) { rootVolume.ResetLODGroup(); if (m_HLODRootContainer != null) { var updater = m_HLODRootContainer.GetComponent <SceneLODUpdater>(); updater.Build(rootVolume); DestroyImmediate(rootVolume.gameObject); } } }), CoroutineOrder.Finish); currentJobCount = 0; maxJobCount = m_JobContainer.First().Value.Count; currentProgress = 0; maxProgress = (int)CoroutineOrder.Finish + 1; }
public static LODVolume Create() { GameObject go = new GameObject(k_DefaultName + s_VolumesCreated++, typeof(LODVolume)); go.layer = LayerMask.NameToLayer(HLODLayer); LODVolume volume = go.GetComponent <LODVolume>(); return(volume); }
IEnumerator BuildHLOD(LODVolume volume) { foreach (var volumeGroup in volume.VolumeGroups) { string groupName = volumeGroup.GroupName; if (m_GroupHLODRootContainer.ContainsKey(groupName) == false) { var groupRoot = new GameObject(groupName); groupRoot.layer = LayerMask.NameToLayer(LODVolume.HLODLayer); groupRoot.transform.parent = m_HLODRootContainer.transform; m_GroupHLODRootContainer.Add(groupName, groupRoot); } yield return(CreateHLODObject(volumeGroup.LODGroups, volume.Bounds, volumeGroup, volume.gameObject.name)); } }
IEnumerator SetRootLODVolume() { if (m_RootVolume) { var rootVolumeTransform = m_RootVolume.transform; var transformRoot = rootVolumeTransform.root; // Handle the case where the BVH has grown if (rootVolumeTransform != transformRoot) { m_RootVolume = transformRoot.GetComponent <LODVolume>(); } yield break; } // Handle initialization or the case where the BVH has shrunk LODVolume lodVolume = null; var scene = SceneManager.GetActiveScene(); var rootGameObjects = scene.GetRootGameObjects(); foreach (var go in rootGameObjects) { if (!go) { continue; } lodVolume = go.GetComponent <LODVolume>(); if (lodVolume) { break; } yield return(null); } if (lodVolume) { m_RootVolume = lodVolume; } m_ExcludedRenderers.Clear(); }
IEnumerator Shrink() { var populatedChildrenNodes = 0; foreach (Transform child in transform) { var lodVolume = child.GetComponent <LODVolume>(); var renderers = lodVolume.renderers; if (renderers != null && renderers.Count > 0 && renderers.Count(r => r != null) > 0) { populatedChildrenNodes++; } yield return(null); } if (populatedChildrenNodes <= 1) { var lodVolumes = GetComponentsInChildren <LODVolume>(); LODVolume newRootVolume = null; if (lodVolumes.Length > 0) { newRootVolume = lodVolumes[lodVolumes.Length - 1]; newRootVolume.transform.parent = null; } // Clean up child HLODs before destroying the GameObject; Otherwise, we'd leak into the scene foreach (var lodVolume in lodVolumes) { if (lodVolume != newRootVolume) { lodVolume.CleanupHLOD(); } } DestroyImmediate(gameObject); if (newRootVolume) { yield return(newRootVolume.Shrink()); } } }
IEnumerator UpdateLODGroup(LODVolume volume) { List <Renderer> lodRenderers = new List <Renderer>(); foreach (var volumeGroup in volume.VolumeGroups) { if (volumeGroup.HLODObject == null) { continue; } lodRenderers.AddRange(volumeGroup.HLODObject.GetComponentsInChildren <Renderer>(false)); } LOD lod = new LOD(); LOD detailLOD = new LOD(); detailLOD.screenRelativeTransitionHeight = Config.LODRange; lod.screenRelativeTransitionHeight = 0.0f; var lodGroup = volume.GetComponent <LODGroup>(); if (!lodGroup) { lodGroup = volume.gameObject.AddComponent <LODGroup>(); } volume.LodGroup = lodGroup; lod.renderers = lodRenderers.ToArray(); lodGroup.SetLODs(new LOD[] { detailLOD, lod }); //bounds is cuboid. //it has the same size each axis. lodGroup.size = volume.Bounds.size.x; yield break; }
bool UpdateLODGroup(LODVolume lodVolume, Camera camera, Vector3 cameraPosition, bool fastPath) { var lodGroupEnabled = s_HLODEnabled; var lodGroup = lodVolume.lodGroup; var lodGroupExists = lodGroup != null && lodGroup.lodGroup; // Start with leaf nodes first var lodVolumeTransform = lodVolume.transform; var childVolumes = lodVolume.childVolumes; foreach (var childVolume in childVolumes) { if (childVolume) { if (!fastPath || !lodGroupExists || !lodGroup.lodGroup.enabled) { lodGroupEnabled &= UpdateLODGroup(childVolume, camera, cameraPosition, fastPath); } } } if (lodGroupEnabled) { var allChildrenUsingCoarsestLOD = true; if (lodVolumeTransform.childCount == 0) // Leaf node { var cached = lodVolume.cached; // Disable all children LODGroups if an HLOD LODGroup could replace it foreach (var r in cached) { var childLODGroup = r as LODVolume.LODGroupHelper; if (childLODGroup != null && childLODGroup.GetCurrentLOD(camera, cameraPosition) != childLODGroup.GetMaxLOD()) { allChildrenUsingCoarsestLOD = false; break; } } foreach (var r in cached) { var childLODGroup = r as LODVolume.LODGroupHelper; if (childLODGroup != null) { childLODGroup.SetEnabled(!allChildrenUsingCoarsestLOD); } else if (r != null) { ((Renderer)r).enabled = !allChildrenUsingCoarsestLOD; } } } else { foreach (var childVolume in childVolumes) { var childLODGroup = childVolume.lodGroup; if (childLODGroup != null && childLODGroup.lodGroup) { var maxLOD = childLODGroup.GetMaxLOD(); if (maxLOD > 0 && childLODGroup.GetCurrentLOD(camera, cameraPosition) != maxLOD) { allChildrenUsingCoarsestLOD = false; break; } } } foreach (var childVolume in childVolumes) { var childLODGroup = childVolume.lodGroup; if (childLODGroup != null && childLODGroup.lodGroup) { childLODGroup.SetEnabled(!allChildrenUsingCoarsestLOD); } } } lodGroupEnabled &= allChildrenUsingCoarsestLOD; } else if (!s_HLODEnabled && lodVolumeTransform.childCount == 0) // Re-enable default renderers { foreach (var r in lodVolume.renderers) { if (!r) { continue; } var childLODGroup = r.GetComponentInParent <LODGroup>(); if (childLODGroup) { childLODGroup.SetEnabled(true); } else { r.enabled = true; } } } if (lodGroupExists) { lodGroup.SetEnabled(lodGroupEnabled); } return(lodGroupEnabled); }
IEnumerator UpdateOctree() { if (!m_RootVolume) { yield return(SetRootLODVolume()); if (!m_RootVolume) { if (m_CreateRootVolumeForScene == SceneManager.GetActiveScene().name) { Dbg.Log("Creating root volume"); m_RootVolume = LODVolume.Create(); } else { yield break; } } } var renderers = m_FoundRenderers; renderers.Clear(); yield return(ObjectUtils.FindObjectsOfType(renderers)); // Remove any renderers that should not be there (e.g. HLODs) renderers.RemoveAll(r => m_ExcludedRenderers.Contains(r)); renderers.RemoveAll(r => { if (r) { // Check against previous collection if (m_ExistingRenderers.Contains(r)) { return(false); } if (r.gameObject.layer == LayerMask.NameToLayer(LODVolume.HLODLayer)) { m_ExcludedRenderers.Add(r); return(true); } var mf = r.GetComponent <MeshFilter>(); if (!mf || (mf.sharedMesh && mf.sharedMesh.GetTopology(0) != MeshTopology.Triangles)) { m_ExcludedRenderers.Add(r); return(true); } var lodGroup = r.GetComponentInParent <LODGroup>(); if (lodGroup) { var lods = lodGroup.GetLODs(); // Skip LOD0, so that we keep the original renderers in the list for (int i = 1; i < lods.Length; i++) { if (lods[i].renderers.Contains(r)) { m_ExcludedRenderers.Add(r); return(true); } } } else { // HLODs should come after traditional LODs, so exclude any standalone renderers m_ExcludedRenderers.Add(r); return(true); } } return(false); }); var existingRenderers = m_ExistingRenderers; existingRenderers.Clear(); existingRenderers.UnionWith(m_RootVolume.renderers); var removed = m_RemovedRenderers; removed.Clear(); removed.UnionWith(m_ExistingRenderers); removed.ExceptWith(renderers); var added = m_AddedRenderers; added.Clear(); added.UnionWith(renderers); added.ExceptWith(existingRenderers); foreach (var r in removed) { if (existingRenderers.Contains(r)) { yield return(m_RootVolume.RemoveRenderer(r)); // Check if the BVH shrunk yield return(SetRootLODVolume()); } } foreach (var r in added) { if (!existingRenderers.Contains(r)) { yield return(m_RootVolume.AddRenderer(r)); r.transform.hasChanged = false; // Check if the BVH grew yield return(SetRootLODVolume()); } } }
public void AddChild(LODVolume volume) { childVolumes.Add(volume); }
public void Build(LODVolume rootLODVolume) { if (rootLODVolume == null) { Debug.LogError("SceneLODUpdate build failed. RootLODVolume is null."); return; } //it build by BFS. List <VolumeBounds> boundsList = new List <VolumeBounds>(); List <VolumeRenderer> rendererList = new List <VolumeRenderer>(); Queue <LODVolume> treeTrevelQueue = new Queue <LODVolume>(); Queue <int> parentIndexQueue = new Queue <int>(); treeTrevelQueue.Enqueue(rootLODVolume); parentIndexQueue.Enqueue(-1); while (treeTrevelQueue.Count > 0) { var current = treeTrevelQueue.Dequeue(); VolumeBounds bounds; int currentIndex = boundsList.Count; int parentIndex = parentIndexQueue.Dequeue(); if (current.VolumeGroups.Count == 0) { continue; } bounds.Center = current.Bounds.center; bounds.Size = current.Bounds.size.x; bounds.Radius = current.Bounds.extents.magnitude; boundsList.Add(bounds); VolumeRenderer renderer; renderer.LODMeshes = new List <Renderer>(); renderer.LODGroups = new List <LODGroupRendererProxy>(); renderer.Volumes = new List <VolumeRendererProxy>(); LODGroup lodGroup = current.GetComponent <LODGroup>(); if (lodGroup != null) { LOD[] lods = lodGroup.GetLODs(); if (lods.Length >= 2) { for (int ri = 0; ri < lods[1].renderers.Length; ++ri) { renderer.LODMeshes.Add(lods[1].renderers[ri]); } } } if (current.ChildVolumes.Count == 0) { for (int vi = 0; vi < current.VolumeGroups.Count; ++vi) { var volumeGroup = current.VolumeGroups[vi]; for (int gi = 0; gi < volumeGroup.LODGroups.Count; ++gi) { renderer.LODGroups.Add(new LODGroupRendererProxy(volumeGroup.LODGroups[gi])); } } } rendererList.Add(renderer); //If FirstChildIndex of the parent is empty, this is the first node. if (parentIndex != -1) { var parentRenderer = rendererList[parentIndex]; parentRenderer.Volumes.Add(new VolumeRendererProxy(currentIndex)); } for (int i = 0; i < current.ChildVolumes.Count; ++i) { treeTrevelQueue.Enqueue(current.ChildVolumes[i]); parentIndexQueue.Enqueue(currentIndex); } } m_Bounds = boundsList; m_Renderers = rendererList; if (m_Bounds.Count > 0) { m_ActiveVolumes.AddLast(0); } }
IEnumerator BuildOctree(LODVolume volume) { var bounds = volume.Bounds; float boundsSize = Mathf.Max(bounds.extents.x, Mathf.Max(bounds.extents.y, bounds.extents.z)); yield return(BuildHLOD(volume)); //reserve logic. //UpdateLODGroup should be run after batch. StartCustomCoroutine(UpdateLODGroup(volume), CoroutineOrder.UpdateLOD); //Make a child if necessary. if (boundsSize < Config.VolumeSize) { yield break; } //Volume doesn't have any group for a split. if (volume.VolumeGroups.Count == 0) { yield break; } ; Vector3 size = bounds.size; size.x /= k_Splits; size.y /= k_Splits; size.z /= k_Splits; for (int i = 0; i < k_Splits; i++) { for (int j = 0; j < k_Splits; j++) { for (int k = 0; k < k_Splits; k++) { var lodVolume = LODVolume.Create(); var lodVolumeTransform = lodVolume.transform; lodVolumeTransform.parent = volume.transform; var center = bounds.min + size * 0.5f + Vector3.Scale(size, new Vector3(i, j, k)); lodVolumeTransform.position = center; lodVolume.Bounds = new Bounds(center, size); foreach (var volumeGroup in volume.VolumeGroups) { List <LODGroup> lodGroups = new List <LODGroup>(volumeGroup.LODGroups.Count); foreach (LODGroup group in volumeGroup.LODGroups) { if (WithinBounds(group, lodVolume.Bounds)) { lodGroups.Add(group); } } if (lodGroups.Count > 0) { lodVolume.SetLODGroups(volumeGroup.GroupName, lodGroups); } } volume.AddChild(lodVolume); yield return(BuildOctree(lodVolume)); } } } }