/// <summary> /// Adds all triangles of node which intersect b to list result, returns exact intersection points in /// intersectionPoints. the function closestPointInTriangle() is used for computing triangle-sphere /// intersections. /// </summary> /// <param name="b"></param> /// <param name="node"></param> /// <param name="result"></param> private void nodeCollisions(BoundingSphere b, BspNode node, LinkedList <Face> result, LinkedList <Vector3> intersectionPoints) { float squaredBoundingSphereRadius = b.Radius * b.Radius; foreach (Face face in node.faces) { Vector3 pos1, pos2, pos3; pos1 = mLevelData.vertices[face.v1]; pos2 = mLevelData.vertices[face.v2]; pos3 = mLevelData.vertices[face.v3]; // check collision with triangle bounding sphere first if (b.Intersects(face.boundingSphere) == false) { continue; } Vector3 closestPoint = closestPointInTriangle(b.Center, pos1, pos2, pos3); float squaredDist = (closestPoint - b.Center).LengthSquared(); if (squaredDist <= squaredBoundingSphereRadius) { result.AddLast(face); intersectionPoints.AddLast(closestPoint); } } }
/// <summary> /// Recursive tree building procedure: reads separating planes from the file, builds up /// the tree in preorder. /// </summary> /// <param name="node">Node we are currently processing</param> /// <param name="file">Binary file to read separating planes from</param> /// <param name="numPlanesRemaining">Number of planes remaining</param> private void buildTree(ref BspNode node, BinaryReader file, ref uint numPlanesRemaining) { // no more planes remaining? append leaf node, stop recursion. if (numPlanesRemaining == 0) { node = new BspNode(); node.faces = new List <Face>(); return; } // read separating plane data double x = file.ReadDouble(); double y = file.ReadDouble(); double z = file.ReadDouble(); double d = file.ReadDouble(); // one plane less to read numPlanesRemaining--; // did we reach a leaf? // in this case: create leaf node and stop this recursion on this branch if (x == 0.0 && y == 0.0 && z == 0.0 && d == 0.0) { node = new BspNode(); node.faces = new List <Face>(); return; } // neither finished, nor leaf reached: create a new node, start two new // recursions to the left and right of the tree node = new BspNode(new Plane((float)x, (float)y, (float)z, (float)d)); buildTree(ref node.pos, file, ref numPlanesRemaining); buildTree(ref node.neg, file, ref numPlanesRemaining); }
/// <summary> /// Inserts the faces in the level geometry into the leafs of the tree. If a face /// intersects a separating plane,it is passed down on both sides of the plane. /// </summary> private void insertGeometryData() { // for each face foreach (Face face in mLevelData.faces) { Vector3 pos1 = mLevelData.vertices[face.v1]; Vector3 pos2 = mLevelData.vertices[face.v2]; Vector3 pos3 = mLevelData.vertices[face.v3]; // create a list of nodes we have to process on our way to the leafs of the tree LinkedList <BspNode> toProcess = new LinkedList <BspNode>(); toProcess.AddFirst(mRoot); // as long as we have nodes to process while (toProcess.Count > 0) { // take first node BspNode curNode = toProcess.First.Value; toProcess.RemoveFirst(); // not leaf? propagate face to correct child node. (both nodes in case of intersection) if (curNode.separatingPlane.Normal.X != 0.0f || curNode.separatingPlane.Normal.Y != 0.0f || curNode.separatingPlane.Normal.Z != 0.0f || curNode.separatingPlane.D != 0.0f) { // compute side each triangle vertex lies on float side1 = Vector3.Dot(curNode.separatingPlane.Normal, pos1) + curNode.separatingPlane.D; float side2 = Vector3.Dot(curNode.separatingPlane.Normal, pos2) + curNode.separatingPlane.D; float side3 = Vector3.Dot(curNode.separatingPlane.Normal, pos3) + curNode.separatingPlane.D; if (side1 > 0 && side2 > 0 && side3 > 0) { // all points on positive side? propagate to positive side toProcess.AddLast(curNode.pos); } else if (side1 <= 0 && side2 <= 0 && side3 <= 0) { // all points on negative side? propagate to negative side toProcess.AddLast(curNode.neg); } else { // no luck, triangle intersects plane, we have to propagate it on both sides toProcess.AddLast(curNode.pos); toProcess.AddLast(curNode.neg); } } else { curNode.faces.Add(face); } } } }
/// <summary> /// Returns the raw number of triangles actually checked for collision /// (all triangles in all nodes that are reached) /// </summary> /// <param name="b"></param> /// <param name="result"></param> public void checkedFaces(BoundingSphere b, LinkedList <Face> result) { LinkedList <BspNode> toProcess = new LinkedList <BspNode>(); toProcess.AddLast(mRoot); while (toProcess.Count > 0) { BspNode curNode = toProcess.First.Value; toProcess.RemoveFirst(); if (curNode.separatingPlane.Normal.X == 0.0f && curNode.separatingPlane.Normal.Y == 0.0f && curNode.separatingPlane.Normal.Z == 0.0f && curNode.separatingPlane.D == 0.0f) { foreach (Face f in curNode.faces) { result.AddLast(f); } } else { PlaneIntersectionType side = curNode.separatingPlane.Intersects(b); if (side == PlaneIntersectionType.Back) { toProcess.AddLast(curNode.neg); } else if (side == PlaneIntersectionType.Front) { toProcess.AddLast(curNode.pos); } else { toProcess.AddLast(curNode.pos); toProcess.AddLast(curNode.neg); } } } }
/// <summary> /// Computes all triangles colliding with the given bounding sphere /// </summary> /// <param name="b">Bounding sphere to be checked for collision</param> /// <param name="collidingFaces">Colliding faces are added to this list (may contain duplicates!)</param> /// <param name="collisionPoints">For each colliding face, the exact collision points is added to this list</param> public void collisions(BoundingSphere b, LinkedList <Face> collidingFaces, LinkedList <Vector3> collisionPoints) { LinkedList <BspNode> toProcess = new LinkedList <BspNode>(); toProcess.AddLast(mRoot); while (toProcess.Count > 0) { BspNode curNode = toProcess.First.Value; toProcess.RemoveFirst(); // have we reached a leaf? check all triangles in leaf for collisions if (curNode.separatingPlane.Normal.X == 0.0f && curNode.separatingPlane.Normal.Y == 0.0f && curNode.separatingPlane.Normal.Z == 0.0f && curNode.separatingPlane.D == 0.0f) { nodeCollisions(b, curNode, collidingFaces, collisionPoints); } else { // propagate bounding sphere down the tree PlaneIntersectionType side = curNode.separatingPlane.Intersects(b); if (side == PlaneIntersectionType.Back) { toProcess.AddLast(curNode.neg); } else if (side == PlaneIntersectionType.Front) { toProcess.AddLast(curNode.pos); } else { toProcess.AddLast(curNode.pos); toProcess.AddLast(curNode.neg); } } } }
/// <summary> /// Adds all triangles of node which intersect b to list result, returns exact intersection points in /// intersectionPoints. the function closestPointInTriangle() is used for computing triangle-sphere /// intersections. /// </summary> /// <param name="b"></param> /// <param name="node"></param> /// <param name="result"></param> private void nodeCollisions(BoundingSphere b, BspNode node, LinkedList<Face> result, LinkedList<Vector3> intersectionPoints) { float squaredBoundingSphereRadius = b.Radius * b.Radius; foreach (Face face in node.faces) { Vector3 pos1, pos2, pos3; pos1 = mLevelData.vertices[face.v1]; pos2 = mLevelData.vertices[face.v2]; pos3 = mLevelData.vertices[face.v3]; // check collision with triangle bounding sphere first if (b.Intersects(face.boundingSphere) == false) continue; Vector3 closestPoint = closestPointInTriangle(b.Center, pos1, pos2, pos3); float squaredDist = (closestPoint - b.Center).LengthSquared(); if (squaredDist <= squaredBoundingSphereRadius) { result.AddLast(face); intersectionPoints.AddLast(closestPoint); } } }
/// <summary> /// Recursive tree building procedure: reads separating planes from the file, builds up /// the tree in preorder. /// </summary> /// <param name="node">Node we are currently processing</param> /// <param name="file">Binary file to read separating planes from</param> /// <param name="numPlanesRemaining">Number of planes remaining</param> private void buildTree(ref BspNode node, BinaryReader file, ref uint numPlanesRemaining) { // no more planes remaining? append leaf node, stop recursion. if (numPlanesRemaining == 0) { node = new BspNode(); node.faces = new List<Face>(); return; } // read separating plane data double x = file.ReadDouble(); double y = file.ReadDouble(); double z = file.ReadDouble(); double d = file.ReadDouble(); // one plane less to read numPlanesRemaining--; // did we reach a leaf? // in this case: create leaf node and stop this recursion on this branch if (x == 0.0 && y == 0.0 && z == 0.0 && d == 0.0) { node = new BspNode(); node.faces = new List<Face>(); return; } // neither finished, nor leaf reached: create a new node, start two new // recursions to the left and right of the tree node = new BspNode(new Plane((float)x, (float)y, (float)z, (float)d)); buildTree(ref node.pos, file, ref numPlanesRemaining); buildTree(ref node.neg, file, ref numPlanesRemaining); }