IsObjectVisible() public method

public IsObjectVisible ( AxisAlignedBox box ) : bool
box Axiom.Math.AxisAlignedBox
return bool
Beispiel #1
0
        /// <summary>
        ///		Internal method which locates any visible objects attached to this node and adds them to the passed in queue.
        /// </summary>
        /// <param name="camera">Active camera.</param>
        /// <param name="queue">Queue to which these objects should be added.</param>
        /// <param name="includeChildren">If true, cascades down to all children.</param>
        /// <param name="displayNodes">Renders the local axes for the node.</param>
        /// <param name="onlyShadowCasters"></param>
        public virtual void FindVisibleObjects(Camera camera, RenderQueue queue, bool includeChildren, bool displayNodes,
                                               bool onlyShadowCasters)
        {
            // if we aren't visible, then quit now
            // TODO: Make sure sphere is calculated properly for all objects, then switch to cull using that
            if (!camera.IsObjectVisible(this.worldAABB))
            {
                return;
            }

            // add visible objects to the render queue
            //objectListMeter.Enter();
            foreach (var obj in this.objectList.Values)
            {
                // tell attached object about current camera in case it wants to know
                //notifyCameraMeter.Enter();
                obj.NotifyCurrentCamera(camera);
                //notifyCameraMeter.Exit();

                // if this object is visible, add it to the render queue
                if (obj.IsVisible && (!onlyShadowCasters || obj.CastShadows))
                {
                    //updateQueueMeter.Enter();
                    obj.UpdateRenderQueue(queue);
                    //updateQueueMeter.Exit();
                }
            }
            //objectListMeter.Exit();

            //childListMeter.Enter();
            if (includeChildren)
            {
                // ask all child nodes to update the render queue with visible objects
                foreach (SceneNode childNode in childNodes.Values)
                {
                    if (childNode.IsVisible)
                    {
                        childNode.FindVisibleObjects(camera, queue, includeChildren, displayNodes, onlyShadowCasters);
                    }
                }
            }
            //childListMeter.Exit();

            // if we wanna display nodes themself..
            if (displayNodes)
            {
                // hey, lets just add ourself right to the render queue
                queue.AddRenderable(GetDebugRenderable());
            }

            // do we wanna show our beautiful bounding box?
            // do it if either we want it, or the SceneManager dictates it
            if (this.showBoundingBox || (this.creator != null && this.creator.ShowBoundingBoxes))
            {
                AddBoundingBoxToQueue(queue);
            }
        }
