private static RooWall ChooseSplitter(IEnumerable <RooWall> Walls) { RooWall best_splitter = null; int best_count = -1; // Minimum # of walls in bigger side of best wall int best_splits = 999999; // # of splits for best root so far int max_count; int side0, side1; foreach (RooWall splitter in Walls) { int pos = 0; int neg = 0; int splits = 0; foreach (RooWall wall in Walls) { side0 = wall.P1.GetSide(splitter.P1, splitter.P2); side1 = wall.P2.GetSide(splitter.P1, splitter.P2); // If both on same side, or one is on line, no split needed if (side0 * side1 >= 0) { // In plane of root? if (side0 == 0 && side1 == 0) { continue; } // On + side of root? if (side0 > 0 || side1 > 0) { pos++; continue; } // On - side of root neg++; continue; } // Split--one on each side pos++; neg++; splits++; } max_count = Math.Max(pos, neg); if (max_count < best_count || (max_count == best_count && splits < best_splits) || best_count == -1) { best_count = max_count; best_splitter = splitter; best_splits = splits; } } return(best_splitter); }
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 SetNums(List <RooBSPItem> Nodes) { int idx; RooPartitionLine splitter; // set wall nums for (int i = 0; i < room.Walls.Count; i++) { RooWall wall = room.Walls[i]; wall.Num = i + 1; if (wall.NextWallInPlane != null) { // set 1-based num (0=unset) idx = room.Walls.IndexOf(wall.NextWallInPlane); wall.NextWallNumInPlane = (short)((idx >= 0) ? idx + 1 : 0); } } foreach (RooBSPItem node in Nodes) { if (node.Type == RooBSPItem.NodeType.Node) { splitter = (RooPartitionLine)node; if (splitter.RightChild != null) { idx = Nodes.IndexOf(splitter.RightChild); // set 1-based num (0=unset) splitter.Right = (ushort)((idx >= 0) ? idx + 1 : 0); } if (splitter.LeftChild != null) { idx = Nodes.IndexOf(splitter.LeftChild); // set 1-based num (0=unset) splitter.Left = (ushort)((idx >= 0) ? idx + 1 : 0); } if (splitter.Wall != null) { idx = room.Walls.IndexOf(splitter.Wall); // set 1-based num (0=unset) splitter.WallReference = (ushort)((idx >= 0) ? idx + 1 : 0); } } } }
/// <summary> /// Creates a RooWall instance based on this RooWallEditor instance. /// </summary> /// <param name="RooVersion"></param> /// <param name="Room"></param> /// <returns></returns> public RooWall ToRooWall(uint RooVersion, RooFile Room) { if (Room == null) { return(null); } V2 q1, q2; // first try get boundingbox as defined by 'Things' BoundingBox2D box = Room.GetBoundingBox2DFromThings(); // no thingsbox? build based on editorwalls if (box == BoundingBox2D.NULL) { box = Room.GetBoundingBox2D(false); } // 1) Convert from 1:64 to 1:1024 // 2) Modify coordinate system (y-axis different) q1.X = (P0.X - box.Min.X) * 16f; q1.Y = (box.Max.Y - P0.Y) * 16f; q2.X = (P1.X - box.Min.X) * 16f; q2.Y = (box.Max.Y - P1.Y) * 16f; // sidenum in editorwall is 0 to n ( 0=unset) // sectnum in editorwall is -1 to n (-1=unset) RooWall wall = new RooWall( RooVersion, 0, (ushort)this.FileSideDef1, // no +1 (ushort)this.FileSideDef2, // no +1 q1, q2, Side1XOffset, Side2XOffset, Side1YOffset, Side2YOffset, (ushort)(Side1Sector + 1), // +1 mapping (ushort)(Side2Sector + 1)); // +1 mapping // now resolve the object references from indices // and fill in heights wall.ResolveIndices(Room); wall.CalculateWallSideHeights(); // done return(wall); }
/// <summary> /// Splits this wall into two using infinite line given by Q1Q2. /// </summary> /// <param name="Q1"></param> /// <param name="Q2"></param> /// <returns>Item1: P1 to I. Item2: I to P2</returns> public Tuple<RooWall, RooWall> Split(V2 Q1, V2 Q2) { V2 intersect; // intersect this wall (finite line) with the infinite line given by Q1Q2 LineInfiniteLineIntersectionType intersecttype = MathUtil.IntersectLineInfiniteLine(P1, P2, Q1, Q2, out intersect); // must have a real intersection, not only boundarypoint or even coincide if (intersecttype != LineInfiniteLineIntersectionType.OneIntersection) return null; /*****************************************************************/ // 1) Piece from P1 to intersection RooWall wall1 = new RooWall( RooVersion, NextWallNumInPlane, RightSideNum, LeftSideNum, P1, intersect, RightXOffset, // readjust below LeftXOffset, // readjust below RightYOffset, // readjust below LeftYOffset, // readjust below RightSectorNum, LeftSectorNum ); // also keep references of old wall wall1.RightSector = RightSector; wall1.LeftSector = LeftSector; wall1.RightSide = RightSide; wall1.LeftSide = LeftSide; wall1.BowtieFlags = BowtieFlags; wall1.CalculateWallSideHeights(); /*****************************************************************/ // 2) Piece from intersection to P2 RooWall wall2 = new RooWall( RooVersion, NextWallNumInPlane, RightSideNum, LeftSideNum, intersect, P2, RightXOffset, // readjust below LeftXOffset, // readjust below RightYOffset, // readjust below LeftYOffset, // readjust below RightSectorNum, LeftSectorNum ); // also keep references of old wall wall2.RightSector = RightSector; wall2.LeftSector = LeftSector; wall2.RightSide = RightSide; wall2.LeftSide = LeftSide; wall2.BowtieFlags = BowtieFlags; wall2.CalculateWallSideHeights(); /*****************************************************************/ // 3) Readjust texture offsets to accoutn for split // RightSide if (wall1.RightSide != null && wall1.RightSide.Flags.IsBackwards) wall1.RightXOffset += (short)wall2.ClientLength; else wall2.RightXOffset += (short)wall1.ClientLength; // LeftSide (Do this backwards, because client exchanges vertices of negative walls) if (wall1.LeftSide != null && wall1.LeftSide.Flags.IsBackwards) wall2.LeftXOffset += (short)wall1.ClientLength; else wall1.LeftXOffset += (short)wall2.ClientLength; /*****************************************************************/ return new Tuple<RooWall, RooWall>(wall1, wall2); }
/// <summary> /// Creates object references from the Sector/Side Num references. /// </summary> /// <param name="RooFile"></param> public void ResolveIndices(RooFile RooFile) { // num properties are not zero-based, but the arrays are // get reference to right SectorDef if (RightSectorNum > 0 && RooFile.Sectors.Count > RightSectorNum - 1) { RightSector = RooFile.Sectors[RightSectorNum - 1]; // save as adjacent wall if (!RightSector.Walls.Contains(this)) RightSector.Walls.Add(this); } // get reference to left SectorDef if (LeftSectorNum > 0 && RooFile.Sectors.Count > LeftSectorNum - 1) { LeftSector = RooFile.Sectors[LeftSectorNum - 1]; // save as adjacent wall if (!LeftSector.Walls.Contains(this)) LeftSector.Walls.Add(this); } // get reference to right SideDef if (RightSideNum > 0 && RooFile.SideDefs.Count > RightSideNum - 1) { RightSide = RooFile.SideDefs[RightSideNum - 1]; // save as adjacent side if (RightSector != null && !RightSector.Sides.Contains(RightSide)) RightSector.Sides.Add(RightSide); // save as adjacent side if (LeftSector != null && !LeftSector.Sides.Contains(RightSide)) LeftSector.Sides.Add(RightSide); } // get reference to left SideDef if (LeftSideNum > 0 && RooFile.SideDefs.Count > LeftSideNum - 1) { LeftSide = RooFile.SideDefs[LeftSideNum - 1]; // save as adjacent side if (RightSector != null && !RightSector.Sides.Contains(LeftSide)) RightSector.Sides.Add(LeftSide); // save as adjacent side if (LeftSector != null && !LeftSector.Sides.Contains(LeftSide)) LeftSector.Sides.Add(LeftSide); } // get reference to next wall in same plane if (NextWallNumInPlane > 0 && RooFile.Walls.Count > NextWallNumInPlane - 1) { NextWallInPlane = RooFile.Walls[NextWallNumInPlane - 1]; } }
private static RooBSPItem BuildNode(List <RooWall> Walls, Polygon Polygon, int Sector) { if (Polygon.Count == 0 || (Walls.Count == 0 && Sector == 0)) { return(null); } Polygon.RemoveZeroEdges(); if (!Polygon.IsConvexPolygon()) { if (FoundNonConvexPolygon != null) { FoundNonConvexPolygon(null, new PolygonEventArgs(Polygon)); } //return null; // WTF ? } // No walls left ==> leaf if (Walls.Count == 0) { RooSubSector leaf = new RooSubSector(RooFile.VERSIONHIGHRESGRID, (ushort)Sector, Polygon); // fills in sector reference leaf.ResolveIndices(room); return(leaf); } // get best splitter of remaining walls RooWall splitter = ChooseSplitter(Walls); if (splitter == null) { return(null); // WTF ? } // split up walls into right/left Tuple <List <RooWall>, List <RooWall> > splitWalls = SplitWalls(Walls, splitter); // split up polygon into right/left Tuple <Polygon, Polygon> splitPolygons = Polygon.SplitConvexPolygon(splitter.P1, splitter.P2); Real a, b, c; GetLineEquation2DCoefficients(splitter.P1, splitter.P2, out a, out b, out c); // create new splitter node RooPartitionLine node = new RooPartitionLine(RooFile.VERSIONHIGHRESGRID, Polygon.GetBoundingBox(), a, b, c, 0, 0, (ushort)splitter.Num); // fills in wall reference node.Wall = splitter; // recursively descend to children node.LeftChild = BuildNode(splitWalls.Item1, splitPolygons.Item1, splitter.LeftSectorNum); node.RightChild = BuildNode(splitWalls.Item2, splitPolygons.Item2, splitter.RightSectorNum); return(node); }
private static Tuple <List <RooWall>, List <RooWall> > SplitWalls(List <RooWall> Walls, RooWall Splitter) { int side0, side1; List <RooWall> wallsRight = new List <RooWall>(); List <RooWall> wallsLeft = new List <RooWall>(); // backwards due to removing items for (int i = Walls.Count - 1; i >= 0; i--) { RooWall wall = Walls[i]; if (wall == Splitter) { continue; } side0 = wall.P1.GetSide(Splitter.P1, Splitter.P2); side1 = wall.P2.GetSide(Splitter.P1, Splitter.P2); // coincide = // both endpoints on wall if (side0 == 0 && side1 == 0) { // attach at end of linked list if not already included bool add = true; RooWall next = Splitter; while (next.NextWallInPlane != null) { // already in there if (next.NextWallInPlane == wall) { add = false; break; } next = next.NextWallInPlane; } if (add) { next.NextWallInPlane = wall; } continue; } // on right side = // both endpoints right side, or one right, one on line if (side0 <= 0 && side1 <= 0) { wallsRight.Add(wall); continue; } // on left side // both endpoints left side, or one left, one on line if (side0 >= 0 && side1 >= 0) { wallsLeft.Add(wall); continue; } // intersection - split the wall Tuple <RooWall, RooWall> splitWall = wall.Split(Splitter.P1, Splitter.P2); // remove old wall and add new chunks // second first so first will be first int idx = room.Walls.IndexOf(wall); room.Walls.RemoveAt(idx); room.Walls.Insert(idx, splitWall.Item2); room.Walls.Insert(idx, splitWall.Item1); if (splitWall != null) { // if p1 was on right side and p2 on left side // add the segment p1 to intersect to right walls // add the segment intersect to p2 to left walls if (side0 < 0 && side1 > 0) { wallsRight.Add(splitWall.Item1); wallsLeft.Add(splitWall.Item2); } // other way round else { wallsLeft.Add(splitWall.Item1); wallsRight.Add(splitWall.Item2); } } else { return(null); // WTF? } } return(new Tuple <List <RooWall>, List <RooWall> >(wallsRight, wallsLeft)); }
protected void DrawWall(Graphics G, RooWall Wall, Pen Pen, bool Infinite, Pen PenInfinite) { V2 p1p2; // transform points to match world of pixeldrawing float transx1 = (float)(Wall.P1.X - boxMin.X) * ZoomInv; float transy1 = (float)(Wall.P1.Y - boxMin.Y) * ZoomInv; float transx2 = (float)(Wall.P2.X - boxMin.X) * ZoomInv; float transy2 = (float)(Wall.P2.Y - boxMin.Y) * ZoomInv; // draw extensions of line if (Infinite) { V2 p1 = new V2(transx1, transy1); V2 p2 = new V2(transx2, transy2); p1p2 = p2 - p1; p2 += 1000f * p1p2; p1 -= 1000f * p1p2; G.DrawLine(PenInfinite, (float)p1.X, (float)p1.Y, (float)p2.X, (float)p2.Y); } // check if this is an issue line (almost horizontal or vertical, but not fully) p1p2 = Wall.GetP1P2(); if (chkVertHortLines.Checked && p1p2.X != 0.0f && p1p2.Y != 0.0f) { Real m = (Real)(p1p2.Y / p1p2.X); if ((m > -0.125f && m < 0.125f) || (m > 8.0f || m < -8.0f)) Pen = penRed2; } // draw line G.DrawLine(Pen, transx1, transy1, transx2, transy2); }
private static Tuple<List<RooWall>, List<RooWall>> SplitWalls(List<RooWall> Walls, RooWall Splitter) { int side0, side1; List<RooWall> wallsRight = new List<RooWall>(); List<RooWall> wallsLeft = new List<RooWall>(); // backwards due to removing items for(int i = Walls.Count - 1 ; i >= 0; i--) { RooWall wall = Walls[i]; if (wall == Splitter) continue; side0 = wall.P1.GetSide(Splitter.P1, Splitter.P2); side1 = wall.P2.GetSide(Splitter.P1, Splitter.P2); // coincide = // both endpoints on wall if (side0 == 0 && side1 == 0) { // attach at end of linked list if not already included bool add = true; RooWall next = Splitter; while (next.NextWallInPlane != null) { // already in there if (next.NextWallInPlane == wall) { add = false; break; } next = next.NextWallInPlane; } if (add) next.NextWallInPlane = wall; continue; } // on right side = // both endpoints right side, or one right, one on line if (side0 <= 0 && side1 <= 0) { wallsRight.Add(wall); continue; } // on left side // both endpoints left side, or one left, one on line if (side0 >= 0 && side1 >= 0) { wallsLeft.Add(wall); continue; } // intersection - split the wall Tuple<RooWall, RooWall> splitWall = wall.Split(Splitter.P1, Splitter.P2); // remove old wall and add new chunks // second first so first will be first int idx = room.Walls.IndexOf(wall); room.Walls.RemoveAt(idx); room.Walls.Insert(idx, splitWall.Item2); room.Walls.Insert(idx, splitWall.Item1); if (splitWall != null) { // if p1 was on right side and p2 on left side // add the segment p1 to intersect to right walls // add the segment intersect to p2 to left walls if (side0 < 0 && side1 > 0) { wallsRight.Add(splitWall.Item1); wallsLeft.Add(splitWall.Item2); } // other way round else { wallsLeft.Add(splitWall.Item1); wallsRight.Add(splitWall.Item2); } } else return null; // WTF? } return new Tuple<List<RooWall>, List<RooWall>>(wallsRight, wallsLeft); }
public int ReadFrom(byte[] Buffer, int StartIndex = 0) { int cursor = StartIndex; uint signature = BitConverter.ToUInt32(Buffer, cursor); cursor += TypeSizes.INT; if (signature == SIGNATURE) { RooVersion = BitConverter.ToUInt32(Buffer, cursor); cursor += TypeSizes.INT; // supported version ? if (RooVersion >= RooFile.MINVERSION) { Challenge = BitConverter.ToUInt32(Buffer, cursor); cursor += TypeSizes.INT; OffsetClient = BitConverter.ToUInt32(Buffer, cursor); cursor += TypeSizes.INT; OffsetServer = BitConverter.ToInt32(Buffer, cursor); cursor += TypeSizes.INT; } else throw new Exception("RooVersion too old, got: " + RooVersion + " Expected greater " + RooFile.MINVERSION); } else throw new Exception("Wrong file signature: " + signature + " (expected " + SIGNATURE + ")."); // // Client part (was encrypted once) // // Check if file is encrypted if (BitConverter.ToUInt32(Buffer, cursor) == ENCRYPTIONFLAG) { #if WINCLR && X86 EncryptionEnabled = true; cursor += TypeSizes.INT; // get additional values for decryption EncryptedStreamLength = BitConverter.ToInt32(Buffer, cursor); cursor += TypeSizes.INT; ExpectedResponse = BitConverter.ToUInt32(Buffer, cursor); cursor += TypeSizes.INT; Crush32.Decrypt(Buffer, cursor, EncryptedStreamLength, Challenge, ExpectedResponse, GetPassword()); #else throw new Exception(ERRORCRUSHPLATFORM); #endif } else EncryptionEnabled = false; // Get the basic infos, like room dimension and section offsets RoomSizeX = BitConverter.ToUInt32(Buffer, cursor); cursor += TypeSizes.INT; RoomSizeY = BitConverter.ToUInt32(Buffer, cursor); cursor += TypeSizes.INT; OffsetBSPTree = BitConverter.ToInt32(Buffer, cursor); cursor += TypeSizes.INT; OffsetWalls = BitConverter.ToInt32(Buffer, cursor); cursor += TypeSizes.INT; OffsetWallsEditor = BitConverter.ToInt32(Buffer, cursor); cursor += TypeSizes.INT; OffsetSideDefs = BitConverter.ToInt32(Buffer, cursor); cursor += TypeSizes.INT; OffsetSectors = BitConverter.ToInt32(Buffer, cursor); cursor += TypeSizes.INT; OffsetThings = BitConverter.ToInt32(Buffer, cursor); cursor += TypeSizes.INT; // used to save section item counts ushort len; // Section 1: BSP-Tree cursor = OffsetBSPTree + (Convert.ToByte(EncryptionEnabled) * 12); len = BitConverter.ToUInt16(Buffer, cursor); cursor += TypeSizes.SHORT; BSPTree = new List<RooBSPItem>(len); for (int i = 0; i < len; i++) { RooBSPItem bspItem = RooBSPItem.ExtractBSPItem(RooVersion, Buffer, cursor); cursor += bspItem.ByteLength; BSPTree.Add(bspItem); } // Section 2: Walls cursor = OffsetWalls + (Convert.ToByte(EncryptionEnabled) * 12); len = BitConverter.ToUInt16(Buffer, cursor); cursor += TypeSizes.SHORT; Walls = new List<RooWall>(len); for (int i = 0; i < len; i++) { RooWall lineDef = new RooWall(RooVersion, Buffer, cursor); cursor += lineDef.ByteLength; lineDef.Num = i + 1; Walls.Add(lineDef); } // Section 3: WallsEditor cursor = OffsetWallsEditor + (Convert.ToByte(EncryptionEnabled) * 12); len = BitConverter.ToUInt16(Buffer, cursor); cursor += TypeSizes.SHORT; WallsEditor = new List<RooWallEditor>(len); for (int i = 0; i < len; i++) { RooWallEditor wallEditor = new RooWallEditor(Buffer, cursor); cursor += wallEditor.ByteLength; wallEditor.Num = i + 1; WallsEditor.Add(wallEditor); } // Section 4: SideDefs cursor = OffsetSideDefs + (Convert.ToByte(EncryptionEnabled) * 12); len = BitConverter.ToUInt16(Buffer, cursor); cursor += TypeSizes.SHORT; SideDefs = new List<RooSideDef>(len); for (int i = 0; i < len; i++) { RooSideDef sideDef = new RooSideDef(Buffer, cursor); cursor += sideDef.ByteLength; sideDef.Num = i + 1; sideDef.TextureChanged += OnSideTextureChanged; SideDefs.Add(sideDef); } // Section 5: Sectors cursor = OffsetSectors + (Convert.ToByte(EncryptionEnabled) * 12); len = BitConverter.ToUInt16(Buffer, cursor); cursor += TypeSizes.SHORT; bool hasSpeed = (RooVersion >= VERSIONSPEED); Sectors = new List<RooSector>(len); for (int i = 0; i < len; i++) { RooSector sectorDef = new RooSector(RooVersion, Buffer, cursor, hasSpeed); cursor += sectorDef.ByteLength; sectorDef.Num = i + 1; sectorDef.TextureChanged += OnSectorTextureChanged; sectorDef.Moved += OnSectorMoved; Sectors.Add(sectorDef); } // Section 6: Things cursor = OffsetThings + (Convert.ToByte(EncryptionEnabled) * 12); len = BitConverter.ToUInt16(Buffer, cursor); cursor += TypeSizes.SHORT; Things = new List<RooThing>(len); if (len > 2) { for (int i = 0; i < len; i++) { RooThingExtended rooThing = new RooThingExtended(Buffer, cursor); cursor += rooThing.ByteLength; Things.Add(rooThing); } } else { for (int i = 0; i < len; i++) { RooThing rooThing = new RooThing(Buffer, cursor); cursor += rooThing.ByteLength; Things.Add(rooThing); } } // some older maps don't have the roomid // so if we've already reached the serveroffset, set it to 0 if (cursor == OffsetServer) { RoomID = 0; } else { // get roomid RoomID = BitConverter.ToInt32(Buffer, cursor); cursor += TypeSizes.INT; } // // Server part // // load grids Grids = new RooGrids(RooVersion, Buffer, cursor); cursor += Grids.ByteLength; return cursor - StartIndex; }
public unsafe void ReadFrom(ref byte* Buffer) { // save the startptr, we need it later byte* readStartPtr = Buffer; uint signature = *((uint*)Buffer); Buffer += TypeSizes.INT; if (signature == SIGNATURE) { RooVersion = *((uint*)Buffer); Buffer += TypeSizes.INT; // supported version ? if (RooVersion >= RooFile.MINVERSION) { Challenge = *((uint*)Buffer); Buffer += TypeSizes.INT; OffsetClient = *((uint*)Buffer); Buffer += TypeSizes.INT; OffsetServer = *((int*)Buffer); Buffer += TypeSizes.INT; } else throw new Exception("RooVersion too old, got: " + RooVersion + " Expected greater " + RooFile.MINVERSION); } else throw new Exception("Wrong file signature: " + signature + " (expected " + SIGNATURE + ")."); // // Client part (was encrypted once) // // Check if file is encrypted if (*((uint*)Buffer) == ENCRYPTIONFLAG) { #if WINCLR && X86 EncryptionEnabled = true; Buffer += TypeSizes.INT; // get additional values for decryption EncryptedStreamLength = *((int*)Buffer); Buffer += TypeSizes.INT; ExpectedResponse = *((uint*)Buffer); Buffer += TypeSizes.INT; Crush32.Decrypt(Buffer, EncryptedStreamLength, Challenge, ExpectedResponse, GetPassword()); #else throw new Exception(ERRORCRUSHPLATFORM); #endif } else EncryptionEnabled = false; // Get the basic infos, like room dimension and section offsets RoomSizeX = *((uint*)Buffer); Buffer += TypeSizes.INT; RoomSizeY = *((uint*)Buffer); Buffer += TypeSizes.INT; OffsetBSPTree = *((int*)Buffer); Buffer += TypeSizes.INT; OffsetWalls = *((int*)Buffer); Buffer += TypeSizes.INT; OffsetWallsEditor = *((int*)Buffer); Buffer += TypeSizes.INT; OffsetSideDefs = *((int*)Buffer); Buffer += TypeSizes.INT; OffsetSectors = *((int*)Buffer); Buffer += TypeSizes.INT; OffsetThings = *((int*)Buffer); Buffer += TypeSizes.INT; // used to save section item counts ushort len; // Section 1: BSP-Tree len = *((ushort*)Buffer); Buffer += TypeSizes.SHORT; BSPTree = new List<RooBSPItem>(len); for (int i = 0; i < len; i++) { RooBSPItem bspItem = RooBSPItem.ExtractBSPItem(RooVersion, ref Buffer); BSPTree.Add(bspItem); } // Section 2: Walls len = *((ushort*)Buffer); Buffer += TypeSizes.SHORT; Walls = new List<RooWall>(len); for (int i = 0; i < len; i++) { RooWall lineDef = new RooWall(RooVersion, ref Buffer); lineDef.Num = i + 1; Walls.Add(lineDef); } // Section 3: WallsEditor len = *((ushort*)Buffer); Buffer += TypeSizes.SHORT; WallsEditor = new List<RooWallEditor>(len); for (int i = 0; i < len; i++) { RooWallEditor wallEditor = new RooWallEditor(ref Buffer); wallEditor.Num = i + 1; WallsEditor.Add(wallEditor); } // Section 4: SideDefs len = *((ushort*)Buffer); Buffer += TypeSizes.SHORT; SideDefs = new List<RooSideDef>(len); for (int i = 0; i < len; i++) { RooSideDef sideDef = new RooSideDef(ref Buffer); sideDef.Num = i + 1; sideDef.TextureChanged += OnSideTextureChanged; SideDefs.Add(sideDef); } // Section 5: Sectors len = *((ushort*)Buffer); Buffer += TypeSizes.SHORT; bool hasSpeed = (RooVersion >= VERSIONSPEED); Sectors = new List<RooSector>(len); for (int i = 0; i < len; i++) { RooSector sectorDef = new RooSector(RooVersion, ref Buffer, hasSpeed); sectorDef.TextureChanged += OnSectorTextureChanged; sectorDef.Moved += OnSectorMoved; sectorDef.Num = i + 1; Sectors.Add(sectorDef); } // Section 6: Things // There is old and new variant of "thing" len = *((ushort*)Buffer); Buffer += TypeSizes.SHORT; Things = new List<RooThing>(len); if (len > 2) { for (int i = 0; i < len; i++) Things.Add(new RooThingExtended(ref Buffer)); } else { for (int i = 0; i < len; i++) Things.Add(new RooThing(ref Buffer)); } // some older maps don't have the roomid // so if we've already reached the serveroffset, set it to 0 if (Buffer - readStartPtr == OffsetServer) { RoomID = 0; } else { // roomid RoomID = *((int*)Buffer); Buffer += TypeSizes.INT; } // // Server part // Grids = new RooGrids(RooVersion, ref Buffer); }
/// <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); }
/// <summary> /// Creates a RooWall instance based on this RooWallEditor instance. /// </summary> /// <param name="RooVersion"></param> /// <param name="Room"></param> /// <returns></returns> public RooWall ToRooWall(uint RooVersion, RooFile Room) { if (Room == null) return null; V2 q1, q2; // first try get boundingbox as defined by 'Things' BoundingBox2D box = Room.GetBoundingBox2DFromThings(); // no thingsbox? build based on editorwalls if (box == BoundingBox2D.NULL) box = Room.GetBoundingBox2D(false); // 1) Convert from 1:64 to 1:1024 // 2) Modify coordinate system (y-axis different) q1.X = (P0.X - box.Min.X) * 16f; q1.Y = (box.Max.Y - P0.Y) * 16f; q2.X = (P1.X - box.Min.X) * 16f; q2.Y = (box.Max.Y - P1.Y) * 16f; // sidenum in editorwall is 0 to n ( 0=unset) // sectnum in editorwall is -1 to n (-1=unset) RooWall wall = new RooWall( RooVersion, 0, (ushort)this.FileSideDef1, // no +1 (ushort)this.FileSideDef2, // no +1 q1, q2, Side1XOffset, Side2XOffset, Side1YOffset, Side2YOffset, (ushort)(Side1Sector + 1), // +1 mapping (ushort)(Side2Sector + 1)); // +1 mapping // now resolve the object references from indices // and fill in heights wall.ResolveIndices(Room); wall.CalculateWallSideHeights(); // done return wall; }