/// <summary> /// /// </summary> /// <param name="RooFile"></param> public override void ResolveIndices(RooFile RooFile) { // indices properties are not zero-based, but the arrays/lists are // get reference to parent wall if (WallReference > 0 && RooFile.Walls.Count > WallReference - 1) { Wall = RooFile.Walls[WallReference - 1]; } // get right tree child if (Right > 0 && RooFile.BSPTree.Count > Right - 1) { RightChild = RooFile.BSPTree[Right - 1]; } // get left tree child if (Left > 0 && RooFile.BSPTree.Count > Left - 1) { LeftChild = RooFile.BSPTree[Left - 1]; } }
public static void Build(RooFile Room) { if (Room == null) { return; } room = Room; if (BuildStarted != null) { BuildStarted(null, new EventArgs()); } /////////////////////////////////////////////////////////////// BoundingBox2D box = Room.GetBoundingBox2D(true); Polygon poly = new Polygon(); poly.Add(box.Min); poly.Add(box.Min + new V2(box.Max.X - box.Min.X, 0f)); poly.Add(box.Max); poly.Add(box.Max - new V2(box.Max.X - box.Min.X, 0f)); /////////////////////////////////////////////////////////////// // clean up old data from room Room.Walls.Clear(); Room.BSPTree.Clear(); foreach (RooSector sector in Room.Sectors) { sector.Walls.Clear(); sector.Sides.Clear(); } // convert roomeditor walls to roowall for (int i = 0; i < Room.WallsEditor.Count; i++) { RooWall wall = Room.WallsEditor[i].ToRooWall(RooFile.VERSIONHIGHRESGRID, Room); Room.Walls.Add(wall); } /////////////////////////////////////////////////////////////// RooBSPItem tree = BuildNode(Room.Walls, poly, 0); /////////////////////////////////////////////////////////////// FillNode(tree, Room.BSPTree); SetNums(Room.BSPTree); }
private static void FillNode(RooBSPItem Node, List <RooBSPItem> NodeList) { if (Node == null) { return; } NodeList.Add(Node); if (Node.Type == RooBSPItem.NodeType.Node) { RooPartitionLine node = (RooPartitionLine)Node; FillNode(node.RightChild, NodeList); FillNode(node.LeftChild, NodeList); } }
private static void FillNode(RooBSPItem Node, List<RooBSPItem> NodeList) { if (Node == null) return; NodeList.Add(Node); if (Node.Type == RooBSPItem.NodeType.Node) { RooPartitionLine node = (RooPartitionLine)Node; FillNode(node.RightChild, NodeList); FillNode(node.LeftChild, NodeList); } }
/// <summary> /// Collisions with wall segments for user movements using the BSP tree. /// Therefore with logarithmic rather than linear costs. /// </summary> /// <remarks> /// The algorithm goes like this (starts at root) /// (1) Get the sides of both endpoints (start, end) using the splitter (node) /// (2) If both endpoints are on the same side, there is no collision with any wall in the splitter /// and only one of the two subtrees must be visited then. /// (3) If there is an intersection with the infinite splitter, check the finite wall segments /// in the splitter for intersection. If none found, split up the vector (start, end) at the intersection /// and recusively start again for both subtrees using the just created two chunks of start vector. /// </remarks> /// <param name="Node"></param> /// <param name="Start"></param> /// <param name="End"></param> /// <param name="PlayerHeight"></param> /// <returns></returns> protected RooWall VerifyMoveTree(RooBSPItem Node, V3 Start, V2 End, Real PlayerHeight) { if (Node == null || Node.Type != RooBSPItem.NodeType.Node) return null; /*************************************************************/ RooPartitionLine line = (RooPartitionLine)Node; int side1 = Math.Sign(line.A * Start.X + line.B * Start.Z + line.C); int side2 = Math.Sign(line.A * End.X + line.B * End.Y + line.C); /*************************************************************/ // both endpoints on the same side of splitter -> no intersection // climb down only one of the two subtrees of the node if (side1 == side2 && side1 != 0) { // left if (side1 < 0) return VerifyMoveTree(line.LeftChild, Start, End, PlayerHeight); // right else return VerifyMoveTree(line.RightChild, Start, End, PlayerHeight); } /*************************************************************/ // endpoints are on different sides or both on infinite line else { RooWall wall = line.Wall; V2 intersect; V2 start2D = new V2(Start.X, Start.Z); if (wall == null) return null; /*************************************************************/ // intersect with infinite splitter LineInfiniteLineIntersectionType typ = MathUtil.IntersectLineInfiniteLine(start2D, End, wall.P1, wall.P2, out intersect); /*************************************************************/ // see if intersection with infinite splitter is on a wall segment in plane while (wall != null) { if (wall.IsBlocking(Start, End, PlayerHeight)) return wall; // loop over next wall in same plane wall = wall.NextWallInPlane; } /*************************************************************/ // no finite wallsegment in splitter plane intersects: // if vector is fully in splitter, we're done if (side1 == 0 && side2 == 0) return null; // otherwise split up the vector in left and right half // and possible check both else if (side1 < 0 && side2 > 0) { RooWall wl = VerifyMoveTree(line.LeftChild, Start, intersect, PlayerHeight); if (wl != null) return wl; else return VerifyMoveTree(line.RightChild, new V3(intersect.X, Start.Y, intersect.Y), End, PlayerHeight); } else { RooWall wl = VerifyMoveTree(line.RightChild, Start, intersect, PlayerHeight); if (wl != null) return wl; else return VerifyMoveTree(line.LeftChild, new V3(intersect.X, Start.Y, intersect.Y), End, PlayerHeight); } } }
/// <summary> /// Recursively walks the BSP tree start at given node, /// to find the subsector (leaf) containing the point. /// </summary> /// <param name="node"></param> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> protected RooBSPItem GetSubSectorAt(RooBSPItem node, Real x, Real y) { if (node == null) return null; int side; if (node.Type == RooBSPItem.NodeType.Leaf) return node; else { RooPartitionLine line = (RooPartitionLine)node; side = Math.Sign(line.A * x + line.B * y + line.C); if (side == 0) { if (line.RightChild != null) { return GetSubSectorAt(line.RightChild, x, y); } else { return GetSubSectorAt(line.LeftChild, x, y); } } else if (side > 0) { return GetSubSectorAt(line.RightChild, x, y); } else { return GetSubSectorAt(line.LeftChild, x, y); } } }
/// <summary> /// /// </summary> /// <param name="Node"></param> /// <param name="Start"></param> /// <param name="End"></param> /// <returns>True if OK, false if collision.</returns> protected bool VerifySightByTree(RooBSPItem Node, V3 Start, V3 End) { if (Node == null || Node.Type != RooBSPItem.NodeType.Node) return true; /*************************************************************/ RooPartitionLine line = (RooPartitionLine)Node; RooWall wall = line.Wall; V2 start2D = new V2(Start.X, Start.Z); V2 end2D = new V2(End.X, End.Z); /*************************************************************/ Real startDist = line.GetDistance(start2D); Real endDist = line.GetDistance(end2D); /*************************************************************/ // both endpoints on negative side if (startDist < 0.0f && endDist < 0.0f) return VerifySightByTree(line.LeftChild, Start, End); // both endpoints on positive side else if (startDist > 0.0f && endDist > 0.0f) return VerifySightByTree(line.RightChild, Start, End); // crosses infinite splitter or one or both points on splitter else { // test walls of splitter while (wall != null) { if (wall.IsBlockingSight(Start, End)) return false; // loop over next wall in same plane wall = wall.NextWallInPlane; } // must climb down both subtrees, go left first bool wl = VerifySightByTree(line.LeftChild, Start, End); // return collision if already found if (wl == false) return wl; // try other subtree otherwise else return VerifySightByTree(line.RightChild, Start, End); } }
/// <summary> /// Collisions with wall segments for user movements using the BSP tree. /// Therefore with logarithmic rather than linear costs. /// </summary> /// <param name="Node"></param> /// <param name="Start"></param> /// <param name="End"></param> /// <param name="PlayerHeight"></param> /// <param name="IgnoreWall"></param> /// <returns></returns> protected RooWall VerifyMoveByTree(RooBSPItem Node, V3 Start, V2 End, Real PlayerHeight, RooWall IgnoreWall = null) { if (Node == null || Node.Type != RooBSPItem.NodeType.Node) return null; /*************************************************************/ RooPartitionLine line = (RooPartitionLine)Node; RooWall wall = line.Wall; V2 start2D = new V2(Start.X, Start.Z); /*************************************************************/ // check node boundingbox if (!line.BoundingBox.IsInside(End, GeometryConstants.WALLMINDISTANCE) && !line.BoundingBox.IsInside(start2D, GeometryConstants.WALLMINDISTANCE)) return null; /*************************************************************/ // test walls of splitter while (wall != null) { if (wall != IgnoreWall && wall.IsBlockingMove(Start, End, PlayerHeight)) return wall; // loop over next wall in same plane wall = wall.NextWallInPlane; } /*************************************************************/ RooWall wl = VerifyMoveByTree(line.LeftChild, Start, End, PlayerHeight, IgnoreWall); if (wl != null) return wl; else return VerifyMoveByTree(line.RightChild, Start, End, PlayerHeight, IgnoreWall); }