Beispiel #2
0
		private void CheckShadowCasters( IList casters,
										 PlaneBoundedVolume nearClipVol,
										 Light light,
										 bool extrudeInSoftware,
										 bool finiteExtrude,
										 bool zfailAlgo,
										 Camera camera,
										 float extrudeDistance,
										 bool stencil2sided,
										 LightList tmpLightList )
		{
			int flags;
			for ( int i = 0; i < casters.Count; i++ )
			{
				ShadowCaster caster = (ShadowCaster)casters[ i ];

				if ( nearClipVol.Intersects( caster.GetWorldBoundingBox() ) )
				{
					// We have a zfail case, we must use zfail for all objects
					zfailAlgo = true;

					break;
				}
			}

			for ( int ci = 0; ci < casters.Count; ci++ )
			{
				ShadowCaster caster = (ShadowCaster)casters[ ci ];
				flags = 0;

				if ( light.Type != LightType.Directional )
				{
					extrudeDistance = caster.GetPointExtrusionDistance( light );
				}

				if ( !extrudeInSoftware && !finiteExtrude )
				{
					// hardware extrusion, to infinity (and beyond!)
					flags |= (int)ShadowRenderableFlags.ExtrudeToInfinity;
				}

				if ( zfailAlgo )
				{
					// We need to include the light and / or dark cap
					// But only if they will be visible
					if ( camera.IsObjectVisible( caster.GetLightCapBounds() ) )
					{
						flags |= (int)ShadowRenderableFlags.IncludeLightCap;
					}
				}

				// Dark cap (no dark cap for directional lights using
				// hardware extrusion to infinity)
				if ( !( ( flags & (int)ShadowRenderableFlags.ExtrudeToInfinity ) != 0 &&
						light.Type == LightType.Directional ) &&
					 camera.IsObjectVisible( caster.GetDarkCapBounds( light, extrudeDistance ) ) )
				{
					flags |= (int)ShadowRenderableFlags.IncludeDarkCap;
				}

				// get shadow renderables
				IEnumerator renderables = caster.GetShadowVolumeRenderableEnumerator(
					this.shadowTechnique, light, this.shadowIndexBuffer, extrudeInSoftware, extrudeDistance, flags );

				// If using one-sided stencil, render the first pass of all shadow
				// renderables before all the second passes
				for ( int i = 0; i < ( stencil2sided ? 1 : 2 ); i++ )
				{
					if ( i == 1 )
					{
						renderables = caster.GetLastShadowVolumeRenderableEnumerator();
					}

					while ( renderables.MoveNext() )
					{
						ShadowRenderable sr = (ShadowRenderable)renderables.Current;

						// omit hidden renderables
						if ( sr.IsVisible )
						{
							// render volume, including dark and (maybe) light caps
							this.RenderSingleShadowVolumeToStencil( sr,
																	zfailAlgo,
																	stencil2sided,
																	tmpLightList,
																	( i > 0 ) );

							// optionally render separate light cap
							if ( sr.IsLightCapSeperate
								 && ( ( flags & (int)ShadowRenderableFlags.IncludeLightCap ) ) > 0 )
							{
								// must always fail depth check
								this.targetRenderSystem.DepthBufferFunction = CompareFunction.AlwaysFail;

								Debug.Assert( sr.LightCapRenderable != null,
											  "Shadow renderable is missing a separate light cap renderable!" );

								this.RenderSingleShadowVolumeToStencil( sr.LightCapRenderable,
																		zfailAlgo,
																		stencil2sided,
																		tmpLightList,
																		( i > 0 ) );
								// reset depth function
                                this.targetRenderSystem.DepthBufferFunction = CompareFunction.Less;
							}
						}
					}
				}
			}
		}
Beispiel #3
0
		/// <summary>
		///		Internal method for locating a list of shadow casters which
		///		could be affecting the frustum for a given light.
		/// </summary>
		/// <remarks>
		///		Custom scene managers are encouraged to override this method to add optimizations,
		///		and to add their own custom shadow casters (perhaps for world geometry)
		/// </remarks>
		/// <param name="light"></param>
		/// <param name="camera"></param>
		protected virtual IList FindShadowCastersForLight( Light light, Camera camera )
		{
			this.shadowCasterList.Clear();

			if ( light.Type == LightType.Directional )
			{
				// Basic AABB query encompassing the frustum and the extrusion of it
				AxisAlignedBox aabb = new AxisAlignedBox();
				Vector3[] corners = camera.WorldSpaceCorners;
				Vector3 min, max;
				Vector3 extrude = light.DerivedDirection * -this.shadowDirLightExtrudeDist;
				// do first corner
				min = max = corners[ 0 ];
				min.Floor( corners[ 0 ] + extrude );
				max.Ceil( corners[ 0 ] + extrude );
				for ( int c = 1; c < 8; ++c )
				{
					min.Floor( corners[ c ] );
					max.Ceil( corners[ c ] );
					min.Floor( corners[ c ] + extrude );
					max.Ceil( corners[ c ] + extrude );
				}
				aabb.SetExtents( min, max );

				if ( this.shadowCasterAABBQuery == null )
				{
					this.shadowCasterAABBQuery = this.CreateAABBRegionQuery( aabb );
				}
				else
				{
					this.shadowCasterAABBQuery.Box = aabb;
				}
				// Execute, use callback
				this.shadowCasterQueryListener.Prepare( false,
														light.GetFrustumClipVolumes( camera ),
														light,
														camera,
														this.shadowCasterList,
														light.ShadowFarDistanceSquared );
				this.shadowCasterAABBQuery.Execute( this.shadowCasterQueryListener );
			}
			else
			{
				Sphere s = new Sphere( light.DerivedPosition, light.AttenuationRange );

				// eliminate early if camera cannot see light sphere
				if ( camera.IsObjectVisible( s ) )
				{
					// create or init a sphere region query
					if ( this.shadowCasterSphereQuery == null )
					{
						this.shadowCasterSphereQuery = this.CreateSphereRegionQuery( s );
					}
					else
					{
						this.shadowCasterSphereQuery.Sphere = s;
					}

					// check if the light is within view of the camera
					bool lightInFrustum = camera.IsObjectVisible( light.DerivedPosition );

					PlaneBoundedVolumeList volumeList = null;

					// Only worth building an external volume list if
					// light is outside the frustum
					if ( !lightInFrustum )
					{
						volumeList = light.GetFrustumClipVolumes( camera );
					}

					// prepare the query and execute using the callback
					this.shadowCasterQueryListener.Prepare(
						lightInFrustum,
						volumeList,
						light,
						camera,
						this.shadowCasterList,
						light.ShadowFarDistanceSquared );

					this.shadowCasterSphereQuery.Execute( this.shadowCasterQueryListener );
				}
			}

			return this.shadowCasterList;
		}
