source: OniSplit/Akira/RoomBuilder.cs

Last change on this file was 1114, checked in by iritscen, 5 years ago

Adding OniSplit source code (v0.9.99.0). Many thanks to Neo for all his work over the years.

File size: 8.1 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.IO;
4using Oni.Imaging;
5
6namespace 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}
Note: See TracBrowser for help on using the repository browser.