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.

TextureManager.cpp 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. /*!
  2. * \file src/TextureManager.cpp
  3. * \brief Texture registry
  4. *
  5. * \author Mongoose
  6. * \author xythobuz
  7. */
  8. #include "imgui/imgui.h"
  9. #include "stb/stb_image.h"
  10. #include "global.h"
  11. #include "Game.h"
  12. #include "Log.h"
  13. #include "RunTime.h"
  14. #include "World.h"
  15. #include "utils/Folder.h"
  16. #include "utils/pcx.h"
  17. #include "utils/pixel.h"
  18. #include "utils/random.h"
  19. #include "utils/strings.h"
  20. #include "TextureManager.h"
  21. glm::vec2 TextureTile::getUV(unsigned int i) {
  22. glm::vec2 uv(vertices.at(i).xPixel,
  23. vertices.at(i).yPixel);
  24. /*! \fixme
  25. * This is my somewhat hacky approach to fixing
  26. * the bad texture-bleeding problems everywhere.
  27. * That's better, but makes the seams between
  28. * each sector much more visible!
  29. */
  30. if (vertices.at(i).xCoordinate == 1) {
  31. uv.x += 0.375f;
  32. }
  33. if (vertices.at(i).yCoordinate == 1) {
  34. uv.y += 0.375f;
  35. }
  36. return uv / 256.0f;
  37. }
  38. // ----------------------------------------------------------------------------
  39. #define COLOR_PALETTE_SIZE 256
  40. std::vector<unsigned int> TextureManager::mTextureIdsGame;
  41. std::vector<unsigned int> TextureManager::mTextureIdsSystem;
  42. std::vector<TextureTile*> TextureManager::tiles;
  43. std::vector<std::vector<int>> TextureManager::animations;
  44. std::vector<int> TextureManager::gameUnits;
  45. std::vector<int> TextureManager::systemUnits;
  46. unsigned int TextureManager::nextFreeTextureUnit = 0;
  47. std::vector<BufferManager> TextureManager::gameBuffers;
  48. std::vector<BufferManager> TextureManager::systemBuffers;
  49. std::array<glm::vec4, 256> TextureManager::colorPalette;
  50. std::vector<std::tuple<unsigned char*, unsigned int, unsigned int>> TextureManager::indexedTextures;
  51. int TextureManager::initialize() {
  52. assertEqual(mTextureIdsGame.size(), 0);
  53. assertEqual(mTextureIdsSystem.size(), 0);
  54. while (mTextureIdsSystem.size() < 2) {
  55. unsigned int id;
  56. glGenTextures(1, &id);
  57. mTextureIdsSystem.push_back(id);
  58. }
  59. unsigned char* image = generateColorTexture(WHITE, 32, 32, 32);
  60. int res = loadBufferSlot(image, 32, 32, ColorMode::RGBA, 32, TextureStorage::SYSTEM, TEXTURE_WHITE,
  61. false);
  62. delete [] image;
  63. if (res < 0) {
  64. return -1;
  65. }
  66. return 0;
  67. }
  68. int TextureManager::initializeSplash() {
  69. Folder f(RunTime::getPakDir());
  70. std::vector<File> files;
  71. f.findRecursiveFilesEndingWith(files, ".pcx");
  72. if (files.size() == 0) {
  73. if (loadImage(RunTime::getDataDir() + "/splash.tga", TextureStorage::SYSTEM, TEXTURE_SPLASH) < 0) {
  74. return -2;
  75. }
  76. } else {
  77. int i = randomInteger(files.size() - 1);
  78. if (loadImage(files.at(i).getPath(), TextureStorage::SYSTEM, TEXTURE_SPLASH) < 0) {
  79. if (loadImage(RunTime::getDataDir() + "/splash.tga", TextureStorage::SYSTEM, TEXTURE_SPLASH) < 0) {
  80. return -3;
  81. }
  82. }
  83. }
  84. return 0;
  85. }
  86. void TextureManager::shutdown() {
  87. while (mTextureIdsSystem.size() > 0) {
  88. unsigned int id = mTextureIdsSystem.at(mTextureIdsSystem.size() - 1);
  89. glDeleteTextures(1, &id);
  90. mTextureIdsSystem.pop_back();
  91. }
  92. gameBuffers.clear();
  93. systemBuffers.clear();
  94. clear();
  95. }
  96. void TextureManager::clear() {
  97. while (mTextureIdsGame.size() > 0) {
  98. unsigned int id = mTextureIdsGame.at(mTextureIdsGame.size() - 1);
  99. glDeleteTextures(1, &id);
  100. mTextureIdsGame.pop_back();
  101. }
  102. while (!tiles.empty()) {
  103. delete tiles.at(tiles.size() - 1);
  104. tiles.pop_back();
  105. }
  106. animations.clear();
  107. gameUnits.clear();
  108. systemUnits.clear();
  109. nextFreeTextureUnit = 0;
  110. indexedTextures.clear();
  111. }
  112. int TextureManager::loadBufferSlot(unsigned char* image,
  113. unsigned int width, unsigned int height,
  114. ColorMode mode, unsigned int bpp,
  115. TextureStorage s, int slot, bool filter) {
  116. assertGreaterThan(width, 0);
  117. assertGreaterThan(height, 0);
  118. assert((mode == ColorMode::RGB)
  119. || (mode == ColorMode::BGR)
  120. || (mode == ColorMode::ARGB)
  121. || (mode == ColorMode::RGBA)
  122. || (mode == ColorMode::BGRA));
  123. assert((bpp == 8) || (bpp == 24) || (bpp == 32));
  124. if (slot < 0)
  125. slot = getIds(s).size();
  126. while (getIds(s).size() <= slot) {
  127. unsigned int id;
  128. glGenTextures(1, &id);
  129. getIds(s).push_back(id);
  130. }
  131. unsigned int glcMode;
  132. switch (mode) {
  133. case ColorMode::BGR:
  134. glcMode = GL_BGR;
  135. break;
  136. case ColorMode::RGB:
  137. glcMode = GL_RGB;
  138. break;
  139. case ColorMode::ARGB:
  140. if (image != nullptr)
  141. argb2rgba32(image, width, height);
  142. glcMode = GL_RGBA;
  143. break;
  144. case ColorMode::BGRA:
  145. glcMode = GL_BGRA;
  146. break;
  147. case ColorMode::RGBA:
  148. glcMode = GL_RGBA;
  149. break;
  150. }
  151. glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  152. bindTexture(slot, s);
  153. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, glcMode, GL_UNSIGNED_BYTE, image);
  154. if (filter) {
  155. // Trilinear filtering
  156. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  157. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  158. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  159. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  160. glGenerateMipmap(GL_TEXTURE_2D);
  161. } else {
  162. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  163. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  164. }
  165. return slot;
  166. }
  167. int TextureManager::numTextures(TextureStorage s) {
  168. return getIds(s).size();
  169. }
  170. void TextureManager::bindTextureId(unsigned int n, TextureStorage s, unsigned int unit) {
  171. assertLessThan(n, getIds(s).size());
  172. assertLessThan(unit, 80); //! \todo Query GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
  173. glActiveTexture(GL_TEXTURE0 + unit);
  174. glBindTexture(GL_TEXTURE_2D, getIds(s).at(n));
  175. }
  176. int TextureManager::bindTexture(unsigned int n, TextureStorage s) {
  177. assertLessThan(n, getIds(s).size());
  178. if ((n < getUnits(s).size()) && (getUnits(s).at(n) >= 0)) {
  179. bindTextureId(n, s, getUnits(s).at(n));
  180. return getUnits(s).at(n);
  181. } else {
  182. while (getUnits(s).size() <= n)
  183. getUnits(s).push_back(-1);
  184. getUnits(s).at(n) = nextFreeTextureUnit;
  185. bindTextureId(n, s, nextFreeTextureUnit);
  186. nextFreeTextureUnit++;
  187. return nextFreeTextureUnit - 1;
  188. }
  189. }
  190. unsigned int TextureManager::getTextureID(int n, TextureStorage s) {
  191. assertLessThan(n, getIds(s).size());
  192. return getIds(s).at(n);
  193. }
  194. void TextureManager::addTile(TextureTile* t) {
  195. tiles.push_back(t);
  196. }
  197. int TextureManager::numTiles() {
  198. return tiles.size();
  199. }
  200. TextureTile& TextureManager::getTile(int index) {
  201. assertGreaterThanEqual(index, 0);
  202. assertLessThan(index, tiles.size());
  203. return *tiles.at(index);
  204. }
  205. void TextureManager::addAnimatedTile(int index, int tile) {
  206. while (index >= animations.size())
  207. animations.push_back(std::vector<int>());
  208. animations.at(index).push_back(tile);
  209. }
  210. int TextureManager::numAnimatedTiles() {
  211. return animations.size();
  212. }
  213. int TextureManager::getFirstTileAnimation(int index) {
  214. assertLessThan(index, animations.size());
  215. assertGreaterThan(animations.at(index).size(), 0);
  216. return animations.at(index).at(0);
  217. }
  218. int TextureManager::getNextTileAnimation(int index, int tile) {
  219. assertLessThan(index, animations.size());
  220. for (int i = 0; i < animations.at(index).size(); i++) {
  221. if (animations.at(index).at(i) == tile) {
  222. if (i < (animations.at(index).size() - 1))
  223. return animations.at(index).at(i + 1);
  224. else
  225. return animations.at(index).at(0);
  226. }
  227. }
  228. return -1;
  229. }
  230. BufferManager* TextureManager::getBufferManager(int tex, TextureStorage store) {
  231. auto& v = (store == TextureStorage::GAME) ? gameBuffers : systemBuffers;
  232. while (v.size() <= (tex + 1)) {
  233. v.emplace_back(v.size(), store);
  234. }
  235. return &(v.at(tex));
  236. }
  237. void TextureManager::setPalette(int index, glm::vec4 color) {
  238. assertGreaterThanEqual(index, 0);
  239. assertLessThan(index, COLOR_PALETTE_SIZE);
  240. colorPalette[index] = color;
  241. }
  242. glm::vec4 TextureManager::getPalette(int index) {
  243. assertGreaterThanEqual(index, 0);
  244. assertLessThan(index, COLOR_PALETTE_SIZE);
  245. return colorPalette[index];
  246. }
  247. void TextureManager::addIndexedTexture(unsigned char* image, unsigned int width, unsigned int height) {
  248. unsigned char* img = new unsigned char[width * height];
  249. for (unsigned int i = 0; i < (width * height); i++)
  250. img[i] = image[i];
  251. indexedTextures.emplace_back(img, width, height);
  252. }
  253. void TextureManager::prepare() {
  254. for (int i = 0; i < indexedTextures.size(); i++) {
  255. auto tex = indexedTextures.at(i);
  256. unsigned char* img = std::get<0>(tex);
  257. unsigned int width = std::get<1>(tex);
  258. unsigned int height = std::get<2>(tex);
  259. unsigned char* image = new unsigned char[width * height * 4];
  260. for (unsigned int i = 0; i < (width * height); i++) {
  261. auto col = getPalette(img[i]);
  262. image[i * 4] = col.x * 255;
  263. image[(i * 4) + 1] = col.y * 255;
  264. image[(i * 4) + 2] = col.z * 255;
  265. image[(i * 4) + 3] = col.w * 255;
  266. }
  267. delete [] img;
  268. loadBufferSlot(image, width, height, ColorMode::RGBA, 32, TextureStorage::GAME, i, true);
  269. }
  270. }
  271. int TextureManager::loadImage(std::string filename, TextureStorage s, int slot) {
  272. if (stringEndsWith(filename, ".pcx")) {
  273. return loadPCX(filename, s, slot);
  274. } else {
  275. int x, y, n;
  276. unsigned char* data = stbi_load(filename.c_str(), &x, &y, &n, 0);
  277. if (data) {
  278. if ((n < 3) || (n > 4)) {
  279. Log::get(LOG_ERROR) << "Image \"" << filename << "\" has unsupported format ("
  280. << n << ")!" << Log::endl;
  281. stbi_image_free(data);
  282. return -2;
  283. }
  284. int id = loadBufferSlot(data, x, y, (n == 3) ? ColorMode::RGB : ColorMode::RGBA,
  285. (n == 3) ? 24 : 32, s, slot);
  286. stbi_image_free(data);
  287. return id;
  288. } else {
  289. Log::get(LOG_ERROR) << "Can't load image \"" << filename << "\"!" << Log::endl;
  290. return -1;
  291. }
  292. }
  293. }
  294. int TextureManager::loadPCX(std::string filename, TextureStorage s, int slot) {
  295. int error = pcxCheck(filename.c_str());
  296. if (!error) {
  297. unsigned char* image;
  298. unsigned int w, h, bpp;
  299. ColorMode c;
  300. error = pcxLoad(filename.c_str(), &image, &w, &h, &c, &bpp);
  301. if (!error) {
  302. unsigned char* image2 = scaleBuffer(image, &w, &h, bpp);
  303. if (image2) {
  304. delete [] image;
  305. image = image2;
  306. }
  307. int id = loadBufferSlot(image, w, h, c, bpp, s, slot);
  308. delete [] image;
  309. return id;
  310. }
  311. return -5;
  312. }
  313. return -4;
  314. }
  315. std::vector<unsigned int>& TextureManager::getIds(TextureStorage s) {
  316. if (s == TextureStorage::GAME)
  317. return mTextureIdsGame;
  318. else
  319. return mTextureIdsSystem;
  320. }
  321. std::vector<int>& TextureManager::getUnits(TextureStorage s) {
  322. if (s == TextureStorage::GAME)
  323. return gameUnits;
  324. else
  325. return systemUnits;
  326. }
  327. void TextureManager::display() {
  328. if (ImGui::CollapsingHeader("Texture Viewer")) {
  329. static bool game = Game::isLoaded();
  330. static int index = 0;
  331. ImGui::SliderInt("##texslide", &index, 0, TextureManager::numTextures(
  332. game ? TextureStorage::GAME : TextureStorage::SYSTEM) - 1);
  333. ImGui::SameLine();
  334. if (ImGui::Button("+##texplus", ImVec2(0, 0), true)) {
  335. if (index < (numTextures(
  336. game ? TextureStorage::GAME : TextureStorage::SYSTEM) - 1))
  337. index++;
  338. else
  339. index = 0;
  340. }
  341. ImGui::SameLine();
  342. if (ImGui::Button("-##texminus", ImVec2(0, 0), true)) {
  343. if (index > 0)
  344. index--;
  345. else
  346. index = numTextures(
  347. game ? TextureStorage::GAME : TextureStorage::SYSTEM) - 1;
  348. }
  349. if ((numTextures(TextureStorage::GAME) > 0)) {
  350. ImGui::SameLine();
  351. ImGui::Checkbox("Game##texgame", &game);
  352. } else {
  353. game = false;
  354. }
  355. if (index >= numTextures(game ? TextureStorage::GAME : TextureStorage::SYSTEM)) {
  356. index = numTextures(game ? TextureStorage::GAME : TextureStorage::SYSTEM) - 1;
  357. if (index < 0) {
  358. game = false;
  359. index = 0;
  360. }
  361. }
  362. auto bm = getBufferManager(index, game ? TextureStorage::GAME
  363. : TextureStorage::SYSTEM);
  364. ImGui::Image(bm, ImVec2(ImGui::GetColumnWidth() * 2 / 3, ImGui::GetColumnWidth() * 2 / 3));
  365. }
  366. if (ImGui::CollapsingHeader("Textile Viewer")) {
  367. if (numTiles() > 0) {
  368. static int index = 0;
  369. ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f);
  370. ImGui::SliderInt("##tileslide", &index, 0, numTiles() - 1);
  371. ImGui::PopItemWidth();
  372. ImGui::SameLine();
  373. if (ImGui::Button("+##tileplus", ImVec2(0, 0), true)) {
  374. if (index < (numTiles() - 1))
  375. index++;
  376. else
  377. index = 0;
  378. }
  379. ImGui::SameLine();
  380. if (ImGui::Button("-##tileminus", ImVec2(0, 0), true)) {
  381. if (index > 0)
  382. index--;
  383. else
  384. index = numTiles() - 1;
  385. }
  386. if (index >= numTiles())
  387. index = 0;
  388. auto& tile = getTile(index);
  389. auto bm = getBufferManager(tile.getTexture(), TextureStorage::GAME);
  390. ImVec2 size(ImGui::GetColumnWidth() * 2 / 3, ImGui::GetColumnWidth() * 2 / 3);
  391. auto uvA = tile.getUV(0);
  392. auto uvB = tile.getUV(2);
  393. ImVec2 uv1(uvA.x, uvA.y);
  394. ImVec2 uv2(uvB.x, uvB.y);
  395. ImGui::Image(bm, size, uv1, uv2);
  396. } else {
  397. ImGui::Text("No textiles are currently loaded...!");
  398. }
  399. }
  400. if (ImGui::CollapsingHeader("Animated Textile Viewer")) {
  401. if (numAnimatedTiles() > 0) {
  402. static int index = 0;
  403. static int tile = getFirstTileAnimation(index);
  404. if (ImGui::SliderInt("##animslide", &index, 0, numAnimatedTiles() - 1)) {
  405. tile = getFirstTileAnimation(index);
  406. }
  407. ImGui::SameLine();
  408. if (ImGui::Button("+##animplus", ImVec2(0, 0), true)) {
  409. if (index < (numAnimatedTiles() - 1))
  410. index++;
  411. else
  412. index = 0;
  413. tile = getFirstTileAnimation(index);
  414. }
  415. ImGui::SameLine();
  416. if (ImGui::Button("-##animminus", ImVec2(0, 0), true)) {
  417. if (index > 0)
  418. index--;
  419. else
  420. index = numAnimatedTiles() - 1;
  421. tile = getFirstTileAnimation(index);
  422. }
  423. if (index >= numAnimatedTiles()) {
  424. index = 0;
  425. tile = getFirstTileAnimation(index);
  426. }
  427. int next = getNextTileAnimation(index, tile);
  428. if (next == -1) {
  429. index = 0;
  430. tile = getFirstTileAnimation(index);
  431. }
  432. ImGui::SameLine();
  433. ImGui::Text("%d", tile);
  434. auto& t = getTile(tile);
  435. auto bm = getBufferManager(t.getTexture(), TextureStorage::GAME);
  436. ImVec2 size(ImGui::GetColumnWidth() * 2 / 3, ImGui::GetColumnWidth() * 2 / 3);
  437. auto uvA = t.getUV(0);
  438. auto uvB = t.getUV(2);
  439. ImVec2 uv1(uvA.x, uvA.y);
  440. ImVec2 uv2(uvB.x, uvB.y);
  441. ImGui::Image(bm, size, uv1, uv2);
  442. static int fr = 0;
  443. if (fr > 0) {
  444. fr--;
  445. } else {
  446. fr = RunTime::getFPS() / 5;
  447. tile = next;
  448. }
  449. } else {
  450. ImGui::Text("No animated textures are currently loaded...!");
  451. }
  452. }
  453. if (ImGui::CollapsingHeader("Sprite Sequence Viewer")) {
  454. if (getWorld().sizeSprite() <= 0) {
  455. ImGui::Text("Please load a level containing sprites!");
  456. } else {
  457. static int index = 0;
  458. static int sprite = 0;
  459. if (ImGui::SliderInt("##spriteslide", &index, 0, getWorld().sizeSpriteSequence() - 1)) {
  460. sprite = 0;
  461. }
  462. ImGui::SameLine();
  463. if (ImGui::Button("+##spriteplus", ImVec2(0, 0), true)) {
  464. if (index < (getWorld().sizeSpriteSequence() - 1))
  465. index++;
  466. else
  467. index = 0;
  468. sprite = 0;
  469. }
  470. ImGui::SameLine();
  471. if (ImGui::Button("-##spriteminus", ImVec2(0, 0), true)) {
  472. if (index > 0)
  473. index--;
  474. else
  475. index = getWorld().sizeSpriteSequence() - 1;
  476. sprite = 0;
  477. }
  478. if (index >= getWorld().sizeSpriteSequence()) {
  479. index = 0;
  480. sprite = 0;
  481. }
  482. if (sprite >= getWorld().getSpriteSequence(index).size()) {
  483. sprite = 0;
  484. }
  485. ImGui::SameLine();
  486. ImGui::Text("Sprite %d/%d", sprite + 1, getWorld().getSpriteSequence(index).size());
  487. auto& s = getWorld().getSprite(getWorld().getSpriteSequence(index).getStart() + sprite);
  488. auto bm = getBufferManager(s.getTexture(), TextureStorage::GAME);
  489. ImVec2 size(ImGui::GetColumnWidth() * 2 / 3, ImGui::GetColumnWidth() * 2 / 3);
  490. auto uv = s.getUVs();
  491. ImVec2 uv1(uv.x, uv.w);
  492. ImVec2 uv2(uv.z, uv.y);
  493. ImGui::Image(bm, size, uv1, uv2);
  494. static int fr = 0;
  495. if (fr > 0) {
  496. fr--;
  497. } else {
  498. fr = RunTime::getFPS() / 10;
  499. if (sprite < (getWorld().getSpriteSequence(index).size() - 1))
  500. sprite++;
  501. else
  502. sprite = 0;
  503. }
  504. }
  505. }
  506. }