|
@@ -10,6 +10,7 @@
|
10
|
10
|
#include <vector>
|
11
|
11
|
|
12
|
12
|
#include "global.h"
|
|
13
|
+#include "Game.h"
|
13
|
14
|
#include "Log.h"
|
14
|
15
|
#include "Mesh.h"
|
15
|
16
|
#include "Room.h"
|
|
@@ -75,7 +76,7 @@ void LoaderTR2::loadPaletteTextiles() {
|
75
|
76
|
for (auto& x : palette)
|
76
|
77
|
x = file.readU32();
|
77
|
78
|
|
78
|
|
- // TODO store palette somewhere
|
|
79
|
+ // TODO store palette somewhere?
|
79
|
80
|
|
80
|
81
|
uint32_t numTextiles = file.readU32();
|
81
|
82
|
|
|
@@ -95,6 +96,11 @@ void LoaderTR2::loadPaletteTextiles() {
|
95
|
96
|
assert(r >= 0); //! \fixme properly handle error when texture could not be loaded!
|
96
|
97
|
delete [] img;
|
97
|
98
|
}
|
|
99
|
+
|
|
100
|
+ if (numTextiles > 0)
|
|
101
|
+ getLog() << "LoaderTR2: Found " << numTextiles << " Textures!" << Log::endl;
|
|
102
|
+ else
|
|
103
|
+ getLog() << "LoaderTR2: No Textures in this level?!" << Log::endl;
|
98
|
104
|
}
|
99
|
105
|
|
100
|
106
|
void LoaderTR2::loadTextures() {
|
|
@@ -124,14 +130,19 @@ void LoaderTR2::loadTextures() {
|
124
|
130
|
uint8_t yCoordinate = file.readU8();
|
125
|
131
|
uint8_t yPixel = file.readU8();
|
126
|
132
|
|
127
|
|
- assert((xCoordinate != 1) || (xCoordinate != 255));
|
128
|
|
- assert((yCoordinate != 1) || (yCoordinate != 255));
|
|
133
|
+ assert((xCoordinate == 1) || (xCoordinate == 255) || (xCoordinate == 0));
|
|
134
|
+ assert((yCoordinate == 1) || (yCoordinate == 255) || (yCoordinate == 0));
|
129
|
135
|
|
130
|
136
|
t->add(new TextureTileVertex(xCoordinate, xPixel, yCoordinate, yPixel));
|
131
|
137
|
}
|
132
|
138
|
|
133
|
139
|
getTextureManager().addTile(t);
|
134
|
140
|
}
|
|
141
|
+
|
|
142
|
+ if (numObjectTextures > 0)
|
|
143
|
+ getLog() << "LoaderTR2: Found " << numObjectTextures << " Textiles!" << Log::endl;
|
|
144
|
+ else
|
|
145
|
+ getLog() << "LoaderTR2: No Textiles in this level?!" << Log::endl;
|
135
|
146
|
}
|
136
|
147
|
|
137
|
148
|
void LoaderTR2::loadAnimatedTextures() {
|
|
@@ -158,8 +169,11 @@ void LoaderTR2::loadAnimatedTextures() {
|
158
|
169
|
pos += count + 1;
|
159
|
170
|
}
|
160
|
171
|
|
|
172
|
+ if ((numAnimatedTextures > 0) || (numWords > 0))
|
|
173
|
+ getLog() << "LoaderTR2: Found " << numAnimatedTextures << " Animated Textures!" << Log::endl;
|
|
174
|
+
|
161
|
175
|
if (pos != numWords)
|
162
|
|
- getLog() << "LoaderTR2: Extra bytes at end of AnimatedTextures?" << Log::endl;
|
|
176
|
+ getLog() << "LoaderTR2: Extra bytes at end of AnimatedTextures?!" << Log::endl;
|
163
|
177
|
}
|
164
|
178
|
|
165
|
179
|
// ---- Rooms ----
|
|
@@ -177,12 +191,11 @@ void LoaderTR2::loadRooms() {
|
177
|
191
|
// Number of data words (2 bytes) to follow
|
178
|
192
|
uint32_t dataToFollow = file.readU32();
|
179
|
193
|
|
180
|
|
-
|
181
|
|
- std::vector<struct vertex_t> vertices;
|
|
194
|
+ std::vector<vertex_t> vertices;
|
182
|
195
|
|
183
|
196
|
uint16_t numVertices = file.readU16();
|
184
|
197
|
for (unsigned int v = 0; v < numVertices; v++) {
|
185
|
|
- struct vertex_t vert;
|
|
198
|
+ vertex_t vert;
|
186
|
199
|
// Vertex coordinates, relative to x/zOffset
|
187
|
200
|
vert.x = file.read16();
|
188
|
201
|
vert.y = file.read16();
|
|
@@ -340,6 +353,11 @@ void LoaderTR2::loadRooms() {
|
340
|
353
|
|
341
|
354
|
getWorld().addRoom(room);
|
342
|
355
|
}
|
|
356
|
+
|
|
357
|
+ if (numRooms > 0)
|
|
358
|
+ getLog() << "LoaderTR2: Found " << numRooms << " Rooms!" << Log::endl;
|
|
359
|
+ else
|
|
360
|
+ getLog() << "LoaderTR2: No Rooms in this Level?!" << Log::endl;
|
343
|
361
|
}
|
344
|
362
|
|
345
|
363
|
void LoaderTR2::loadFloorData() {
|
|
@@ -389,6 +407,12 @@ void LoaderTR2::loadSprites() {
|
389
|
407
|
}
|
390
|
408
|
getWorld().addSprite(ss);
|
391
|
409
|
}
|
|
410
|
+
|
|
411
|
+ if ((numSpriteTextures > 0) || (numSpriteSequences > 0))
|
|
412
|
+ getLog() << "LoaderTR2: Found " << numSpriteTextures << " Sprites in " << numSpriteSequences <<
|
|
413
|
+ " Sequences!" << Log::endl;
|
|
414
|
+ else
|
|
415
|
+ getLog() << "LoaderTR2: No Sprites in this level?!" << Log::endl;
|
392
|
416
|
}
|
393
|
417
|
|
394
|
418
|
// ---- Meshes ----
|
|
@@ -493,6 +517,9 @@ void LoaderTR2::loadMeshes() {
|
493
|
517
|
|
494
|
518
|
// TODO store mesh data somewhere
|
495
|
519
|
}
|
|
520
|
+
|
|
521
|
+ if (numMeshPointers > 0)
|
|
522
|
+ getLog() << "LoaderTR2: Found " << numMeshPointers << " Meshes, unimplemented!" << Log::endl;
|
496
|
523
|
}
|
497
|
524
|
|
498
|
525
|
void LoaderTR2::loadStaticMeshes() {
|
|
@@ -528,12 +555,45 @@ void LoaderTR2::loadStaticMeshes() {
|
528
|
555
|
|
529
|
556
|
if (numStaticMeshes > 0)
|
530
|
557
|
getLog() << "LoaderTR2: Found " << numStaticMeshes << " StaticMeshes, unimplemented!" << Log::endl;
|
|
558
|
+ else
|
|
559
|
+ getLog() << "LoaderTR2: No StaticMeshes in this level?!" << Log::endl;
|
531
|
560
|
}
|
532
|
561
|
|
533
|
562
|
// ---- Moveables ----
|
534
|
563
|
|
|
564
|
+struct Animation_t {
|
|
565
|
+ uint32_t frameOffset;
|
|
566
|
+ uint8_t frameRate, frameSize;
|
|
567
|
+ uint16_t stateID, frameStart, frameEnd, nextAnimation;
|
|
568
|
+ uint16_t nextFrame, numStateChanges, stateChangeOffset;
|
|
569
|
+ uint16_t numAnimCommands, animCommandOffset;
|
|
570
|
+
|
|
571
|
+ Animation_t(uint32_t fo, uint8_t fr, uint8_t fs, uint16_t si,
|
|
572
|
+ uint16_t fst, uint16_t fe, uint16_t na, uint16_t nf,
|
|
573
|
+ uint16_t ns, uint16_t so, uint16_t nac, uint16_t ao)
|
|
574
|
+ : frameOffset(fo), frameRate(fr), frameSize(fs),
|
|
575
|
+ stateID(si), frameStart(fst), frameEnd(fe), nextAnimation(na),
|
|
576
|
+ nextFrame(nf), numStateChanges(ns), stateChangeOffset(so),
|
|
577
|
+ numAnimCommands(nac), animCommandOffset(ao) { }
|
|
578
|
+};
|
|
579
|
+
|
|
580
|
+struct StateChange_t {
|
|
581
|
+ uint16_t stateID, numAnimDispatches, animDispatchOffset;
|
|
582
|
+
|
|
583
|
+ StateChange_t(uint16_t s, uint16_t n, uint16_t a)
|
|
584
|
+ : stateID(s), numAnimDispatches(n), animDispatchOffset(a) { }
|
|
585
|
+};
|
|
586
|
+
|
|
587
|
+struct AnimDispatch_t {
|
|
588
|
+ int16_t low, high, nextAnimation, nextFrame;
|
|
589
|
+
|
|
590
|
+ AnimDispatch_t(int16_t l, int16_t h, int16_t na, int16_t nf)
|
|
591
|
+ : low(l), high(h), nextAnimation(na), nextFrame(nf) { }
|
|
592
|
+};
|
|
593
|
+
|
535
|
594
|
void LoaderTR2::loadMoveables() {
|
536
|
595
|
uint32_t numAnimations = file.readU32();
|
|
596
|
+ std::vector<Animation_t> animations;
|
537
|
597
|
for (unsigned int a = 0; a < numAnimations; a++) {
|
538
|
598
|
// *Byte* Offset into Frames[] (so divide by 2!)
|
539
|
599
|
uint32_t frameOffset = file.readU32();
|
|
@@ -559,59 +619,109 @@ void LoaderTR2::loadMoveables() {
|
559
|
619
|
uint16_t numAnimCommands = file.readU16(); // How many animation commands to use
|
560
|
620
|
uint16_t animCommandOffset = file.readU16(); // Index into AnimCommand[]
|
561
|
621
|
|
562
|
|
- // TODO store animations somewhere
|
|
622
|
+ animations.emplace_back(frameOffset, frameRate, frameSize,
|
|
623
|
+ stateID, frameStart, frameEnd, nextAnimation, nextFrame, numStateChanges,
|
|
624
|
+ stateChangeOffset, numAnimCommands, animCommandOffset);
|
563
|
625
|
}
|
564
|
626
|
|
|
627
|
+ if (numAnimations > 0)
|
|
628
|
+ getLog() << "LoaderTR2: Found " << numAnimations << " Animations!" << Log::endl;
|
|
629
|
+ else
|
|
630
|
+ getLog() << "LoaderTR2: No Animations in this level?!" << Log::endl;
|
|
631
|
+
|
565
|
632
|
uint32_t numStateChanges = file.readU32();
|
|
633
|
+ std::vector<StateChange_t> stateChanges;
|
566
|
634
|
for (unsigned int s = 0; s < numStateChanges; s++) {
|
567
|
635
|
uint16_t stateID = file.readU16();
|
568
|
|
- uint16_t numAnimDispatches = file.readU16();
|
|
636
|
+ uint16_t numAnimDispatches = file.readU16(); // Number of ranges (always 1..5?)
|
569
|
637
|
uint16_t animDispatchOffset = file.readU16(); // Index into AnimDispatches[]
|
570
|
638
|
|
571
|
|
- // TODO store state changes somewhere
|
|
639
|
+ stateChanges.emplace_back(stateID, numAnimDispatches, animDispatchOffset);
|
572
|
640
|
}
|
573
|
641
|
|
|
642
|
+ if (numStateChanges > 0)
|
|
643
|
+ getLog() << "LoaderTR2: Found " << numStateChanges << " StateChanges!" << Log::endl;
|
|
644
|
+ else
|
|
645
|
+ getLog() << "LoaderTR2: No StateChanges in this level?!" << Log::endl;
|
|
646
|
+
|
574
|
647
|
uint32_t numAnimDispatches = file.readU32();
|
|
648
|
+ std::vector<AnimDispatch_t> animDispatches;
|
575
|
649
|
for (unsigned int a = 0; a < numAnimDispatches; a++) {
|
576
|
650
|
int16_t low = file.read16(); // Lowest frame that uses this range
|
577
|
651
|
int16_t high = file.read16(); // Highest frame (+1?) that uses this range
|
578
|
652
|
int16_t nextAnimation = file.read16(); // Animation to go to
|
579
|
653
|
int16_t nextFrame = file.read16(); // Frame offset to go to
|
580
|
654
|
|
581
|
|
- // TODO store animation dispatches somewhere
|
|
655
|
+ animDispatches.emplace_back(low, high, nextAnimation, nextFrame);
|
582
|
656
|
}
|
583
|
657
|
|
|
658
|
+ if (numAnimDispatches > 0)
|
|
659
|
+ getLog() << "LoaderTR2: Found " << numAnimDispatches << " AnimationDispatches!" << Log::endl;
|
|
660
|
+ else
|
|
661
|
+ getLog() << "LoaderTR2: No AnimationDispatches in this level?!" << Log::endl;
|
|
662
|
+
|
584
|
663
|
uint32_t numAnimCommands = file.readU32();
|
585
|
664
|
std::vector<int16_t> animCommands;
|
586
|
665
|
for (unsigned int a = 0; a < numAnimCommands; a++) {
|
|
666
|
+ // A list of Opcodes with zero or more operands each,
|
|
667
|
+ // some referring to the whole animation (jump/grab points),
|
|
668
|
+ // some to specific frames (sound, bubbles, ...).
|
587
|
669
|
animCommands.push_back(file.read16());
|
588
|
670
|
}
|
589
|
671
|
|
|
672
|
+ if (numAnimCommands > 0)
|
|
673
|
+ getLog() << "LoaderTR2: Found " << numAnimCommands << " AnimationCommands!" << Log::endl;
|
|
674
|
+ else
|
|
675
|
+ getLog() << "LoaderTR2: No AnimationCommands in this level?!" << Log::endl;
|
|
676
|
+
|
|
677
|
+ // This is really one uint32_t flags, followed by
|
|
678
|
+ // three int32_t x, y, z. However, we're given the number
|
|
679
|
+ // of 32bits, as well as byte indices later, so we store
|
|
680
|
+ // it as a single list of int32_t.
|
590
|
681
|
uint32_t numMeshTrees = file.readU32();
|
|
682
|
+ std::vector<int32_t> meshTrees;
|
591
|
683
|
for (unsigned int m = 0; m < numMeshTrees; m++) {
|
592
|
684
|
// 0x0002 - Put parent mesh on the mesh stack
|
593
|
685
|
// 0x0001 - Pop mesh from stack, use as parent mesh
|
594
|
686
|
// When both are not set, use previous mesh as parent mesh
|
595
|
687
|
// When both are set, do 0x0001 first, then 0x0002, thereby
|
596
|
688
|
// reading the stack but not changing it
|
597
|
|
- uint32_t flags = file.readU32();
|
598
|
|
-
|
|
689
|
+ //uint32_t flags = file.readU32();
|
599
|
690
|
|
600
|
691
|
// Offset of mesh origin from the parent mesh origin
|
601
|
692
|
//int32_t x = file.read32();
|
602
|
693
|
//int32_t y = file.read32();
|
603
|
694
|
//int32_t z = file.read32();
|
604
|
|
- // Does not appear to be true...?
|
605
|
695
|
|
606
|
|
- // TODO store mesh trees somewhere
|
|
696
|
+ meshTrees.push_back(file.read32());
|
607
|
697
|
}
|
608
|
698
|
|
|
699
|
+ if (numMeshTrees > 0)
|
|
700
|
+ getLog() << "LoaderTR2: Found " << numMeshTrees << " MeshTrees!" << Log::endl;
|
|
701
|
+ else
|
|
702
|
+ getLog() << "LoaderTR2: No MeshTrees in this level?!" << Log::endl;
|
|
703
|
+
|
609
|
704
|
uint32_t numFrames = file.readU32();
|
610
|
705
|
std::vector<uint16_t> frames;
|
611
|
706
|
for (unsigned int f = 0; f < numFrames; f++) {
|
|
707
|
+ // int16 bb1x, bb1y, bb1z
|
|
708
|
+ // int16 bb2x, bb2y, bb2z
|
|
709
|
+ // int16 offsetX, offsetY, offsetZ
|
|
710
|
+ // What follows next is a list of angles with numMeshes (from Moveable) entries.
|
|
711
|
+ // If the top bit (0x8000) of the first uint16 is set, a single X angle follows,
|
|
712
|
+ // if the second bit (0x4000) is set, a Y angle follows, both are a Z angle.
|
|
713
|
+ // If none is set, it's a three-axis rotation. The next 10 bits (0x3FF0) are
|
|
714
|
+ // the X rotation, the next 10 (0x000F 0xFC00) are Y, the next (0x03FF) are
|
|
715
|
+ // the Z rotation. The scaling is always 0x100->90deg.
|
|
716
|
+ // Rotation order: Y, X, Z!
|
612
|
717
|
frames.push_back(file.readU16());
|
613
|
718
|
}
|
614
|
719
|
|
|
720
|
+ if (numFrames > 0)
|
|
721
|
+ getLog() << "LoaderTR2: Found " << numFrames << " Frames!" << Log::endl;
|
|
722
|
+ else
|
|
723
|
+ getLog() << "LoaderTR2: No Frames in this level?!" << Log::endl;
|
|
724
|
+
|
615
|
725
|
uint32_t numMoveables = file.readU32();
|
616
|
726
|
for (unsigned int m = 0; m < numMoveables; m++) {
|
617
|
727
|
// Item identifier, matched in Items[]
|
|
@@ -620,16 +730,80 @@ void LoaderTR2::loadMoveables() {
|
620
|
730
|
uint16_t startingMesh = file.readU16(); // Offset into MeshPointers[]
|
621
|
731
|
uint32_t meshTree = file.readU32(); // Offset into MeshTree[]
|
622
|
732
|
// *Byte* offset into Frames[] (divide by 2 for Frames[i])
|
623
|
|
- uint32_t frameOffset = file.readU32();
|
|
733
|
+ uint32_t frameOffset = file.readU32(); // Only needed if no animation
|
624
|
734
|
|
625
|
735
|
// If animation index is 0xFFFF, the object is stationary or
|
626
|
736
|
// animated by the engine (ponytail)
|
627
|
737
|
uint16_t animation = file.readU16();
|
628
|
738
|
|
629
|
|
- // TODO store moveables somewhere
|
|
739
|
+ // TODO load all animations, not only the first frame!
|
|
740
|
+ //if (animation == 0xFFFF) {
|
|
741
|
+
|
|
742
|
+ // Just add the frame indicated in frameOffset, nothing else
|
|
743
|
+ char* tmp = reinterpret_cast<char*>(&frames[0]) + frameOffset;
|
|
744
|
+ BinaryMemory frame(tmp + 12, (numFrames * 2) - frameOffset - 12); // skip two BBs
|
|
745
|
+ float pos[3];
|
|
746
|
+ pos[0] = frame.read16();
|
|
747
|
+ pos[1] = frame.read16();
|
|
748
|
+ pos[2] = frame.read16();
|
|
749
|
+ BoneFrame* bf = new BoneFrame(pos);
|
|
750
|
+
|
|
751
|
+ for (int i = 0; i < numMeshes; i++) {
|
|
752
|
+ int mesh = startingMesh + i;
|
|
753
|
+ float offset[3] = { 0.0f, 0.0f, 0.0f };
|
|
754
|
+ float rotation[3] = { 0.0f, 0.0f, 0.0f };
|
|
755
|
+ char flag = (i == 0) ? 2 : 0;
|
|
756
|
+
|
|
757
|
+ // Nonprimary tag - positioned relative to first tag
|
|
758
|
+ if (i != 0) {
|
|
759
|
+ tmp = reinterpret_cast<char*>(&meshTrees[0]) + meshTree; // TODO (meshTree * 4)?
|
|
760
|
+ tmp += (i - 1) * 16; // TODO ?
|
|
761
|
+ BinaryMemory tree(tmp, (numMeshTrees * 4) - meshTree - ((i - 1) * 16));
|
|
762
|
+ flag = (char)tree.readU32();
|
|
763
|
+ offset[0] = tree.read32();
|
|
764
|
+ offset[1] = tree.read32();
|
|
765
|
+ offset[2] = tree.read32();
|
|
766
|
+
|
|
767
|
+ uint16_t a = frame.readU16();
|
|
768
|
+ if (a & 0xC000) {
|
|
769
|
+ // Single angle
|
|
770
|
+ int index = 0;
|
|
771
|
+ if ((a & 0x8000) && (a & 0x4000))
|
|
772
|
+ index = 2;
|
|
773
|
+ else if (a & 0x4000)
|
|
774
|
+ index = 1;
|
|
775
|
+ rotation[index] = ((float)(a & 0x03FF)) * 360.0f / 1024.0f;
|
|
776
|
+ } else {
|
|
777
|
+ // Three angles
|
|
778
|
+ uint16_t b = frame.readU16();
|
|
779
|
+ rotation[0] = (a & 0x3FF0) >> 4;
|
|
780
|
+ rotation[1] = ((a & 0x000F) << 6) | ((b & 0xFC00) >> 10);
|
|
781
|
+ rotation[2] = b & 0x03FF;
|
|
782
|
+ for (int i = 0; i < 3; i++)
|
|
783
|
+ rotation[i] = rotation[i] * 360.0f / 1024.0f;
|
|
784
|
+ }
|
|
785
|
+ }
|
|
786
|
+
|
|
787
|
+ BoneTag* bt = new BoneTag(mesh, offset, rotation, flag);
|
|
788
|
+ bf->add(bt);
|
|
789
|
+ }
|
|
790
|
+
|
|
791
|
+ AnimationFrame* af = new AnimationFrame(0);
|
|
792
|
+ af->add(bf);
|
|
793
|
+
|
|
794
|
+ SkeletalModel* sm = new SkeletalModel(objectID);
|
|
795
|
+ sm->add(af);
|
|
796
|
+ getWorld().addSkeletalModel(sm);
|
|
797
|
+
|
|
798
|
+ //} else {
|
|
799
|
+ // Add the whole animation hierarchy
|
|
800
|
+ //}
|
630
|
801
|
}
|
631
|
802
|
|
632
|
|
- // TODO combine all this into moveables with their animations
|
|
803
|
+ if (numMoveables > 0)
|
|
804
|
+ getLog() << "LoaderTR2: Found " << numMoveables << " Moveables!" << Log::endl;
|
|
805
|
+ else
|
|
806
|
+ getLog() << "LoaderTR2: No Moveables in this level?!" << Log::endl;
|
633
|
807
|
}
|
634
|
808
|
|
635
|
809
|
void LoaderTR2::loadItems() {
|
|
@@ -651,11 +825,35 @@ void LoaderTR2::loadItems() {
|
651
|
825
|
// 0x3E00 - Activation mask, open, can be XORed with related FloorData list fields.
|
652
|
826
|
uint16_t flags = file.readU16();
|
653
|
827
|
|
654
|
|
- // TODO store items somewhere
|
|
828
|
+ // TODO for now we're only creating Entities for each Moveable Item
|
|
829
|
+ for (int m = 0; m < getWorld().sizeSkeletalModel(); m++) {
|
|
830
|
+ if (getWorld().getSkeletalModel(m).getId() == objectID) {
|
|
831
|
+ float pos[3] = {
|
|
832
|
+ static_cast<float>(x),
|
|
833
|
+ static_cast<float>(y),
|
|
834
|
+ static_cast<float>(z)
|
|
835
|
+ };
|
|
836
|
+
|
|
837
|
+ float rot[3] = {
|
|
838
|
+ 0.0f,
|
|
839
|
+ OR_DEG_TO_RAD(((angle >> 14) & 0x03) * 90.0f),
|
|
840
|
+ 0.0f
|
|
841
|
+ };
|
|
842
|
+
|
|
843
|
+ Entity* e = new Entity(pos, rot, objectID, room, m);
|
|
844
|
+ getWorld().addEntity(e);
|
|
845
|
+
|
|
846
|
+ if (objectID == 0) {
|
|
847
|
+ getGame().setLara(getWorld().sizeEntity() - 1);
|
|
848
|
+ }
|
|
849
|
+ }
|
|
850
|
+ }
|
655
|
851
|
}
|
656
|
852
|
|
657
|
853
|
if (numItems > 0)
|
658
|
854
|
getLog() << "LoaderTR2: Found " << numItems << " Items, unimplemented!" << Log::endl;
|
|
855
|
+ else
|
|
856
|
+ getLog() << "LoaderTR2: No Items in this level?!" << Log::endl;
|
659
|
857
|
}
|
660
|
858
|
|
661
|
859
|
void LoaderTR2::loadBoxesOverlapsZones() {
|