コード例 #1
0
ファイル: HDRaytracingManager.cs プロジェクト: KoniroIris/VFX
        public HDRaytracingLightCluster RequestLightCluster(LayerMask layerMask)
        {
            HDRayTracingSubScene currentSubScene = null;

            if (m_SubScenes.TryGetValue(layerMask.value, out currentSubScene))
            {
                return(currentSubScene.valid ? currentSubScene.lightCluster : null);
            }
            return(null);
        }
コード例 #2
0
ファイル: HDRaytracingManager.cs プロジェクト: KoniroIris/VFX
        public HDRayTracingSubScene RequestSubScene(LayerMask layerMask)
        {
            HDRayTracingSubScene currentSubScene = null;

            if (m_SubScenes.TryGetValue(layerMask.value, out currentSubScene))
            {
                return(currentSubScene.valid ? currentSubScene : null);
            }
            return(null);
        }
コード例 #3
0
ファイル: HDRaytracingManager.cs プロジェクト: KoniroIris/VFX
        public RaytracingAccelerationStructure RequestAccelerationStructure(LayerMask layerMask)
        {
            HDRayTracingSubScene currentSubScene = null;

            if (m_SubScenes.TryGetValue(layerMask.value, out currentSubScene))
            {
                return(currentSubScene.valid ? currentSubScene.accelerationStructure : null);
            }
            return(null);
        }
コード例 #4
0
        // This function finds which sub-scenes are going to be used for the camera and computes their light clusters
        public void UpdateCameraData(CommandBuffer cmd, HDCamera hdCamera)
        {
            // Set all the acceleration structures that are currently allocated to not updated
            foreach (var subScene in m_SubScenes)
            {
                subScene.Value.needUpdate = false;
            }

            // Grab the current environment
            HDRaytracingEnvironment rtEnv = CurrentEnvironment();

            if (rtEnv == null)
            {
                return;
            }

            // If Reflection is on flag its light cluster
            // if (rtEnv.raytracedReflections)
            {
                HDRayTracingSubScene currentSubScene = RequestSubScene(rtEnv.reflLayerMask);
                currentSubScene.needUpdate = true;
            }

            // If Primary Visibility is on flag its light cluster
            // if (rtEnv.raytracedObjects)
            {
                HDRayTracingSubScene currentSubScene = RequestSubScene(rtEnv.raytracedLayerMask);
                currentSubScene.needUpdate = true;
            }

            // If indirect diffuse is on flag its light cluster
            // if (rtEnv.raytracedIndirectDiffuse)
            {
                HDRayTracingSubScene currentSubScene = RequestSubScene(rtEnv.indirectDiffuseLayerMask);
                currentSubScene.needUpdate = true;
            }

            // Let's go through all the sub-scenes that are flagged needUpdate and update their light clusters
            foreach (var subScene in m_SubScenes)
            {
                HDRayTracingSubScene currentSubScene = subScene.Value;

                // If it need update, go through it
                if (currentSubScene.needUpdate)
                {
                    // Evaluate the light cluster
                    currentSubScene.lightCluster.EvaluateLightClusters(cmd, hdCamera, currentSubScene.lights);

                    // It doesn't need RAS update anymore
                    currentSubScene.needUpdate = false;
                }
            }
        }
コード例 #5
0
ファイル: HDRaytracingManager.cs プロジェクト: KoniroIris/VFX
 public void DestroySubSceneStructure(ref HDRayTracingSubScene subScene)
 {
     if (subScene.accelerationStructure != null)
     {
         subScene.accelerationStructure.Dispose();
         subScene.targetRenderers       = null;
         subScene.accelerationStructure = null;
         subScene.hdLightArray          = null;
         subScene.lightCluster.ReleaseResources();
         subScene.lightCluster = null;
     }
 }
コード例 #6
0
 public void DestroySubSceneStructure(ref HDRayTracingSubScene subScene)
 {
     if (subScene.accelerationStructure != null)
     {
         for (var i = 0; i < subScene.targetRenderers.Count; i++)
         {
             subScene.accelerationStructure.RemoveInstance(subScene.targetRenderers[i]);
         }
         subScene.accelerationStructure.Dispose();
         subScene.targetRenderers       = null;
         subScene.accelerationStructure = null;
     }
 }
