| 1 | using System; | 
 
 
 
 
 | 2 | using System.Collections.Generic; | 
 
 
 
 
 | 3 | using System.IO; | 
 
 
 
 
 | 4 | using Oni.Collections; | 
 
 
 
 
 | 5 | using Oni.Imaging; | 
 
 
 
 
 | 6 |  | 
 
 
 
 
 | 7 | namespace Oni.Akira | 
 
 
 
 
 | 8 | { | 
 
 
 
 
 | 9 | internal class RoomGridBuilder | 
 
 
 
 
 | 10 | { | 
 
 
 
 
 | 11 | private readonly Dae.Scene roomsScene; | 
 
 
 
 
 | 12 | private readonly PolygonMesh geometryMesh; | 
 
 
 
 
 | 13 | private PolygonMesh roomsMesh; | 
 
 
 
 
 | 14 | private OctreeNode geometryOcttree; | 
 
 
 
 
 | 15 | private OctreeNode dangerOcttree; | 
 
 
 
 
 | 16 |  | 
 
 
 
 
 | 17 | public RoomGridBuilder(Dae.Scene roomsScene, PolygonMesh geometryMesh) | 
 
 
 
 
 | 18 | { | 
 
 
 
 
 | 19 | this.roomsScene = roomsScene; | 
 
 
 
 
 | 20 | this.geometryMesh = geometryMesh; | 
 
 
 
 
 | 21 | } | 
 
 
 
 
 | 22 |  | 
 
 
 
 
 | 23 | public PolygonMesh Mesh => roomsMesh; | 
 
 
 
 
 | 24 |  | 
 
 
 
 
 | 25 | public void Build() | 
 
 
 
 
 | 26 | { | 
 
 
 
 
 | 27 | roomsMesh = RoomDaeReader.Read(roomsScene); | 
 
 
 
 
 | 28 |  | 
 
 
 
 
 | 29 | RoomBuilder.BuildRooms(roomsMesh); | 
 
 
 
 
 | 30 |  | 
 
 
 
 
 | 31 | Console.Error.WriteLine("Read {0} rooms", roomsMesh.Rooms.Count); | 
 
 
 
 
 | 32 |  | 
 
 
 
 
 | 33 | geometryOcttree = OctreeBuilder.Build(geometryMesh, GunkFlags.NoCollision | GunkFlags.NoCharacterCollision); | 
 
 
 
 
 | 34 | dangerOcttree = OctreeBuilder.Build(geometryMesh, p => (p.Flags & GunkFlags.Danger) != 0); | 
 
 
 
 
 | 35 |  | 
 
 
 
 
 | 36 | ProcessStairsCollision(); | 
 
 
 
 
 | 37 |  | 
 
 
 
 
 | 38 | Parallel.ForEach(roomsMesh.Rooms, room => | 
 
 
 
 
 | 39 | { | 
 
 
 
 
 | 40 | BuildGrid(room); | 
 
 
 
 
 | 41 | }); | 
 
 
 
 
 | 42 | } | 
 
 
 
 
 | 43 |  | 
 
 
 
 
 | 44 | private void ProcessStairsCollision() | 
 
 
 
 
 | 45 | { | 
 
 
 
 
 | 46 | var verticalTolerance1 = new Vector3(0.0f, 0.1f, 0.0f); | 
 
 
 
 
 | 47 | var verticalTolerance2 = new Vector3(0.0f, 7.5f, 0.0f); | 
 
 
 
 
 | 48 |  | 
 
 
 
 
 | 49 | foreach (var stairs in geometryMesh.Polygons.Where(p => p.IsStairs && p.VertexCount == 4)) | 
 
 
 
 
 | 50 | { | 
 
 
 
 
 | 51 | var floorPoints = stairs.Points.Select(v => v + verticalTolerance1).ToArray(); | 
 
 
 
 
 | 52 | var ceilPoints = stairs.Points.Select(v => v + verticalTolerance2).ToArray(); | 
 
 
 
 
 | 53 | var bbox = BoundingBox.CreateFromPoints(floorPoints.Concatenate(ceilPoints)); | 
 
 
 
 
 | 54 |  | 
 
 
 
 
 | 55 | var floorPlane = new Plane(floorPoints[0], floorPoints[1], floorPoints[2]); | 
 
 
 
 
 | 56 | var ceilingPlane = new Plane(ceilPoints[0], ceilPoints[1], ceilPoints[2]); | 
 
 
 
 
 | 57 |  | 
 
 
 
 
 | 58 | foreach (var node in geometryOcttree.FindLeafs(bbox)) | 
 
 
 
 
 | 59 | { | 
 
 
 
 
 | 60 | foreach (var poly in node.Polygons) | 
 
 
 
 
 | 61 | { | 
 
 
 
 
 | 62 | if ((poly.Flags & (GunkFlags.NoCollision | GunkFlags.NoCharacterCollision)) != 0) | 
 
 
 
 
 | 63 | { | 
 
 
 
 
 | 64 | // | 
 
 
 
 
 | 65 | // already a no collision polygon, skip it | 
 
 
 
 
 | 66 | // | 
 
 
 
 
 | 67 |  | 
 
 
 
 
 | 68 | continue; | 
 
 
 
 
 | 69 | } | 
 
 
 
 
 | 70 |  | 
 
 
 
 
 | 71 | if (!poly.BoundingBox.Intersects(bbox)) | 
 
 
 
 
 | 72 | continue; | 
 
 
 
 
 | 73 |  | 
 
 
 
 
 | 74 | var points = poly.Points.ToList(); | 
 
 
 
 
 | 75 |  | 
 
 
 
 
 | 76 | points = PolygonUtils.ClipToPlane(points, floorPlane); | 
 
 
 
 
 | 77 |  | 
 
 
 
 
 | 78 | if (points == null) | 
 
 
 
 
 | 79 | { | 
 
 
 
 
 | 80 | // | 
 
 
 
 
 | 81 | // this polygon is below stairs, skip it | 
 
 
 
 
 | 82 | // | 
 
 
 
 
 | 83 |  | 
 
 
 
 
 | 84 | continue; | 
 
 
 
 
 | 85 | } | 
 
 
 
 
 | 86 |  | 
 
 
 
 
 | 87 | points = PolygonUtils.ClipToPlane(points, ceilingPlane); | 
 
 
 
 
 | 88 |  | 
 
 
 
 
 | 89 | if (points != null) | 
 
 
 
 
 | 90 | { | 
 
 
 
 
 | 91 | // | 
 
 
 
 
 | 92 | // this polygon is too high above the stairs, skip it | 
 
 
 
 
 | 93 | // | 
 
 
 
 
 | 94 |  | 
 
 
 
 
 | 95 | continue; | 
 
 
 
 
 | 96 | } | 
 
 
 
 
 | 97 |  | 
 
 
 
 
 | 98 | poly.Flags |= GunkFlags.NoCharacterCollision; | 
 
 
 
 
 | 99 | } | 
 
 
 
 
 | 100 | } | 
 
 
 
 
 | 101 | } | 
 
 
 
 
 | 102 | } | 
 
 
 
 
 | 103 |  | 
 
 
 
 
 | 104 | private void BuildGrid(Room room) | 
 
 
 
 
 | 105 | { | 
 
 
 
 
 | 106 | var floor = room.FloorPolygon; | 
 
 
 
 
 | 107 | var bbox = room.BoundingBox; | 
 
 
 
 
 | 108 |  | 
 
 
 
 
 | 109 | // | 
 
 
 
 
 | 110 | // Create an empty grid and mark all tiles as 'danger' | 
 
 
 
 
 | 111 | // | 
 
 
 
 
 | 112 |  | 
 
 
 
 
 | 113 | var rasterizer = new RoomGridRasterizer(bbox); | 
 
 
 
 
 | 114 | rasterizer.Clear(RoomGridWeight.Danger); | 
 
 
 
 
 | 115 |  | 
 
 
 
 
 | 116 | // | 
 
 
 
 
 | 117 | // Collect all polygons that intersect the room | 
 
 
 
 
 | 118 | // | 
 
 
 
 
 | 119 |  | 
 
 
 
 
 | 120 | bbox.Inflate(2.0f * new Vector3(rasterizer.TileSize, 0.0f, rasterizer.TileSize)); | 
 
 
 
 
 | 121 |  | 
 
 
 
 
 | 122 | var testbox = bbox; | 
 
 
 
 
 | 123 | testbox.Min.X -= 1.0f; | 
 
 
 
 
 | 124 | testbox.Min.Y = bbox.Min.Y - 6.0f; | 
 
 
 
 
 | 125 | testbox.Min.Z -= 1.0f; | 
 
 
 
 
 | 126 | testbox.Max.X += 1.0f; | 
 
 
 
 
 | 127 | testbox.Max.Y = bbox.Max.Y - 6.0f; | 
 
 
 
 
 | 128 | testbox.Max.Z += 1.0f; | 
 
 
 
 
 | 129 |  | 
 
 
 
 
 | 130 | var polygons = new Set<Polygon>(); | 
 
 
 
 
 | 131 | var dangerPolygons = new Set<Polygon>(); | 
 
 
 
 
 | 132 |  | 
 
 
 
 
 | 133 | foreach (var node in geometryOcttree.FindLeafs(testbox)) | 
 
 
 
 
 | 134 | { | 
 
 
 
 
 | 135 | polygons.UnionWith(node.Polygons); | 
 
 
 
 
 | 136 | } | 
 
 
 
 
 | 137 |  | 
 
 
 
 
 | 138 | foreach (var node in dangerOcttree.FindLeafs(testbox)) | 
 
 
 
 
 | 139 | { | 
 
 
 
 
 | 140 | dangerPolygons.UnionWith(node.Polygons); | 
 
 
 
 
 | 141 | } | 
 
 
 
 
 | 142 |  | 
 
 
 
 
 | 143 | // | 
 
 
 
 
 | 144 | // Draw all the floors on the grid. This will overwrite the 'danger' | 
 
 
 
 
 | 145 | // areas with 'clear' areas. This means danger area will remain | 
 
 
 
 
 | 146 | // where there isn't any floor. | 
 
 
 
 
 | 147 | // | 
 
 
 
 
 | 148 |  | 
 
 
 
 
 | 149 | foreach (var polygon in polygons) | 
 
 
 
 
 | 150 | { | 
 
 
 
 
 | 151 | if (polygon.Plane.Normal.Y > 0.5f) | 
 
 
 
 
 | 152 | rasterizer.DrawFloor(polygon.Points); | 
 
 
 
 
 | 153 | } | 
 
 
 
 
 | 154 |  | 
 
 
 
 
 | 155 | if (room.FloorPlane.Normal.Y >= 0.999f) | 
 
 
 
 
 | 156 | { | 
 
 
 
 
 | 157 | // | 
 
 
 
 
 | 158 | // Draw the walls. | 
 
 
 
 
 | 159 | // | 
 
 
 
 
 | 160 |  | 
 
 
 
 
 | 161 | float floorMaxY = floor.BoundingBox.Max.Y; | 
 
 
 
 
 | 162 | var floorPlane = new Plane(floor.Plane.Normal, floor.Plane.D - 4.0f); | 
 
 
 
 
 | 163 | var ceilingPlane = new Plane(-floor.Plane.Normal, -(floor.Plane.D - 20.0f)); | 
 
 
 
 
 | 164 |  | 
 
 
 
 
 | 165 | foreach (var polygon in polygons) | 
 
 
 
 
 | 166 | { | 
 
 
 
 
 | 167 | if ((polygon.Flags & (GunkFlags.Stairs | GunkFlags.NoCharacterCollision | GunkFlags.Impassable)) == 0) | 
 
 
 
 
 | 168 | { | 
 
 
 
 
 | 169 | // | 
 
 
 
 
 | 170 | // Remove curbs from character collision. | 
 
 
 
 
 | 171 | // | 
 
 
 
 
 | 172 |  | 
 
 
 
 
 | 173 | var polyBox = polygon.BoundingBox; | 
 
 
 
 
 | 174 |  | 
 
 
 
 
 | 175 | if (Math.Abs(polygon.Plane.Normal.Y) < MathHelper.Eps | 
 
 
 
 
 | 176 | && polyBox.Height <= 4.0f | 
 
 
 
 
 | 177 | && Math.Abs(polyBox.Max.Y - floorMaxY) <= 4.0f) | 
 
 
 
 
 | 178 | { | 
 
 
 
 
 | 179 | polygon.Flags |= GunkFlags.NoCharacterCollision; | 
 
 
 
 
 | 180 | continue; | 
 
 
 
 
 | 181 | } | 
 
 
 
 
 | 182 | } | 
 
 
 
 
 | 183 |  | 
 
 
 
 
 | 184 | if ((polygon.Flags & (GunkFlags.Stairs | GunkFlags.GridIgnore | GunkFlags.NoCollision | GunkFlags.NoCharacterCollision)) != 0) | 
 
 
 
 
 | 185 | continue; | 
 
 
 
 
 | 186 |  | 
 
 
 
 
 | 187 | var points = polygon.Points.ToList(); | 
 
 
 
 
 | 188 |  | 
 
 
 
 
 | 189 | points = PolygonUtils.ClipToPlane(points, floorPlane); | 
 
 
 
 
 | 190 |  | 
 
 
 
 
 | 191 | if (points == null) | 
 
 
 
 
 | 192 | continue; | 
 
 
 
 
 | 193 |  | 
 
 
 
 
 | 194 | points = PolygonUtils.ClipToPlane(points, ceilingPlane); | 
 
 
 
 
 | 195 |  | 
 
 
 
 
 | 196 | if (points == null) | 
 
 
 
 
 | 197 | continue; | 
 
 
 
 
 | 198 |  | 
 
 
 
 
 | 199 | if (Math.Abs(polygon.Plane.Normal.Y) <= 0.1f) | 
 
 
 
 
 | 200 | rasterizer.DrawWall(points); | 
 
 
 
 
 | 201 | else | 
 
 
 
 
 | 202 | rasterizer.DrawImpassable(points); | 
 
 
 
 
 | 203 | } | 
 
 
 
 
 | 204 |  | 
 
 
 
 
 | 205 | // | 
 
 
 
 
 | 206 | // Draw ramp entry/exit lines. Hmm, this seems useless. | 
 
 
 
 
 | 207 | // | 
 
 
 
 
 | 208 |  | 
 
 
 
 
 | 209 | //if (room.FloorPlane.Normal.Y >= 0.98f) | 
 
 
 
 
 | 210 | //{ | 
 
 
 
 
 | 211 | //    foreach (RoomAdjacency adj in room.Ajacencies) | 
 
 
 
 
 | 212 | //    { | 
 
 
 
 
 | 213 | //        if (Math.Abs(room.FloorPlane.Normal.Y - adj.AdjacentRoom.FloorPlane.Normal.Y) > 0.001f) | 
 
 
 
 
 | 214 | //            rasterizer.DrawStairsEntry(adj.Ghost.Points); | 
 
 
 
 
 | 215 | //    } | 
 
 
 
 
 | 216 | //} | 
 
 
 
 
 | 217 |  | 
 
 
 
 
 | 218 | // | 
 
 
 
 
 | 219 | // Draw 'danger' quads. | 
 
 
 
 
 | 220 | // | 
 
 
 
 
 | 221 |  | 
 
 
 
 
 | 222 | foreach (var polygon in dangerPolygons) | 
 
 
 
 
 | 223 | { | 
 
 
 
 
 | 224 | rasterizer.DrawDanger(polygon.Points); | 
 
 
 
 
 | 225 | } | 
 
 
 
 
 | 226 |  | 
 
 
 
 
 | 227 | // | 
 
 
 
 
 | 228 | // Draw 'near wall' borders. | 
 
 
 
 
 | 229 | // | 
 
 
 
 
 | 230 |  | 
 
 
 
 
 | 231 | rasterizer.AddBorders(); | 
 
 
 
 
 | 232 | } | 
 
 
 
 
 | 233 |  | 
 
 
 
 
 | 234 | // | 
 
 
 
 
 | 235 | // Finally, get the rasterized pathfinding grid. | 
 
 
 
 
 | 236 | // | 
 
 
 
 
 | 237 |  | 
 
 
 
 
 | 238 | room.Grid = rasterizer.GetGrid(); | 
 
 
 
 
 | 239 |  | 
 
 
 
 
 | 240 | if (room.Grid.XTiles * room.Grid.ZTiles > 256 * 256) | 
 
 
 
 
 | 241 | Console.Error.WriteLine("Warning: pathfinding grid too large"); | 
 
 
 
 
 | 242 | } | 
 
 
 
 
 | 243 | } | 
 
 
 
 
 | 244 | } |