1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 | using System.IO;
|
---|
4 | using Oni.Imaging;
|
---|
5 |
|
---|
6 | namespace Oni.Akira
|
---|
7 | {
|
---|
8 | internal class RoomBuilder
|
---|
9 | {
|
---|
10 | private const float roomHeight = 20.0f;
|
---|
11 | private readonly PolygonMesh mesh;
|
---|
12 | private OctreeNode octtree;
|
---|
13 |
|
---|
14 | public static void BuildRooms(PolygonMesh mesh)
|
---|
15 | {
|
---|
16 | var builder = new RoomBuilder(mesh);
|
---|
17 | builder.BuildRooms();
|
---|
18 | }
|
---|
19 |
|
---|
20 | private RoomBuilder(PolygonMesh mesh)
|
---|
21 | {
|
---|
22 | this.mesh = mesh;
|
---|
23 | }
|
---|
24 |
|
---|
25 | private void BuildRooms()
|
---|
26 | {
|
---|
27 | foreach (Polygon floor in mesh.Floors)
|
---|
28 | mesh.Rooms.Add(CreateRoom(floor, roomHeight));
|
---|
29 |
|
---|
30 | ConnectRooms();
|
---|
31 | UpdateRoomsHeight();
|
---|
32 | }
|
---|
33 |
|
---|
34 | private Room CreateRoom(Polygon floor, float height)
|
---|
35 | {
|
---|
36 | var floorPlane = floor.Plane;
|
---|
37 |
|
---|
38 | var bbox = floor.BoundingBox;
|
---|
39 | bbox.Max.Y += height * floorPlane.Normal.Y;
|
---|
40 |
|
---|
41 | var room = new Room
|
---|
42 | {
|
---|
43 | FloorPolygon = floor,
|
---|
44 | BoundingBox = bbox,
|
---|
45 | FloorPlane = floor.Plane,
|
---|
46 | Height = height * floorPlane.Normal.Y,
|
---|
47 | BspTree = BuildBspTree(floor, height * floorPlane.Normal.Y)
|
---|
48 | };
|
---|
49 |
|
---|
50 | if (floor.Material != null)
|
---|
51 | room.Grid = CreateRoomGrid(floor);
|
---|
52 |
|
---|
53 | return room;
|
---|
54 | }
|
---|
55 |
|
---|
56 | private static RoomBspNode BuildBspTree(Polygon floor, float height)
|
---|
57 | {
|
---|
58 | var points = floor.Points.ToArray();
|
---|
59 | var floorPlane = floor.Plane;
|
---|
60 |
|
---|
61 | var bottom = new Plane(-floorPlane.Normal, -floorPlane.D);
|
---|
62 | var node = new RoomBspNode(bottom, null, null);
|
---|
63 |
|
---|
64 | var top = new Plane(floorPlane.Normal, floorPlane.D - height);
|
---|
65 | node = new RoomBspNode(top, node, null);
|
---|
66 |
|
---|
67 | for (int i = 0; i < points.Length; i++)
|
---|
68 | {
|
---|
69 | var p0 = points[i];
|
---|
70 | var p1 = points[(i + 1) % points.Length];
|
---|
71 | var p2 = p1 + Vector3.Up;
|
---|
72 |
|
---|
73 | node = new RoomBspNode(new Plane(p0, p1, p2), node, null);
|
---|
74 | }
|
---|
75 |
|
---|
76 | return node;
|
---|
77 | }
|
---|
78 |
|
---|
79 | private static RoomGrid CreateRoomGrid(Polygon floor)
|
---|
80 | {
|
---|
81 | if (!File.Exists(floor.Material.ImageFilePath))
|
---|
82 | return null;
|
---|
83 |
|
---|
84 | var image = TgaReader.Read(floor.Material.ImageFilePath);
|
---|
85 | var grid = RoomGrid.FromImage(image);
|
---|
86 |
|
---|
87 | //BoundingBox bbox = floor.GetBoundingBox();
|
---|
88 |
|
---|
89 | //
|
---|
90 | // TODO: don't use hardcoded constants
|
---|
91 | //
|
---|
92 |
|
---|
93 | //int gx = (int)(((bbox.Max.X - bbox.Min.X) / 4) + 5);
|
---|
94 | //int gz = (int)(((bbox.Max.Z - bbox.Min.Z) / 4) + 5);
|
---|
95 |
|
---|
96 | //if (gx != image.Width || gz != image.Height)
|
---|
97 | //{
|
---|
98 | // //Console.Error.WriteLine("Warning: Grid {0} has wrong size, expecting {1}x{2}, got {3}x{4}",
|
---|
99 | // // floor.Material.Name,
|
---|
100 | // // gx, gz,
|
---|
101 | // // image.Width, image.Height);
|
---|
102 | //}
|
---|
103 |
|
---|
104 | return grid;
|
---|
105 | }
|
---|
106 |
|
---|
107 | private void ConnectRooms()
|
---|
108 | {
|
---|
109 | octtree = OctreeBuilder.BuildRoomsOctree(mesh);
|
---|
110 |
|
---|
111 | foreach (Polygon ghost in mesh.Ghosts)
|
---|
112 | {
|
---|
113 | float minY = ghost.Points.Select(p => p.Y).Min();
|
---|
114 | Vector3[] points = ghost.Points.Where(p => Math.Abs(p.Y - minY) <= 0.1f).ToArray();
|
---|
115 |
|
---|
116 | if (points.Length != 2)
|
---|
117 | {
|
---|
118 | Console.Error.WriteLine("BNV Builder: Bad ghost, it must have 2 lowest points, it has {0}, ignoring", points.Length);
|
---|
119 | continue;
|
---|
120 | }
|
---|
121 |
|
---|
122 | Vector3 mid = (points[0] + points[1]) / 2.0f;
|
---|
123 | Vector3 normal = ghost.Plane.Normal;
|
---|
124 |
|
---|
125 | Vector3 p0 = mid - normal + Vector3.Up * 2.0f;
|
---|
126 | Vector3 p1 = mid + normal + Vector3.Up * 2.0f;
|
---|
127 |
|
---|
128 | RoomPair pair = PairRooms(p0, p1);
|
---|
129 |
|
---|
130 | if (pair == null)
|
---|
131 | {
|
---|
132 | Console.WriteLine("BNV Builder: Ghost '{0}' has no adjacencies at {1} and {2}, ignoring", ghost.ObjectName, p0, p1);
|
---|
133 | continue;
|
---|
134 | }
|
---|
135 |
|
---|
136 | if (pair.Room0.IsStairs || pair.Room1.IsStairs)
|
---|
137 | {
|
---|
138 | var stairs = pair.Room0;
|
---|
139 |
|
---|
140 | if (!stairs.IsStairs)
|
---|
141 | stairs = pair.Room1;
|
---|
142 |
|
---|
143 | ghost.Flags &= ~GunkFlags.Ghost;
|
---|
144 |
|
---|
145 | if (ghost.Material != null)
|
---|
146 | ghost.Material.Flags &= ~GunkFlags.Ghost;
|
---|
147 |
|
---|
148 | if (ghost.BoundingBox.Min.Y > stairs.FloorPolygon.BoundingBox.Max.Y - 1.0f)
|
---|
149 | ghost.Flags |= GunkFlags.StairsDown;
|
---|
150 | else
|
---|
151 | ghost.Flags |= GunkFlags.StairsUp;
|
---|
152 | }
|
---|
153 | else
|
---|
154 | {
|
---|
155 | ghost.Flags |= GunkFlags.Ghost;
|
---|
156 | }
|
---|
157 |
|
---|
158 | pair.Room1.Ajacencies.Add(new RoomAdjacency(pair.Room0, ghost));
|
---|
159 | pair.Room0.Ajacencies.Add(new RoomAdjacency(pair.Room1, ghost));
|
---|
160 | }
|
---|
161 | }
|
---|
162 |
|
---|
163 | #region private class RoomPair
|
---|
164 |
|
---|
165 | private class RoomPair : IComparable<RoomPair>
|
---|
166 | {
|
---|
167 | public readonly Room Room0;
|
---|
168 | public readonly Room Room1;
|
---|
169 | public readonly float HeightDelta;
|
---|
170 | public readonly float VolumeDelta;
|
---|
171 |
|
---|
172 | public RoomPair(Room r0, Vector3 p0, Room r1, Vector3 p1)
|
---|
173 | {
|
---|
174 | Room0 = r0;
|
---|
175 | Room1 = r1;
|
---|
176 | HeightDelta = r0.FloorPlane.DotCoordinate(p0) - r1.FloorPlane.DotCoordinate(p1);
|
---|
177 | VolumeDelta = r0.BoundingBox.Volume() - r1.BoundingBox.Volume();
|
---|
178 | }
|
---|
179 |
|
---|
180 | int IComparable<RoomPair>.CompareTo(RoomPair other)
|
---|
181 | {
|
---|
182 | if (Math.Abs(HeightDelta - other.HeightDelta) < 1e-5f)
|
---|
183 | return VolumeDelta.CompareTo(other.VolumeDelta);
|
---|
184 | else if (HeightDelta < other.HeightDelta)
|
---|
185 | return -1;
|
---|
186 | else
|
---|
187 | return 1;
|
---|
188 | }
|
---|
189 | }
|
---|
190 |
|
---|
191 | #endregion
|
---|
192 |
|
---|
193 | private RoomPair PairRooms(Vector3 p0, Vector3 p1)
|
---|
194 | {
|
---|
195 | var pairs = new List<RoomPair>();
|
---|
196 |
|
---|
197 | var rooms0 = FindRooms(p0);
|
---|
198 | var rooms1 = FindRooms(p1);
|
---|
199 |
|
---|
200 | foreach (Room r0 in rooms0)
|
---|
201 | {
|
---|
202 | foreach (Room r1 in rooms1)
|
---|
203 | {
|
---|
204 | if (r0 != r1)
|
---|
205 | pairs.Add(new RoomPair(r0, p0, r1, p1));
|
---|
206 | }
|
---|
207 | }
|
---|
208 |
|
---|
209 | pairs.Sort();
|
---|
210 |
|
---|
211 | return pairs.Count > 0 ? pairs[0] : null;
|
---|
212 | }
|
---|
213 |
|
---|
214 | private List<Room> FindRooms(Vector3 point)
|
---|
215 | {
|
---|
216 | var rooms = new List<Room>();
|
---|
217 |
|
---|
218 | var node = octtree.FindLeaf(point);
|
---|
219 |
|
---|
220 | if (node != null)
|
---|
221 | {
|
---|
222 | foreach (var room in node.Rooms)
|
---|
223 | {
|
---|
224 | if (room.Contains(point))
|
---|
225 | rooms.Add(room);
|
---|
226 | }
|
---|
227 | }
|
---|
228 |
|
---|
229 | return rooms;
|
---|
230 | }
|
---|
231 |
|
---|
232 | /// <summary>
|
---|
233 | /// Set the height of the room to the max height of adjacencies.
|
---|
234 | /// </summary>
|
---|
235 | private void UpdateRoomsHeight()
|
---|
236 | {
|
---|
237 | foreach (var room in mesh.Rooms)
|
---|
238 | {
|
---|
239 | float maxFloorY = room.FloorPolygon.Points.Max(p => p.Y);
|
---|
240 | float maxRoomY;
|
---|
241 |
|
---|
242 | if (room.Ajacencies.Count == 0)
|
---|
243 | maxRoomY = maxFloorY + 20.0f;
|
---|
244 | else
|
---|
245 | maxRoomY = room.Ajacencies.Max(a => a.Ghost.Points.Max(p => p.Y));
|
---|
246 |
|
---|
247 | var bbox = room.FloorPolygon.BoundingBox;
|
---|
248 | bbox.Max.Y = maxRoomY;
|
---|
249 |
|
---|
250 | room.BoundingBox = bbox;
|
---|
251 | room.Height = (maxRoomY - maxFloorY) * room.FloorPlane.Normal.Y;
|
---|
252 | room.BspTree = BuildBspTree(room.FloorPolygon, room.Height);
|
---|
253 | }
|
---|
254 | }
|
---|
255 | }
|
---|
256 | }
|
---|