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 17KB

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