Beispiel #4
0
		/// <summary>
		///		Internal method for locating a list of lights which could be affecting the frustum.
		/// </summary>
		/// <remarks>
		///		Custom scene managers are encouraged to override this method to make use of their
		///		scene partitioning scheme to more efficiently locate lights, and to eliminate lights
		///		which may be occluded by word geometry.
		/// </remarks>
		/// <param name="camera">Camera to find lights within it's view.</param>
		protected virtual void FindLightsAffectingFrustum( Camera camera )
		{
			// Basic iteration for this scene manager
			this.lightsAffectingFrustum.Clear();

			MovableObjectCollection lightList = this.GetMovableObjectCollection( LightFactory.TypeName );

			// sphere to use for testing
			Sphere sphere = new Sphere();

			foreach ( Light light in lightList.Values )
			{
				if ( light.IsVisible )
				{
					if ( light.Type == LightType.Directional )
					{
						// Always visible
						this.lightsAffectingFrustum.Add( light );
					}
					else
					{
						// treating spotlight as point for simplicity
						// Just see if the lights attenuation range is within the frustum
						sphere.Center = light.DerivedPosition;
						sphere.Radius = light.AttenuationRange;

						if ( camera.IsObjectVisible( sphere ) )
						{
							this.lightsAffectingFrustum.Add( light );
						}
					}
				}
			}

			// notify light dirty, so all movable objects will re-populate
			// their light list next time
			NotifyLightsDirty();
		}
