Пример #1
0
        protected void TagNodesWithObject(BspNode node, MovableObject obj, Vector3 pos)
        {
            if (node.IsLeaf)
            {
                // Add to movable->node map
                // Insert all the time, will get current if already there
                objectToNodeMap.Insert(obj, node);
                node.AddObject(obj);
            }
            else
            {
                // Find distance to dividing plane
                float dist = node.GetDistance(pos);

                //CHECK: treat obj as bounding box?
                if (MathUtil.Abs(dist) < obj.BoundingRadius)
                {
                    // Bounding sphere crosses the plane, do both.
                    TagNodesWithObject(node.BackNode, obj, pos);
                    TagNodesWithObject(node.FrontNode, obj, pos);
                }
                else if (dist < 0)
                {
                    // Do back.
                    TagNodesWithObject(node.BackNode, obj, pos);
                }
                else
                {
                    // Do front.
                    TagNodesWithObject(node.FrontNode, obj, pos);
                }
            }
        }
Пример #2
0
        /// <summary>
        ///		Walks the entire BSP tree and returns the leaf which contains the given point.
        /// </summary>r
        public BspNode FindLeaf(Vector3 point)
        {
            BspNode node = nodes[0];

            while (!node.IsLeaf)
            {
                node = node.GetNextNode(point);
            }

            return(node);
        }
Пример #3
0
        private void CreateLeaves(Quake3Level q3lvl)
        {
            for (int i = 0; i < q3lvl.NumLeaves; ++i)
            {
                BspNode         node   = nodes[i + this.LeafStart];
                InternalBspLeaf q3leaf = q3lvl.Leaves[i];

                node.IsLeaf = true;
                node.Owner  = this;

                // Set bounding box
                node.BoundingBox.Minimum = new Vector3(
                    q3leaf.bbox[0],
                    q3leaf.bbox[1],
                    q3leaf.bbox[2]
                    );
                node.BoundingBox.Maximum = new Vector3(
                    q3leaf.bbox[3],
                    q3leaf.bbox[4],
                    q3leaf.bbox[5]
                    );

                // Set faces
                node.FaceGroupStart = q3leaf.faceStart;
                node.NumFaceGroups  = q3leaf.faceCount;
                node.VisCluster     = q3leaf.cluster;

                // Load Brushes for this leaf
                int realBrushIdx = 0, solidIdx = 0;
                int brushCount = q3leaf.brushCount;
                int brushIdx   = q3leaf.brushStart;

                node.SolidBrushes = new BspBrush[brushCount];

                while (brushCount-- > 0)
                {
                    realBrushIdx = q3lvl.LeafBrushes[brushIdx];
                    InternalBspBrush q3brush = q3lvl.Brushes[realBrushIdx];

                    // Only load solid ones, we don't care about any other types
                    // Shader determines this.
                    InternalBspShader brushShader = q3lvl.Shaders[q3brush.shaderIndex];

                    if ((brushShader.contentFlags & ContentFlags.Solid) == ContentFlags.Solid)
                    {
                        node.SolidBrushes[solidIdx] = brushes[realBrushIdx];
                    }

                    brushIdx++;
                    solidIdx++;
                }
            }
        }
Пример #4
0
        /// <summary>
        ///		Determines if one leaf node is visible from another.
        /// </summary>
        /// <param name="?"></param>
        /// <returns></returns>
        public bool IsLeafVisible(BspNode from, BspNode to)
        {
            if (to.VisCluster == -1)
            {
                return(false);
            }
            if (from.VisCluster == -1)
            {
                // Camera outside world?
                return(true);
            }

            if (!from.IsLeaf || !to.IsLeaf)
            {
                throw new AxiomException("Both nodes must be leaf nodes for visibility testing.");
            }

            // Use PVS to determine visibility

            /*
             * // In wordier terms, the fairly cryptic (but fast) version is doing this:
             * //   Could make it a macro for even more speed?
             *
             * // Row offset = from cluster number * row size
             * int offset = from->mVisCluster*mVisData.rowLength;
             *
             * // Column offset (in bytes) = to cluster number divided by 8 (since 8 bits per bytes)
             * offset += to->mVisCluster >> 3;
             *
             * // Get the right bit within the byte, i.e. bitwise 'and' with bit at remainder position
             * int result = *(mVisData.tableData + offset) & (1 << (to->mVisCluster & 7));
             *
             * return (result != 0);
             */

            byte visSet = visData.tableData[(from.VisCluster * visData.rowLength) + (to.VisCluster >> 3)];
            int  result = visSet & (1 << ((to.VisCluster) & 7));

            return(result != 0);
        }
