gcsx_texture.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. /* GCSx
  2. ** TEXTURE.CPP
  3. **
  4. ** Texture-management for OpenGL
  5. */
  6. /*****************************************************************************
  7. ** Copyright (C) 2003-2006 Janson
  8. **
  9. ** This program is free software; you can redistribute it and/or modify
  10. ** it under the terms of the GNU General Public License as published by
  11. ** the Free Software Foundation; either version 2 of the License, or
  12. ** (at your option) any later version.
  13. **
  14. ** This program is distributed in the hope that it will be useful,
  15. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ** GNU General Public License for more details.
  18. **
  19. ** You should have received a copy of the GNU General Public License
  20. ** along with this program; if not, write to the Free Software
  21. ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
  22. *****************************************************************************/
  23. #include "all.h"
  24. // TextureMap overview:
  25. // You specify how many graphics to store, and the size (all the same size)
  26. // TextureMap will then allocate anything from a portion of an existing texture
  27. // up to multiple textures.
  28. // An alternate mode of operation simply allocates one texture per graphic to store.
  29. // Partial updates to graphics are cached and all updates sent to video memory at once
  30. // upon request.
  31. vector<TextureMap::TextureList>* TextureMap::globalTextures = NULL;
  32. TextureMap* TextureMap::headUpdate = NULL;
  33. GLuint TextureMap::lastTexture = 0;
  34. void TextureMap::setupOptimizations() { start_func
  35. lastTexture = 0;
  36. }
  37. int TextureMap::optionalPowerOfTwo(int val) { start_func
  38. assert(val >= 1);
  39. assert(val <= config->readNum(OGL_MAX_SIZE));
  40. int minSize = config->readNum(OGL_MIN_SIZE);
  41. if (val < minSize) return minSize;
  42. if (!config->readNum(OGL_POWER_OF_TWO)) return val;
  43. int bit = 1;
  44. while (bit < val) {
  45. bit <<= 1;
  46. }
  47. return bit;
  48. }
  49. int TextureMap::groupTextures(int count, int w, int h, vector<tGroup>& sizes) {
  50. sizes.clear();
  51. int startW = optionalPowerOfTwo(w);
  52. int startH = optionalPowerOfTwo(h);
  53. int max = config->readNum(OGL_MAX_SIZE);
  54. // If only one graphic, or requested, or size too large, we just go with 1 per!
  55. if ((count == 1) || (config->readNum(OGL_FORCE_SINGLE)) ||
  56. ((startW * 2 > max) && (startH * 2 > max))) {
  57. tGroup group;
  58. group.texW = startW;
  59. group.texH = startH;
  60. group.countW = 1;
  61. group.countH = 1;
  62. group.qty = count;
  63. sizes.push_back(group);
  64. return count;
  65. }
  66. else if (config->readNum(OGL_PREFER_GROUPING)) {
  67. // Find smallest sizing that holds *everything*, if possible
  68. int largestCount = (max / w) * (max / h);
  69. int total = 0;
  70. // Too many for one texture?
  71. if (count > largestCount) {
  72. tGroup group;
  73. group.texW = optionalPowerOfTwo((max / w) * w);
  74. group.texH = optionalPowerOfTwo((max / h) * h);
  75. group.countW = max / w;
  76. group.countH = max / h;
  77. group.qty = count / largestCount;
  78. sizes.push_back(group);
  79. count -= group.qty * largestCount;
  80. total += group.qty;
  81. }
  82. if (count) {
  83. int optimalSize = -1;
  84. int optimalW, optimalH;
  85. if (config->readNum(OGL_POWER_OF_TWO)) {
  86. for (int testW = startW; testW <= max; testW *= 2) {
  87. int countW = testW / w;
  88. for (int testH = startH; testH <= testW; testH *= 2) {
  89. int total = countW * (testH / h);
  90. if (count <= total) {
  91. if ((testW * testH < optimalSize) || (optimalSize == -1)) {
  92. optimalSize = testW * testH;
  93. optimalW = testW;
  94. optimalH = testH;
  95. // Abort loop if perfect match found
  96. if (total == count) {
  97. testW = max;
  98. break;
  99. }
  100. }
  101. }
  102. }
  103. }
  104. }
  105. else {
  106. int attempts = 0;
  107. for (int testW = w; testW <= max; testW += w) {
  108. int countW = testW / w;
  109. for (int testH = h; testH <= max; testH += h) {
  110. ++attempts;
  111. int total = countW * (testH / h);
  112. if (count <= total) {
  113. if ((testW * testH < optimalSize) || (optimalSize == -1)) {
  114. optimalSize = testW * testH;
  115. optimalW = testW;
  116. optimalH = testH;
  117. // Abort loop if perfect match found
  118. if (total == count) {
  119. testW = max;
  120. break;
  121. }
  122. }
  123. }
  124. }
  125. }
  126. }
  127. tGroup group;
  128. group.texW = optimalW;
  129. group.texH = optimalH;
  130. group.countW = optimalW / w;
  131. group.countH = optimalH / h;
  132. group.qty = 1;
  133. sizes.push_back(group);
  134. ++total;
  135. }
  136. return total;
  137. }
  138. else {
  139. // Find largest sizing that generates the least waste
  140. // This isn't 100% optimal, but a decent compromise
  141. int optimalWaste = -1;
  142. int optimalW, optimalH, optimalFG, optimalLO;
  143. for (int testW = startW; testW <= max; testW *= 2) {
  144. int countW = testW / w;
  145. for (int testH = startH; testH <= testW; testH *= 2) {
  146. int countH = testH / h;
  147. // Calculate edge waste on one full grouping
  148. int waste = testW * testH - countW * w * countH * h;
  149. // Calculate edge waste for all full groupings
  150. int fullGroups = count / (countW * countH);
  151. waste *= fullGroups;
  152. // Calculate final grouping, if any
  153. int leftOver = count - (fullGroups * countW * countH);
  154. if (leftOver) {
  155. int finalH = optionalPowerOfTwo((leftOver + countW - 1) / countW * h);
  156. int fCountH = finalH / h;
  157. int finalW = optionalPowerOfTwo((leftOver + fCountH - 1) / fCountH * w);
  158. waste += finalW * finalH - leftOver * w * h;
  159. }
  160. if ((waste < optimalWaste) || (optimalWaste == -1) ||
  161. ((waste == optimalWaste) && (testW * testH > optimalW * optimalH))) {
  162. optimalWaste = waste;
  163. optimalW = testW;
  164. optimalH = testH;
  165. optimalFG = fullGroups;
  166. optimalLO = leftOver;
  167. }
  168. }
  169. }
  170. if (optimalFG) {
  171. tGroup group;
  172. group.texW = optimalW;
  173. group.texH = optimalH;
  174. group.countW = optimalW / w;
  175. group.countH = optimalH / h;
  176. group.qty = optimalFG;
  177. sizes.push_back(group);
  178. }
  179. if (optimalLO) {
  180. tGroup group;
  181. group.countW = optimalW / w;
  182. group.texH = optionalPowerOfTwo((optimalLO + group.countW - 1) / group.countW * h);
  183. group.countH = group.texH / h;
  184. group.texW = optionalPowerOfTwo((optimalLO + group.countH - 1) / group.countH * h);
  185. group.countW = group.texW / w;
  186. group.qty = 1;
  187. sizes.push_back(group);
  188. }
  189. return optimalFG + (optimalLO ? 1 : 0);
  190. }
  191. }
  192. TextureMap::TextureMap(int count, int width, int height,
  193. void (*tileCoords)(int position, const SDL_Surface*& src, int& x, int& y)
  194. ) : updates() { start_func
  195. int max = config->readNum(OGL_MAX_SIZE);
  196. nextUpdate = NULL;
  197. prevUpdate = NULL;
  198. graphics = NULL;
  199. dispWidth = width;
  200. dispHeight = height;
  201. graphicCount = count;
  202. GLuint* texNames = NULL;
  203. Uint8* data = NULL;
  204. if ((width > max) || (height > max)) {
  205. // Graphic is too large for one texture
  206. multiTexture = ((max + width - 1) / width) * ((max + height - 1) / height);
  207. graphics = new TexturePos[multiTexture * (count + 1)];
  208. // @TODO:
  209. assert(0);
  210. }
  211. else {
  212. // One texture has room for multiple graphics (or one-per)
  213. // Determine size(s)
  214. multiTexture = 1;
  215. graphics = new TexturePos[count + 1];
  216. vector<tGroup> sizes;
  217. int numTextures = groupTextures(count, width, height, sizes);
  218. // Allocate textures
  219. texNames = new GLuint[numTextures];
  220. // Generate textures
  221. // @TODO: Error checking?
  222. glGenTextures(numTextures, texNames);
  223. int tNum = 0;
  224. int gNum = 1;
  225. int done = 0;
  226. // Loop through texture groups
  227. for (vector<tGroup>::iterator pos = sizes.begin(); (!done) && (pos != sizes.end()); ++pos) {
  228. // Create texture data
  229. data = new Uint8[(*pos).texW * (*pos).texH * 4];
  230. // Loop through individual textures
  231. for (int tPos = 0; tPos < (*pos).qty; ++tPos, ++tNum) {
  232. // Technically not required, texture display should never
  233. // exceed bounds of known textures
  234. memset(data, 0, (*pos).texW * (*pos).texH * 4);
  235. // Assign to graphics
  236. int numW = (*pos).countW;
  237. int numH = (*pos).countH;
  238. GLfloat texW = (*pos).texW;
  239. GLfloat texH = (*pos).texH;
  240. int noSubTex = 0;
  241. if ((numW == 1) && (numH == 1)) noSubTex = 1;
  242. for (int y = 0; y < numH; ++y) {
  243. for (int x = 0; x < numW; ++x) {
  244. // Blit texture to data?
  245. if (tileCoords) {
  246. const SDL_Surface* src;
  247. int gx, gy;
  248. tileCoords(gNum, src, gx, gy);
  249. matrixCopy((Uint8*)src->pixels + gx * 4 + gy * src->pitch,
  250. data + x * width * 4 + y * (*pos).texW * height * 4,
  251. width * 4, height,
  252. src->pitch, (*pos).texW * 4);
  253. }
  254. // Remember subtexture stats
  255. graphics[gNum].tex = texNames[tNum];
  256. graphics[gNum].subtex = noSubTex ? -1 : (x + y * numW);
  257. graphics[gNum].x1 = (GLfloat)(x * width) / texW;
  258. graphics[gNum].x2 = (GLfloat)((x + 1) * width) / texW;
  259. graphics[gNum].y1 = (GLfloat)(y * height) / texH;
  260. graphics[gNum].y2 = (GLfloat)((y + 1) * height) / texH;
  261. graphics[gNum].x = x * width;
  262. graphics[gNum].y = y * height;
  263. if (++gNum > count) {
  264. // Abort all loops if done
  265. y = numH;
  266. tPos = (*pos).qty;
  267. done = 1;
  268. break;
  269. }
  270. }
  271. }
  272. // Create individual texture
  273. // @TODO: Error checking?
  274. glBindTexture(GL_TEXTURE_2D, texNames[tNum]);
  275. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  276. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  277. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (*pos).texW, (*pos).texH, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
  278. // @TODO: Reference in globalTextures
  279. }
  280. delete[] data;
  281. }
  282. }
  283. delete[] texNames;
  284. }
  285. TextureMap::~TextureMap() { start_func
  286. // @TODO: remove our textures from globalTextures
  287. // @TODO: deallocate our textures
  288. // when deallocating, clear lastTexture if matches
  289. delete[] graphics;
  290. removeFromUpdates();
  291. }
  292. void TextureMap::removeFromUpdates() { start_func
  293. if (nextUpdate) {
  294. // (safe even if we're pointing to ourselves)
  295. nextUpdate->prevUpdate = prevUpdate;
  296. prevUpdate->nextUpdate = nextUpdate;
  297. if (headUpdate == this) {
  298. if (nextUpdate == this) headUpdate = NULL;
  299. else headUpdate = nextUpdate;
  300. }
  301. }
  302. }
  303. void TextureMap::store(int graphic, const SDL_Surface* src, int sx, int sy, int dx, int dy, int dw, int dh) { start_func
  304. // @TODO: doesn't actually cache anything
  305. if (dw == -1) dw = dispWidth;
  306. if (dh == -1) dh = dispHeight;
  307. assert(dx >= 0);
  308. assert(dy >= 0);
  309. assert(dx + dw <= dispWidth);
  310. assert(dy + dh <= dispHeight);
  311. assert(dw >= 0);
  312. assert(dh >= 0);
  313. // Create texture data
  314. Uint8* data = new Uint8[dw * dh * 4];
  315. matrixCopy((Uint8*)src->pixels + sx * 4 + sy * src->pitch, data,
  316. dw * 4, dh,
  317. src->pitch, dw * 4);
  318. // @TODO: Error checking?
  319. glBindTexture(GL_TEXTURE_2D, graphics[graphic].tex);
  320. glTexSubImage2D(GL_TEXTURE_2D, 0, graphics[graphic].x + dx, graphics[graphic].y + dy,
  321. dw, dh, GL_RGBA, GL_UNSIGNED_BYTE, data);
  322. delete[] data;
  323. }
  324. void TextureMap::updateTexture() { start_func
  325. // @TODO:
  326. removeFromUpdates();
  327. }
  328. void TextureMap::updateAllTextures() { start_func
  329. // @TODO:
  330. }
  331. void TextureMap::draw(int graphic, GLint x, GLint y) const { start_func
  332. assert(graphic > 0);
  333. assert(graphic <= graphicCount);
  334. // @TODO: Entire function- error checking?
  335. if (graphics[graphic].tex != lastTexture) {
  336. lastTexture = graphics[graphic].tex;
  337. glBindTexture(GL_TEXTURE_2D, lastTexture);
  338. }
  339. glBegin(GL_QUADS);
  340. glTexCoord2f(graphics[graphic].x1, graphics[graphic].y1); glVertex3i(x, y, 0);
  341. glTexCoord2f(graphics[graphic].x2, graphics[graphic].y1); glVertex3i(x + dispWidth, y, 0);
  342. glTexCoord2f(graphics[graphic].x2, graphics[graphic].y2); glVertex3i(x + dispWidth, y + dispHeight, 0);
  343. glTexCoord2f(graphics[graphic].x1, graphics[graphic].y2); glVertex3i(x, y + dispHeight, 0);
  344. glEnd();
  345. }
  346. void TextureMap::drawScale(int graphic, GLfloat x, GLfloat y, GLfloat scale) const { start_func
  347. assert(graphic > 0);
  348. assert(graphic <= graphicCount);
  349. // @TODO: Entire function- error checking?
  350. if (graphics[graphic].tex != lastTexture) {
  351. lastTexture = graphics[graphic].tex;
  352. glBindTexture(GL_TEXTURE_2D, lastTexture);
  353. }
  354. glBegin(GL_QUADS);
  355. GLfloat w = scale * dispWidth;
  356. GLfloat h = scale * dispHeight;
  357. glTexCoord2f(graphics[graphic].x1, graphics[graphic].y1); glVertex3f(x, y, 0);
  358. glTexCoord2f(graphics[graphic].x2, graphics[graphic].y1); glVertex3f(x + w, y, 0);
  359. glTexCoord2f(graphics[graphic].x2, graphics[graphic].y2); glVertex3f(x + w, y + h, 0);
  360. glTexCoord2f(graphics[graphic].x1, graphics[graphic].y2); glVertex3f(x, y + h, 0);
  361. glEnd();
  362. }
  363. void TextureMap::draw(int graphic, GLint x, GLint y, int orient) const { start_func
  364. assert(graphic > 0);
  365. assert(graphic <= graphicCount);
  366. // @TODO: Entire function- error checking?
  367. if (graphics[graphic].tex != lastTexture) {
  368. lastTexture = graphics[graphic].tex;
  369. glBindTexture(GL_TEXTURE_2D, lastTexture);
  370. }
  371. GLfloat x1 = graphics[graphic].x1;
  372. GLfloat x2 = graphics[graphic].x2;
  373. GLfloat y1 = graphics[graphic].y1;
  374. GLfloat y3 = graphics[graphic].y2;
  375. if (orient & TEXTURE_FLIP) swap(y1, y3);
  376. if (orient & TEXTURE_MIRROR) swap(x1, x2);
  377. GLfloat x3 = x2, x4, y2 = y1, y4 = y3;
  378. if (orient & TEXTURE_ROTATE) {
  379. x4 = x2;
  380. x2 = x1;
  381. swap(y1, y3);
  382. }
  383. else {
  384. x4 = x1;
  385. }
  386. glBegin(GL_QUADS);
  387. glTexCoord2f(x1, y1); glVertex3i(x, y, 0);
  388. glTexCoord2f(x2, y2); glVertex3i(x + dispWidth, y, 0);
  389. glTexCoord2f(x3, y3); glVertex3i(x + dispWidth, y + dispHeight, 0);
  390. glTexCoord2f(x4, y4); glVertex3i(x, y + dispHeight, 0);
  391. glEnd();
  392. }
  393. void TextureMap::draw(int graphic, GLint x, GLint y, Rect clip) const { start_func
  394. assert(graphic > 0);
  395. assert(graphic <= graphicCount);
  396. // @TODO:
  397. assert(0);
  398. }