Beispiel #5
0
		/// <summary>
		///		Tags geometry in the leaf specified for later rendering.
		/// </summary>
		protected void ProcessVisibleLeaf( BspNode leaf, Camera camera, bool onlyShadowCasters )
		{
			// Skip world geometry if we're only supposed to process shadow casters
			// World is pre-lit
			if ( !onlyShadowCasters )
			{
				// Parse the leaf node's faces, add face groups to material map
				int numGroups = leaf.NumFaceGroups;
				int idx = leaf.FaceGroupStart;

				while ( numGroups-- > 0 )
				{
					int realIndex = this.level.LeafFaceGroups[ idx++ ];

					// Is it already checked ?
					if ( this.faceGroupChecked.ContainsKey( realIndex ) && this.faceGroupChecked[ realIndex ] == true )
					{
						continue;
					}

					this.faceGroupChecked[ realIndex ] = true;

					BspStaticFaceGroup faceGroup = this.level.FaceGroups[ realIndex ];

					// Get Material reference by handle
					Material mat = GetMaterial( faceGroup.materialHandle );

					// Check normal (manual culling)
					ManualCullingMode cullMode = mat.GetTechnique( 0 ).GetPass( 0 ).ManualCullingMode;

					if ( cullMode != ManualCullingMode.None )
					{
						float dist = faceGroup.plane.GetDistance( camera.DerivedPosition );

						if ( ( ( dist < 0 ) && ( cullMode == ManualCullingMode.Back ) ) ||
						     ( ( dist > 0 ) && ( cullMode == ManualCullingMode.Front ) ) )
						{
							continue;
						}
					}

					// Try to insert, will find existing if already there
					this.matFaceGroupMap.Add( mat, faceGroup );
				}
			}

			// Add movables to render queue, provided it hasn't been seen already.
			foreach ( MovableObject obj in leaf.Objects.Values )
			{
				if ( !this.objectsForRendering.ContainsKey( obj.Name ) )
				{
					if ( obj.IsVisible && ( !onlyShadowCasters || obj.CastShadows ) &&
					     camera.IsObjectVisible( obj.GetWorldBoundingBox() ) )
					{
						obj.NotifyCurrentCamera( camera );
						obj.UpdateRenderQueue( renderQueue );
						// Check if the bounding box should be shown.
						var node = (SceneNode)obj.ParentNode;
						if ( node.ShowBoundingBox || showBoundingBoxes )
						{
							node.AddBoundingBoxToQueue( renderQueue );
						}
						this.objectsForRendering.Add( obj );
					}
				}
			}
		}
Beispiel #6
0
		/// <summary>
		///		Determines whether the supplied billboard is visible in the camera or not.
		///	 </summary>
		/// <param name="camera"></param>
		/// <param name="billboard"></param>
		/// <returns></returns>
		protected bool IsBillboardVisible( Camera camera, Billboard billboard )
		{
			// if not culling each one, return true always
			if ( !this.cullIndividual )
			{
				return true;
			}

			// get the world matrix of this billboard set
			this.GetWorldTransforms( this.world );

			// get the center of the bounding sphere
			this.sphere.Center = this.world[ 0 ] * billboard.Position;

			// calculate the radius of the bounding sphere for the billboard
			if ( billboard.HasOwnDimensions )
			{
				this.sphere.Radius = Utility.Max( billboard.Width, billboard.Height );
			}
			else
			{
				this.sphere.Radius = Utility.Max( this.defaultParticleWidth, this.defaultParticleHeight );
			}

			// finally, see if the sphere is visible in the camera
			return camera.IsObjectVisible( this.sphere );
		}
 public override void NotifyCurrentCamera(Camera cam)
 {
     if (cam.IsObjectVisible(worldAABB))
     {
         isVisible = true;
     }
     else
     {
         isVisible = false;
         return;
     }
 }
 public override void NotifyCurrentCamera(Camera camera)
 {
     if (!camera.IsObjectVisible(GetWorldBoundingBox(true))) {
         widgetNode.NodeVisible = false;
     } else {
         widgetNode.NodeVisible = true;
     }
 }
        public void Notify(Vector3 pos, Camera Cam)
        {
            if (isLoaded
                && Cam.IsObjectVisible(pageNode.WorldAABB))
            //-----------------
                //&& Cam->getVisibility (mBoundsExt))
            {

                for ( long i = 0; i < numTiles; i++ )
                {
                    for ( long j = 0; j < numTiles; j++ )
                    {
                        tiles[ i ][ j].Notify( pos, Cam);
                    }
                }
            }
        }