コード例 #7
0
ファイル: HDRaytracingManager.cs プロジェクト: KoniroIris/VFX
        public void Release()
        {
            // Destroy all the sub-scenes
            foreach (var subScene in m_SubScenes)
            {
                HDRayTracingSubScene currentSubScene = subScene.Value;
                DestroySubSceneStructure(ref currentSubScene);
            }

            // Clear the sub-scenes list
            m_SubScenes.Clear();
            m_RayCountManager.Release();
        }
コード例 #8
0
        // This function is to be called when the layers used for an effect have changed. It is called either through the inspector or using the scripting API
        public void UpdateEnvironmentSubScenes()
        {
            // Grab the current environment
            HDRaytracingEnvironment rtEnv = CurrentEnvironment();

            // We do not have any current environment, we need to clear all the subscenes we have and leave.
            if (rtEnv == null)
            {
                foreach (var subScene in m_SubScenes)
                {
                    // Destroy the sub-scene to remove
                    HDRayTracingSubScene currentSubscene = subScene.Value;
                    DestroySubSceneStructure(ref currentSubscene);
                }
                m_SubScenes.Clear();
                return;
            }

            // Update the references for the sub-scenes
            UpdateEffectSubScene(rtEnv.aoLayerMask.value, 0);
            UpdateEffectSubScene(rtEnv.reflLayerMask.value, 1);
            UpdateEffectSubScene(rtEnv.shadowLayerMask.value, 2);
            UpdateEffectSubScene(rtEnv.raytracedLayerMask.value, 3);
            UpdateEffectSubScene(rtEnv.indirectDiffuseLayerMask.value, 4);

            // Let's now go through all the sub-scenes and delete the ones that are not referenced by anyone
            var nonReferencedSubScenes = m_SubScenes.Where(x => x.Value.references == 0).ToArray();

            foreach (var subScene in nonReferencedSubScenes)
            {
                // Destroy the sub-scene to remove
                HDRayTracingSubScene currentSubscene = subScene.Value;
                DestroySubSceneStructure(ref currentSubscene);

                // Remove it from the array
                m_SubScenes.Remove(subScene.Key);
            }
        }
コード例 #9
0
        public void UpdateSubSceneData(CommandBuffer cmd, HDCamera hdCamera)
        {
            HDRayTracingSubScene subScene = RequestSubScene(hdCamera);

            if (subScene != null)
            {
                // Update the acceleration structure
                if (subScene.accelerationStructure != null)
                {
                    for (var i = 0; i < subScene.targetRenderers.Count; i++)
                    {
                        if (subScene.targetRenderers[i] != null)
                        {
                            subScene.accelerationStructure.UpdateInstanceTransform(subScene.targetRenderers[i]);
                        }
                    }
                    subScene.accelerationStructure.Update();
                }

                // Evaluate the light cluster
                subScene.lightCluster.EvaluateLightClusters(cmd, hdCamera, subScene.hdLightArray);
            }
        }
コード例 #10
0
        public void BuildSubSceneStructure(ref HDRayTracingSubScene subScene)
        {
            // Destroy the acceleration structure
            subScene.targetRenderers = new List <Renderer>();

            // Create the acceleration structure
            subScene.accelerationStructure = new RaytracingAccelerationStructure();

            // Grab all the renderers from the scene
            var rendererArray = UnityEngine.GameObject.FindObjectsOfType <Renderer>();

            for (var i = 0; i < rendererArray.Length; i++)
            {
                // Convert the object's layer to an int
                int objectLayerValue = 1 << rendererArray[i].gameObject.layer;

                // Is this object in one of the allowed layers ?
                if ((objectLayerValue & subScene.mask.value) != 0)
                {
                    // Add this fella to the renderer list
                    subScene.targetRenderers.Add(rendererArray[i]);
                }
            }

            // If any object build the acceleration structure
            if (subScene.targetRenderers.Count != 0)
            {
                for (var i = 0; i < subScene.targetRenderers.Count; i++)
                {
                    // Add it to the acceleration structure
                    subScene.accelerationStructure.AddInstance(subScene.targetRenderers[i]);
                }
            }

            // build the acceleration structure
            subScene.accelerationStructure.Build();
        }
