1 |
using System; |
2 |
using System.Collections.Generic; |
3 |
using System.IO; |
4 |
using Oni.Imaging; |
5 |
using Oni.Motoko; |
6 |
|
7 |
namespace Oni.Akira |
8 |
{ |
9 |
internal class AkiraDatReader |
10 |
{ |
11 |
#region Private data |
12 |
private InstanceDescriptor akev; |
13 |
private InstanceDescriptor agdb; |
14 |
private InstanceDescriptor pnta; |
15 |
private InstanceDescriptor plea; |
16 |
private InstanceDescriptor txca; |
17 |
private InstanceDescriptor agqg; |
18 |
private InstanceDescriptor agqc; |
19 |
private InstanceDescriptor agqr; |
20 |
private InstanceDescriptor txma; |
21 |
private InstanceDescriptor akva; |
22 |
private InstanceDescriptor akba; |
23 |
private InstanceDescriptor idxa1; |
24 |
private InstanceDescriptor idxa2; |
25 |
private InstanceDescriptor akbp; |
26 |
private InstanceDescriptor akaa; |
27 |
|
28 |
private PolygonMesh mesh; |
29 |
private Plane[] planes; |
30 |
private Polygon[] polygons; |
31 |
#endregion |
32 |
|
33 |
#region private class DatRoom |
34 |
|
35 |
private class DatRoom |
36 |
{ |
37 |
public readonly int BspRootIndex; |
38 |
public readonly int SideListStart; |
39 |
public readonly int SideListEnd; |
40 |
public readonly int ChildIndex; |
41 |
public readonly int SiblingIndex; |
42 |
public readonly int XTiles; |
43 |
public readonly int ZTiles; |
44 |
public readonly BoundingBox BoundingBox; |
45 |
public readonly float TileSize; |
46 |
public readonly int XOrigin; |
47 |
public readonly int ZOrigin; |
48 |
public readonly RoomFlags Flags; |
49 |
public readonly Plane Floor; |
50 |
public readonly float Height; |
51 |
public readonly byte[] CompressedGridData; |
52 |
|
53 |
public DatRoom(InstanceDescriptor descriptor, BinaryReader reader) |
54 |
{ |
55 |
BspRootIndex = reader.ReadInt32(); |
56 |
reader.Skip(4); |
57 |
SideListStart = reader.ReadInt32(); |
58 |
SideListEnd = reader.ReadInt32(); |
59 |
ChildIndex = reader.ReadInt32(); |
60 |
SiblingIndex = reader.ReadInt32(); |
61 |
reader.Skip(4); |
62 |
XTiles = reader.ReadInt32(); |
63 |
ZTiles = reader.ReadInt32(); |
64 |
int ofsGridData = reader.ReadInt32(); |
65 |
int lenGridData = reader.ReadInt32(); |
66 |
TileSize = reader.ReadSingle(); |
67 |
BoundingBox = reader.ReadBoundingBox(); |
68 |
XOrigin = reader.ReadInt16(); |
69 |
ZOrigin = reader.ReadInt16(); |
70 |
reader.Skip(16); |
71 |
Flags = (RoomFlags)reader.ReadInt32(); |
72 |
Floor = reader.ReadPlane(); |
73 |
Height = reader.ReadSingle(); |
74 |
|
75 |
if (ofsGridData != 0 && lenGridData != 0) |
76 |
{ |
77 |
using (BinaryReader rawReader = descriptor.GetRawReader(ofsGridData)) |
78 |
CompressedGridData = rawReader.ReadBytes(lenGridData); |
79 |
} |
80 |
} |
81 |
} |
82 |
|
83 |
#endregion |
84 |
#region private class DatRoomBspNode |
85 |
|
86 |
private class DatRoomBspNode |
87 |
{ |
88 |
public readonly int PlaneIndex; |
89 |
public readonly int FrontChildIndex; |
90 |
public readonly int BackChildIndex; |
91 |
|
92 |
public DatRoomBspNode(BinaryReader reader) |
93 |
{ |
94 |
PlaneIndex = reader.ReadInt32(); |
95 |
BackChildIndex = reader.ReadInt32(); |
96 |
FrontChildIndex = reader.ReadInt32(); |
97 |
} |
98 |
} |
99 |
|
100 |
#endregion |
101 |
#region private class DatRoomSide |
102 |
|
103 |
private class DatRoomSide |
104 |
{ |
105 |
public readonly int SideListStart; |
106 |
public readonly int SideListEnd; |
107 |
|
108 |
public DatRoomSide(BinaryReader reader) |
109 |
{ |
110 |
reader.Skip(4); |
111 |
SideListStart = reader.ReadInt32(); |
112 |
SideListEnd = reader.ReadInt32(); |
113 |
reader.Skip(16); |
114 |
} |
115 |
} |
116 |
|
117 |
#endregion |
118 |
#region private class DatRoomAdjacency |
119 |
|
120 |
private class DatRoomAdjacency |
121 |
{ |
122 |
public readonly int RoomIndex; |
123 |
public readonly int QuadIndex; |
124 |
|
125 |
public DatRoomAdjacency(BinaryReader reader) |
126 |
{ |
127 |
RoomIndex = reader.ReadInt32(); |
128 |
QuadIndex = reader.ReadInt32(); |
129 |
reader.Skip(4); |
130 |
} |
131 |
} |
132 |
|
133 |
#endregion |
134 |
|
135 |
public static PolygonMesh Read(InstanceDescriptor akev) |
136 |
{ |
137 |
var reader = new AkiraDatReader |
138 |
{ |
139 |
akev = akev, |
140 |
mesh = new PolygonMesh(new MaterialLibrary()) |
141 |
}; |
142 |
|
143 |
reader.Read(); |
144 |
return reader.mesh; |
145 |
} |
146 |
|
147 |
private void Read() |
148 |
{ |
149 |
using (var reader = akev.OpenRead()) |
150 |
{ |
151 |
pnta = reader.ReadInstance(); |
152 |
plea = reader.ReadInstance(); |
153 |
txca = reader.ReadInstance(); |
154 |
agqg = reader.ReadInstance(); |
155 |
agqr = reader.ReadInstance(); |
156 |
agqc = reader.ReadInstance(); |
157 |
agdb = reader.ReadInstance(); |
158 |
txma = reader.ReadInstance(); |
159 |
akva = reader.ReadInstance(); |
160 |
akba = reader.ReadInstance(); |
161 |
idxa1 = reader.ReadInstance(); |
162 |
idxa2 = reader.ReadInstance(); |
163 |
akbp = reader.ReadInstance(); |
164 |
reader.Skip(8); |
165 |
akaa = reader.ReadInstance(); |
166 |
} |
167 |
|
168 |
ReadGeometry(); |
169 |
ReadDebugInfo(); |
170 |
ReadMaterials(); |
171 |
ReadScriptIndices(); |
172 |
ReadRooms(); |
173 |
} |
174 |
|
175 |
private void ReadGeometry() |
176 |
{ |
177 |
int[] planeIndices; |
178 |
|
179 |
using (var reader = pnta.OpenRead(52)) |
180 |
mesh.Points.AddRange(reader.ReadVector3VarArray()); |
181 |
|
182 |
using (var reader = txca.OpenRead(20)) |
183 |
mesh.TexCoords.AddRange(reader.ReadVector2VarArray()); |
184 |
|
185 |
using (var reader = plea.OpenRead(20)) |
186 |
planes = reader.ReadPlaneVarArray(); |
187 |
|
188 |
using (var reader = agqc.OpenRead(20)) |
189 |
{ |
190 |
planeIndices = new int[reader.ReadInt32()]; |
191 |
|
192 |
for (int i = 0; i < planeIndices.Length; i++) |
193 |
{ |
194 |
planeIndices[i] = reader.ReadInt32(); |
195 |
|
196 |
// |
197 |
// Ignore bounding boxes, we don't need them |
198 |
// |
199 |
|
200 |
reader.Skip(24); |
201 |
} |
202 |
} |
203 |
|
204 |
using (var reader = agqg.OpenRead(20)) |
205 |
{ |
206 |
polygons = new Polygon[reader.ReadInt32()]; |
207 |
|
208 |
for (int i = 0; i < polygons.Length; i++) |
209 |
{ |
210 |
var pointIndices = reader.ReadInt32Array(4); |
211 |
var texCoordIndices = reader.ReadInt32Array(4); |
212 |
var colors = reader.ReadColorArray(4); |
213 |
var flags = (GunkFlags)reader.ReadInt32(); |
214 |
int objectId = reader.ReadInt32(); |
215 |
|
216 |
if ((flags & GunkFlags.Triangle) != 0) |
217 |
{ |
218 |
Array.Resize(ref pointIndices, 3); |
219 |
Array.Resize(ref texCoordIndices, 3); |
220 |
Array.Resize(ref colors, 3); |
221 |
|
222 |
flags &= ~GunkFlags.Triangle; |
223 |
} |
224 |
|
225 |
var polygon = new Polygon(mesh, pointIndices, PlaneFromIndex(planeIndices[i])) |
226 |
{ |
227 |
Flags = flags & ~GunkFlags.Transparent, |
228 |
TexCoordIndices = texCoordIndices, |
229 |
Colors = colors |
230 |
}; |
231 |
|
232 |
if (objectId == -1) |
233 |
{ |
234 |
polygon.ObjectType = -1; |
235 |
polygon.ObjectId = -1; |
236 |
} |
237 |
else |
238 |
{ |
239 |
polygon.ObjectType = (objectId >> 24) & 0xff; |
240 |
polygon.ObjectId = objectId & 0xffffff; |
241 |
} |
242 |
|
243 |
polygons[i] = polygon; |
244 |
} |
245 |
} |
246 |
|
247 |
foreach (var polygon in polygons) |
248 |
{ |
249 |
if ((polygon.Flags & (GunkFlags.Ghost | GunkFlags.StairsUp | GunkFlags.StairsDown)) != 0) |
250 |
mesh.Ghosts.Add(polygon); |
251 |
else |
252 |
mesh.Polygons.Add(polygon); |
253 |
} |
254 |
} |
255 |
|
256 |
private Plane PlaneFromIndex(int index) |
257 |
{ |
258 |
var plane = planes[index & int.MaxValue]; |
259 |
|
260 |
if (index < 0) |
261 |
{ |
262 |
plane.Normal = -plane.Normal; |
263 |
plane.D = -plane.D; |
264 |
} |
265 |
|
266 |
return plane; |
267 |
} |
268 |
|
269 |
private void ReadMaterials() |
270 |
{ |
271 |
// |
272 |
// Read material list from TXMA |
273 |
// |
274 |
|
275 |
Material[] materials; |
276 |
|
277 |
using (var reader = txma.OpenRead(20)) |
278 |
{ |
279 |
materials = new Material[reader.ReadInt32()]; |
280 |
|
281 |
for (int i = 0; i < materials.Length; i++) |
282 |
{ |
283 |
var texture = reader.ReadInstance(); |
284 |
|
285 |
if (texture == null) |
286 |
continue; |
287 |
|
288 |
var material = mesh.Materials.GetMaterial(Utils.CleanupTextureName(texture.Name)); |
289 |
material.Image = TextureDatReader.Read(texture).Surfaces[0]; |
290 |
|
291 |
if (material.Image.HasAlpha) |
292 |
material.Flags |= GunkFlags.Transparent; |
293 |
|
294 |
materials[i] = material; |
295 |
} |
296 |
} |
297 |
|
298 |
// |
299 |
// Assign materials to polygons based on AGQR |
300 |
// |
301 |
|
302 |
using (var reader = agqr.OpenRead(20)) |
303 |
{ |
304 |
int count = reader.ReadInt32(); |
305 |
|
306 |
for (int i = 0; i < count; i++) |
307 |
polygons[i].Material = materials[reader.ReadInt32() & 0xffff]; |
308 |
} |
309 |
|
310 |
// |
311 |
// Assign special materials: danger, stairs etc. |
312 |
// |
313 |
|
314 |
foreach (var polygon in polygons) |
315 |
{ |
316 |
var marker = mesh.Materials.Markers.GetMarker(polygon); |
317 |
|
318 |
if (marker != null) |
319 |
polygon.Material = marker; |
320 |
} |
321 |
} |
322 |
|
323 |
private void ReadScriptIndices() |
324 |
{ |
325 |
if (idxa1 == null || idxa2 == null) |
326 |
return; |
327 |
|
328 |
int[] scriptQuadIndices; |
329 |
int[] scriptIds; |
330 |
|
331 |
using (var reader = idxa1.OpenRead(20)) |
332 |
scriptQuadIndices = reader.ReadInt32VarArray(); |
333 |
|
334 |
using (var reader = idxa2.OpenRead(20)) |
335 |
scriptIds = reader.ReadInt32VarArray(); |
336 |
|
337 |
for (int i = 0; i < scriptQuadIndices.Length; i++) |
338 |
polygons[scriptQuadIndices[i]].ScriptId = scriptIds[i]; |
339 |
} |
340 |
|
341 |
private void ReadDebugInfo() |
342 |
{ |
343 |
if (agdb == null) |
344 |
{ |
345 |
var debugFileName = "AGDB" + akev.Name + ".oni"; |
346 |
var debugFilePath = Path.Combine(Path.GetDirectoryName(akev.File.FilePath), debugFileName); |
347 |
|
348 |
if (!File.Exists(debugFilePath)) |
349 |
return; |
350 |
|
351 |
Console.WriteLine(debugFilePath); |
352 |
|
353 |
var debugFile = akev.File.FileManager.OpenFile(debugFilePath); |
354 |
|
355 |
if (debugFile == null) |
356 |
return; |
357 |
|
358 |
agdb = debugFile.Descriptors[0]; |
359 |
} |
360 |
|
361 |
if (agdb == null || agdb.Template.Tag != TemplateTag.AGDB) |
362 |
return; |
363 |
|
364 |
using (var reader = agdb.OpenRead(20)) |
365 |
{ |
366 |
int count = reader.ReadInt32(); |
367 |
|
368 |
var fileNames = new Dictionary<int, string>(); |
369 |
var objectNames = new Dictionary<int, string>(); |
370 |
|
371 |
for (int i = 0; i < count; i++) |
372 |
{ |
373 |
int objectNameOffset = reader.ReadInt32(); |
374 |
string objectName; |
375 |
|
376 |
if (!objectNames.TryGetValue(objectNameOffset, out objectName)) |
377 |
{ |
378 |
using (var rawReader = agdb.GetRawReader(objectNameOffset)) |
379 |
objectName = rawReader.ReadString(256); |
380 |
|
381 |
objectName = objectName.Replace('.', '_'); |
382 |
objectNames.Add(objectNameOffset, objectName); |
383 |
} |
384 |
|
385 |
int fileNameOffset = reader.ReadInt32(); |
386 |
string fileName; |
387 |
|
388 |
if (!fileNames.TryGetValue(fileNameOffset, out fileName)) |
389 |
{ |
390 |
using (var rawReader = agdb.GetRawReader(fileNameOffset)) |
391 |
fileName = rawReader.ReadString(256); |
392 |
|
393 |
fileName = Path.GetFileNameWithoutExtension(fileName); |
394 |
fileNames.Add(fileNameOffset, fileName); |
395 |
} |
396 |
|
397 |
if (!string.IsNullOrEmpty(objectName)) |
398 |
mesh.HasDebugInfo = true; |
399 |
|
400 |
polygons[i].ObjectName = objectName; |
401 |
polygons[i].FileName = fileName; |
402 |
} |
403 |
} |
404 |
} |
405 |
|
406 |
private void ReadRooms() |
407 |
{ |
408 |
DatRoomBspNode[] bspTrees; |
409 |
DatRoomSide[] roomSides; |
410 |
DatRoomAdjacency[] roomAdjacencies; |
411 |
DatRoom[] roomsData; |
412 |
|
413 |
using (var reader = akbp.OpenRead(22)) |
414 |
{ |
415 |
bspTrees = new DatRoomBspNode[reader.ReadUInt16()]; |
416 |
|
417 |
for (int i = 0; i < bspTrees.Length; i++) |
418 |
bspTrees[i] = new DatRoomBspNode(reader); |
419 |
} |
420 |
|
421 |
using (var reader = akba.OpenRead(20)) |
422 |
{ |
423 |
roomSides = new DatRoomSide[reader.ReadInt32()]; |
424 |
|
425 |
for (int i = 0; i < roomSides.Length; i++) |
426 |
roomSides[i] = new DatRoomSide(reader); |
427 |
} |
428 |
|
429 |
using (var reader = akaa.OpenRead(20)) |
430 |
{ |
431 |
roomAdjacencies = new DatRoomAdjacency[reader.ReadInt32()]; |
432 |
|
433 |
for (int i = 0; i < roomAdjacencies.Length; i++) |
434 |
roomAdjacencies[i] = new DatRoomAdjacency(reader); |
435 |
} |
436 |
|
437 |
using (var reader = akva.OpenRead(20)) |
438 |
{ |
439 |
roomsData = new DatRoom[reader.ReadInt32()]; |
440 |
|
441 |
for (int i = 0; i < roomsData.Length; i++) |
442 |
roomsData[i] = new DatRoom(akva, reader); |
443 |
} |
444 |
|
445 |
var rooms = new Room[roomsData.Length]; |
446 |
|
447 |
for (int i = 0; i < roomsData.Length; i++) |
448 |
{ |
449 |
var data = roomsData[i]; |
450 |
|
451 |
var room = new Room |
452 |
{ |
453 |
BspTree = BspNodeDataToBspNode(bspTrees, data.BspRootIndex), |
454 |
BoundingBox = data.BoundingBox |
455 |
}; |
456 |
|
457 |
if ((data.Flags & RoomFlags.Stairs) != 0) |
458 |
{ |
459 |
room.FloorPlane = data.Floor; |
460 |
room.Height = data.Height; |
461 |
} |
462 |
else |
463 |
{ |
464 |
room.FloorPlane = new Plane(Vector3.Up, -data.BoundingBox.Min.Y); |
465 |
room.Height = data.BoundingBox.Max.Y - data.BoundingBox.Min.Y; |
466 |
} |
467 |
|
468 |
room.Grid = RoomGrid.FromCompressedData(data.XTiles, data.ZTiles, data.CompressedGridData); |
469 |
rooms[i] = room; |
470 |
} |
471 |
|
472 |
for (int i = 0; i < roomsData.Length; i++) |
473 |
{ |
474 |
var data = roomsData[i]; |
475 |
var room = rooms[i]; |
476 |
|
477 |
//if (data.SiblingIndex != -1) |
478 |
// room.Sibling = rooms[data.SiblingIndex]; |
479 |
|
480 |
//if (data.ChildIndex != -1) |
481 |
// room.Child = rooms[data.ChildIndex]; |
482 |
|
483 |
for (int j = data.SideListStart; j < data.SideListEnd; j++) |
484 |
{ |
485 |
var sideData = roomSides[j]; |
486 |
|
487 |
for (int k = sideData.SideListStart; k < sideData.SideListEnd; k++) |
488 |
{ |
489 |
var adjData = roomAdjacencies[k]; |
490 |
var adjacentRoom = rooms[adjData.RoomIndex]; |
491 |
var ghost = polygons[adjData.QuadIndex]; |
492 |
|
493 |
room.Ajacencies.Add(new RoomAdjacency(adjacentRoom, ghost)); |
494 |
} |
495 |
} |
496 |
} |
497 |
|
498 |
mesh.Rooms.AddRange(rooms); |
499 |
} |
500 |
|
501 |
private RoomBspNode BspNodeDataToBspNode(DatRoomBspNode[] data, int index) |
502 |
{ |
503 |
var nodeData = data[index]; |
504 |
RoomBspNode front = null, back = null; |
505 |
|
506 |
if (nodeData.BackChildIndex != -1) |
507 |
back = BspNodeDataToBspNode(data, nodeData.BackChildIndex); |
508 |
|
509 |
if (nodeData.FrontChildIndex != -1) |
510 |
front = BspNodeDataToBspNode(data, nodeData.FrontChildIndex); |
511 |
|
512 |
return new RoomBspNode(PlaneFromIndex(nodeData.PlaneIndex), back, front); |
513 |
} |
514 |
} |
515 |
} |