Пример #5
0
		protected virtual void ProcessNode( BspNode node, Ray tracingRay, float maxDistance, float traceDistance )
		{
			// check if ray already encountered a solid brush
			if ( this.StopRayTracing )
			{
				return;
			}

			if ( node.IsLeaf )
			{
				ProcessLeaf( node, tracingRay, maxDistance, traceDistance );
				return;
			}

			IntersectResult result = tracingRay.Intersects( node.SplittingPlane );
			if ( result.Hit )
			{
				if ( result.Distance < maxDistance )
				{
					if ( node.GetSide( tracingRay.Origin ) == PlaneSide.Negative )
					{
						ProcessNode( node.BackNode, tracingRay, result.Distance, traceDistance );
						Vector3 splitPoint = tracingRay.Origin + tracingRay.Direction*result.Distance;
						ProcessNode( node.FrontNode, new Ray( splitPoint, tracingRay.Direction ), maxDistance - result.Distance,
						             traceDistance + result.Distance );
					}
					else
					{
						ProcessNode( node.FrontNode, tracingRay, result.Distance, traceDistance );
						Vector3 splitPoint = tracingRay.Origin + tracingRay.Direction*result.Distance;
						ProcessNode( node.BackNode, new Ray( splitPoint, tracingRay.Direction ), maxDistance - result.Distance,
						             traceDistance + result.Distance );
					}
				}
				else
				{
					ProcessNode( node.GetNextNode( tracingRay.Origin ), tracingRay, maxDistance, traceDistance );
				}
			}
			else
			{
				ProcessNode( node.GetNextNode( tracingRay.Origin ), tracingRay, maxDistance, traceDistance );
			}
		}
Пример #6
0
 /// <summary>
 ///		Determines if the passed in node (must also be a leaf) is visible from this leaf.
 ///	</summary>
 ///	<remarks>
 ///		Must only be called on a leaf node, and the parameter must also be a leaf node. If
 ///		this method returns true, then the leaf passed in is visible from this leaf.
 ///		Note that internally this uses the Potentially Visible Set (PVS) which is precalculated
 ///		and stored with the BSP level.
 ///	</remarks>
 public bool IsLeafVisible(BspNode leaf)
 {
     return owner.IsLeafVisible(this, leaf);
 }
Пример #7
0
        private void CreateNodes(Quake3Level q3lvl)
        {
            // Allocate memory for all nodes (leaves and splitters)
            nodes = new BspNode[q3lvl.NumNodes + q3lvl.NumLeaves];
            numLeaves = q3lvl.NumLeaves;
            leafStart = q3lvl.NumNodes;

            // Run through and initialize the array so front/back node pointers
            // aren't null.
            for(int i = 0; i < nodes.Length; i++)
                nodes[i] = new BspNode();

            // Convert nodes
            // In our array, first q3lvl.NumNodes are non-leaf, others are leaves
            for(int i = 0; i < q3lvl.NumNodes; i++)
            {
                BspNode node = nodes[i];
                InternalBspNode q3node = q3lvl.Nodes[i];

                node.IsLeaf = false;
                node.Owner = this;

                Plane splitPlane = new Plane();

                // Set plane
                splitPlane.Normal = new Vector3(
                    q3lvl.Planes[q3node.plane].normal[0],
                    q3lvl.Planes[q3node.plane].normal[1],
                    q3lvl.Planes[q3node.plane].normal[2]
                    );
                splitPlane.D = -q3lvl.Planes[q3node.plane].distance;

                node.SplittingPlane = splitPlane;

                // Set bounding box
                node.BoundingBox = new AxisAlignedBox(
                    new Vector3(
                        q3node.bbox[0],
                        q3node.bbox[1],
                        q3node.bbox[2]
                        ),
                    new Vector3(
                        q3node.bbox[3],
                        q3node.bbox[4],
                        q3node.bbox[5]
                        )
                    );

                // Set back pointer
                // Negative indexes in Quake3 mean leaves.
                if(q3node.back < 0)
                    node.BackNode = nodes[leafStart + (~(q3node.back))];
                else
                    node.BackNode = nodes[q3node.back];

                // Set front pointer
                // Negative indexes in Quake3 mean leaves
                if(q3node.front < 0)
                    node.FrontNode = nodes[leafStart + (~(q3node.front))];
                else
                    node.FrontNode = nodes[q3node.front];
            }
        }
Пример #8
0
        protected void TagNodesWithObject(BspNode node, MovableObject obj, Vector3 pos)
        {
            if(node.IsLeaf)
            {
                // Add to movable->node map
                // Insert all the time, will get current if already there
                objectToNodeMap.Insert(obj, node);
                node.AddObject(obj);
            }
            else
            {
                // Find distance to dividing plane
                float dist = node.GetDistance(pos);

                //CHECK: treat obj as bounding box?
                if(MathUtil.Abs(dist) < obj.BoundingRadius)
                {
                    // Bounding sphere crosses the plane, do both.
                    TagNodesWithObject(node.BackNode, obj, pos);
                    TagNodesWithObject(node.FrontNode, obj, pos);
                }
                else if(dist < 0)
                {
                    // Do back.
                    TagNodesWithObject(node.BackNode, obj, pos);
                }
                else
                {
                    // Do front.
                    TagNodesWithObject(node.FrontNode, obj, pos);
                }
            }
        }
