source: OniSplit/Akira/AkiraDatWriter.cs@ 1147

Last change on this file since 1147 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: 36.2 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.IO;
4using Oni.Imaging;
5
6namespace Oni.Akira
7{
8 internal class AkiraDatWriter
9 {
10 #region Private data
11 private Importer importer;
12 private string name;
13 private bool debug;
14 private PolygonMesh source;
15
16 private List<DatPolygon> polygons = new List<DatPolygon>();
17 private Dictionary<Polygon, DatPolygon> polygonMap = new Dictionary<Polygon, DatPolygon>();
18
19 private UniqueList<Vector3> points = new UniqueList<Vector3>();
20 private UniqueList<Vector2> texCoords = new UniqueList<Vector2>();
21 private UniqueList<Material> materials = new UniqueList<Material>();
22 private UniqueList<Plane> planes = new UniqueList<Plane>();
23
24 private List<DatAlphaBspNode> alphaBspNodes = new List<DatAlphaBspNode>();
25
26 private List<DatRoom> rooms = new List<DatRoom>();
27 private Dictionary<Room, DatRoom> roomMap = new Dictionary<Room, DatRoom>();
28 private List<DatRoomBspNode> roomBspNodes = new List<DatRoomBspNode>();
29 private List<DatRoomSide> roomSides = new List<DatRoomSide>();
30 private List<DatRoomAdjacency> roomAdjacencies = new List<DatRoomAdjacency>();
31
32 private DatOctree octree;
33 #endregion
34
35 #region private class UniqueList<T>
36
37 private class UniqueList<T> : ICollection<T>
38 {
39 private readonly List<T> list = new List<T>();
40 private readonly Dictionary<T, int> indices = new Dictionary<T, int>();
41
42 public int Add(T t)
43 {
44 int index;
45
46 if (!indices.TryGetValue(t, out index))
47 {
48 index = list.Count;
49 indices.Add(t, index);
50 list.Add(t);
51 }
52
53 return index;
54 }
55
56 #region ICollection<T> Members
57
58 void ICollection<T>.Add(T item) => Add(item);
59
60 public int Count => list.Count;
61
62 void ICollection<T>.Clear()
63 {
64 list.Clear();
65 indices.Clear();
66 }
67
68 bool ICollection<T>.Contains(T item) => indices.ContainsKey(item);
69
70 void ICollection<T>.CopyTo(T[] array, int arrayIndex)
71 {
72 list.CopyTo(array, arrayIndex);
73 }
74
75 bool ICollection<T>.IsReadOnly => false;
76
77 bool ICollection<T>.Remove(T item)
78 {
79 throw new NotImplementedException();
80 }
81
82 IEnumerator<T> IEnumerable<T>.GetEnumerator() => list.GetEnumerator();
83
84 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => list.GetEnumerator();
85
86 #endregion
87 }
88
89 #endregion
90
91 #region private class DatObject<T>
92
93 private class DatObject<T>
94 {
95 private readonly T source;
96
97 public DatObject(T source)
98 {
99 this.source = source;
100 }
101
102 public T Source => source;
103 }
104
105 #endregion
106 #region private class DatPolygon
107
108 private class DatPolygon : DatObject<Polygon>
109 {
110 private static readonly Color defaultColor = new Color(207, 207, 207, 255);
111 private static readonly Color[] defaultColors = new[] { defaultColor, defaultColor, defaultColor, defaultColor };
112 private GunkFlags flags;
113 private readonly int index;
114 public readonly int[] PointIndices = new int[4];
115 public readonly int[] TexCoordIndices = new int[4];
116 public readonly Color[] Colors = new Color[4];
117 private readonly int objectId;
118 private readonly int scriptId;
119 private int materialIndex;
120 private int planeIndex;
121
122 public DatPolygon(Polygon source, int index, UniqueList<Vector3> points, UniqueList<Vector2> texCoords)
123 : base(source)
124 {
125 this.index = index;
126 this.scriptId = source.ScriptId;
127
128 objectId = source.ObjectType << 24 | (source.ObjectId & 0xffffff);
129 flags = source.Flags;
130
131 if (source.VertexCount == 3)
132 {
133 flags |= GunkFlags.Triangle;
134
135 Array.Copy(source.PointIndices, PointIndices, 3);
136 PointIndices[3] = PointIndices[2];
137
138 Array.Copy(source.TexCoordIndices, TexCoordIndices, 3);
139 TexCoordIndices[3] = TexCoordIndices[2];
140
141 if (source.Colors == null)
142 {
143 Colors = defaultColors;
144 }
145 else
146 {
147 Array.Copy(source.Colors, Colors, 3);
148 Colors[3] = Colors[2];
149 }
150 }
151 else
152 {
153 Array.Copy(source.PointIndices, PointIndices, 4);
154 Array.Copy(source.TexCoordIndices, TexCoordIndices, 4);
155
156 if (source.Colors != null)
157 Colors = source.Colors;
158 else
159 Colors = defaultColors;
160 }
161
162 for (int i = 0; i < 4; i++)
163 {
164 PointIndices[i] = points.Add(source.Mesh.Points[PointIndices[i]]);
165 TexCoordIndices[i] = texCoords.Add(source.Mesh.TexCoords[TexCoordIndices[i]]);
166 }
167 }
168
169 public int Index => index;
170 public int ObjectId => objectId;
171 public int ScriptId => scriptId;
172
173 public GunkFlags Flags
174 {
175 get { return flags; }
176 set { flags = value; }
177 }
178
179 public int MaterialIndex
180 {
181 get { return materialIndex; }
182 set { materialIndex = value; }
183 }
184
185 public int PlaneIndex
186 {
187 get { return planeIndex; }
188 set { planeIndex = value; }
189 }
190 }
191
192 #endregion
193 #region private class DatBspNode
194
195 private class DatBspNode<T> : DatObject<T>
196 where T : BspNode<T>
197 {
198 public int PlaneIndex;
199 public int FrontChildIndex = -1;
200 public int BackChildIndex = -1;
201
202 public DatBspNode(T source)
203 : base(source)
204 {
205 }
206 }
207
208 #endregion
209 #region private class DatAlphaBspNode
210
211 private class DatAlphaBspNode : DatBspNode<AlphaBspNode>
212 {
213 public readonly int PolygonIndex;
214
215 public DatAlphaBspNode(AlphaBspNode source, int polygonIndex)
216 : base(source)
217 {
218 PolygonIndex = polygonIndex;
219 }
220 }
221
222 #endregion
223 #region private class DatRoom
224
225 private class DatRoom : DatObject<Room>
226 {
227 private readonly int index;
228 private readonly RoomFlags flags;
229
230 public int BspTreeIndex;
231 public int SideListStart;
232 public int SideListEnd;
233 public int ChildIndex = -1;
234 public int SiblingIndex = -1;
235 public byte[] CompressedGridData;
236 public byte[] DebugData;
237
238 public DatRoom(Room source, int index)
239 : base(source)
240 {
241 this.index = index;
242 this.flags = RoomFlags.Room;
243
244 if (source.FloorPlane.Normal.Y < 0.999f)
245 this.flags |= RoomFlags.Stairs;
246 }
247
248 public int Index => index;
249 public int Flags => (int)flags;
250 }
251
252 #endregion
253 #region private class DatRoomBspNode
254
255 private class DatRoomBspNode : DatBspNode<RoomBspNode>
256 {
257 public DatRoomBspNode(RoomBspNode source)
258 : base(source)
259 {
260 }
261 }
262
263 #endregion
264 #region private class DatRoomSide
265
266 private class DatRoomSide
267 {
268 public int AdjacencyListStart;
269 public int AdjacencyListEnd;
270 }
271
272 #endregion
273 #region private class DatRoomAdjacency
274
275 private class DatRoomAdjacency : DatObject<RoomAdjacency>
276 {
277 public DatRoomAdjacency(RoomAdjacency source)
278 : base(source)
279 {
280 }
281
282 public int AdjacentRoomIndex;
283 public int GhostIndex;
284 }
285
286 #endregion
287 #region private class DatOctree
288
289 private class DatOctree
290 {
291 public int[] Nodes;
292 public int[] QuadTrees;
293 public int[] Adjacency;
294 public DatOctreeBoundingBox[] BoundingBoxes;
295 public DatOctreePolygonRange[] PolygonLists;
296 public DatOctreeBnvRange[] BnvLists;
297 public int[] PolygonIndex;
298 public int[] BnvIndex;
299 }
300
301 #endregion
302 #region private struct DatOctreeBoundingBox
303
304 private struct DatOctreeBoundingBox
305 {
306 private readonly uint value;
307
308 public DatOctreeBoundingBox(BoundingBox bbox)
309 {
310 int size = (int)(Math.Log(bbox.Max.X - bbox.Min.X, 2)) - 4;
311 int maxX = (int)(bbox.Max.X + 4080.0f);
312 int maxY = (int)(bbox.Max.Y + 4080.0f);
313 int maxZ = (int)(bbox.Max.Z + 4080.0f);
314
315 value = (uint)((maxX << 14) | (maxY << 5) | (maxZ >> 4) | (size << 27));
316 }
317
318 public uint PackedValue => value;
319 }
320
321 #endregion
322 #region private struct DatOctreeBnvRange
323
324 private struct DatOctreeBnvRange
325 {
326 private const int indexBitOffset = 8;
327 private const int lengthBitMask = 255;
328 private readonly uint value;
329
330 public DatOctreeBnvRange(int start, int length)
331 {
332 ValidateRange(start, length);
333 value = (uint)((start << indexBitOffset) | (length & lengthBitMask));
334 }
335
336 private static void ValidateRange(int start, int length)
337 {
338 if (start > 16777215)
339 throw new ArgumentException(string.Format("Invalid bnv list start index {0}", start), "start");
340
341 if (length > 255)
342 throw new ArgumentException(string.Format("Invalid bnv list length {0}", length), "length");
343 }
344
345 public uint PackedValue => value;
346 }
347
348 #endregion
349 #region private struct DatOctreePolygonRange
350
351 private struct DatOctreePolygonRange
352 {
353 private const int indexBitOffset = 12;
354 private const int lengthBitMask = 4095;
355 private readonly uint value;
356
357 public DatOctreePolygonRange(int start, int length)
358 {
359 //ValidateRange(start, length);
360
361 if (start > 1048575)
362 {
363 start = 1048575;
364 length = 0;
365 }
366
367 value = (uint)((start << indexBitOffset) | (length & lengthBitMask));
368 }
369
370 private static void ValidateRange(int start, int length)
371 {
372 if (start > 1048575)
373 throw new ArgumentException(string.Format("Invalid quad list start index {0}", start), "start");
374
375 if (length > 4095)
376 throw new ArgumentException(string.Format("Invalid quad list length {0}", length), "length");
377 }
378
379 public uint PackedValue => value;
380 }
381
382 #endregion
383
384 public static void Write(PolygonMesh mesh, Importer importer, string name, bool debug)
385 {
386 var writer = new AkiraDatWriter {
387 name = name,
388 importer = importer,
389 source = mesh,
390 debug = debug
391 };
392
393 writer.Write();
394 }
395
396 private void Write()
397 {
398 Console.Error.WriteLine("Environment bounding box is {0}", source.GetBoundingBox());
399
400 RoomBuilder.BuildRooms(source);
401
402 ConvertPolygons(source.Polygons);
403 ConvertPolygons(source.Doors);
404 ConvertPolygons(source.Ghosts);
405 ConvertAlphaBspTree(AlphaBspBuilder.Build(source, debug));
406 ConvertRooms();
407 ConvertOctree();
408
409 WriteAKEV();
410
411 //foreach (Material material in materials)
412 //{
413 // if (File.Exists(material.ImageFilePath))
414 // importer.AddDependency(material.ImageFilePath, TemplateTag.TXMP);
415 //}
416 }
417
418 private void ConvertPolygons(List<Polygon> sourcePolygons)
419 {
420 foreach (var polygon in sourcePolygons)
421 {
422 if (polygon.VertexCount > 4)
423 {
424 Console.Error.WriteLine("Geometry '{0}' has a {1}-gon, ignoring.", polygon.ObjectName, polygon.VertexCount);
425 continue;
426 }
427
428 if (polygon.TexCoordIndices == null)
429 {
430 Console.Error.WriteLine("Geometry '{0}' does not contain texture coordinates, ignoring.", polygon.ObjectName);
431 continue;
432 }
433
434 var datPolygon = new DatPolygon(polygon, polygons.Count, points, texCoords) {
435 PlaneIndex = planes.Add(polygon.Plane),
436 MaterialIndex = materials.Add(polygon.Material)
437 };
438
439 polygons.Add(datPolygon);
440 polygonMap.Add(polygon, datPolygon);
441 }
442
443 //var noneMaterial = source.Materials.GetMaterial("NONE");
444 //int noneMaterialIndex = materials.Add(noneMaterial);
445
446 //foreach (var polygon in polygons)
447 //{
448 // var material = polygon.Source.Material;
449
450 //if (material.IsMarker)
451 //{
452 // polygon.MaterialIndex = noneMaterialIndex;
453
454 // if (material != source.Materials.Markers.Blackness && (material.Flags & GunkFlags.Invisible) == 0)
455 // polygon.Flags |= GunkFlags.Transparent;
456 //}
457 //else
458 //{
459 // polygon.MaterialIndex = materials.Add(material);
460 //}
461 //}
462 }
463
464 private int ConvertAlphaBspTree(AlphaBspNode source)
465 {
466 if (source == null)
467 return -1;
468
469 int index = alphaBspNodes.Count;
470
471 var node = new DatAlphaBspNode(source, polygonMap[source.Polygon].Index) {
472 PlaneIndex = planes.Add(source.Plane)
473 };
474
475 alphaBspNodes.Add(node);
476
477 if (source.FrontChild != null)
478 node.FrontChildIndex = ConvertAlphaBspTree((AlphaBspNode)source.FrontChild);
479
480 if (source.BackChild != null)
481 node.BackChildIndex = ConvertAlphaBspTree((AlphaBspNode)source.BackChild);
482
483 return index;
484 }
485
486 private void ConvertRooms()
487 {
488 foreach (var room in source.Rooms)
489 {
490 var datRoom = new DatRoom(room, rooms.Count) {
491 BspTreeIndex = ConvertRoomBspTree(room.BspTree),
492 CompressedGridData = room.Grid.Compress(),
493 DebugData = room.Grid.DebugData,
494 SideListStart = roomSides.Count
495 };
496
497 if (room.Ajacencies.Count > 0)
498 {
499 var datSide = new DatRoomSide {
500 AdjacencyListStart = roomAdjacencies.Count
501 };
502
503 foreach (var adjacency in room.Ajacencies)
504 {
505 roomAdjacencies.Add(new DatRoomAdjacency(adjacency) {
506 AdjacentRoomIndex = source.Rooms.IndexOf(adjacency.AdjacentRoom),
507 GhostIndex = polygonMap[adjacency.Ghost].Index
508 });
509 }
510
511 datSide.AdjacencyListEnd = roomAdjacencies.Count;
512 roomSides.Add(datSide);
513 }
514
515 datRoom.SideListEnd = roomSides.Count;
516
517 rooms.Add(datRoom);
518 roomMap.Add(room, datRoom);
519 }
520 }
521
522 private int ConvertRoomBspTree(RoomBspNode node)
523 {
524 int index = roomBspNodes.Count;
525
526 var datNode = new DatRoomBspNode(node) {
527 PlaneIndex = planes.Add(node.Plane)
528 };
529
530 roomBspNodes.Add(datNode);
531
532 if (node.FrontChild != null)
533 datNode.FrontChildIndex = ConvertRoomBspTree(node.FrontChild);
534
535 if (node.BackChild != null)
536 datNode.BackChildIndex = ConvertRoomBspTree(node.BackChild);
537
538 return index;
539 }
540
541 private void ConvertOctree()
542 {
543 Console.Error.WriteLine("Building octtree for {0} polygons...", source.Polygons.Count);
544
545 var root = OctreeBuilder.Build(source, debug);
546
547 var nodeList = new List<OctreeNode>();
548 var leafList = new List<OctreeNode>();
549 int quadListLength = 0;
550 int roomListLength = 0;
551
552 //
553 // Assign indices to nodes/leafs and compute the length of the quad and room indexes.
554 //
555
556 root.DfsTraversal(node => {
557 if (node.IsLeaf)
558 {
559 node.Index = leafList.Count;
560 leafList.Add(node);
561 quadListLength += node.Polygons.Count;
562 roomListLength += node.Rooms.Count;
563 }
564 else
565 {
566 node.Index = nodeList.Count;
567 nodeList.Add(node);
568 }
569 });
570
571 //
572 // Create the octtree data structure that will be written to the file.
573 //
574
575 octree = new DatOctree {
576 Nodes = new int[nodeList.Count * OctreeNode.ChildCount],
577 Adjacency = new int[leafList.Count * OctreeNode.FaceCount],
578 BoundingBoxes = new DatOctreeBoundingBox[leafList.Count],
579 PolygonIndex = new int[quadListLength],
580 PolygonLists = new DatOctreePolygonRange[leafList.Count],
581 BnvLists = new DatOctreeBnvRange[leafList.Count],
582 BnvIndex = new int[roomListLength]
583 };
584
585 Console.WriteLine("Octtree: {0} interior nodes, {1} leafs", nodeList.Count, leafList.Count);
586
587 //
588 // Populate the node array.
589 // The octree builder stores child nodes in a different order than Oni (by mistake)
590 //
591
592 var interiorNodeIndexRemap = new int[] { 0, 4, 2, 6, 1, 5, 3, 7 };
593 var datOcttree = octree;
594
595 foreach (var node in nodeList)
596 {
597 int i = node.Index * OctreeNode.ChildCount;
598
599 for (int j = 0; j < OctreeNode.ChildCount; j++)
600 {
601 var child = node.Children[j];
602 int k = interiorNodeIndexRemap[j];
603
604 if (child.IsLeaf)
605 datOcttree.Nodes[i + k] = child.Index | int.MinValue;
606 else
607 datOcttree.Nodes[i + k] = child.Index;
608 }
609 }
610
611 //
612 // Generate the data needed by the leafs: bounding box, quad range and room range.
613 //
614
615 int quadListIndex = 0;
616 int bnvListIndex = 0;
617
618 foreach (var leaf in leafList)
619 {
620 datOcttree.BoundingBoxes[leaf.Index] = new DatOctreeBoundingBox(leaf.BoundingBox);
621
622 if (leaf.Polygons.Count > 0)
623 {
624 datOcttree.PolygonLists[leaf.Index] = new DatOctreePolygonRange(quadListIndex, leaf.Polygons.Count);
625
626 foreach (var polygon in leaf.Polygons)
627 datOcttree.PolygonIndex[quadListIndex++] = polygonMap[polygon].Index;
628 }
629
630 if (leaf.Rooms.Count > 0)
631 {
632 datOcttree.BnvLists[leaf.Index] = new DatOctreeBnvRange(bnvListIndex, leaf.Rooms.Count);
633
634 foreach (var room in leaf.Rooms)
635 datOcttree.BnvIndex[bnvListIndex++] = roomMap[room].Index;
636 }
637 }
638
639 //
640 // Generate the quad trees. Note that the octtree builder doesn't build quad trees because
641 // they're only needed when writing the octtree to the file. Currently OniSplit doesn't use
642 // the octtree for raycasting.
643 //
644
645 var quadTrees = new List<int>();
646
647 foreach (var leaf in leafList)
648 {
649 leaf.RefineAdjacency();
650
651 foreach (var face in OctreeNode.Face.All)
652 {
653 var adjacentLeaf = leaf.Adjacency[face.Index];
654 int index = leaf.Index * OctreeNode.FaceCount + face.Index;
655
656 if (adjacentLeaf == null)
657 {
658 //
659 // There's no adjacent node or leaf, this should only happen
660 // on the edges of the octtree.
661 //
662
663 datOcttree.Adjacency[index] = -1;
664 }
665 else if (adjacentLeaf.IsLeaf)
666 {
667 //
668 // The adjacent node is a leaf, there's no need for a quad tree.
669 //
670
671 datOcttree.Adjacency[index] = adjacentLeaf.Index | int.MinValue;
672 }
673 else
674 {
675 //
676 // The adjacent node has children, a quad tree needs to be built.
677 //
678
679 int quadTreeBaseIndex = quadTrees.Count / 4;
680 datOcttree.Adjacency[index] = quadTreeBaseIndex;
681
682 var quadTreeRoot = leaf.BuildFaceQuadTree(face);
683
684 foreach (var node in quadTreeRoot.GetDfsList())
685 {
686 for (int i = 0; i < 4; i++)
687 {
688 if (node.Nodes[i] != null)
689 quadTrees.Add(quadTreeBaseIndex + node.Nodes[i].Index);
690 else
691 quadTrees.Add(node.Leafs[i].Index | int.MinValue);
692 }
693 }
694 }
695 }
696 }
697
698 datOcttree.QuadTrees = quadTrees.ToArray();
699 }
700
701 private void WriteAKEV()
702 {
703 var akev = importer.CreateInstance(TemplateTag.AKEV, name);
704 var pnta = importer.CreateInstance(TemplateTag.PNTA);
705 var plea = importer.CreateInstance(TemplateTag.PLEA);
706 var txca = importer.CreateInstance(TemplateTag.TXCA);
707 var agqg = importer.CreateInstance(TemplateTag.AGQG);
708 var agqr = importer.CreateInstance(TemplateTag.AGQR);
709 var agqc = importer.CreateInstance(TemplateTag.AGQC);
710 var agdb = importer.CreateInstance(TemplateTag.AGDB);
711 var txma = importer.CreateInstance(TemplateTag.TXMA);
712 var akva = importer.CreateInstance(TemplateTag.AKVA);
713 var akba = importer.CreateInstance(TemplateTag.AKBA);
714 var idxa1 = importer.CreateInstance(TemplateTag.IDXA);
715 var idxa2 = importer.CreateInstance(TemplateTag.IDXA);
716 var akbp = importer.CreateInstance(TemplateTag.AKBP);
717 var abna = importer.CreateInstance(TemplateTag.ABNA);
718 var akot = importer.CreateInstance(TemplateTag.AKOT);
719 var akaa = importer.CreateInstance(TemplateTag.AKAA);
720 var akda = importer.CreateInstance(TemplateTag.AKDA);
721
722 using (var writer = akev.OpenWrite())
723 {
724 writer.Write(pnta);
725 writer.Write(plea);
726 writer.Write(txca);
727 writer.Write(agqg);
728 writer.Write(agqr);
729 writer.Write(agqc);
730 writer.Write(agdb);
731 writer.Write(txma);
732 writer.Write(akva);
733 writer.Write(akba);
734 writer.Write(idxa1);
735 writer.Write(idxa2);
736 writer.Write(akbp);
737 writer.Write(abna);
738 writer.Write(akot);
739 writer.Write(akaa);
740 writer.Write(akda);
741 writer.Write(source.GetBoundingBox());
742 writer.Skip(24);
743 writer.Write(12.0f);
744 }
745
746 pnta.WritePoints(points);
747 plea.WritePlanes(planes);
748 txca.WriteTexCoords(texCoords);
749 WriteAGQG(agqg);
750 WriteAGQR(agqr);
751 WriteAGQC(agqc);
752 WriteTXMA(txma);
753 WriteAKVA(akva);
754 WriteAKBA(akba);
755 WriteAKBP(akbp);
756 WriteABNA(abna);
757 WriteAKOT(akot);
758 WriteAKAA(akaa);
759 WriteAKDA(akda);
760 WriteScriptIds(idxa1, idxa2);
761 WriteAGDB(agdb);
762 }
763
764 private void WriteAGQG(ImporterDescriptor descriptor)
765 {
766 using (var writer = descriptor.OpenWrite(20))
767 {
768 writer.Write(polygons.Count);
769
770 foreach (var polygon in polygons)
771 {
772 writer.Write(polygon.PointIndices);
773 writer.Write(polygon.TexCoordIndices);
774 writer.Write(polygon.Colors);
775 writer.Write((uint)polygon.Flags);
776 writer.Write(polygon.ObjectId);
777 }
778 }
779 }
780
781 private void WriteAGQC(ImporterDescriptor descriptor)
782 {
783 using (var writer = descriptor.OpenWrite(20))
784 {
785 writer.Write(polygons.Count);
786
787 foreach (var polygon in polygons)
788 {
789 writer.Write(polygon.PlaneIndex);
790 writer.Write(polygon.Source.BoundingBox);
791 }
792 }
793 }
794
795 private void WriteAGQR(ImporterDescriptor descriptor)
796 {
797 using (var writer = descriptor.OpenWrite(20))
798 {
799 writer.Write(polygons.Count);
800
801 foreach (var polygon in polygons)
802 {
803 writer.Write(polygon.MaterialIndex);
804 }
805 }
806 }
807
808 private void WriteTXMA(ImporterDescriptor descriptor)
809 {
810 using (var writer = descriptor.OpenWrite(20))
811 {
812 writer.Write(materials.Count);
813
814 foreach (var material in materials)
815 {
816 writer.Write(importer.CreateInstance(TemplateTag.TXMP, material.Name));
817 }
818 }
819 }
820
821 private void WriteABNA(ImporterDescriptor descriptor)
822 {
823 using (var writer = descriptor.OpenWrite(20))
824 {
825 writer.Write(alphaBspNodes.Count);
826
827 foreach (var node in alphaBspNodes)
828 {
829 writer.Write(node.PolygonIndex);
830 writer.Write(node.PlaneIndex);
831 writer.Write(node.FrontChildIndex);
832 writer.Write(node.BackChildIndex);
833 }
834 }
835 }
836
837 private void WriteAKVA(ImporterDescriptor descriptor)
838 {
839 using (var writer = descriptor.OpenWrite(20))
840 {
841 writer.Write(rooms.Count);
842
843 foreach (var room in rooms)
844 {
845 writer.Write(room.BspTreeIndex);
846 writer.Write(room.Index);
847 writer.Write(room.SideListStart);
848 writer.Write(room.SideListEnd);
849 writer.Write(room.ChildIndex);
850 writer.Write(room.SiblingIndex);
851 writer.Skip(4);
852 writer.Write(room.Source.Grid.XTiles);
853 writer.Write(room.Source.Grid.ZTiles);
854 writer.Write(importer.WriteRawPart(room.CompressedGridData));
855 writer.Write(room.CompressedGridData.Length);
856 writer.Write(room.Source.Grid.TileSize);
857 writer.Write(room.Source.BoundingBox);
858 writer.WriteInt16(room.Source.Grid.XOrigin);
859 writer.WriteInt16(room.Source.Grid.ZOrigin);
860 writer.Write(room.Index);
861 writer.Skip(4);
862
863 if (room.DebugData != null)
864 {
865 writer.Write(room.DebugData.Length);
866 writer.Write(importer.WriteRawPart(room.DebugData));
867 }
868 else
869 {
870 writer.Write(0);
871 writer.Write(0);
872 }
873
874 writer.Write(room.Flags);
875 writer.Write(room.Source.FloorPlane);
876 writer.Write(room.Source.Height);
877 }
878 }
879 }
880
881 private void WriteAKBA(ImporterDescriptor descriptor)
882 {
883 using (var writer = descriptor.OpenWrite(20))
884 {
885 writer.Write(roomSides.Count);
886
887 foreach (var side in roomSides)
888 {
889 writer.Write(0);
890 writer.Write(side.AdjacencyListStart);
891 writer.Write(side.AdjacencyListEnd);
892 writer.Skip(16);
893 }
894 }
895 }
896
897 private void WriteAKBP(ImporterDescriptor descriptor)
898 {
899 using (var writer = descriptor.OpenWrite(22))
900 {
901 writer.WriteUInt16(roomBspNodes.Count);
902
903 foreach (var node in roomBspNodes)
904 {
905 writer.Write(node.PlaneIndex);
906 writer.Write(node.BackChildIndex);
907 writer.Write(node.FrontChildIndex);
908 }
909 }
910 }
911
912 private void WriteAKAA(ImporterDescriptor descriptor)
913 {
914 using (var writer = descriptor.OpenWrite(20))
915 {
916 writer.Write(roomAdjacencies.Count);
917
918 foreach (var adjacency in roomAdjacencies)
919 {
920 writer.Write(adjacency.AdjacentRoomIndex);
921 writer.Write(adjacency.GhostIndex);
922 writer.Write(0);
923 }
924 }
925 }
926
927 private void WriteAKDA(ImporterDescriptor descriptor)
928 {
929 using (var writer = descriptor.OpenWrite(20))
930 {
931 writer.Write(0);
932 }
933 }
934
935 private void WriteAKOT(ImporterDescriptor akot)
936 {
937 var otit = importer.CreateInstance(TemplateTag.OTIT);
938 var otlf = importer.CreateInstance(TemplateTag.OTLF);
939 var qtna = importer.CreateInstance(TemplateTag.QTNA);
940 var idxa1 = importer.CreateInstance(TemplateTag.IDXA);
941 var idxa2 = importer.CreateInstance(TemplateTag.IDXA);
942
943 using (var writer = akot.OpenWrite())
944 {
945 writer.Write(otit);
946 writer.Write(otlf);
947 writer.Write(qtna);
948 writer.Write(idxa1);
949 writer.Write(idxa2);
950 }
951
952 WriteOTIT(otit);
953 WriteOTLF(otlf);
954 WriteQTNA(qtna);
955
956 idxa1.WriteIndices(octree.PolygonIndex);
957 idxa2.WriteIndices(octree.BnvIndex);
958 }
959
960 private void WriteOTIT(ImporterDescriptor descriptor)
961 {
962 using (var writer = descriptor.OpenWrite(20))
963 {
964 writer.Write(octree.Nodes.Length / OctreeNode.ChildCount);
965 writer.Write(octree.Nodes);
966 }
967 }
968
969 private void WriteOTLF(ImporterDescriptor descriptor)
970 {
971 using (var writer = descriptor.OpenWrite(20))
972 {
973 writer.Write(octree.BoundingBoxes.Length);
974
975 for (int i = 0; i < octree.BoundingBoxes.Length; i++)
976 {
977 writer.Write(octree.PolygonLists[i].PackedValue);
978 writer.Write(octree.Adjacency, i * OctreeNode.FaceCount, OctreeNode.FaceCount);
979 writer.Write(octree.BoundingBoxes[i].PackedValue);
980 writer.Write(octree.BnvLists[i].PackedValue);
981 }
982 }
983 }
984
985 private void WriteQTNA(ImporterDescriptor descriptor)
986 {
987 using (var writer = descriptor.OpenWrite(20))
988 {
989 writer.Write(octree.QuadTrees.Length / 4);
990 writer.Write(octree.QuadTrees);
991 }
992 }
993
994 private void WriteScriptIds(ImporterDescriptor idxa1, ImporterDescriptor idxa2)
995 {
996 var scriptIdMap = new List<KeyValuePair<int, int>>(256);
997
998 foreach (var polygon in polygons)
999 {
1000 if (polygon.ScriptId != 0)
1001 scriptIdMap.Add(new KeyValuePair<int, int>(polygon.ScriptId, polygon.Index));
1002 }
1003
1004 scriptIdMap.Sort((x, y) => x.Key.CompareTo(y.Key));
1005
1006 var scriptIds = new int[scriptIdMap.Count];
1007 var polygonIndices = new int[scriptIdMap.Count];
1008
1009 for (int i = 0; i < scriptIdMap.Count; i++)
1010 {
1011 scriptIds[i] = scriptIdMap[i].Key;
1012 polygonIndices[i] = scriptIdMap[i].Value;
1013 }
1014
1015 idxa1.WriteIndices(polygonIndices);
1016 idxa2.WriteIndices(scriptIds);
1017 }
1018
1019 private void WriteAGDB(ImporterDescriptor descriptor)
1020 {
1021 if (!debug)
1022 {
1023 using (var writer = descriptor.OpenWrite(20))
1024 {
1025 writer.Write(0);
1026 }
1027
1028 return;
1029 }
1030
1031 var objectNames = new Dictionary<string, int>(polygons.Count, StringComparer.Ordinal);
1032 var fileNames = new Dictionary<string, int>(polygons.Count, StringComparer.Ordinal);
1033
1034 using (var writer = descriptor.OpenWrite(20))
1035 {
1036 writer.Write(polygons.Count);
1037
1038 foreach (var polygon in polygons)
1039 {
1040 var objectName = polygon.Source.ObjectName;
1041 var fileName = polygon.Source.FileName;
1042
1043 if (string.IsNullOrEmpty(objectName))
1044 objectName = "(none)";
1045
1046 if (string.IsNullOrEmpty(fileName))
1047 fileName = "(none)";
1048
1049 int objectOffset;
1050 int fileOffset;
1051
1052 if (!objectNames.TryGetValue(objectName, out objectOffset))
1053 {
1054 objectOffset = importer.WriteRawPart(objectName);
1055 objectNames.Add(objectName, objectOffset);
1056 }
1057
1058 if (!fileNames.TryGetValue(fileName, out fileOffset))
1059 {
1060 fileOffset = importer.WriteRawPart(fileName);
1061 fileNames.Add(fileName, fileOffset);
1062 }
1063
1064 writer.Write(objectOffset);
1065 writer.Write(fileOffset);
1066 }
1067 }
1068 }
1069 }
1070}
Note: See TracBrowser for help on using the repository browser.