コード例 #11
0
        public void InitAccelerationStructures()
        {
            // Create the sub-scene structure
            m_SubScenes = new Dictionary <int, HDRayTracingSubScene>();

            // Grab all the ray tracing graphs in the scene
            HDRayTracingFilter[] tracingGraphs = UnityEngine.Object.FindObjectsOfType <HDRayTracingFilter>();

            m_LayerMasks = new List <int>();
            // Build an array with all the layer combinations that are requested
            for (var filterIndex = 0; filterIndex < tracingGraphs.Length; filterIndex++)
            {
                // Grab the current graph
                HDRayTracingFilter currentFilter = tracingGraphs[filterIndex];

                // Fetch or create the sub-scene
                HDRayTracingSubScene currentSubScene = null;
                if (!m_SubScenes.TryGetValue(currentFilter.layermask.value, out currentSubScene))
                {
                    currentSubScene      = new HDRayTracingSubScene();
                    currentSubScene.mask = currentFilter.layermask.value;
                    m_SubScenes.Add(currentFilter.layermask.value, currentSubScene);
                    m_LayerMasks.Add(currentFilter.layermask.value);
                }

                // Mark the current graph for a reference
                currentSubScene.referenceFilters.Add(currentFilter);
            }

            // Create all the ray tracing
            for (var subSceneIndex = 0; subSceneIndex < m_LayerMasks.Count; subSceneIndex++)
            {
                HDRayTracingSubScene currentSubScene = m_SubScenes[m_LayerMasks[subSceneIndex]];
                BuildSubSceneStructure(ref currentSubScene);
            }
        }