Пример #9
0
        /// <summary>
        ///		Determines if one leaf node is visible from another.
        /// </summary>
        /// <param name="?"></param>
        /// <returns></returns>
        public bool IsLeafVisible(BspNode from, BspNode to)
        {
            if(to.VisCluster == -1)
                return false;
            if(from.VisCluster == -1)
                // Camera outside world?
                return true;

            if(!from.IsLeaf || !to.IsLeaf)
                throw new AxiomException("Both nodes must be leaf nodes for visibility testing.");

            // Use PVS to determine visibility

            /*
            // In wordier terms, the fairly cryptic (but fast) version is doing this:
            //   Could make it a macro for even more speed?

            // Row offset = from cluster number * row size
            int offset = from->mVisCluster*mVisData.rowLength;

            // Column offset (in bytes) = to cluster number divided by 8 (since 8 bits per bytes)
            offset += to->mVisCluster >> 3;

            // Get the right bit within the byte, i.e. bitwise 'and' with bit at remainder position
            int result = *(mVisData.tableData + offset) & (1 << (to->mVisCluster & 7));

            return (result != 0);
            */

            byte visSet = visData.tableData[(from.VisCluster * visData.rowLength) + (to.VisCluster >> 3)];
            int result = visSet & (1 << ((to.VisCluster) & 7));

            return (result != 0);
        }
        protected virtual void ProcessLeaf(BspNode leaf)
        {
            MovableObjectCollection objects = leaf.Objects;
            int numObjects = objects.Count;

            //Check sphere against objects
            for(int a = 0; a < numObjects; a++)
            {
                MovableObject obj = objects[a];
                // Skip this object if collision not enabled
                if((obj.QueryFlags & queryMask) == 0)
                    continue;

                //Test object as bounding box
                if(sphere.Intersects(obj.GetWorldBoundingBox()))
                {
                    if (!foundIntersections.Contains(obj))
                    {
                        listener.OnQueryResult(obj);
                        foundIntersections.Add(obj);
                    }
                }
            }

            PlaneBoundedVolume boundedVolume = new PlaneBoundedVolume(PlaneSide.Positive);

            // Check ray against brushes
            for (int brushPoint=0; brushPoint < leaf.SolidBrushes.Length; brushPoint++)
            {
                BspBrush brush = leaf.SolidBrushes[brushPoint];
                if (brush == null) continue;

                boundedVolume.planes = brush.Planes;
                if(boundedVolume.Intersects(sphere))
                {
                    listener.OnQueryResult(brush.Fragment);
                }
            }
        }
Пример #11
0
		protected virtual void ProcessLeaf( BspNode leaf )
		{
			//Check sphere against objects
			foreach ( MovableObject obj in leaf.Objects.Values )
			{
				// Skip this object if collision not enabled
				if ( ( obj.QueryFlags & queryMask ) == 0 )
				{
					continue;
				}

				//Test object as bounding box
				if ( sphere.Intersects( obj.GetWorldBoundingBox() ) )
				{
					if ( !this.foundIntersections.Contains( obj ) )
					{
						this.listener.OnQueryResult( obj );
						this.foundIntersections.Add( obj );
					}
				}
			}

			var boundedVolume = new PlaneBoundedVolume( PlaneSide.Positive );

			// Check ray against brushes
			for ( int brushPoint = 0; brushPoint < leaf.SolidBrushes.Length; brushPoint++ )
			{
				BspBrush brush = leaf.SolidBrushes[ brushPoint ];
				if ( brush == null )
				{
					continue;
				}

				boundedVolume.planes = brush.Planes;
				if ( boundedVolume.Intersects( sphere ) )
				{
					this.listener.OnQueryResult( brush.Fragment );
				}
			}
		}
