OpenRaider  0.1.4-dev
Open Source Tomb Raider Game Engine implementation
LoaderTR2.cpp
Go to the documentation of this file.
1 
8 #include <vector>
9 
10 #include "global.h"
11 #include "Game.h"
12 #include "Log.h"
13 #include "Mesh.h"
14 #include "Room.h"
15 #include "SoundManager.h"
16 #include "TextureManager.h"
17 #include "World.h"
18 #include "system/Sound.h"
19 #include "utils/pixel.h"
20 #include "loader/LoaderTR2.h"
21 
22 #include <glm/gtc/matrix_transform.hpp>
23 
24 int LoaderTR2::load(std::string f) {
25  if (file.open(f) != 0) {
26  return 1; // Could not open file
27  }
28 
29  if (file.readU32() != 0x2D) {
30  return 2; // Not a TR2 level?!
31  }
32 
33  loadPalette();
34  loadTextures();
35 
36  file.seek(file.tell() + 4); // Unused value?
37 
38  loadRooms();
39  loadFloorData();
40  loadMeshes();
41  loadMoveables();
43  loadTextiles();
44  loadSprites();
45  loadCameras();
49  loadItems();
50 
51  file.seek(file.tell() + 8192); // Skip Light map, only for 8bit coloring
52 
54  loadDemoData();
55  loadSoundMap();
58 
60 
61  return 0;
62 }
63 
64 // ---- Textures ----
65 
67  file.seek(file.tell() + 768); // Skip 8bit palette, 256 * 3 bytes
68 
69  // Read the 16bit palette, 256 * 4 bytes, RGBA, A unused
70  for (int i = 0; i < 256; i++) {
71  uint8_t r = file.readU8();
72  uint8_t g = file.readU8();
73  uint8_t b = file.readU8();
74  uint8_t a = file.readU8();
75 
76  glm::vec4 c(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f);
78  }
79 }
80 
82  uint32_t numTextures = file.readU32();
83 
84  file.seek(file.tell() + (numTextures * 256 * 256)); // Skip 8bit textures
85 
86  // Read the 16bit textures, numTextures * 256 * 256 * 2 bytes
87  for (unsigned int i = 0; i < numTextures; i++) {
88  std::array<uint8_t, 256 * 256 * 2> arr;
89  for (auto& x : arr) {
90  x = file.readU8();
91  }
92 
93  // Convert 16bit textures to 32bit textures
94  unsigned char* img = argb16to32(&arr[0], 256, 256);
95  int r = TextureManager::loadBufferSlot(img, 256, 256,
96  ColorMode::ARGB, 32,
99  delete [] img;
100  }
101 
102  if (numTextures > 0)
103  Log::get(LOG_INFO) << "LoaderTR2: Found " << numTextures << " Textures!" << Log::endl;
104  else
105  Log::get(LOG_INFO) << "LoaderTR2: No Textures in this level?!" << Log::endl;
106 }
107 
109  uint32_t numObjectTextures = file.readU32();
110  for (unsigned int o = 0; o < numObjectTextures; o++) {
111  // 0 means that a texture is all-opaque, and that transparency
112  // information is ignored.
113  //
114  // 1 means that transparency information is used. In 8-bit color,
115  // index 0 is the transparent color, while in 16-bit color, the
116  // top bit (0x8000) is the alpha channel (1 = opaque, 0 = transparent)
117  //
118  // 2 (TR3 only) means that the opacity (alpha) is equal to the intensity;
119  // the brighter the color, the more opaque it is. The intensity is probably
120  // calculated as the maximum of the individual color values.
121  uint16_t attribute = file.readU16();
122 
123  // Index into the texture list
124  uint16_t tile = file.readU16();
125 
126  TextureTile* t = new TextureTile(attribute, tile);
127 
128  // The four corner vertices of the texture
129  // The Pixel values are the actual coordinates of the vertexs pixel
130  // The Coordinate values depend on where the other vertices are in
131  // the object texture. And if the object texture is used to specify
132  // a triangle, then the fourth vertexs values will all be zero
133  // Coordinate is 1 if Pixel is the low val, 255 if high val in object texture
134  for (int i = 0; i < 4; i++) {
135  uint8_t xCoordinate = file.readU8();
136  uint8_t xPixel = file.readU8();
137  uint8_t yCoordinate = file.readU8();
138  uint8_t yPixel = file.readU8();
139 
140  assert((xCoordinate == 1) || (xCoordinate == 255) || (xCoordinate == 0));
141  assert((yCoordinate == 1) || (yCoordinate == 255) || (yCoordinate == 0));
142 
143  t->add(TextureTileVertex(xCoordinate, xPixel, yCoordinate, yPixel));
144  }
145 
147  }
148 
149  if (numObjectTextures > 0)
150  Log::get(LOG_INFO) << "LoaderTR2: Found " << numObjectTextures << " Textiles!" << Log::endl;
151  else
152  Log::get(LOG_INFO) << "LoaderTR2: No Textiles in this level?!" << Log::endl;
153 }
154 
156  uint32_t numWords = file.readU32() - 1;
157  uint16_t numAnimatedTextures = file.readU16();
158  std::vector<uint16_t> animatedTextures;
159  for (unsigned int a = 0; a < numWords; a++) {
160  animatedTextures.push_back(file.readU16());
161  }
162 
163  int pos = 0;
164  for (unsigned int a = 0; a < numAnimatedTextures; a++) {
165  int count = animatedTextures.at(pos) + 1;
166  if ((pos + count) >= numWords) {
167  Log::get(LOG_DEBUG) << "LoaderTR2: Invalid AnimatedTextures ("
168  << pos + count << " >= " << numWords << ")!" << Log::endl;
169  return;
170  }
171 
172  for (int i = 0; i < count; i++) {
173  TextureManager::addAnimatedTile(a, animatedTextures.at(pos + i + 1));
174  }
175 
176  pos += count + 1;
177  }
178 
179  if ((numAnimatedTextures > 0) || (numWords > 0))
180  Log::get(LOG_INFO) << "LoaderTR2: Found " << numAnimatedTextures << " Animated Textures!" <<
181  Log::endl;
182  else
183  Log::get(LOG_INFO) << "LoaderTR2: No Animated Textures in this level?!" << Log::endl;
184 
185  if (pos != numWords)
186  Log::get(LOG_DEBUG) << "LoaderTR2: Extra bytes at end of AnimatedTextures?!" << Log::endl;
187 }
188 
189 // ---- Rooms ----
190 
192  int16_t intensity1 = file.read16();
193  int16_t intensity2 = file.read16();
194  int16_t lightMode = file.read16();
195 
196  uint16_t numLights = file.readU16();
197  for (unsigned int l = 0; l < numLights; l++) {
198  // Position of light, in world coordinates
199  int32_t x = file.read32();
200  int32_t y = file.read32();
201  int32_t z = file.read32();
202 
203  uint16_t intensity1 = file.readU16();
204  uint16_t intensity2 = file.readU16(); // Almost always equal to intensity1
205 
206  uint32_t fade1 = file.readU32(); // Falloff value?
207  uint32_t fade2 = file.readU32(); // Falloff value?
208 
209  // TODO store light somewhere
210  }
211 }
212 
213 void LoaderTR2::loadRoomStaticMeshes(std::vector<StaticModel*>& staticModels) {
214  uint16_t numStaticMeshes = file.readU16();
215  for (unsigned int s = 0; s < numStaticMeshes; s++) {
216  // Absolute position in world coordinates
217  int32_t x = file.read32();
218  int32_t y = file.read32();
219  int32_t z = file.read32();
220 
221  // High two bits (0xC000) indicate steps of
222  // 90 degrees (eg. (rotation >> 14) * 90)
223  uint16_t rotation = file.readU16();
224 
225  // Constant lighting, 0xFFFF means use mesh lighting
227  uint16_t intensity1 = file.readU16();
228  uint16_t intensity2 = file.readU16();
229 
230  // Which StaticMesh item to draw
231  uint16_t objectID = file.readU16();
232 
233  staticModels.push_back(new StaticModel(glm::vec3(x, y, z),
234  glm::radians((rotation >> 14) * 90.0f),
235  objectID));
236  }
237 }
238 
239 void LoaderTR2::loadRoomDataEnd(int16_t& alternateRoom, unsigned int& roomFlags) {
240  alternateRoom = file.read16();
241 
242  uint16_t flags = file.readU16();
243  roomFlags = 0;
244  if (flags & 0x0001) {
245  roomFlags |= RoomFlagUnderWater;
246  }
247 }
248 
250  vert.x = file.read16();
251  vert.y = file.read16();
252  vert.z = file.read16();
253  vert.light1 = file.read16();
254  vert.attributes = file.readU16();
255  vert.light2 = file.read16();
256 }
257 
258 void LoaderTR2::loadRoomMesh(std::vector<IndexedRectangle>& rectangles,
259  std::vector<IndexedRectangle>& triangles,
260  uint16_t& numRectangles, uint16_t& numTriangles) {
261  numRectangles = file.readU16();
262  for (unsigned int r = 0; r < numRectangles; r++) {
263  // Indices into the vertex list read just before
264  uint16_t vertex1 = file.readU16();
265  uint16_t vertex2 = file.readU16();
266  uint16_t vertex3 = file.readU16();
267  uint16_t vertex4 = file.readU16();
268 
269  // Index into the object-texture list
270  uint16_t texture = file.readU16();
271 
272  rectangles.emplace_back(texture, vertex1, vertex2, vertex3, vertex4);
273  }
274 
275  numTriangles = file.readU16();
276  for (unsigned int t = 0; t < numTriangles; t++) {
277  // Indices into the room vertex list
278  uint16_t vertex1 = file.readU16();
279  uint16_t vertex2 = file.readU16();
280  uint16_t vertex3 = file.readU16();
281 
282  // Index into the object-texture list
283  uint16_t texture = file.readU16();
284 
285  triangles.emplace_back(texture, vertex1, vertex2, vertex3);
286  }
287 }
288 
290  uint16_t numRooms = file.readU16();
291  for (unsigned int i = 0; i < numRooms; i++) {
292  // Room Header
293  int32_t xOffset = file.read32();
294  int32_t zOffset = file.read32();
295  int32_t yBottom = file.read32(); // lowest point == largest y value
296  int32_t yTop = file.read32(); // highest point == smallest y value
297 
298  glm::vec3 pos(xOffset, 0.0f, zOffset);
299 
300  // Number of data words (2 bytes) to follow
301  uint32_t dataToFollow = file.readU32();
302 
303  glm::vec3 bbox[2] = {
304  glm::vec3(0.0f, 0.0f, 0.0f),
305  glm::vec3(0.0f, 0.0f, 0.0f)
306  };
307 
308  uint16_t numVertices = file.readU16();
309  std::vector<RoomVertexTR2> vertices;
310  for (unsigned int v = 0; v < numVertices; v++) {
311  RoomVertexTR2 vert;
312  loadRoomVertex(vert);
313  vertices.push_back(vert);
314 
315  // Fill bounding box
316  if (v == 0) {
317  for (int i = 0; i < 2; i++) {
318  bbox[i].x = vert.x;
319  bbox[i].y = vert.y;
320  bbox[i].z = vert.z;
321  }
322  } else {
323  if (vert.x < bbox[0].x)
324  bbox[0].x = vert.x;
325  if (vert.x > bbox[1].x)
326  bbox[1].x = vert.x;
327  if (vert.y < bbox[0].y)
328  bbox[0].y = vert.y;
329  if (vert.y > bbox[1].y)
330  bbox[1].y = vert.y;
331  if (vert.z < bbox[0].z)
332  bbox[0].z = vert.z;
333  if (vert.z > bbox[1].z)
334  bbox[1].z = vert.z;
335  }
336  }
337 
338  bbox[0] += pos;
339  bbox[1] += pos;
340 
341  std::vector<IndexedRectangle> rectangles;
342  std::vector<IndexedRectangle> triangles;
343  uint16_t numRectangles, numTriangles;
344  loadRoomMesh(rectangles, triangles, numRectangles, numTriangles);
345 
346  uint16_t numSprites = file.readU16();
347  std::vector<RoomSprite*> roomSprites;
348  for (unsigned int s = 0; s < numSprites; s++) {
349  uint16_t vertex = file.readU16(); // Index into vertex list
350  uint16_t sprite = file.readU16(); // Index into sprite list
351 
352  auto& v = vertices.at(vertex);
353  roomSprites.push_back(new RoomSprite(glm::vec3(v.x, v.y, v.z) + pos, sprite));
354  }
355 
356  uint16_t numPortals = file.readU16();
357  std::vector<Portal*> portals;
358  for (unsigned int p = 0; p < numPortals; p++) {
359  // Which room this portal leads to
360  uint16_t adjoiningRoom = file.readU16();
361 
362  // Which way the portal faces
363  // The normal points away from the adjacent room
364  // To be seen through, it must point toward the viewpoint
365  int16_t xNormal = file.read16();
366  int16_t yNormal = file.read16();
367  int16_t zNormal = file.read16();
368 
369  // The corners of this portal
370  // The right-hand rule applies with respect to the normal
371  int16_t xCorner1 = file.read16();
372  int16_t yCorner1 = file.read16();
373  int16_t zCorner1 = file.read16();
374  int16_t xCorner2 = file.read16();
375  int16_t yCorner2 = file.read16();
376  int16_t zCorner2 = file.read16();
377  int16_t xCorner3 = file.read16();
378  int16_t yCorner3 = file.read16();
379  int16_t zCorner3 = file.read16();
380  int16_t xCorner4 = file.read16();
381  int16_t yCorner4 = file.read16();
382  int16_t zCorner4 = file.read16();
383 
384  // TODO translate vertices by room offset!
385 
386  portals.push_back(new Portal(adjoiningRoom,
387  glm::vec3(xNormal, yNormal, zNormal),
388  glm::vec3(xCorner1, yCorner1, zCorner1),
389  glm::vec3(xCorner2, yCorner2, zCorner2),
390  glm::vec3(xCorner3, yCorner3, zCorner3),
391  glm::vec3(xCorner4, yCorner4, zCorner4)));
392  }
393 
394  uint16_t numZSectors = file.readU16();
395  uint16_t numXSectors = file.readU16();
396  for (unsigned int s = 0; s < (numZSectors * numXSectors); s++) {
397  // Sectors are 1024*1024 world coordinates. Floor and Ceiling are
398  // signed numbers of 256 units of height.
399  // Floor/Ceiling value of 0x81 is used to indicate impenetrable
400  // walls around the sector.
401  // Floor values are used by the original engine to determine
402  // what objects can be traversed and how. Relative steps of 1 (256)
403  // can be walked up, 2..7 must be jumped up, larger than 7 is too high
404  // If RoomAbove/Below is not none, the Ceiling/Floor is a collisional
405  // portal to that room
406  uint16_t indexFloorData = file.readU16();
407  uint16_t indexBox = file.readU16(); // 0xFFFF if none
408  uint8_t roomBelow = file.readU8(); // 0xFF if none
409  int8_t floor = file.read8(); // Absolute height of floor (divided by 256)
410  uint8_t roomAbove = file.readU8(); // 0xFF if none
411  int8_t ceiling = file.read8(); // Absolute height of ceiling (/ 256)
412 
413  // In TR3 indexBox is more complicated. Only bits 4-14 are the 'real' index.
414  // Bits 0-3 are most likely some kind of flag (footstep sound?).
415  // There is a special value of the 'real' index, 2047 or 0x7FF.
416 
417  bool wall = false;
418  if ((((uint8_t)floor) == 0x81) || (((uint8_t)ceiling) == 0x81)) {
419  wall = true;
420  }
421 
422  //room->addSector(new Sector(floor * 256.0f, ceiling * 256.0f, wall));
423  // TODO store sectors
424  }
425 
426  loadRoomLights();
427 
428  std::vector<StaticModel*> staticModels;
429  loadRoomStaticMeshes(staticModels);
430 
431  int16_t alternateRoom = -1;
432  unsigned int roomFlags = 0;
433  loadRoomDataEnd(alternateRoom, roomFlags);
434 
435  BoundingBox* boundingbox = new BoundingBox(bbox[0], bbox[1]);
436  RoomMesh* mesh = new RoomMesh(vertices, rectangles, triangles);
437  Room* room = new Room(pos, boundingbox, mesh, roomFlags, alternateRoom,
438  numXSectors, numZSectors, i);
439 
440  for (auto p : portals)
441  room->addPortal(p);
442 
443  for (auto m : staticModels)
444  room->addModel(m);
445 
446  for (auto s : roomSprites)
447  room->addSprite(s);
448 
449  getWorld().addRoom(room);
450 
451  // Sanity check
452  if ((numPortals == 0) && (numVertices == 0)
453  && (numRectangles == 0) && (numTriangles == 0))
454  Log::get(LOG_DEBUG) << "LoaderTR2: Room " << i << " seems invalid: " << numPortals << "p "
455  << numRectangles << "r " << numTriangles << "t " << numVertices
456  << "v" << Log::endl;
457  }
458 
459  if (numRooms > 0)
460  Log::get(LOG_INFO) << "LoaderTR2: Found " << numRooms << " Rooms!" << Log::endl;
461  else
462  Log::get(LOG_INFO) << "LoaderTR2: No Rooms in this Level?!" << Log::endl;
463 }
464 
466  uint32_t numFloorData = file.readU32();
467  for (unsigned int f = 0; f < numFloorData; f++) {
468  uint16_t unused = file.readU16();
469 
470  // TODO store floor data somewhere
471  }
472 
473  if (numFloorData > 0)
474  Log::get(LOG_INFO) << "LoaderTR2: Found " << numFloorData << " words FloorData, unimplemented!" <<
475  Log::endl;
476  else
477  Log::get(LOG_INFO) << "LoaderTR2: No FloorData in this level?!" << Log::endl;
478 }
479 
481  uint32_t numSpriteTextures = file.readU32();
482  for (unsigned int s = 0; s < numSpriteTextures; s++) {
483  uint16_t tile = file.readU16();
484  uint8_t x = file.readU8();
485  uint8_t y = file.readU8();
486  uint16_t width = file.readU16(); // Actually (width * 256) + 255
487  uint16_t height = file.readU16(); // Actually (height * 256) + 255
488 
489  // Required for what?
490  int16_t leftSide = file.read16();
491  int16_t topSide = file.read16();
492  int16_t rightSide = file.read16();
493  int16_t bottomSide = file.read16();
494 
495  Sprite* sp = new Sprite(tile, x, y, width, height);
496  getWorld().addSprite(sp);
497  }
498 
499  uint32_t numSpriteSequences = file.readU32();
500  for (unsigned int s = 0; s < numSpriteSequences; s++) {
501  int32_t objectID = file.read32(); // Item identifier, matched in Items[]
502  int16_t negativeLength = file.read16(); // Negative sprite count
503  int16_t offset = file.read16(); // Where sequence starts in sprite texture list
504 
505  assertLessThan(negativeLength, 0);
506  assertGreaterThanEqual(offset, 0);
507  assertLessThanEqual(offset + (negativeLength * -1), numSpriteTextures);
508 
509  SpriteSequence* ss = new SpriteSequence(objectID, offset, (negativeLength * -1));
511  }
512 
513  if ((numSpriteTextures > 0) || (numSpriteSequences > 0))
514  Log::get(LOG_INFO) << "LoaderTR2: Found " << numSpriteTextures << " Sprites in " <<
515  numSpriteSequences <<
516  " Sequences!" << Log::endl;
517  else
518  Log::get(LOG_INFO) << "LoaderTR2: No Sprites in this level?!" << Log::endl;
519 }
520 
521 // ---- Meshes ----
522 
523 int LoaderTR2::getPaletteIndex(uint16_t index) {
524  return (index & 0xFF00) >> 8; // Use index into 16bit palette
525 }
526 
528  // Number of bitu16s of mesh data to follow
529  // Read all the mesh data into a buffer, because
530  // only afterward we can read the number of meshes
531  // in this data block
532  uint32_t numMeshData = file.readU32();
533  std::vector<uint16_t> buffer;
534  for (unsigned int i = 0; i < numMeshData; i++) {
535  buffer.push_back(file.readU16());
536  }
537 
538  uint32_t numMeshPointers = file.readU32();
539  for (unsigned int i = 0; i < numMeshPointers; i++) {
540  uint32_t meshPointer = file.readU32();
541 
542  if (numMeshData < (meshPointer / 2)) {
543  Log::get(LOG_DEBUG) << "LoaderTR2: Invalid Mesh: "
544  << (meshPointer / 2) << " > " << numMeshData << Log::endl;
545  continue;
546  }
547 
548  char* tmpPtr = reinterpret_cast<char*>(&buffer[meshPointer / 2]);
549  BinaryMemory mem(tmpPtr, (numMeshData * 2) - meshPointer);
550 
551  int16_t mx = mem.read16();
552  int16_t my = mem.read16();
553  int16_t mz = mem.read16();
554  int32_t collisionSize = mem.read32();
555  // TODO store mesh collision info somewhere
556 
557  uint16_t numVertices = mem.readU16();
558  std::vector<glm::vec3> vertices;
559  for (int v = 0; v < numVertices; v++) {
560  int16_t x = mem.read16();
561  int16_t y = mem.read16();
562  int16_t z = mem.read16();
563  vertices.emplace_back(x, y, z);
564  }
565 
566  int16_t numNormals = mem.read16();
567  if (numNormals > 0) {
568  // External vertex lighting is used, with the lighting calculated
569  // from the rooms ambient and point-source lighting values. The
570  // latter appears to use a simple Lambert law for directionality:
571  // intensity is proportional to
572  // max((normal direction).(direction to source), 0)
573  for (int n = 0; n < numNormals; n++) {
574  int16_t x = mem.read16();
575  int16_t y = mem.read16();
576  int16_t z = mem.read16();
577 
578  //mesh->addNormal(glm::vec3(x, y, z));
579  }
580  } else if (numNormals < 0) {
581  // Internal vertex lighting is used,
582  // using the data included with the mesh
583  for (int l = 0; l < (numNormals * -1); l++) {
584  int16_t light = mem.read16();
585  // TODO store lights somewhere
586  }
587  }
588 
589  int16_t numTexturedRectangles = mem.read16();
590  std::vector<IndexedRectangle> texturedRectangles;
591  for (int r = 0; r < numTexturedRectangles; r++) {
592  uint16_t vertex1 = mem.readU16();
593  uint16_t vertex2 = mem.readU16();
594  uint16_t vertex3 = mem.readU16();
595  uint16_t vertex4 = mem.readU16();
596  uint16_t texture = mem.readU16();
597 
598  texturedRectangles.emplace_back(texture, vertex1, vertex2, vertex3, vertex4);
599  }
600 
601  int16_t numTexturedTriangles = mem.read16();
602  std::vector<IndexedRectangle> texturedTriangles;
603  for (int t = 0; t < numTexturedTriangles; t++) {
604  uint16_t vertex1 = mem.readU16();
605  uint16_t vertex2 = mem.readU16();
606  uint16_t vertex3 = mem.readU16();
607  uint16_t texture = mem.readU16();
608 
609  texturedTriangles.emplace_back(texture, vertex1, vertex2, vertex3);
610  }
611 
612  int16_t numColoredRectangles = mem.read16();
613  std::vector<IndexedColoredRectangle> coloredRectangles;
614  for (int r = 0; r < numColoredRectangles; r++) {
615  uint16_t vertex1 = mem.readU16();
616  uint16_t vertex2 = mem.readU16();
617  uint16_t vertex3 = mem.readU16();
618  uint16_t vertex4 = mem.readU16();
619  uint16_t texture = mem.readU16();
620 
621  int index = getPaletteIndex(texture);
622  coloredRectangles.emplace_back(index, vertex1, vertex2, vertex3, vertex4);
623  }
624 
625  int16_t numColoredTriangles = mem.read16();
626  std::vector<IndexedColoredRectangle> coloredTriangles;
627  for (int t = 0; t < numColoredTriangles; t++) {
628  uint16_t vertex1 = mem.readU16();
629  uint16_t vertex2 = mem.readU16();
630  uint16_t vertex3 = mem.readU16();
631  uint16_t texture = mem.readU16();
632 
633  int index = getPaletteIndex(texture);
634  coloredTriangles.emplace_back(index, vertex1, vertex2, vertex3);
635  }
636 
637  Mesh* mesh = new Mesh(vertices, texturedRectangles, texturedTriangles,
638  coloredRectangles, coloredTriangles);
639  getWorld().addMesh(mesh);
640  }
641 
642  if (numMeshPointers > 0)
643  Log::get(LOG_INFO) << "LoaderTR2: Found " << numMeshPointers << " Meshes!" << Log::endl;
644  else
645  Log::get(LOG_INFO) << "LoaderTR2: No Meshes in this level?!" << Log::endl;
646 }
647 
649  uint32_t numStaticMeshes = file.readU32();
650  for (unsigned int s = 0; s < numStaticMeshes; s++) {
651  uint32_t objectID = file.readU32(); // Matched in Items[]
652  uint16_t mesh = file.readU16(); // Offset into MeshPointers[]
653 
654  // tr2_vertex BoundingBox[2][2];
655  // First index is which one, second index is opposite corners
656  int16_t x11 = file.read16();
657  int16_t y11 = file.read16();
658  int16_t z11 = file.read16();
659 
660  int16_t x12 = file.read16();
661  int16_t y12 = file.read16();
662  int16_t z12 = file.read16();
663 
664  int16_t x21 = file.read16();
665  int16_t y21 = file.read16();
666  int16_t z21 = file.read16();
667 
668  int16_t x22 = file.read16();
669  int16_t y22 = file.read16();
670  int16_t z22 = file.read16();
671 
672  // Meaning uncertain. Usually 2, and 3 for objects Lara can
673  // travel through, like TR2s skeletons and underwater plants
674  uint16_t flags = file.readU16();
675 
676  BoundingBox* bbox1 = new BoundingBox(glm::vec3(x11, y11, z11), glm::vec3(x12, y12, z12));
677  BoundingBox* bbox2 = new BoundingBox(glm::vec3(x21, y21, z21), glm::vec3(x22, y22, z22));
678  getWorld().addStaticMesh(new StaticMesh(objectID, mesh, bbox1, bbox2));
679  }
680 
681  if (numStaticMeshes > 0)
682  Log::get(LOG_INFO) << "LoaderTR2: Found " << numStaticMeshes << " StaticMeshes!" << Log::endl;
683  else
684  Log::get(LOG_INFO) << "LoaderTR2: No StaticMeshes in this level?!" << Log::endl;
685 }
686 
687 // ---- Moveables ----
688 
689 struct Animation_t {
690  uint32_t frameOffset;
691  uint8_t frameRate, frameSize;
692  uint16_t stateID, frameStart, frameEnd, nextAnimation;
693  uint16_t nextFrame, numStateChanges, stateChangeOffset;
694  uint16_t numAnimCommands, animCommandOffset;
695 
696  Animation_t(uint32_t fo, uint8_t fr, uint8_t fs, uint16_t si,
697  uint16_t fst, uint16_t fe, uint16_t na, uint16_t nf,
698  uint16_t ns, uint16_t so, uint16_t nac, uint16_t ao)
699  : frameOffset(fo), frameRate(fr), frameSize(fs),
700  stateID(si), frameStart(fst), frameEnd(fe), nextAnimation(na),
701  nextFrame(nf), numStateChanges(ns), stateChangeOffset(so),
702  numAnimCommands(nac), animCommandOffset(ao) { }
703 };
704 
705 struct StateChange_t {
706  uint16_t stateID, numAnimDispatches, animDispatchOffset;
707 
708  StateChange_t(uint16_t s, uint16_t n, uint16_t a)
709  : stateID(s), numAnimDispatches(n), animDispatchOffset(a) { }
710 };
711 
712 struct AnimDispatch_t {
713  int16_t low, high, nextAnimation, nextFrame;
714 
715  AnimDispatch_t(int16_t l, int16_t h, int16_t na, int16_t nf)
716  : low(l), high(h), nextAnimation(na), nextFrame(nf) { }
717 };
718 
719 void LoaderTR2::loadAngleSet(BoneFrame* bf, BinaryReader& frame, uint16_t numMeshes,
720  uint16_t startingMesh, uint32_t meshTree,
721  uint32_t numMeshTrees, std::vector<int32_t> meshTrees) {
722  for (int i = 0; i < numMeshes; i++) {
723  int mesh = startingMesh + i;
724  glm::vec3 offset(0.0f, 0.0f, 0.0f);
725  float rotation[3] = { 0.0f, 0.0f, 0.0f };
726  char flag = (i == 0) ? 2 : 0;
727 
728  // Nonprimary tag - positioned relative to first tag
729  if (i != 0) {
730  char* tmp = reinterpret_cast<char*>(&meshTrees[0]) + meshTree; // TODO (meshTree * 4)?
731  tmp += (i - 1) * 16; // TODO ?
732  BinaryMemory tree(tmp, (numMeshTrees * 4) - meshTree - ((i - 1) * 16));
733  flag = (char)tree.readU32();
734  offset.x = tree.read32();
735  offset.y = tree.read32();
736  offset.z = tree.read32();
737 
738  uint16_t a = frame.readU16();
739  if (a & 0xC000) {
740  // Single angle
741  int index = 0;
742  if ((a & 0x8000) && (a & 0x4000))
743  index = 2;
744  else if (a & 0x4000)
745  index = 1;
746  rotation[index] = ((float)(a & 0x03FF)) * 360.0f / 1024.0f;
747  } else {
748  // Three angles
749  uint16_t b = frame.readU16();
750  rotation[0] = (a & 0x3FF0) >> 4;
751  rotation[1] = ((a & 0x000F) << 6) | ((b & 0xFC00) >> 10);
752  rotation[2] = b & 0x03FF;
753  for (int i = 0; i < 3; i++)
754  rotation[i] = rotation[i] * 360.0f / 1024.0f;
755  }
756  }
757 
758  glm::vec3 rot(rotation[0], rotation[1], rotation[2]);
759  BoneTag* bt = new BoneTag(mesh, offset, rot, flag);
760  bf->add(bt);
761  }
762 }
763 
764 BoneFrame* LoaderTR2::loadFrame(BinaryReader& frame, uint16_t numMeshes,
765  uint16_t startingMesh, uint32_t meshTree,
766  uint32_t numMeshTrees, std::vector<int32_t> meshTrees) {
767  int16_t bb1x = frame.read16();
768  int16_t bb1y = frame.read16();
769  int16_t bb1z = frame.read16();
770  int16_t bb2x = frame.read16();
771  int16_t bb2y = frame.read16();
772  int16_t bb2z = frame.read16();
773 
774  glm::vec3 pos;
775  pos.x = frame.read16();
776  pos.y = frame.read16();
777  pos.z = frame.read16();
778 
779  BoneFrame* bf = new BoneFrame(pos);
780  loadAngleSet(bf, frame, numMeshes, startingMesh, meshTree, numMeshTrees, meshTrees);
781 
782  return bf;
783 }
784 
786  uint32_t numAnimations = file.readU32();
787  std::vector<Animation_t> animations;
788  for (unsigned int a = 0; a < numAnimations; a++) {
789  // *Byte* Offset into Frames[] (so divide by 2!)
790  uint32_t frameOffset = file.readU32();
791  uint8_t frameRate = file.readU8(); // Engine ticks per frame
792 
793  // Number of bit16s in Frames[] used by this animation
794  // Be careful when parsing frames using the FrameSize value
795  // as the size of each frame, since an animations frame range
796  // may extend into the next animations frame range, and that
797  // may have a different FrameSize value.
798  uint8_t frameSize = file.readU8();
799 
800  uint16_t stateID = file.readU16();
801 
802  file.seek(file.tell() + 8); // Skip 8 unknown bytes
803 
804  uint16_t frameStart = file.readU16(); // First frame in this animation
805  uint16_t frameEnd = file.readU16(); // Last frame in this animation
806  uint16_t nextAnimation = file.readU16();
807  uint16_t nextFrame = file.readU16();
808  uint16_t numStateChanges = file.readU16();
809  uint16_t stateChangeOffset = file.readU16(); // Index into StateChanges[]
810  uint16_t numAnimCommands = file.readU16(); // How many animation commands to use
811  uint16_t animCommandOffset = file.readU16(); // Index into AnimCommand[]
812 
813  animations.emplace_back(frameOffset, frameRate, frameSize,
814  stateID, frameStart, frameEnd, nextAnimation, nextFrame, numStateChanges,
815  stateChangeOffset, numAnimCommands, animCommandOffset);
816  }
817 
818  if (numAnimations > 0)
819  Log::get(LOG_INFO) << "LoaderTR2: Found " << numAnimations << " Animations!" << Log::endl;
820  else
821  Log::get(LOG_INFO) << "LoaderTR2: No Animations in this level?!" << Log::endl;
822 
823  uint32_t numStateChanges = file.readU32();
824  std::vector<StateChange_t> stateChanges;
825  for (unsigned int s = 0; s < numStateChanges; s++) {
826  uint16_t stateID = file.readU16();
827  uint16_t numAnimDispatches = file.readU16(); // Number of ranges (always 1..5?)
828  uint16_t animDispatchOffset = file.readU16(); // Index into AnimDispatches[]
829 
830  stateChanges.emplace_back(stateID, numAnimDispatches, animDispatchOffset);
831  }
832 
833  if (numStateChanges > 0)
834  Log::get(LOG_INFO) << "LoaderTR2: Found " << numStateChanges << " StateChanges!" << Log::endl;
835  else
836  Log::get(LOG_INFO) << "LoaderTR2: No StateChanges in this level?!" << Log::endl;
837 
838  uint32_t numAnimDispatches = file.readU32();
839  std::vector<AnimDispatch_t> animDispatches;
840  for (unsigned int a = 0; a < numAnimDispatches; a++) {
841  int16_t low = file.read16(); // Lowest frame that uses this range
842  int16_t high = file.read16(); // Highest frame (+1?) that uses this range
843  int16_t nextAnimation = file.read16(); // Animation to go to
844  int16_t nextFrame = file.read16(); // Frame offset to go to
845 
846  animDispatches.emplace_back(low, high, nextAnimation, nextFrame);
847  }
848 
849  if (numAnimDispatches > 0)
850  Log::get(LOG_INFO) << "LoaderTR2: Found " << numAnimDispatches << " AnimationDispatches!" <<
851  Log::endl;
852  else
853  Log::get(LOG_INFO) << "LoaderTR2: No AnimationDispatches in this level?!" << Log::endl;
854 
855  uint32_t numAnimCommands = file.readU32();
856  std::vector<int16_t> animCommands;
857  for (unsigned int a = 0; a < numAnimCommands; a++) {
858  // A list of Opcodes with zero or more operands each,
859  // some referring to the whole animation (jump/grab points),
860  // some to specific frames (sound, bubbles, ...).
861  animCommands.push_back(file.read16());
862  }
863 
864  if (numAnimCommands > 0)
865  Log::get(LOG_INFO) << "LoaderTR2: Found " << numAnimCommands << " AnimationCommands!" << Log::endl;
866  else
867  Log::get(LOG_INFO) << "LoaderTR2: No AnimationCommands in this level?!" << Log::endl;
868 
869  // This is really one uint32_t flags, followed by
870  // three int32_t x, y, z. However, we're given the number
871  // of 32bits, as well as byte indices later, so we store
872  // it as a single list of int32_t.
873  uint32_t numMeshTrees = file.readU32();
874  std::vector<int32_t> meshTrees;
875  for (unsigned int m = 0; m < numMeshTrees; m++) {
876  // 0x0002 - Put parent mesh on the mesh stack
877  // 0x0001 - Pop mesh from stack, use as parent mesh
878  // When both are not set, use previous mesh as parent mesh
879  // When both are set, do 0x0001 first, then 0x0002, thereby
880  // reading the stack but not changing it
881  //uint32_t flags = file.readU32();
882 
883  // Offset of mesh origin from the parent mesh origin
884  //int32_t x = file.read32();
885  //int32_t y = file.read32();
886  //int32_t z = file.read32();
887 
888  meshTrees.push_back(file.read32());
889  }
890 
891  if (numMeshTrees > 0)
892  Log::get(LOG_INFO) << "LoaderTR2: Found " << numMeshTrees << " MeshTrees!" << Log::endl;
893  else
894  Log::get(LOG_INFO) << "LoaderTR2: No MeshTrees in this level?!" << Log::endl;
895 
896  uint32_t numFrames = file.readU32();
897  std::vector<uint16_t> frames;
898  for (unsigned int f = 0; f < numFrames; f++) {
899  // int16 bb1x, bb1y, bb1z
900  // int16 bb2x, bb2y, bb2z
901  // int16 offsetX, offsetY, offsetZ
902  // What follows next is a list of angles with numMeshes (from Moveable) entries.
903  // If the top bit (0x8000) of the first uint16 is set, a single X angle follows,
904  // if the second bit (0x4000) is set, a Y angle follows, both are a Z angle.
905  // If none is set, it's a three-axis rotation. The next 10 bits (0x3FF0) are
906  // the X rotation, the next 10 (0x000F 0xFC00) are Y, the next (0x03FF) are
907  // the Z rotation. The scaling is always 0x100->90deg.
908  // Rotation order: Y, X, Z!
909  frames.push_back(file.readU16());
910  }
911 
912  if (numFrames > 0)
913  Log::get(LOG_INFO) << "LoaderTR2: Found " << numFrames << " Frames!" << Log::endl;
914  else
915  Log::get(LOG_INFO) << "LoaderTR2: No Frames in this level?!" << Log::endl;
916 
917  uint32_t numMoveables = file.readU32();
918  for (unsigned int m = 0; m < numMoveables; m++) {
919  // Item identifier, matched in Items[]
920  uint32_t objectID = file.readU32();
921  uint16_t numMeshes = file.readU16();
922  uint16_t startingMesh = file.readU16(); // Offset into MeshPointers[]
923  uint32_t meshTree = file.readU32(); // Offset into MeshTree[]
924  // *Byte* offset into Frames[] (divide by 2 for Frames[i])
925  uint32_t frameOffset = file.readU32(); // Only needed if no animation
926 
927  // If animation index is 0xFFFF, the object is stationary or
928  // animated by the engine (ponytail)
929  uint16_t animation = file.readU16();
930 
931  /*
932  if (animation == 0xFFFF) {
933  */
934  // Just add the frame indicated in frameOffset, nothing else
935  char* tmp = reinterpret_cast<char*>(&frames[0]) + frameOffset;
936  BinaryMemory frame(tmp, (numFrames * 2) - frameOffset);
937 
938  if (((numFrames * 2) - frameOffset) <= 0)
939  continue; // TR1/LEVEL3A crashes without this?!
940 
941  BoneFrame* bf = loadFrame(frame, numMeshes, startingMesh, meshTree, numMeshTrees, meshTrees);
942  AnimationFrame* af = new AnimationFrame(0);
943  af->add(bf);
944 
945  SkeletalModel* sm = new SkeletalModel(objectID);
946  sm->add(af);
948  /*
949  } else {
950  // TODO Add the whole animation hierarchy
951  auto& anim = animations.at(animation);
952 
953  char* tmp = reinterpret_cast<char*>(&frames[0]) + anim.frameOffset;
954  BinaryMemory frame(tmp, (numFrames * 2) - anim.frameOffset);
955  AnimationFrame* af = new AnimationFrame(0);
956 
957  for (int i = 0; i < ((anim.frameEnd - anim.frameStart) + 1); i++) {
958  BoneFrame* bf = loadFrame(frame, numMeshes, startingMesh,
959  meshTree, numMeshTrees, meshTrees);
960  af->add(bf);
961  }
962 
963  SkeletalModel* sm = new SkeletalModel(objectID);
964  sm->add(af);
965  getWorld().addSkeletalModel(sm);
966  }
967  */
968  }
969 
970  if (numMoveables > 0)
971  Log::get(LOG_INFO) << "LoaderTR2: Found " << numMoveables << " Moveables!" << Log::endl;
972  else
973  Log::get(LOG_INFO) << "LoaderTR2: No Moveables in this level?!" << Log::endl;
974 }
975 
977  uint32_t numItems = file.readU32();
978  for (unsigned int i = 0; i < numItems; i++) {
979  int16_t objectID = file.read16();
980  int16_t room = file.read16();
981 
982  // Item position in world coordinates
983  int32_t x = file.read32();
984  int32_t y = file.read32();
985  int32_t z = file.read32();
986 
987  uint16_t angle = file.readU16(); // (0xC000 >> 14) * 90deg
988  int16_t intensity1 = file.read16(); // Constant lighting; -1 means mesh lighting
989  int16_t intensity2 = file.read16(); // Almost always like intensity1
990 
991  // 0x0100 - Initially visible
992  // 0x3E00 - Activation mask, open, can be XORed with related FloorData list fields.
993  uint16_t flags = file.readU16();
994 
995  glm::vec3 pos(
996  static_cast<float>(x),
997  static_cast<float>(y),
998  static_cast<float>(z)
999  );
1000 
1001  glm::vec3 rot(
1002  0.0f,
1003  glm::radians(((angle >> 14) & 0x03) * 90.0f),
1004  0.0f
1005  );
1006 
1007  Entity* e = new Entity(objectID, room, pos, rot);
1008  getWorld().addEntity(e);
1009 
1010  if (objectID == 0) {
1011  Game::setLara(getWorld().sizeEntity() - 1);
1012  }
1013  }
1014 
1015  if (numItems > 0)
1016  Log::get(LOG_INFO) << "LoaderTR2: Found " << numItems << " Items!" << Log::endl;
1017  else
1018  Log::get(LOG_INFO) << "LoaderTR2: No Items in this level?!" << Log::endl;
1019 }
1020 
1022  uint32_t numBoxes = file.readU32();
1023  for (unsigned int b = 0; b < numBoxes; b++) {
1024  // Sectors (* 1024 units)
1025  uint8_t zMin = file.readU8();
1026  uint8_t zMax = file.readU8();
1027  uint8_t xMin = file.readU8();
1028  uint8_t xMax = file.readU8();
1029 
1030  int16_t trueFloor = file.read16(); // Y value (no scaling)
1031 
1032  // Index into overlaps[]. The high bit is sometimes set
1033  // this occurs in front of swinging doors and the like
1034  uint16_t overlapIndex = file.readU16();
1035 
1036  // TODO store boxes somewhere
1037  }
1038 
1039  uint32_t numOverlaps = file.readU32();
1040  std::vector<std::vector<uint16_t>> overlaps;
1041  overlaps.emplace_back();
1042  unsigned int list = 0;
1043  for (unsigned int o = 0; o < numOverlaps; o++) {
1044  // Apparently used by NPCs to decide where to go next.
1045  // List of neighboring boxes for each box.
1046  // Each entry is a uint16, 0x8000 set marks end of list.
1047  uint16_t e = file.readU16();
1048  overlaps.at(list).push_back(e);
1049  if (e & 0x8000) {
1050  overlaps.emplace_back();
1051  list++;
1052  }
1053  }
1054 
1055  // TODO store overlaps somewhere
1056 
1057  for (unsigned int z = 0; z < numBoxes; z++) {
1058  // Normal room state
1059  int16_t ground1 = file.read16();
1060  int16_t ground2 = file.read16();
1061  int16_t ground3 = file.read16();
1062  int16_t ground4 = file.read16();
1063  int16_t fly = file.read16();
1064 
1065  // Alternate room state
1066  int16_t ground1alt = file.read16();
1067  int16_t ground2alt = file.read16();
1068  int16_t ground3alt = file.read16();
1069  int16_t ground4alt = file.read16();
1070  int16_t flyAlt = file.read16();
1071 
1072  // TODO store zones somewhere
1073  }
1074 
1075  if ((numBoxes > 0) || (numOverlaps > 0))
1076  Log::get(LOG_INFO) << "LoaderTR2: Found NPC NavigationHints (" << numBoxes
1077  << ", " << numOverlaps << ", " << list << "), unimplemented!" << Log::endl;
1078  else
1079  Log::get(LOG_INFO) << "LoaderTR2: No NPC NavigationHints in this level?!" << Log::endl;
1080 }
1081 
1082 // ---- Sound ----
1083 
1085  uint32_t numSoundSources = file.readU32();
1086  for (unsigned int s = 0; s < numSoundSources; s++) {
1087  // Absolute world coordinate positions of sound source
1088  int32_t x = file.read32();
1089  int32_t y = file.read32();
1090  int32_t z = file.read32();
1091 
1092  // Internal sound index
1093  uint16_t soundID = file.readU16();
1094 
1095  // Unknown, 0x40, 0x80 or 0xC0
1096  uint16_t flags = file.readU16();
1097 
1098  SoundManager::addSoundSource(glm::vec3(x, y, z), soundID, flags);
1099  }
1100 
1101  if (numSoundSources > 0)
1102  Log::get(LOG_INFO) << "LoaderTR2: Found " << numSoundSources << " SoundSources" << Log::endl;
1103  else
1104  Log::get(LOG_INFO) << "LoaderTR2: No SoundSources in this level?!" << Log::endl;
1105 }
1106 
1108  for (int i = 0; i < 370; i++) {
1110  }
1111 }
1112 
1114  uint32_t numSoundDetails = file.readU32();
1115  for (unsigned int s = 0; s < numSoundDetails; s++) {
1116  uint16_t sample = file.readU16(); // Index into SampleIndices[]
1117  uint16_t volume = file.readU16();
1118 
1119  // sound range? distance at which this sound can be heard?
1120  uint16_t unknown1 = file.readU16();
1121 
1122  // Bits 8-15: priority?
1123  // Bits 2-7: number of samples in this group
1124  // Bits 0-1: channel number?
1125  uint16_t unknown2 = file.readU16();
1126 
1127  SoundManager::addSoundDetail(sample, ((float)volume) / 32767.0f);
1128  }
1129 
1130  if (numSoundDetails > 0)
1131  Log::get(LOG_INFO) << "LoaderTR2: Found " << numSoundDetails << " SoundDetails" << Log::endl;
1132  else
1133  Log::get(LOG_INFO) << "LoaderTR2: No SoundDetails in this level?!" << Log::endl;
1134 }
1135 
1137  uint32_t numSampleIndices = file.readU32();
1138  for (unsigned int i = 0; i < numSampleIndices; i++) {
1140  }
1141 
1142  if (numSampleIndices > 0)
1143  Log::get(LOG_INFO) << "LoaderTR2: Found " << numSampleIndices << " SampleIndices" << Log::endl;
1144  else
1145  Log::get(LOG_INFO) << "LoaderTR2: No SampleIndices in this level?!" << Log::endl;
1146 }
1147 
1149  size_t dir = f.find_last_of("/\\");
1150  if (dir != std::string::npos) {
1151  f.replace(dir + 1, std::string::npos, "MAIN.SFX");
1152  } else {
1153  f = "MAIN.SFX";
1154  }
1155 
1156  BinaryFile sfx;
1157  if (sfx.open(f) != 0) {
1158  Log::get(LOG_INFO) << "LoaderTR2: Can't open \"" << f << "\"!" << Log::endl;
1159  return;
1160  }
1161 
1162  Log::get(LOG_INFO) << "LoaderTR2: Loading \"" << f << "\"" << Log::endl;
1163 
1164  int riffCount = loadSoundFiles(sfx);
1165  if (riffCount > 0)
1166  Log::get(LOG_INFO) << "LoaderTR2: Loaded " << riffCount << " SoundSamples" << Log::endl;
1167  else if (riffCount == 0)
1168  Log::get(LOG_INFO) << "LoaderTR2: No SoundSamples found!" << Log::endl;
1169  else
1170  Log::get(LOG_ERROR) << "LoaderTR2: Error loading SoundSamples!" << Log::endl;
1171 }
1172 
1173 int LoaderTR2::loadSoundFiles(BinaryReader& sfx, unsigned int count) {
1174  int riffCount = 0;
1175  while (!sfx.eof()) {
1176  if ((count > 0) && (riffCount >= count))
1177  break;
1178 
1179  char test[5];
1180  test[4] = '\0';
1181  for (int i = 0; i < 4; i++)
1182  test[i] = sfx.read8();
1183 
1184  if (std::string("RIFF") != std::string(test)) {
1185  Log::get(LOG_DEBUG) << "LoaderTR2: SoundSamples invalid! (" << riffCount
1186  << ", \"" << test << "\" != \"RIFF\")" << Log::endl;
1187  return -1;
1188  }
1189 
1190  // riffSize is (fileLength - 8)
1191  uint32_t riffSize = sfx.readU32();
1192 
1193  unsigned char* buff = new unsigned char[riffSize + 8];
1194  sfx.seek(sfx.tell() - 8);
1195  for (int i = 0; i < (riffSize + 8); i++)
1196  buff[i] = sfx.readU8();
1197 
1198  int ret = Sound::loadBuffer(buff, riffSize + 8);
1199  delete [] buff;
1200  assertGreaterThanEqual(ret, 0);
1201 
1202  riffCount++;
1203  }
1204  return riffCount;
1205 }
1206 
1207 // ---- Stuff ----
1208 
1210  uint32_t numCameras = file.readU32();
1211  for (unsigned int c = 0; c < numCameras; c++) {
1212  int32_t x = file.read32();
1213  int32_t y = file.read32();
1214  int32_t z = file.read32();
1215  int16_t room = file.read16();
1216 
1217  file.seek(file.tell() + 2); // Unknown, correlates to Boxes? Zones?
1218 
1219  // TODO store cameras somewhere
1220  }
1221 
1222  if (numCameras > 0)
1223  Log::get(LOG_INFO) << "LoaderTR2: Found " << numCameras << " Cameras, unimplemented!" << Log::endl;
1224 }
1225 
1227  uint16_t numCinematicFrames = file.readU16();
1228  for (unsigned int c = 0; c < numCinematicFrames; c++) {
1229  int16_t rotY = file.read16(); // Y rotation, +-32767 = +-180deg
1230  int16_t rotZ = file.read16(); // Z rotation, like rotY
1231  int16_t rotZ2 = file.read16(); // Like rotZ?
1232  int16_t posZ = file.read16(); // Camera pos relative to what?
1233  int16_t posY = file.read16();
1234  int16_t posX = file.read16();
1235  int16_t unknown = file.read16(); // Changing this can cause runtime error
1236  int16_t rotX = file.read16(); // X rotation, like rotY
1237 
1238  // TODO store cinematic frames somewhere
1239  }
1240 
1241  if (numCinematicFrames > 0)
1242  Log::get(LOG_INFO) << "LoaderTR2: Found " << numCinematicFrames
1243  << " CinematicFrames, unimplemented!" << Log::endl;
1244 }
1245 
1247  uint16_t numDemoData = file.readU16();
1248  for (unsigned int d = 0; d < numDemoData; d++)
1249  file.readU8();
1250 
1251  // TODO store demo data somewhere, find out meaning
1252  if (numDemoData > 0)
1253  Log::get(LOG_INFO) << "LoaderTR2: Found " << numDemoData << " bytes DemoData, unimplemented!" <<
1254  Log::endl;
1255 }
1256 
void addSkeletalModel(SkeletalModel *model)
Definition: World.cpp:77
Definition: Mesh.h:35
virtual void loadPalette()
Definition: LoaderTR2.cpp:66
void addModel(StaticModel *s)
Definition: Room.h:47
virtual void loadAngleSet(BoneFrame *bf, BinaryReader &frame, uint16_t numMeshes, uint16_t startingMesh, uint32_t meshTree, uint32_t numMeshTrees, std::vector< int32_t > meshTrees)
Definition: LoaderTR2.cpp:719
virtual void loadAnimatedTextures()
Definition: LoaderTR2.cpp:155
BinaryFile file
Definition: Loader.h:34
TR2 level file loader.
#define assertLessThanEqual(x, y)
Definition: global.h:154
virtual uint8_t readU8()
Definition: binary.cpp:14
virtual int8_t read8()
Definition: binary.cpp:70
void add(BoneTag *t)
void addRoom(Room *room)
Definition: World.cpp:25
void add(AnimationFrame *f)
static void addAnimatedTile(int index, int tile)
virtual void loadSoundSources()
Definition: LoaderTR2.cpp:1084
World Model.
static void addTile(TextureTile *t)
virtual void loadSoundMap()
Definition: LoaderTR2.cpp:1107
virtual long long tell()
Definition: binary.cpp:123
Sound Source Manager.
virtual int getPaletteIndex(uint16_t index)
Definition: LoaderTR2.cpp:523
virtual int loadSoundFiles(BinaryReader &sfx, unsigned int count=0)
Definition: LoaderTR2.cpp:1173
World & getWorld()
Definition: main.cpp:32
Included everywhere.
virtual void loadCinematicFrames()
Definition: LoaderTR2.cpp:1226
virtual void seek(long long pos=0)
Definition: binary.cpp:128
virtual void loadSampleIndices()
Definition: LoaderTR2.cpp:1136
static LogLevel & get(int level)
Definition: Log.cpp:14
virtual void loadTextiles()
Definition: LoaderTR2.cpp:108
virtual void loadMeshes()
Definition: LoaderTR2.cpp:527
virtual int16_t read16()
Definition: binary.cpp:77
virtual void loadItems()
Definition: LoaderTR2.cpp:976
virtual void loadStaticMeshes()
Definition: LoaderTR2.cpp:648
virtual BoneFrame * loadFrame(BinaryReader &frame, uint16_t numMeshes, uint16_t startingMesh, uint32_t meshTree, uint32_t numMeshTrees, std::vector< int32_t > meshTrees)
Definition: LoaderTR2.cpp:764
Global Logging Utility.
virtual void loadRoomVertex(RoomVertexTR2 &vert)
Definition: LoaderTR2.cpp:249
void addMesh(Mesh *mesh)
Definition: World.cpp:103
Pixel buffer utilities.
Definition: Sprite.h:13
#define assert(x)
Definition: global.h:124
unsigned int attributes
Definition: RoomMesh.h:25
Definition: Entity.h:11
static int test(const char *name)
Definition: binary.cpp:39
#define LOG_DEBUG
Definition: Log.h:22
#define LOG_INFO
Definition: Log.h:21
static void addSampleIndex(int index)
virtual void loadRoomLights()
Definition: LoaderTR2.cpp:191
static int loadBuffer(unsigned char *buffer, unsigned int length)
Definition: Sound.cpp:43
virtual void loadRoomMesh(std::vector< IndexedRectangle > &rectangles, std::vector< IndexedRectangle > &triangles, uint16_t &numRectangles, uint16_t &numTriangles)
Definition: LoaderTR2.cpp:258
static void addSoundSource(glm::vec3 p, int id, int flags)
virtual void loadSprites()
Definition: LoaderTR2.cpp:480
void addSprite(RoomSprite *s)
Definition: Room.h:46
virtual bool eof()=0
virtual void loadCameras()
Definition: LoaderTR2.cpp:1209
void addSprite(Sprite *sprite)
Definition: World.cpp:38
virtual uint16_t readU16()
Definition: binary.cpp:21
static void addSoundMapEntry(int id)
static const char endl
Definition: Log.h:35
#define assertGreaterThanEqual(x, y)
Definition: global.h:170
virtual int load(std::string f)
Definition: LoaderTR2.cpp:24
virtual void loadRoomStaticMeshes(std::vector< StaticModel * > &staticModels)
Definition: LoaderTR2.cpp:213
virtual void loadRoomDataEnd(int16_t &alternateRoom, unsigned int &roomFlags)
Definition: LoaderTR2.cpp:239
virtual void loadTextures()
Definition: LoaderTR2.cpp:81
#define LOG_ERROR
Definition: Log.h:19
virtual void loadMoveables()
Definition: LoaderTR2.cpp:785
void addSpriteSequence(SpriteSequence *sprite)
Definition: World.cpp:51
virtual void loadSoundDetails()
Definition: LoaderTR2.cpp:1113
virtual void loadRooms()
Definition: LoaderTR2.cpp:289
virtual uint32_t readU32()
Definition: binary.cpp:27
Textured/Colored Mesh.
static void setLara(long lara)
Definition: Game.cpp:121
void addPortal(Portal *p)
Definition: Room.h:50
void add(TextureTileVertex t)
virtual void seek(long long pos=0)=0
virtual void loadDemoData()
Definition: LoaderTR2.cpp:1246
virtual int32_t read32()
Definition: binary.cpp:85
Definition: Room.h:22
Texture Registry.
virtual long long tell()=0
static void setPalette(int index, glm::vec4 color)
void addEntity(Entity *entity)
Definition: World.cpp:64
virtual void loadBoxesOverlapsZones()
Definition: LoaderTR2.cpp:1021
#define assertLessThan(x, y)
Definition: global.h:146
void add(BoneFrame *f)
virtual void loadFloorData()
Definition: LoaderTR2.cpp:465
static int loadBufferSlot(unsigned char *image=nullptr, unsigned int width=256, unsigned int height=256, ColorMode mode=ColorMode::RGBA, unsigned int bpp=32, TextureStorage s=TextureStorage::GAME, int slot=-1, bool filter=true)
Loads Buffer as texture.
Room in World.
unsigned char * argb16to32(unsigned char *image, unsigned int w, unsigned int h)
Definition: pixel.cpp:44
virtual void loadExternalSoundFile(std::string f)
Definition: LoaderTR2.cpp:1148
Gameplay Handler.
Sound Interface.
int open(std::string f="")
Definition: binary.cpp:112
void addStaticMesh(StaticMesh *model)
Definition: World.cpp:90
static void addSoundDetail(int sample, float volume)