Beispiel #10
0
		//---------------------------------------------------------------------
		protected override void FindLightsAffectingFrustum( Camera camera )
		{
			base.FindLightsAffectingFrustum( camera );
			return;
			// Similar to the basic SceneManager, we iterate through
			// lights to see which ones affect the frustum.  However,
			// since we have camera & lights partitioned by zones,
			// we can check only those lights which either affect the
			// zone the camera is in, or affect zones which are visible to
			// the camera

			MovableObjectCollection lights = GetMovableObjectCollection( PCZLightFactory.TypeName );

			lock ( lights )
			{
				foreach ( PCZLight l in lights.Values )
				{
					if ( l.IsVisible /* && l.AffectsVisibleZone */ )
					{
						LightInfo lightInfo;
						lightInfo.light = l;
						lightInfo.type = (int)l.Type;
						if ( lightInfo.type == (int)LightType.Directional )
						{
							// Always visible
							lightInfo.position = Vector3.Zero;
							lightInfo.range = 0;
							this.mTestLightInfos.Add( lightInfo );
						}
						else
						{
							// NB treating spotlight as point for simplicity
							// Just see if the lights attenuation range is within the frustum
							lightInfo.range = l.AttenuationRange;
							lightInfo.position = l.GetDerivedPosition();
							var sphere = new Sphere( lightInfo.position, lightInfo.range );
							if ( camera.IsObjectVisible( sphere ) )
							{
								this.mTestLightInfos.Add( lightInfo );
							}
						}
					}
				}
			} // release lock on lights collection

			base.FindLightsAffectingFrustum( camera );

			// from here on down this function is same as Ogre::SceneManager

			// Update lights affecting frustum if changed
			if ( this.mCachedLightInfos != this.mTestLightInfos )
			{
				//mLightsAffectingFrustum.resize(mTestLightInfos.size());
				//LightInfoList::const_iterator i;
				//LightList::iterator j = mLightsAffectingFrustum.begin();
				//for (i = mTestLightInfos.begin(); i != mTestLightInfos.end(); ++i, ++j)
				//{
				//    *j = i->light;
				//    // add cam distance for sorting if texture shadows
				//    if (isShadowTechniqueTextureBased())
				//    {
				//        (*j)->tempSquareDist =
				//            (camera->getDerivedPosition() - (*j)->getDerivedPosition()).squaredLength();
				//    }
				//}

				foreach ( LightInfo i in this.mTestLightInfos )
				{
					if ( IsShadowTechniqueTextureBased )
					{
						i.light.TempSquaredDist = ( camera.DerivedPosition - i.light.GetDerivedPosition() ).LengthSquared;
					}
				}

				if ( IsShadowTechniqueTextureBased )
				{
				}

				// Sort the lights if using texture shadows, since the first 'n' will be
				// used to generate shadow textures and we should pick the most appropriate
				//if (IsShadowTechniqueTextureBased)
				//{
				//    // Allow a ShadowListener to override light sorting
				//    // Reverse iterate so last takes precedence
				//    bool overridden = false;
				//    foreach(object o in base.)
				//    for (ListenerList::reverse_iterator ri = mListeners.rbegin();
				//        ri != mListeners.rend(); ++ri)
				//    {
				//        overridden = (*ri)->sortLightsAffectingFrustum(mLightsAffectingFrustum);
				//        if (overridden)
				//            break;
				//    }
				//    if (!overridden)
				//    {
				//        // default sort (stable to preserve directional light ordering
				//        std::stable_sort(
				//            mLightsAffectingFrustum.begin(), mLightsAffectingFrustum.end(),
				//            lightsForShadowTextureLess());
				//    }

				//}

				// Use swap instead of copy operator for efficiently
				//mCachedLightInfos.swap(mTestLightInfos);
				this.mCachedLightInfos = this.mTestLightInfos;

				// notify light dirty, so all movable objects will re-populate
				// their light list next time
				//_notifyLightsDirty();
				//Check: do we have something like this here?
			}
		}