コード例 #12
0
        public void BuildSubSceneStructure(ref HDRayTracingSubScene subScene)
        {
            // If there is no render environments, then we should not generate acceleration structure
            if (m_Environments.Count > 0)
            {
                // This structure references all the renderers that are considered to be processed
                Dictionary <int, int> rendererReference = new Dictionary <int, int>();

                // Destroy the acceleration structure
                subScene.targetRenderers = new List <Renderer>();

                // Create the acceleration structure
                subScene.accelerationStructure = new Experimental.Rendering.RayTracingAccelerationStructure();

                // We need to define the maximal number of meshes for our geometries
                int maxNumSubMeshes = 1;

                // First of all let's process all the LOD groups
                LODGroup[] lodGroupArray = UnityEngine.GameObject.FindObjectsOfType <LODGroup>();
                for (var i = 0; i < lodGroupArray.Length; i++)
                {
                    // Grab the current LOD group
                    LODGroup lodGroup = lodGroupArray[i];

                    // Get the set of LODs
                    LOD[] lodArray = lodGroup.GetLODs();
                    for (int lodIdx = 0; lodIdx < lodArray.Length; ++lodIdx)
                    {
                        LOD currentLOD = lodArray[lodIdx];
                        // We only want to push to the acceleration structure the first fella
                        if (lodIdx == 0)
                        {
                            for (int rendererIdx = 0; rendererIdx < currentLOD.renderers.Length; ++rendererIdx)
                            {
                                // Convert the object's layer to an int
                                int objectLayerValue = 1 << currentLOD.renderers[rendererIdx].gameObject.layer;

                                // Is this object in one of the allowed layers ?
                                if ((objectLayerValue & subScene.mask.value) != 0)
                                {
                                    Renderer currentRenderer = currentLOD.renderers[rendererIdx];

                                    // Add this fella to the renderer list
                                    subScene.targetRenderers.Add(currentRenderer);

                                    // Also, we need to contribute to the maximal number of sub-meshes
                                    MeshFilter currentFilter = currentRenderer.GetComponent <MeshFilter>();
                                    if (currentFilter != null && currentFilter.sharedMesh != null)
                                    {
                                        maxNumSubMeshes = Mathf.Max(maxNumSubMeshes, currentFilter.sharedMesh.subMeshCount);
                                    }
                                }
                            }
                        }

                        // Add them to the processed set
                        for (int rendererIdx = 0; rendererIdx < currentLOD.renderers.Length; ++rendererIdx)
                        {
                            Renderer currentRenderer = currentLOD.renderers[rendererIdx];
                            // Add this fella to the renderer list
                            rendererReference.Add(currentRenderer.GetInstanceID(), 1);
                        }
                    }
                }


                // Grab all the renderers from the scene
                var rendererArray = UnityEngine.GameObject.FindObjectsOfType <Renderer>();
                for (var i = 0; i < rendererArray.Length; i++)
                {
                    // Fetch the current renderer
                    Renderer currentRenderer = rendererArray[i];

                    // If it is not active skip it
                    if (currentRenderer.enabled == false)
                    {
                        continue;
                    }

                    // Grab the current game object
                    GameObject gameObject = currentRenderer.gameObject;

                    // Has this object already been processed, jsut skip
                    if (rendererReference.ContainsKey(currentRenderer.GetInstanceID()))
                    {
                        continue;
                    }

                    // Does this object have a reflection probe component? if yes we do not want to see it in the raytracing environment
                    ReflectionProbe targetProbe = gameObject.GetComponent <ReflectionProbe>();
                    if (targetProbe != null)
                    {
                        continue;
                    }

                    // Convert the object's layer to an int
                    int objectLayerValue = 1 << currentRenderer.gameObject.layer;

                    // Is this object in one of the allowed layers ?
                    if ((objectLayerValue & subScene.mask.value) != 0)
                    {
                        // Add this fella to the renderer list
                        subScene.targetRenderers.Add(currentRenderer);
                    }

                    // Also, we need to contribute to the maximal number of sub-meshes
                    MeshFilter currentFilter = currentRenderer.GetComponent <MeshFilter>();
                    if (currentFilter != null && currentFilter.sharedMesh != null)
                    {
                        maxNumSubMeshes = Mathf.Max(maxNumSubMeshes, currentFilter.sharedMesh.subMeshCount);
                    }
                }

                bool[] subMeshFlagArray   = new bool[maxNumSubMeshes];
                bool[] subMeshCutoffArray = new bool[maxNumSubMeshes];

                // If any object build the acceleration structure
                if (subScene.targetRenderers.Count != 0)
                {
                    // For all the renderers that we need to push in the acceleration structure
                    for (var i = 0; i < subScene.targetRenderers.Count; i++)
                    {
                        // Grab the current renderer
                        Renderer currentRenderer = subScene.targetRenderers[i];
                        bool     singleSided     = false;
                        if (currentRenderer.sharedMaterials != null)
                        {
                            // For every sub-mesh/sub-material let's build the right flags
                            int numSubMeshes = currentRenderer.sharedMaterials.Length;

                            // We need to build the instance flag for this renderer
                            uint instanceFlag = 0x00;

                            // Incorporate the shadow casting flag
                            instanceFlag |= ((currentRenderer.shadowCastingMode == ShadowCastingMode.On) ? (uint)(1 << 2) : 0x00);

                            for (int meshIdx = 0; meshIdx < numSubMeshes; ++meshIdx)
                            {
                                Material currentMaterial = currentRenderer.sharedMaterials[meshIdx];
                                // The material is transparent if either it has the requested keyword or is in the transparent queue range
                                if (currentMaterial != null)
                                {
                                    subMeshFlagArray[meshIdx] = true;

                                    // Is the material transparent?
                                    bool materialIsTransparent = currentMaterial.IsKeywordEnabled("_SURFACE_TYPE_TRANSPARENT") ||
                                                                 (HDRenderQueue.k_RenderQueue_Transparent.lowerBound <= currentMaterial.renderQueue &&
                                                                  HDRenderQueue.k_RenderQueue_Transparent.upperBound >= currentMaterial.renderQueue) ||
                                                                 (HDRenderQueue.k_RenderQueue_AllTransparentRaytracing.lowerBound <= currentMaterial.renderQueue &&
                                                                  HDRenderQueue.k_RenderQueue_AllTransparentRaytracing.upperBound >= currentMaterial.renderQueue);

                                    // Propagate the right mask
                                    instanceFlag |= materialIsTransparent ? (uint)(1 << 1) : (uint)(1 << 0);

                                    // Is the material alpha tested?
                                    subMeshCutoffArray[meshIdx] = currentMaterial.IsKeywordEnabled("_ALPHATEST_ON") ||
                                                                  (HDRenderQueue.k_RenderQueue_OpaqueAlphaTest.lowerBound <= currentMaterial.renderQueue &&
                                                                   HDRenderQueue.k_RenderQueue_OpaqueAlphaTest.upperBound >= currentMaterial.renderQueue);

                                    // Force it to be non single sided if it has the keyword if there is a reason
                                    bool doubleSided = currentMaterial.doubleSidedGI || currentMaterial.IsKeywordEnabled("_DOUBLESIDED_ON");
                                    singleSided |= !doubleSided;
                                }
                                else
                                {
                                    subMeshFlagArray[meshIdx]   = false;
                                    subMeshCutoffArray[meshIdx] = false;
                                    singleSided = true;
                                }
                            }

                            // Add it to the acceleration structure
                            subScene.accelerationStructure.AddInstance(currentRenderer, subMeshMask: subMeshFlagArray, subMeshTransparencyFlags: subMeshCutoffArray, enableTriangleCulling: singleSided, mask: instanceFlag);
                        }
                    }
                }

                // build the acceleration structure
                subScene.accelerationStructure.Build();

                // Allocate the array for the lights
                subScene.lights = new HDRayTracingLights();
                subScene.lights.hdLightArray            = new List <HDAdditionalLightData>();
                subScene.lights.hdDirectionalLightArray = new List <HDAdditionalLightData>();

                // fetch all the lights
                HDAdditionalLightData[] hdLightArray = UnityEngine.GameObject.FindObjectsOfType <HDAdditionalLightData>();

                // Here an important thing is to make sure that the point lights are first in the list then line then area
                List <HDAdditionalLightData> pointLights = new List <HDAdditionalLightData>();
                List <HDAdditionalLightData> lineLights  = new List <HDAdditionalLightData>();
                List <HDAdditionalLightData> rectLights  = new List <HDAdditionalLightData>();

                for (int lightIdx = 0; lightIdx < hdLightArray.Length; ++lightIdx)
                {
                    HDAdditionalLightData hdLight = hdLightArray[lightIdx];
                    if (hdLight.enabled)
                    {
                        // Convert the object's layer to an int
                        int lightayerValue = 1 << hdLight.gameObject.layer;
                        if ((lightayerValue & subScene.mask.value) != 0)
                        {
                            if (hdLight.GetComponent <Light>().type == LightType.Directional)
                            {
                                subScene.lights.hdDirectionalLightArray.Add(hdLight);
                            }
                            else
                            {
                                if (hdLight.lightTypeExtent == LightTypeExtent.Punctual)
                                {
                                    pointLights.Add(hdLight);
                                }
                                else if (hdLight.lightTypeExtent == LightTypeExtent.Tube)
                                {
                                    lineLights.Add(hdLight);
                                }
                                else
                                {
                                    rectLights.Add(hdLight);
                                }
                            }
                        }
                    }
                }

                subScene.lights.hdLightArray.AddRange(pointLights);
                subScene.lights.hdLightArray.AddRange(lineLights);
                subScene.lights.hdLightArray.AddRange(rectLights);

                // Fetch all the reflection probes in the scene
                subScene.lights.reflectionProbeArray = new List <HDProbe>();

                HDAdditionalReflectionData[] reflectionProbeArray = UnityEngine.GameObject.FindObjectsOfType <HDAdditionalReflectionData>();
                for (int reflIdx = 0; reflIdx < reflectionProbeArray.Length; ++reflIdx)
                {
                    HDAdditionalReflectionData reflectionProbe = reflectionProbeArray[reflIdx];
                    // Add it to the list if enabled
                    if (reflectionProbe.enabled)
                    {
                        subScene.lights.reflectionProbeArray.Add(reflectionProbe);
                    }
                }

                // Build the light cluster
                subScene.lightCluster = new HDRaytracingLightCluster();
                subScene.lightCluster.Initialize(m_Resources, m_RTResources, this, m_SharedRTManager, m_RenderPipeline);

                // Mark this sub-scene as valid
                subScene.valid = true;
            }
            else
            {
                subScene.valid = false;
            }
        }