Пример #12
0
        private void CreateNodes(Quake3Level q3lvl)
        {
            // Allocate memory for all nodes (leaves and splitters)
            nodes     = new BspNode[q3lvl.NumNodes + q3lvl.NumLeaves];
            numLeaves = q3lvl.NumLeaves;
            leafStart = q3lvl.NumNodes;

            // Run through and initialize the array so front/back node pointers
            // aren't null.
            for (int i = 0; i < nodes.Length; i++)
            {
                nodes[i] = new BspNode();
            }

            // Convert nodes
            // In our array, first q3lvl.NumNodes are non-leaf, others are leaves
            for (int i = 0; i < q3lvl.NumNodes; i++)
            {
                BspNode         node   = nodes[i];
                InternalBspNode q3node = q3lvl.Nodes[i];

                node.IsLeaf = false;
                node.Owner  = this;

                Plane splitPlane = new Plane();

                // Set plane
                splitPlane.Normal = new Vector3(
                    q3lvl.Planes[q3node.plane].normal[0],
                    q3lvl.Planes[q3node.plane].normal[1],
                    q3lvl.Planes[q3node.plane].normal[2]
                    );
                splitPlane.D = -q3lvl.Planes[q3node.plane].distance;

                node.SplittingPlane = splitPlane;

                // Set bounding box
                node.BoundingBox = new AxisAlignedBox(
                    new Vector3(
                        q3node.bbox[0],
                        q3node.bbox[1],
                        q3node.bbox[2]
                        ),
                    new Vector3(
                        q3node.bbox[3],
                        q3node.bbox[4],
                        q3node.bbox[5]
                        )
                    );

                // Set back pointer
                // Negative indexes in Quake3 mean leaves.
                if (q3node.back < 0)
                {
                    node.BackNode = nodes[leafStart + (~(q3node.back))];
                }
                else
                {
                    node.BackNode = nodes[q3node.back];
                }

                // Set front pointer
                // Negative indexes in Quake3 mean leaves
                if (q3node.front < 0)
                {
                    node.FrontNode = nodes[leafStart + (~(q3node.front))];
                }
                else
                {
                    node.FrontNode = nodes[q3node.front];
                }
            }
        }
        /// <summary>
        ///		Overriden from SceneManager.
        /// </summary>
        /// <param name="position">The position at which to evaluate the list of lights</param>
        /// <param name="radius">The bounding radius to test</param>
        /// <param name="destList">List to be populated with ordered set of lights; will be cleared by this method before population.</param>
        protected override void PopulateLightList(Vector3 position, float radius, LightList destList)
        {
            BspNode positionNode = level.FindLeaf(position);
            BspNode[] lightNodes = new BspNode[lightList.Count];

            for (int i = 0; i < lightList.Count; i++)
            {
                Light light = lightList[i];
                lightNodes[i] = (BspNode) level.objectToNodeMap.FindFirst(light);
            }

            // Trawl of the lights that are visible from position, then sort
            destList.Clear();
            float squaredRadius = radius * radius;

            // loop through the scene lights an add ones in range and visible from positionNode
            for(int i = 0; i < lightList.Count; i++)
            {
                TextureLight light = (TextureLight) lightList[i];

                if(light.IsVisible && level.IsLeafVisible(positionNode, lightNodes[i]))
                {
                    if(light.Type == LightType.Directional)
                    {
                        // no distance
                        light.TempSquaredDist = 0.0f;
                        destList.Add(light);
                    }
                    else
                    {
                        light.TempSquaredDist = (light.DerivedPosition - position).LengthSquared;
                        light.TempSquaredDist -= squaredRadius;
                        // only add in-range lights
                        float range = light.AttenuationRange;
                        if(light.TempSquaredDist <= (range * range))
                        {
                            destList.Add(light);
                        }
                    }
                } // if
            } // for

            // Sort Destination light list.
            // TODO: Not needed yet since the current LightList is a sorted list under the hood already
            //destList.Sort();
        }
        protected virtual void ProcessLeaf(BspNode leaf, Ray tracingRay, float maxDistance, float traceDistance)
        {
            MovableObjectCollection objects = leaf.Objects;
            int numObjects = objects.Count;

            //Check ray against objects
            for(int a = 0; a < numObjects; a++)
            {
                MovableObject obj = objects[a];
                // Skip this object if collision not enabled
                if((obj.QueryFlags & queryMask) == 0)
                    continue;

                //Test object as bounding box
                IntersectResult result = tracingRay.Intersects(obj.GetWorldBoundingBox());
                // if the result came back positive and intersection point is inside
                // the node, fire the event handler
                if(result.Hit && result.Distance <= maxDistance)
                {
                    listener.OnQueryResult(obj, result.Distance + traceDistance);
                }
            }

            PlaneBoundedVolume boundedVolume = new PlaneBoundedVolume(PlaneSide.Positive);
            BspBrush intersectBrush = null;
            float intersectBrushDist = float.PositiveInfinity;

            // Check ray against brushes
            for (int brushPoint=0; brushPoint < leaf.SolidBrushes.Length; brushPoint++)
            {
                BspBrush brush = leaf.SolidBrushes[brushPoint];

                if (brush == null) continue;

                boundedVolume.planes = brush.Planes;

                IntersectResult result = tracingRay.Intersects(boundedVolume);
                // if the result came back positive and intersection point is inside
                // the node, check if this brush is closer
                if(result.Hit && result.Distance <= maxDistance)
                {
                    if (result.Distance < intersectBrushDist)
                    {
                        intersectBrushDist = result.Distance;
                        intersectBrush = brush;
                    }
                }
            }

            if (intersectBrush != null)
            {
                listener.OnQueryResult(intersectBrush.Fragment, intersectBrushDist + traceDistance);
                StopRayTracing = true;
            }
        }
Пример #15
0
		protected virtual void ProcessLeaf( BspNode leaf, Ray tracingRay, float maxDistance, float traceDistance )
		{
			//Check ray against objects
			foreach ( MovableObject obj in leaf.Objects.Values )
			{
				// Skip this object if collision not enabled
				if ( ( obj.QueryFlags & queryMask ) == 0 )
				{
					continue;
				}

				//Test object as bounding box
				IntersectResult result = tracingRay.Intersects( obj.GetWorldBoundingBox() );
				// if the result came back positive and intersection point is inside
				// the node, fire the event handler
				if ( result.Hit && result.Distance <= maxDistance )
				{
					this.listener.OnQueryResult( obj, result.Distance + traceDistance );
				}
			}

			var boundedVolume = new PlaneBoundedVolume( PlaneSide.Positive );
			BspBrush intersectBrush = null;
			float intersectBrushDist = float.PositiveInfinity;

			if ( ( QueryTypeMask & (ulong)SceneQueryTypeMask.WorldGeometry ) != 0 )
			{
				// Check ray against brushes
				if ( ( QueryTypeMask & (ulong)SceneQueryTypeMask.WorldGeometry ) != 0 )
				{
					for ( int brushPoint = 0; brushPoint < leaf.SolidBrushes.Length; brushPoint++ )
					{
						BspBrush brush = leaf.SolidBrushes[ brushPoint ];

						if ( brush == null )
						{
							continue;
						}

						boundedVolume.planes = brush.Planes;

						IntersectResult result = tracingRay.Intersects( boundedVolume );
						// if the result came back positive and intersection point is inside
						// the node, check if this brush is closer
						if ( result.Hit && result.Distance <= maxDistance )
						{
							if ( result.Distance < intersectBrushDist )
							{
								intersectBrushDist = result.Distance;
								intersectBrush = brush;
							}
						}
					}

					if ( intersectBrush != null )
					{
						this.listener.OnQueryResult( intersectBrush.Fragment, intersectBrushDist + traceDistance );
						this.StopRayTracing = true;
					}
				}
			}

			if ( intersectBrush != null )
			{
				this.listener.OnQueryResult( intersectBrush.Fragment, intersectBrushDist + traceDistance );
				this.StopRayTracing = true;
			}
		}