Beispiel #11
0
		/// <summary>
		///		Internal method which locates any visible objects attached to this node and adds them to the passed in queue.
		/// </summary>
		/// <param name="camera">Active camera.</param>
		/// <param name="queue">Queue to which these objects should be added.</param>
		/// <param name="includeChildren">If true, cascades down to all children.</param>
		/// <param name="displayNodes">Renders the local axes for the node.</param>
		/// <param name="onlyShadowCasters"></param>
		public virtual void FindVisibleObjects( Camera camera, RenderQueue queue, bool includeChildren, bool displayNodes, bool onlyShadowCasters )
		{
			// if we aren't visible, then quit now
			// TODO: Make sure sphere is calculated properly for all objects, then switch to cull using that
			if ( !camera.IsObjectVisible( worldAABB ) )
				return;

			// add visible objects to the render queue
			//objectListMeter.Enter();
			foreach ( MovableObject obj in objectList.Values )
			{
				// tell attached object about current camera in case it wants to know
				//notifyCameraMeter.Enter();
				obj.NotifyCurrentCamera( camera );
				//notifyCameraMeter.Exit();

				// if this object is visible, add it to the render queue
				if ( obj.IsVisible &&
					( !onlyShadowCasters || obj.CastShadows ) )
				{
					//updateQueueMeter.Enter();
					obj.UpdateRenderQueue( queue );
					//updateQueueMeter.Exit();
				}
			}
			//objectListMeter.Exit();

			//childListMeter.Enter();
			if ( includeChildren )
			{
				// ask all child nodes to update the render queue with visible objects
				foreach ( SceneNode childNode in childNodes.Values )
				{
					if ( childNode.IsVisible )
						childNode.FindVisibleObjects( camera, queue, includeChildren, displayNodes, onlyShadowCasters );
				}
			}
			//childListMeter.Exit();

			// if we wanna display nodes themself..
			if ( displayNodes )
			{
				// hey, lets just add ourself right to the render queue
				queue.AddRenderable( GetDebugRenderable() );
			}

			// do we wanna show our beautiful bounding box?
			// do it if either we want it, or the SceneManager dictates it
			if ( showBoundingBox || ( creator != null && creator.ShowBoundingBoxes ) )
			{
				AddBoundingBoxToQueue( queue );
			}
		}
        public void PerFrameProcessing(float time, Camera camera)
        {
            Debug.Assert(inBoundary);
            bool updateVisibility = false;

            if (pendingTreeTypes != null)
            {
                ProcessTreeTypes();
                TerrainManager.Instance.RecreateCollisionTiles();
                updateVisibility = true;
            }
            SpeedTreeWrapper.Time = time;

            if (bounds != null && camera.IsObjectVisible(bounds))
            { // this stuff only needs to be done if the forest is visible
                // process wind
                speedWind.Advance(time, windStrength, SpeedTreeUtil.ToSpeedTree(windDirection));

                // determine whether the camera changed direction or location since the last frame
                if (camera.Direction != lastCameraDirection || camera.Position != lastCameraLocation)
                {
                    updateVisibility = true;
                    lastCameraLocation = camera.Position;
                    lastCameraDirection = camera.Direction;
                }

                // if the camera changed position or direction, or new trees were added, then recompute the visibility of this tree
                if (updateVisibility)
                {
                    foreach (TreeGroup group in groups)
                    {
                        group.CameraChange(camera);
                    }
                }
                foreach (TreeGroup group in groups)
                {
                    group.UpdateMaterials();
                }
            }
        }
        /// <summary>
        ///		Internal method for locating a list of lights which could be affecting the frustum.
        /// </summary>
        /// <remarks>
        ///		Custom scene managers are encouraged to override this method to make use of their
        ///		scene partitioning scheme to more efficiently locate lights, and to eliminate lights
        ///		which may be occluded by word geometry.
        /// </remarks>
        /// <param name="camera">Camera to find lights within it's view.</param>
        protected virtual void FindLightsAffectingFrustum(Camera camera)
        {
            // Basic iteration for this scene manager
            lightsAffectingFrustum.Clear();

            // sphere to use for testing
            Sphere sphere = new Sphere();

            ICollection<MovableObject> lightList = GetMovableObjectCollection("Light");
            foreach (Light light in lightList) {
                if (light.IsVisible) {
                    if(light.Type == LightType.Directional) {
                        // Always visible
                        lightsAffectingFrustum.Add(light);
                    }
                    else {
                        // treating spotlight as point for simplicity
                        // Just see if the lights attenuation range is within the frustum
                        sphere.Center = light.DerivedPosition;
                        sphere.Radius = light.AttenuationRange;

                        if (camera.IsObjectVisible(sphere)) {
                            lightsAffectingFrustum.Add(light);
                        }
                    }
                }
            }
        }
 // formerly UpdateVisibility(Camera camera)
 public void CameraChange(Camera camera)
 {
     // we need to draw the tree if it intersects with the camera frustrum
     renderFlag = camera.IsObjectVisible(bounds);
     if (renderFlag)
     {
         // if the tree is still visible, update the camera dependent rendering args
         UpdateRenderArgs();
     }
 }
        public void CameraChange(Camera camera)
        {
            visible = camera.IsObjectVisible(bounds);

            // mark branches, fronds and leaves as not visible.  If any of the trees have them visible,
            //  the tree will set them to true in their own CameraChange() method.
            visibleBranches.Clear();
            visibleFronds.Clear();
            visibleLeaves.Clear();
            visibleBillboards.Clear();

            // force rebuilding of billboards before next render
            billboardsDirty = true;

            if (visible)
            {
                foreach (Tree t in trees)
                {
                    t.CameraChange(camera);
                }
                //SortTrees();
            }
            else
            {
                //ClearBuckets();
            }
        }
        /// <summary>
        ///		Walks the BSP tree looking for the node which the camera is in, and tags any geometry 
        ///		which is in a visible leaf for later processing.
        /// </summary>
        protected BspNode WalkTree(Camera camera, bool onlyShadowCasters)
        {
            // Locate the leaf node where the camera is located
            BspNode cameraNode = level.FindLeaf(camera.DerivedPosition);

            matFaceGroupMap.Clear();
            faceGroupChecked = new bool[level.FaceGroups.Length];

            TextureLight[] lights = new TextureLight[lightList.Count];
            BspNode[] lightNodes = new BspNode[lightList.Count];
            Sphere[] lightSpheres = new Sphere[lightList.Count];

            // The base SceneManager uses this for shadows.
            // The BspSceneManager uses this for texture lighting as well.
            if (shadowTechnique == ShadowTechnique.None)
            {
                lightsAffectingFrustum.Clear();
                lightAddedToFrustum = new bool[lightList.Count];
            }

            for (int lp=0; lp < lightList.Count; lp++)
            {
                TextureLight light = (TextureLight) lightList[lp];
                lights[lp] = light;
                lightNodes[lp] = (BspNode) level.objectToNodeMap.FindFirst(light);
                if (light.Type != LightType.Directional)
                {
                    // treating spotlight as point for simplicity
                    lightSpheres[lp] = new Sphere(light.DerivedPosition, light.AttenuationRange);
                }
            }

            // Scan through all the other leaf nodes looking for visibles
            int i = level.NumNodes - level.LeafStart;
            int p = level.LeafStart;
            BspNode node;

            while(i-- > 0)
            {
                node = level.Nodes[p];

                if(level.IsLeafVisible(cameraNode, node))
                {
                    // Visible according to PVS, check bounding box against frustum
                    FrustumPlane plane;

                    if(camera.IsObjectVisible(node.BoundingBox, out plane))
                    {
                        if (!onlyShadowCasters)
                        {
                            for (int lp=0; lp < lights.Length; lp++)
                            {
                                if (lightAddedToFrustum[lp] || !lights[lp].IsVisible)
                                    continue;

                                if (level.IsLeafVisible(lightNodes[lp], node) &&
                                    (lights[lp].Type == LightType.Directional ||
                                    lightSpheres[lp].Intersects(node.BoundingBox)))
                                {
                                    // This is set so that the lights are rendered with ascending
                                    // Priority order.
                                    lights[lp].TempSquaredDist = lights[lp].Priority;

                                    lightsAffectingFrustum.Add(lights[lp]);
                                    lightAddedToFrustum[lp] = true;
                                }
                            }
                        }

                        ProcessVisibleLeaf(node, camera, onlyShadowCasters);

                        if(showNodeAABs)
                            AddBoundingBox(node.BoundingBox, true);
                    }
                }

                p++;
            }

            return cameraNode;
        }