Open Source Tomb Raider Engine
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

LoaderTR1.cpp 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. /*!
  2. * \file src/loader/LoaderTR1.cpp
  3. * \brief TR1 level file loader
  4. *
  5. * \author xythobuz
  6. */
  7. #include "global.h"
  8. #include "Game.h"
  9. #include "Log.h"
  10. #include "SoundManager.h"
  11. #include "World.h"
  12. #include "utils/strings.h"
  13. #include "loader/LoaderTR1.h"
  14. int LoaderTR1::load(std::string f) {
  15. if (file.open(f) != 0) {
  16. return 1; // Could not open file
  17. }
  18. uint32_t version = file.readU32();
  19. if (version != 0x20) {
  20. return 2; // Not a TR1 level?!
  21. }
  22. bool unfinishedBusiness = stringEndsWith(f, ".tub");
  23. if (unfinishedBusiness)
  24. Log::get(LOG_INFO) << "LoaderTR1: Detected Unfinished Business level!" << Log::endl;
  25. loadTextures();
  26. file.seek(file.tell() + 4); // Unused value?
  27. loadRooms();
  28. loadFloorData();
  29. loadMeshes();
  30. loadMoveables();
  31. loadStaticMeshes();
  32. loadTextiles();
  33. loadSprites();
  34. if (unfinishedBusiness)
  35. loadPalette();
  36. loadCameras();
  37. loadSoundSources();
  38. loadBoxesOverlapsZones();
  39. loadAnimatedTextures();
  40. loadItems();
  41. file.seek(file.tell() + 8192); // TODO light map!
  42. if (!unfinishedBusiness)
  43. loadPalette();
  44. loadCinematicFrames();
  45. loadDemoData();
  46. loadSoundMap();
  47. loadSoundDetails();
  48. loadSoundSamples();
  49. return 0;
  50. }
  51. void LoaderTR1::loadPalette() {
  52. // Read the 8bit palette, 256 * 3 bytes, RGB
  53. for (int i = 0; i < 256; i++) {
  54. uint8_t r = file.readU8();
  55. uint8_t g = file.readU8();
  56. uint8_t b = file.readU8();
  57. // Color values range from 0 to 63, so multiply by 4
  58. static const uint8_t lightFactor = 4;
  59. r *= lightFactor;
  60. g *= lightFactor;
  61. b *= lightFactor;
  62. glm::vec4 c(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f);
  63. TextureManager::setPalette(i, c);
  64. }
  65. }
  66. void LoaderTR1::loadTextures() {
  67. uint32_t numTextures = file.readU32();
  68. for (unsigned int i = 0; i < numTextures; i++) {
  69. std::array<uint8_t, 256 * 256> arr;
  70. for (auto& x : arr) {
  71. x = file.readU8(); // Palette index
  72. }
  73. TextureManager::addIndexedTexture(&arr[0], 256, 256);
  74. }
  75. if (numTextures > 0)
  76. Log::get(LOG_INFO) << "LoaderTR1: Found " << numTextures << " Textures!" << Log::endl;
  77. else
  78. Log::get(LOG_INFO) << "LoaderTR1: No Textures in this level?!" << Log::endl;
  79. }
  80. void LoaderTR1::loadRoomLights() {
  81. int16_t intensity = file.read16();
  82. uint16_t numLights = file.readU16();
  83. for (unsigned int l = 0; l < numLights; l++) {
  84. // Position of light, in world coordinates
  85. int32_t x = file.read32();
  86. int32_t y = file.read32();
  87. int32_t z = file.read32();
  88. uint16_t intensity1 = file.readU16();
  89. uint32_t fade = file.readU32(); // Falloff value?
  90. // TODO store light somewhere
  91. }
  92. }
  93. void LoaderTR1::loadRoomStaticMeshes(std::vector<StaticModel*>& staticModels) {
  94. uint16_t numStaticMeshes = file.readU16();
  95. for (unsigned int s = 0; s < numStaticMeshes; s++) {
  96. // Absolute position in world coordinates
  97. int32_t x = file.read32();
  98. int32_t y = file.read32();
  99. int32_t z = file.read32();
  100. // High two bits (0xC000) indicate steps of
  101. // 90 degrees (eg. (rotation >> 14) * 90)
  102. uint16_t rotation = file.readU16();
  103. // Constant lighting, 0xFFFF means use mesh lighting
  104. //! \fixme Use static mesh lighting information
  105. uint16_t intensity1 = file.readU16();
  106. // Which StaticMesh item to draw
  107. uint16_t objectID = file.readU16();
  108. staticModels.push_back(new StaticModel(glm::vec3(x, y, z),
  109. glm::radians((rotation >> 14) * 90.0f),
  110. objectID));
  111. }
  112. }
  113. void LoaderTR1::loadRoomVertex(RoomVertexTR2& vert) {
  114. vert.x = file.read16();
  115. vert.y = file.read16();
  116. vert.z = file.read16();
  117. vert.light1 = file.read16();
  118. vert.attributes = 0;
  119. vert.light2 = vert.light1;
  120. }
  121. void LoaderTR1::loadItems() {
  122. uint32_t numItems = file.readU32();
  123. for (unsigned int i = 0; i < numItems; i++) {
  124. int16_t objectID = file.read16();
  125. int16_t room = file.read16();
  126. // Item position in world coordinates
  127. int32_t x = file.read32();
  128. int32_t y = file.read32();
  129. int32_t z = file.read32();
  130. uint16_t angle = file.readU16(); // (0xC000 >> 14) * 90deg
  131. int16_t intensity = file.read16(); // Constant lighting; -1 means mesh lighting
  132. // 0x0100 - Initially visible
  133. // 0x3E00 - Activation mask, open, can be XORed with related FloorData list fields.
  134. uint16_t flags = file.readU16();
  135. glm::vec3 pos(
  136. static_cast<float>(x),
  137. static_cast<float>(y),
  138. static_cast<float>(z)
  139. );
  140. glm::vec3 rot(
  141. 0.0f,
  142. glm::radians(((angle >> 14) & 0x03) * 90.0f),
  143. 0.0f
  144. );
  145. Entity* e = new Entity(objectID, room, pos, rot);
  146. getWorld().addEntity(e);
  147. if (objectID == 0) {
  148. Game::setLara(getWorld().sizeEntity() - 1);
  149. }
  150. }
  151. if (numItems > 0)
  152. Log::get(LOG_INFO) << "LoaderTR1: Found " << numItems << " Items!" << Log::endl;
  153. else
  154. Log::get(LOG_INFO) << "LoaderTR1: No Items in this level?!" << Log::endl;
  155. }
  156. void LoaderTR1::loadBoxesOverlapsZones() {
  157. uint32_t numBoxes = file.readU32();
  158. for (unsigned int b = 0; b < numBoxes; b++) {
  159. // Sectors (not scaled!)
  160. int32_t zMin = file.read32();
  161. int32_t zMax = file.read32();
  162. int32_t xMin = file.read32();
  163. int32_t xMax = file.read32();
  164. int16_t trueFloor = file.read16(); // Y value (no scaling)
  165. // Index into overlaps[]. The high bit is sometimes set
  166. // this occurs in front of swinging doors and the like
  167. uint16_t overlapIndex = file.readU16();
  168. // TODO store boxes somewhere
  169. }
  170. uint32_t numOverlaps = file.readU32();
  171. std::vector<std::vector<uint16_t>> overlaps;
  172. overlaps.emplace_back();
  173. unsigned int list = 0;
  174. for (unsigned int o = 0; o < numOverlaps; o++) {
  175. // Apparently used by NPCs to decide where to go next.
  176. // List of neighboring boxes for each box.
  177. // Each entry is a uint16, 0x8000 set marks end of list.
  178. uint16_t e = file.readU16();
  179. overlaps.at(list).push_back(e);
  180. if (e & 0x8000) {
  181. overlaps.emplace_back();
  182. list++;
  183. }
  184. }
  185. // TODO store overlaps somewhere
  186. for (unsigned int z = 0; z < numBoxes; z++) {
  187. // Normal room state
  188. int16_t ground1 = file.read16();
  189. int16_t ground2 = file.read16();
  190. int16_t fly = file.read16();
  191. // Alternate room state
  192. int16_t ground1alt = file.read16();
  193. int16_t ground2alt = file.read16();
  194. int16_t flyAlt = file.read16();
  195. // TODO store zones somewhere
  196. }
  197. if ((numBoxes > 0) || (numOverlaps > 0))
  198. Log::get(LOG_INFO) << "LoaderTR1: Found NPC NavigationHints (" << numBoxes
  199. << ", " << numOverlaps << ", " << list << "), unimplemented!" << Log::endl;
  200. else
  201. Log::get(LOG_INFO) << "LoaderTR1: No NPC NavigationHints in this level?!" << Log::endl;
  202. }
  203. void LoaderTR1::loadSoundMap() {
  204. for (int i = 0; i < 256; i++) {
  205. SoundManager::addSoundMapEntry(file.read16());
  206. }
  207. }
  208. void LoaderTR1::loadSoundSamples() {
  209. uint32_t soundSampleSize = file.readU32();
  210. std::vector<uint8_t> buffer;
  211. for (int i = 0; i < soundSampleSize; i++) {
  212. buffer.push_back(file.readU8());
  213. }
  214. uint32_t numSampleIndices = file.readU32();
  215. for (unsigned int i = 0; i < numSampleIndices; i++) {
  216. SoundManager::addSampleIndex(i);
  217. uint32_t sampleOffset = file.readU32();
  218. orAssertLessThan(sampleOffset, soundSampleSize);
  219. char* tmpPtr = reinterpret_cast<char*>(&buffer[sampleOffset]);
  220. BinaryMemory sample(tmpPtr, soundSampleSize - sampleOffset);
  221. int ret = loadSoundFiles(sample, 1);
  222. orAssertEqual(ret, 1);
  223. }
  224. if (numSampleIndices > 0)
  225. Log::get(LOG_INFO) << "LoaderTR1: Found " << numSampleIndices << " SoundSamples" << Log::endl;
  226. else
  227. Log::get(LOG_INFO) << "LoaderTR1: No SoundSamples in this level?!" << Log::endl;
  228. }
  229. int LoaderTR1::getPaletteIndex(uint16_t index) {
  230. return index;
  231. }
  232. void LoaderTR1::loadAngleSet(BoneFrame* bf, BinaryReader& frame, uint16_t numMeshes,
  233. uint16_t startingMesh, uint32_t meshTree,
  234. uint32_t numMeshTrees, std::vector<int32_t> meshTrees) {
  235. /*! \fixme
  236. * The TR Rosetta Stone documentation says:
  237. * number of angle sets to follow;
  238. * these start with the first mesh, and meshes
  239. * without angles get zero angles.
  240. * I'm not sure what this means. This code may be wrong.
  241. */
  242. uint16_t numValues = frame.readU16();
  243. for (int i = 0; i < numValues; i++) {
  244. int mesh = startingMesh + i;
  245. glm::vec3 offset(0.0f, 0.0f, 0.0f);
  246. float rotation[3] = { 0.0f, 0.0f, 0.0f };
  247. char flag = (i == 0) ? 2 : 0;
  248. // Nonprimary tag - positioned relative to first tag
  249. if (i != 0) {
  250. char* tmp = reinterpret_cast<char*>(&meshTrees[0]) + meshTree; // TODO (meshTree * 4)?
  251. tmp += (i - 1) * 16; // TODO ?
  252. BinaryMemory tree(tmp, (numMeshTrees * 4) - meshTree - ((i - 1) * 16));
  253. flag = (char)tree.readU32();
  254. offset.x = tree.read32();
  255. offset.y = tree.read32();
  256. offset.z = tree.read32();
  257. uint16_t b = frame.readU16();
  258. uint16_t a = frame.readU16();
  259. rotation[0] = (a & 0x3FF0) >> 4;
  260. rotation[1] = ((a & 0x000F) << 6) | ((b & 0xFC00) >> 10);
  261. rotation[2] = b & 0x03FF;
  262. for (int i = 0; i < 3; i++)
  263. rotation[i] = rotation[i] * 360.0f / 1024.0f;
  264. }
  265. glm::vec3 rot(rotation[0], rotation[1], rotation[2]);
  266. BoneTag* bt = new BoneTag(mesh, offset, rot, flag);
  267. bf->add(bt);
  268. }
  269. }