Пример #16
0
		/// <summary>
		///		/** Internal utility function for loading data from Quake3.
		/// </summary>
		protected void LoadQuake3Level( Quake3Level q3lvl )
		{
			ResourceGroupManager rgm = ResourceGroupManager.Instance;
			rgm.notifyWorldGeometryStageStarted( "Parsing entities" );
			LoadEntities( q3lvl );
			rgm.notifyWorldGeometryStageEnded();

			rgm.notifyWorldGeometryStageStarted( "Extracting lightmaps" );
			q3lvl.ExtractLightmaps();
			rgm.notifyWorldGeometryStageEnded();

			//-----------------------------------------------------------------------
			// Vertices
			//-----------------------------------------------------------------------
			// Allocate memory for vertices & copy
			vertexData = new VertexData();

			// Create vertex declaration
			VertexDeclaration decl = vertexData.vertexDeclaration;
			int offset = 0;
			int lightTexOffset = 0;
			decl.AddElement( 0, offset, VertexElementType.Float3, VertexElementSemantic.Position );
			offset += VertexElement.GetTypeSize( VertexElementType.Float3 );
			decl.AddElement( 0, offset, VertexElementType.Float3, VertexElementSemantic.Normal );
			offset += VertexElement.GetTypeSize( VertexElementType.Float3 );
			decl.AddElement( 0, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 0 );
			offset += VertexElement.GetTypeSize( VertexElementType.Float2 );
			decl.AddElement( 0, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 1 );

			// Build initial patches - we need to know how big the vertex buffer needs to be
			// to accommodate the subdivision
			// we don't want to include the elements for texture lighting, so we clone it
			rgm.notifyWorldGeometryStageStarted( "Initializing patches" );
			InitQuake3Patches( q3lvl, (VertexDeclaration)decl.Clone() );
			rgm.notifyWorldGeometryStageEnded();

			// this is for texture lighting color and alpha
			decl.AddElement( 1, lightTexOffset, VertexElementType.Color, VertexElementSemantic.Diffuse );
			lightTexOffset += VertexElement.GetTypeSize( VertexElementType.Color );
			// this is for texture lighting coords
			decl.AddElement( 1, lightTexOffset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 2 );

			rgm.notifyWorldGeometryStageStarted( "Setting up vertex data" );
			// Create the vertex buffer, allow space for patches
			HardwareVertexBuffer vbuf = HardwareBufferManager.Instance.CreateVertexBuffer( decl.Clone(0), q3lvl.NumVertices + patchVertexCount,	BufferUsage.StaticWriteOnly /* the vertices will be read often for texture lighting, use shadow buffer */, true );

			// Create the vertex buffer for texture lighting, allow space for patches
			HardwareVertexBuffer texLightBuf = HardwareBufferManager.Instance.CreateVertexBuffer( decl.Clone(1), q3lvl.NumVertices + patchVertexCount, BufferUsage.DynamicWriteOnly, false );

			// COPY static vertex data - Note that we can't just block-copy the vertex data because we have to reorder
			// our vertex elements; this is to ensure compatibility with older cards when using
			// hardware vertex buffers - Direct3D requires that the buffer format maps onto a
			// FVF in those older drivers.
			// Lock just the non-patch area for now.

			unsafe
			{
				BspVertex vert = new BspVertex();
				TextureLightMap texLightMap = new TextureLightMap();

				// Keep another base pointer for use later in patch building
				for ( int v = 0; v < q3lvl.NumVertices; v++ )
				{
					QuakeVertexToBspVertex( q3lvl.Vertices[ v ], out vert, out texLightMap );

					BspVertex* bvptr = &vert;
					TextureLightMap* tlptr = &texLightMap;

					vbuf.WriteData(
						v * sizeof( BspVertex ),
						sizeof( BspVertex ),
						(IntPtr)bvptr
						);

					texLightBuf.WriteData(
						v * sizeof( TextureLightMap ),
						sizeof( TextureLightMap ),
						(IntPtr)tlptr
						);

				}
			}

			// Setup binding
			vertexData.vertexBufferBinding.SetBinding( 0, vbuf );

			// Setup texture lighting binding
			vertexData.vertexBufferBinding.SetBinding( 1, texLightBuf );

			// Set other data
			vertexData.vertexStart = 0;
			vertexData.vertexCount = q3lvl.NumVertices + patchVertexCount;
			rgm.notifyWorldGeometryStageEnded();

			//-----------------------------------------------------------------------
			// Faces
			//-----------------------------------------------------------------------
			rgm.notifyWorldGeometryStageStarted( "Setting up face data" );
			leafFaceGroups = new int[ q3lvl.LeafFaces.Length ];
			Array.Copy( q3lvl.LeafFaces, 0, leafFaceGroups, 0, leafFaceGroups.Length );

			faceGroups = new BspStaticFaceGroup[ q3lvl.Faces.Length ];

			// Set up index buffer
			// NB Quake3 indexes are 32-bit
			// Copy the indexes into a software area for staging
			numIndexes = q3lvl.NumElements + patchIndexCount;

			// Create an index buffer manually in system memory, allow space for patches
			indexes = HardwareBufferManager.Instance.CreateIndexBuffer(
				IndexType.Size32,
				numIndexes,
				BufferUsage.Dynamic
				);

			// Write main indexes
			indexes.WriteData( 0, Marshal.SizeOf( typeof( uint ) ) * q3lvl.NumElements, q3lvl.Elements, true );
			rgm.notifyWorldGeometryStageEnded();

			// now build patch information
			rgm.notifyWorldGeometryStageStarted( "Building patches" );
			BuildQuake3Patches( q3lvl.NumVertices, q3lvl.NumElements );
			rgm.notifyWorldGeometryStageEnded();

			//-----------------------------------------------------------------------
			// Create materials for shaders
			//-----------------------------------------------------------------------
			// NB this only works for the 'default' shaders for now
			// i.e. those that don't have a .shader script and thus default
			// to just texture + lightmap
			// TODO: pre-parse all .shader files and create lookup for next stage (use ROGL shader_file_t)

			// Material names are shadername#lightmapnumber
			// This is because I like to define materials up front completely
			// rather than combine lightmap and shader dynamically (it's
			// more generic). It results in more materials, but they're small
			// beer anyway. Texture duplication is prevented by infrastructure.
			// To do this I actually need to parse the faces since they have the
			// shader/lightmap combo (lightmap number is not in the shader since
			// it can be used with multiple lightmaps)
			string shaderName;
			int face = q3lvl.Faces.Length;
			int progressCountdown = 100;
			int progressCount = 0;

			while ( face-- > 0 )
			{
				// Progress reporting
				if ( progressCountdown == 100 )
				{
					++progressCount;
					String str = String.Format( "Loading materials (phase {0})", progressCount );
					rgm.notifyWorldGeometryStageStarted( str );
				}
				else if ( progressCountdown == 0 )
				{
					// stage report
					rgm.notifyWorldGeometryStageEnded();
					progressCountdown = 100 + 1;
				}

				// Check to see if existing material
				// Format shader#lightmap
				int shadIdx = q3lvl.Faces[ face ].shader;

				shaderName = String.Format( "{0}#{1}", q3lvl.Shaders[ shadIdx ].name, q3lvl.Faces[ face ].lmTexture );
				Material shadMat = (Material)MaterialManager.Instance.GetByName( shaderName );

				if ( shadMat == null && !bspOptions.useLightmaps )
				{
					// try the no-lightmap material
					shaderName = String.Format( "{0}#n", q3lvl.Shaders[ shadIdx ].name );
					shadMat = (Material)MaterialManager.Instance.GetByName( shaderName );
				}

				if ( shadMat == null )
				{
					// Color layer
					// NB no extension in Q3A(doh), have to try shader, .jpg, .tga
					string tryName = q3lvl.Shaders[ shadIdx ].name;

					// Try shader first
					Quake3Shader shader = (Quake3Shader)Quake3ShaderManager.Instance.GetByName( tryName );

					if ( shader != null )
					{
						shadMat = shader.CreateAsMaterial( q3lvl.Faces[ face ].lmTexture );
					}
					else
					{
						// No shader script, try default type texture
						shadMat = (Material)MaterialManager.Instance.Create( shaderName, rgm.WorldResourceGroupName );
						Pass shadPass = shadMat.GetTechnique( 0 ).GetPass( 0 );

						// Try jpg
						TextureUnitState tex = null;
						if ( ResourceGroupManager.Instance.ResourceExists( rgm.WorldResourceGroupName, tryName + ".jpg" ) )
						{
							tex = shadPass.CreateTextureUnitState( tryName + ".jpg" );
						}
						if ( ResourceGroupManager.Instance.ResourceExists( rgm.WorldResourceGroupName, tryName + ".tga" ) )
						{
							tex = shadPass.CreateTextureUnitState( tryName + ".tga" );
						}

						if ( tex != null )
						{
							// Set replace on all first layer textures for now
							tex.SetColorOperation( LayerBlendOperation.Replace );
                            tex.SetTextureAddressingMode( TextureAddressing.Wrap );
							// for ambient lighting
							tex.ColorBlendMode.source2 = LayerBlendSource.Manual;
						}

						if ( bspOptions.useLightmaps && q3lvl.Faces[ face ].lmTexture != -1 )
						{
							// Add lightmap, additive blending
							tex = shadPass.CreateTextureUnitState( String.Format( "@lightmap{0}", q3lvl.Faces[ face ].lmTexture ) );

							// Blend
							tex.SetColorOperation( LayerBlendOperation.Modulate );

							// Use 2nd texture co-ordinate set
							tex.TextureCoordSet = 1;

							// Clamp
                            tex.SetTextureAddressingMode( TextureAddressing.Clamp );
						}

						shadMat.CullingMode = CullingMode.None;
						shadMat.Lighting = false;
					}
				}

				shadMat.Load();

				// Copy face data
				BspStaticFaceGroup dest = new BspStaticFaceGroup();
				InternalBspFace src = q3lvl.Faces[ face ];

				if ( ( q3lvl.Shaders[ src.shader ].surfaceFlags & SurfaceFlags.Sky ) == SurfaceFlags.Sky )
					dest.isSky = true;
				else
					dest.isSky = false;

				dest.materialHandle = shadMat.Handle;
				dest.elementStart = src.elemStart;
				dest.numElements = src.elemCount;
				dest.numVertices = src.vertCount;
				dest.vertexStart = src.vertStart;
				dest.plane = new Plane();

				if ( Quake3ShaderManager.Instance.GetByName( q3lvl.Shaders[ shadIdx ].name ) != null )
				{
					// it's a quake shader
					dest.isQuakeShader = true;
				}

				if ( src.type == BspFaceType.Normal )
				{
					dest.type = FaceGroup.FaceList;

					// Assign plane
					dest.plane.Normal = new Vector3(
						src.normal[ 0 ],
						src.normal[ 1 ],
						src.normal[ 2 ]
						);
					dest.plane.D = -dest.plane.Normal.Dot(
						new Vector3(
						src.org[ 0 ],
						src.org[ 1 ],
						src.org[ 2 ]
						)
						);

					// Don't rebase indexes here - Quake3 re-uses some indexes for multiple vertex
					// groups eg repeating small details have the same relative vertex data but
					// use the same index data.
				}
				else if ( src.type == BspFaceType.Patch )
				{
					// Seems to be some crap in the Q3 level where vertex count = 0 or num control points = 0?
					if ( ( dest.numVertices == 0 ) || ( src.meshCtrl[ 0 ] == 0 ) )
					{
						dest.type = FaceGroup.Unknown;
					}
					else
					{
						// Set up patch surface
						dest.type = FaceGroup.Patch;

						// Locate the patch we already built
						if ( !patches.ContainsKey( face ) )
							throw new AxiomException( "Patch not found from previous built state." );

						dest.patchSurf = (PatchSurface)patches[ face ];
					}
				}
				else if ( src.type == BspFaceType.Mesh )
				{
					dest.type = FaceGroup.FaceList;

					// Assign plane
					dest.plane.Normal = new Vector3( src.normal[ 0 ], src.normal[ 1 ], src.normal[ 2 ] );
					dest.plane.D = -dest.plane.Normal.Dot( new Vector3( src.org[ 0 ], src.org[ 1 ], src.org[ 2 ] ) );
				}
				else
				{
					LogManager.Instance.Write( "!!! Unknown face type !!!" );
				}

				faceGroups[ face ] = dest;
			}

			//-----------------------------------------------------------------------
			// Nodes
			//-----------------------------------------------------------------------
			// Allocate memory for all nodes (leaves and splitters)
			nodes = new BspNode[ q3lvl.NumNodes + q3lvl.NumLeaves ];
			numLeaves = q3lvl.NumLeaves;
			leafStart = q3lvl.NumNodes;

			// Run through and initialize the array so front/back node pointers
			// aren't null.
			for ( int i = 0; i < nodes.Length; i++ )
				nodes[ i ] = new BspNode();

			// Convert nodes
			// In our array, first q3lvl.NumNodes are non-leaf, others are leaves
			for ( int i = 0; i < q3lvl.NumNodes; i++ )
			{
				BspNode node = nodes[ i ];
				InternalBspNode q3node = q3lvl.Nodes[ i ];

				node.IsLeaf = false;
				node.Owner = this;

				Plane splitPlane = new Plane();

				// Set plane
				splitPlane.Normal = new Vector3(
					q3lvl.Planes[ q3node.plane ].normal[ 0 ],
					q3lvl.Planes[ q3node.plane ].normal[ 1 ],
					q3lvl.Planes[ q3node.plane ].normal[ 2 ]
					);
				splitPlane.D = -q3lvl.Planes[ q3node.plane ].distance;

				node.SplittingPlane = splitPlane;

				// Set bounding box
				node.BoundingBox = new AxisAlignedBox(
					new Vector3(
						q3node.bbox[ 0 ],
						q3node.bbox[ 1 ],
						q3node.bbox[ 2 ]
					),
					new Vector3(
						q3node.bbox[ 3 ],
						q3node.bbox[ 4 ],
						q3node.bbox[ 5 ]
					)
				);

				// Set back pointer
				// Negative indexes in Quake3 mean leaves.
				if ( q3node.back < 0 )
					node.BackNode = nodes[ leafStart + ( ~( q3node.back ) ) ];
				else
					node.BackNode = nodes[ q3node.back ];

				// Set front pointer
				// Negative indexes in Quake3 mean leaves
				if ( q3node.front < 0 )
					node.FrontNode = nodes[ leafStart + ( ~( q3node.front ) ) ];
				else
					node.FrontNode = nodes[ q3node.front ];
			}

			//-----------------------------------------------------------------------
			// Brushes
			//-----------------------------------------------------------------------
			// Reserve enough memory for all brushes, solid or not (need to maintain indexes)
			brushes = new BspBrush[ q3lvl.NumBrushes ];

			for ( int i = 0; i < q3lvl.NumBrushes; i++ )
			{
				InternalBspBrush q3brush = q3lvl.Brushes[ i ];

				// Create a new OGRE brush
				BspBrush brush = new BspBrush();
				int numBrushSides = q3brush.numSides;
				int brushSideIdx = q3brush.firstSide;

				// Iterate over the sides and create plane for each
				while ( numBrushSides-- > 0 )
				{
					InternalBspPlane side = q3lvl.Planes[ q3lvl.BrushSides[ brushSideIdx ].planeNum ];

					// Notice how we normally invert Q3A plane distances, but here we do not
					// Because we want plane normals pointing out of solid brushes, not in.
					Plane brushSide = new Plane(
						new Vector3(
							q3lvl.Planes[ q3lvl.BrushSides[ brushSideIdx ].planeNum ].normal[ 0 ],
							q3lvl.Planes[ q3lvl.BrushSides[ brushSideIdx ].planeNum ].normal[ 1 ],
							q3lvl.Planes[ q3lvl.BrushSides[ brushSideIdx ].planeNum ].normal[ 2 ]
						), q3lvl.Planes[ q3lvl.BrushSides[ brushSideIdx ].planeNum ].distance );

					brush.Planes.Add( brushSide );
					brushSideIdx++;
				}

				// Build world fragment
				brush.Fragment.FragmentType = WorldFragmentType.PlaneBoundedRegion;
				brush.Fragment.Planes = brush.Planes;

				brushes[ i ] = brush;
			}

			//-----------------------------------------------------------------------
			// Leaves
			//-----------------------------------------------------------------------
			for ( int i = 0; i < q3lvl.NumLeaves; ++i )
			{
				BspNode node = nodes[ i + this.LeafStart ];
				InternalBspLeaf q3leaf = q3lvl.Leaves[ i ];

				node.IsLeaf = true;
				node.Owner = this;

				// Set bounding box
				node.BoundingBox.Minimum = new Vector3(
					q3leaf.bbox[ 0 ],
					q3leaf.bbox[ 1 ],
					q3leaf.bbox[ 2 ]
					);
				node.BoundingBox.Maximum = new Vector3(
					q3leaf.bbox[ 3 ],
					q3leaf.bbox[ 4 ],
					q3leaf.bbox[ 5 ]
					);

				// Set faces
				node.FaceGroupStart = q3leaf.faceStart;
				node.NumFaceGroups = q3leaf.faceCount;
				node.VisCluster = q3leaf.cluster;

				// Load Brushes for this leaf
				int realBrushIdx = 0, solidIdx = 0;
				int brushCount = q3leaf.brushCount;
				int brushIdx = q3leaf.brushStart;

				node.SolidBrushes = new BspBrush[ brushCount ];

				while ( brushCount-- > 0 )
				{
					realBrushIdx = q3lvl.LeafBrushes[ brushIdx ];
					InternalBspBrush q3brush = q3lvl.Brushes[ realBrushIdx ];

					// Only load solid ones, we don't care about any other types
					// Shader determines this.
					InternalBspShader brushShader = q3lvl.Shaders[ q3brush.shaderIndex ];

					if ( ( brushShader.contentFlags & ContentFlags.Solid ) == ContentFlags.Solid )
						node.SolidBrushes[ solidIdx ] = brushes[ realBrushIdx ];

					brushIdx++;
					solidIdx++;
				}
			}

			// Vis - just copy
			visData.numClusters = q3lvl.VisData.clusterCount;
			visData.rowLength = q3lvl.VisData.rowSize;
			visData.tableData = new byte[ q3lvl.VisData.rowSize * q3lvl.VisData.clusterCount ];

			Array.Copy( q3lvl.VisData.data, 0, visData.tableData, 0, visData.tableData.Length );
		}
Пример #17
0
		protected virtual void ProcessNode( BspNode node )
		{
			if ( node.IsLeaf )
			{
				ProcessLeaf( node );
				return;
			}

			float distance = node.GetDistance( sphere.Center );

			if ( Utility.Abs( distance ) < sphere.Radius )
			{
				// Sphere crosses the plane, do both.
				ProcessNode( node.BackNode );
				ProcessNode( node.FrontNode );
			}
			else if ( distance < 0 )
			{
				// Do back.
				ProcessNode( node.BackNode );
			}
			else
			{
				// Do front.
				ProcessNode( node.FrontNode );
			}
		}
 /// <summary>
 ///		Determines if the passed in node (must also be a leaf) is visible from this leaf.
 ///	</summary>
 ///	<remarks>
 ///		Must only be called on a leaf node, and the parameter must also be a leaf node. If
 ///		this method returns true, then the leaf passed in is visible from this leaf.
 ///		Note that internally this uses the Potentially Visible Set (PVS) which is precalculated
 ///		and stored with the BSP level.
 ///	</remarks>
 public bool IsLeafVisible(BspNode leaf)
 {
     return(owner.IsLeafVisible(this, leaf));
 }
Пример #19
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 );
					}
				}
			}
		}
        /// <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;
        }