コード例 #13
0
        // This function defines which acceleration structures are going to be used during the following frame
        // and updates their RAS
        public void UpdateFrameData()
        {
            // Set all the acceleration structures that are currently allocated to not updated
            foreach (var subScene in m_SubScenes)
            {
                subScene.Value.needUpdate = false;
            }

            // Grab the current environment
            HDRaytracingEnvironment rtEnv = CurrentEnvironment();

            if (rtEnv == null)
            {
                return;
            }

            // If AO is on flag its RAS needUpdate
            // if (rtEnv.raytracedAO)
            {
                HDRayTracingSubScene currentSubScene = RequestSubScene(rtEnv.aoLayerMask);
                currentSubScene.needUpdate = true;
            }

            // If Reflection is on flag its RAS needUpdate
            // if (rtEnv.raytracedReflections)
            {
                HDRayTracingSubScene currentSubScene = RequestSubScene(rtEnv.reflLayerMask);
                currentSubScene.needUpdate = true;
            }

            // If Area Shadow is on flag its RAS needUpdate
            //if (rtEnv.raytracedShadows)
            {
                HDRayTracingSubScene currentSubScene = RequestSubScene(rtEnv.shadowLayerMask);
                currentSubScene.needUpdate = true;
            }

            // If Primary Visibility is on flag its RAS needUpdate
            // if (rtEnv.raytracedObjects)
            {
                HDRayTracingSubScene currentSubScene = RequestSubScene(rtEnv.raytracedLayerMask);
                currentSubScene.needUpdate = true;
            }

            // If indirect diffuse is on flag its RAS needUpdate
            // if (rtEnv.raytracedIndirectDiffuse)
            {
                HDRayTracingSubScene currentSubScene = RequestSubScene(rtEnv.indirectDiffuseLayerMask);
                currentSubScene.needUpdate = true;
            }

            // Let's go through all the sub-scenes that are flagged needUpdate and update their RAS
            foreach (var subScene in m_SubScenes)
            {
                HDRayTracingSubScene currentSubScene = subScene.Value;
                if (currentSubScene.accelerationStructure != null && currentSubScene.needUpdate)
                {
                    for (var i = 0; i < currentSubScene.targetRenderers.Count; i++)
                    {
                        if (currentSubScene.targetRenderers[i] != null)
                        {
                            currentSubScene.accelerationStructure.UpdateInstanceTransform(currentSubScene.targetRenderers[i]);
                        }
                    }
                    currentSubScene.accelerationStructure.Update();

                    // It doesn't need RAS update anymore
                    currentSubScene.needUpdate = false;
                }
            }
        }
