Axiom.Core.Light.GetFrustumClipVolumes C# (CSharp) Method

GetFrustumClipVolumes() private method

Internal method for calculating the clip volumes outside of the frustum which can be used to determine which objects are casting shadow on the frustum as a whole.
Each of the volumes is a pyramid for a point/spot light and a cuboid for a directional light.
private GetFrustumClipVolumes ( Camera camera ) : PlaneBoundedVolumeList
camera Camera
return PlaneBoundedVolumeList
		internal virtual PlaneBoundedVolumeList GetFrustumClipVolumes( Camera camera )
		{
			// Homogenous light position
			Vector4 lightPos = this.GetAs4DVector();

			// 3D version (not the same as DerivedPosition, is -direction for
			// directional lights)
			Vector3 lightPos3 = new Vector3( lightPos.x, lightPos.y, lightPos.z );
			Vector3 lightDir;

			Vector3[] clockwiseVerts = new Vector3[ 4 ];

			Matrix4 eyeToWorld = camera.ViewMatrix.Inverse();

			// Get worldspace frustum corners
			Vector3[] corners = camera.WorldSpaceCorners;

			bool infiniteViewDistance = ( camera.Far == 0 );

			this.frustumClipVolumes.Clear();

			for ( int n = 0; n < 6; n++ )
			{
				FrustumPlane frustumPlane = (FrustumPlane)n;

				// skip far plane if infinite view frustum
				if ( infiniteViewDistance && ( frustumPlane == FrustumPlane.Far ) )
				{
					continue;
				}

				Plane plane = camera[ frustumPlane ];

				Vector4 planeVec = new Vector4( plane.Normal.x, plane.Normal.y, plane.Normal.z, plane.D );

				// planes face inwards, we need to know if light is on negative side
				float d = planeVec.Dot( lightPos );

				if ( d < -1e-06f )
				{
					// Ok, this is a valid one
					// clockwise verts mean we can cross-product and always get normals
					// facing into the volume we create
					this.frustumClipVolumes.Add( new PlaneBoundedVolume() );
					PlaneBoundedVolume vol =
							(PlaneBoundedVolume)this.frustumClipVolumes[ this.frustumClipVolumes.Count - 1 ];

					switch ( frustumPlane )
					{
						case ( FrustumPlane.Near ):
							clockwiseVerts[ 0 ] = corners[ 3 ];
							clockwiseVerts[ 1 ] = corners[ 2 ];
							clockwiseVerts[ 2 ] = corners[ 1 ];
							clockwiseVerts[ 3 ] = corners[ 0 ];
							break;
						case ( FrustumPlane.Far ):
							clockwiseVerts[ 0 ] = corners[ 7 ];
							clockwiseVerts[ 1 ] = corners[ 6 ];
							clockwiseVerts[ 2 ] = corners[ 5 ];
							clockwiseVerts[ 3 ] = corners[ 4 ];
							break;
						case ( FrustumPlane.Left ):
							clockwiseVerts[ 0 ] = corners[ 2 ];
							clockwiseVerts[ 1 ] = corners[ 6 ];
							clockwiseVerts[ 2 ] = corners[ 5 ];
							clockwiseVerts[ 3 ] = corners[ 1 ];
							break;
						case ( FrustumPlane.Right ):
							clockwiseVerts[ 0 ] = corners[ 7 ];
							clockwiseVerts[ 1 ] = corners[ 3 ];
							clockwiseVerts[ 2 ] = corners[ 0 ];
							clockwiseVerts[ 3 ] = corners[ 4 ];
							break;
						case ( FrustumPlane.Top ):
							clockwiseVerts[ 0 ] = corners[ 0 ];
							clockwiseVerts[ 1 ] = corners[ 1 ];
							clockwiseVerts[ 2 ] = corners[ 5 ];
							clockwiseVerts[ 3 ] = corners[ 4 ];
							break;
						case ( FrustumPlane.Bottom ):
							clockwiseVerts[ 0 ] = corners[ 7 ];
							clockwiseVerts[ 1 ] = corners[ 6 ];
							clockwiseVerts[ 2 ] = corners[ 2 ];
							clockwiseVerts[ 3 ] = corners[ 3 ];
							break;
					}

					// Build a volume
					// Iterate over world points and form side planes
					Vector3 normal;

					for ( int i = 0; i < 4; i++ )
					{
						// Figure out light dir
						lightDir = lightPos3 - ( clockwiseVerts[ i ] * lightPos.w );

						// Cross with anticlockwise corner, therefore normal points in
						// Note: C++ mod returns 3 for the first case where C# returns -1
						int test = i > 0 ? ( ( i - 1 ) % 4 ) : 3;

						// Cross with anticlockwise corner, therefore normal points in
						normal = ( clockwiseVerts[ i ] - clockwiseVerts[ test ] ).Cross( lightDir );
						normal.Normalize();

						// NB last param to Plane constructor is negated because it's -d
						vol.planes.Add( new Plane( normal, normal.Dot( clockwiseVerts[ i ] ) ) );
					}

					// Now do the near plane (this is the plane of the side we're
					// talking about, with the normal inverted (d is already interpreted as -ve)
					vol.planes.Add( new Plane( -plane.Normal, plane.D ) );

					// Finally, for a point/spot light we can add a sixth plane
					// This prevents false positives from behind the light
					if ( this.type != LightType.Directional )
					{
						// re-use our own plane normal
						// remember the -d negation in plane constructor
						vol.planes.Add( new Plane( plane.Normal, plane.Normal.Dot( lightPos3 ) ) );
					}
				}
			}

			return this.frustumClipVolumes;
		}

Usage Example

Example #1
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;
		}