コード例 #14
0
        public void CheckSubScenes()
        {
            // Here there is two options, either the full things needs to be rebuilded or we should only rebuild the ones that have been flagged obsolete
            if (m_DirtyEnvironment)
            {
                // First of let's reset all the obsolescence flags
                int numFilters = m_Filters.Count;
                for (int filterIdx = 0; filterIdx < numFilters; ++filterIdx)
                {
                    // Grab the target graph component
                    HDRayTracingFilter filterComponent = m_Filters[filterIdx];

                    // If this camera had a graph component had an obsolete flag
                    if (filterComponent != null)
                    {
                        filterComponent.ResetDirty();
                    }
                }

                // Also let's mark all the sub-scenes as obsolete
                for (var subSceneIndex = 0; subSceneIndex < m_LayerMasks.Count; subSceneIndex++)
                {
                    HDRayTracingSubScene currentSubScene = m_SubScenes[m_LayerMasks[subSceneIndex]];
                    currentSubScene.obsolete = true;
                }
                m_DirtyEnvironment = false;
            }
            else
            {
                // First of all propagate the obsolete flags to the sub scenes
                int numGraphs = m_Filters.Count;
                for (int filterIdx = 0; filterIdx < numGraphs; ++filterIdx)
                {
                    // Grab the target graph component
                    HDRayTracingFilter filterComponent = m_Filters[filterIdx];

                    // If this camera had a graph component had an obsolete flag
                    if (filterComponent != null && filterComponent.IsDirty())
                    {
                        // Get the sub-scene  that matches
                        HDRayTracingSubScene currentSubScene = null;
                        if (m_SubScenes.TryGetValue(filterComponent.layermask, out currentSubScene))
                        {
                            currentSubScene.obsolete = true;
                        }
                        filterComponent.ResetDirty();
                    }
                }
            }


            // Rebuild all the obsolete scenes
            for (var subSceneIndex = 0; subSceneIndex < m_LayerMasks.Count; subSceneIndex++)
            {
                // Grab the current sub-scene
                HDRayTracingSubScene subScene = m_SubScenes[m_LayerMasks[subSceneIndex]];

                // Does this scene need rebuilding?
                if (subScene.obsolete)
                {
                    DestroySubSceneStructure(ref subScene);
                    BuildSubSceneStructure(ref subScene);
                    subScene.obsolete = false;
                }
            }
        }