123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- /* GCSx
- ** TEXTURE.CPP
- **
- ** Texture-management for OpenGL
- */
- /*****************************************************************************
- ** Copyright (C) 2003-2006 Janson
- **
- ** This program is free software; you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation; either version 2 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program; if not, write to the Free Software
- ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
- *****************************************************************************/
- #include "all.h"
- // TextureMap overview:
- // You specify how many graphics to store, and the size (all the same size)
- // TextureMap will then allocate anything from a portion of an existing texture
- // up to multiple textures.
- // An alternate mode of operation simply allocates one texture per graphic to store.
- // Partial updates to graphics are cached and all updates sent to video memory at once
- // upon request.
- vector<TextureMap::TextureList>* TextureMap::globalTextures = NULL;
- TextureMap* TextureMap::headUpdate = NULL;
- GLuint TextureMap::lastTexture = 0;
- void TextureMap::setupOptimizations() { start_func
- lastTexture = 0;
- }
- int TextureMap::optionalPowerOfTwo(int val) { start_func
- assert(val >= 1);
- assert(val <= config->readNum(OGL_MAX_SIZE));
- int minSize = config->readNum(OGL_MIN_SIZE);
-
- if (val < minSize) return minSize;
- if (!config->readNum(OGL_POWER_OF_TWO)) return val;
- int bit = 1;
- while (bit < val) {
- bit <<= 1;
- }
- return bit;
- }
- int TextureMap::groupTextures(int count, int w, int h, vector<tGroup>& sizes) {
- sizes.clear();
- int startW = optionalPowerOfTwo(w);
- int startH = optionalPowerOfTwo(h);
- int max = config->readNum(OGL_MAX_SIZE);
- // If only one graphic, or requested, or size too large, we just go with 1 per!
- if ((count == 1) || (config->readNum(OGL_FORCE_SINGLE)) ||
- ((startW * 2 > max) && (startH * 2 > max))) {
- tGroup group;
- group.texW = startW;
- group.texH = startH;
- group.countW = 1;
- group.countH = 1;
- group.qty = count;
- sizes.push_back(group);
- return count;
- }
- else if (config->readNum(OGL_PREFER_GROUPING)) {
- // Find smallest sizing that holds *everything*, if possible
- int largestCount = (max / w) * (max / h);
- int total = 0;
- // Too many for one texture?
- if (count > largestCount) {
- tGroup group;
- group.texW = optionalPowerOfTwo((max / w) * w);
- group.texH = optionalPowerOfTwo((max / h) * h);
- group.countW = max / w;
- group.countH = max / h;
- group.qty = count / largestCount;
- sizes.push_back(group);
- count -= group.qty * largestCount;
- total += group.qty;
- }
- if (count) {
- int optimalSize = -1;
- int optimalW, optimalH;
- if (config->readNum(OGL_POWER_OF_TWO)) {
- for (int testW = startW; testW <= max; testW *= 2) {
- int countW = testW / w;
- for (int testH = startH; testH <= testW; testH *= 2) {
- int total = countW * (testH / h);
- if (count <= total) {
- if ((testW * testH < optimalSize) || (optimalSize == -1)) {
- optimalSize = testW * testH;
- optimalW = testW;
- optimalH = testH;
- // Abort loop if perfect match found
- if (total == count) {
- testW = max;
- break;
- }
- }
- }
- }
- }
- }
- else {
- int attempts = 0;
- for (int testW = w; testW <= max; testW += w) {
- int countW = testW / w;
- for (int testH = h; testH <= max; testH += h) {
- ++attempts;
- int total = countW * (testH / h);
- if (count <= total) {
- if ((testW * testH < optimalSize) || (optimalSize == -1)) {
- optimalSize = testW * testH;
- optimalW = testW;
- optimalH = testH;
- // Abort loop if perfect match found
- if (total == count) {
- testW = max;
- break;
- }
- }
- }
- }
- }
- }
- tGroup group;
- group.texW = optimalW;
- group.texH = optimalH;
- group.countW = optimalW / w;
- group.countH = optimalH / h;
- group.qty = 1;
- sizes.push_back(group);
- ++total;
- }
- return total;
- }
- else {
- // Find largest sizing that generates the least waste
- // This isn't 100% optimal, but a decent compromise
- int optimalWaste = -1;
- int optimalW, optimalH, optimalFG, optimalLO;
- for (int testW = startW; testW <= max; testW *= 2) {
- int countW = testW / w;
- for (int testH = startH; testH <= testW; testH *= 2) {
- int countH = testH / h;
- // Calculate edge waste on one full grouping
- int waste = testW * testH - countW * w * countH * h;
- // Calculate edge waste for all full groupings
- int fullGroups = count / (countW * countH);
- waste *= fullGroups;
- // Calculate final grouping, if any
- int leftOver = count - (fullGroups * countW * countH);
- if (leftOver) {
- int finalH = optionalPowerOfTwo((leftOver + countW - 1) / countW * h);
- int fCountH = finalH / h;
- int finalW = optionalPowerOfTwo((leftOver + fCountH - 1) / fCountH * w);
- waste += finalW * finalH - leftOver * w * h;
- }
- if ((waste < optimalWaste) || (optimalWaste == -1) ||
- ((waste == optimalWaste) && (testW * testH > optimalW * optimalH))) {
- optimalWaste = waste;
- optimalW = testW;
- optimalH = testH;
- optimalFG = fullGroups;
- optimalLO = leftOver;
- }
- }
- }
- if (optimalFG) {
- tGroup group;
- group.texW = optimalW;
- group.texH = optimalH;
- group.countW = optimalW / w;
- group.countH = optimalH / h;
- group.qty = optimalFG;
- sizes.push_back(group);
- }
- if (optimalLO) {
- tGroup group;
- group.countW = optimalW / w;
- group.texH = optionalPowerOfTwo((optimalLO + group.countW - 1) / group.countW * h);
- group.countH = group.texH / h;
- group.texW = optionalPowerOfTwo((optimalLO + group.countH - 1) / group.countH * h);
- group.countW = group.texW / w;
- group.qty = 1;
- sizes.push_back(group);
- }
- return optimalFG + (optimalLO ? 1 : 0);
- }
- }
- TextureMap::TextureMap(int count, int width, int height,
- void (*tileCoords)(int position, const SDL_Surface*& src, int& x, int& y)
- ) : updates() { start_func
- int max = config->readNum(OGL_MAX_SIZE);
- nextUpdate = NULL;
- prevUpdate = NULL;
- graphics = NULL;
-
- dispWidth = width;
- dispHeight = height;
- graphicCount = count;
-
- GLuint* texNames = NULL;
- Uint8* data = NULL;
-
- if ((width > max) || (height > max)) {
- // Graphic is too large for one texture
- multiTexture = ((max + width - 1) / width) * ((max + height - 1) / height);
- graphics = new TexturePos[multiTexture * (count + 1)];
-
- // @TODO:
- assert(0);
- }
- else {
- // One texture has room for multiple graphics (or one-per)
- // Determine size(s)
- multiTexture = 1;
- graphics = new TexturePos[count + 1];
- vector<tGroup> sizes;
- int numTextures = groupTextures(count, width, height, sizes);
-
- // Allocate textures
- texNames = new GLuint[numTextures];
-
- // Generate textures
- // @TODO: Error checking?
- glGenTextures(numTextures, texNames);
- int tNum = 0;
- int gNum = 1;
- int done = 0;
- // Loop through texture groups
- for (vector<tGroup>::iterator pos = sizes.begin(); (!done) && (pos != sizes.end()); ++pos) {
- // Create texture data
- data = new Uint8[(*pos).texW * (*pos).texH * 4];
- // Loop through individual textures
- for (int tPos = 0; tPos < (*pos).qty; ++tPos, ++tNum) {
- // Technically not required, texture display should never
- // exceed bounds of known textures
- memset(data, 0, (*pos).texW * (*pos).texH * 4);
-
- // Assign to graphics
- int numW = (*pos).countW;
- int numH = (*pos).countH;
- GLfloat texW = (*pos).texW;
- GLfloat texH = (*pos).texH;
- int noSubTex = 0;
- if ((numW == 1) && (numH == 1)) noSubTex = 1;
- for (int y = 0; y < numH; ++y) {
- for (int x = 0; x < numW; ++x) {
- // Blit texture to data?
- if (tileCoords) {
- const SDL_Surface* src;
- int gx, gy;
- tileCoords(gNum, src, gx, gy);
- matrixCopy((Uint8*)src->pixels + gx * 4 + gy * src->pitch,
- data + x * width * 4 + y * (*pos).texW * height * 4,
- width * 4, height,
- src->pitch, (*pos).texW * 4);
- }
-
- // Remember subtexture stats
- graphics[gNum].tex = texNames[tNum];
- graphics[gNum].subtex = noSubTex ? -1 : (x + y * numW);
- graphics[gNum].x1 = (GLfloat)(x * width) / texW;
- graphics[gNum].x2 = (GLfloat)((x + 1) * width) / texW;
- graphics[gNum].y1 = (GLfloat)(y * height) / texH;
- graphics[gNum].y2 = (GLfloat)((y + 1) * height) / texH;
- graphics[gNum].x = x * width;
- graphics[gNum].y = y * height;
- if (++gNum > count) {
- // Abort all loops if done
- y = numH;
- tPos = (*pos).qty;
- done = 1;
- break;
- }
- }
- }
-
- // Create individual texture
- // @TODO: Error checking?
- glBindTexture(GL_TEXTURE_2D, texNames[tNum]);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (*pos).texW, (*pos).texH, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
- // @TODO: Reference in globalTextures
- }
- delete[] data;
- }
- }
-
- delete[] texNames;
- }
- TextureMap::~TextureMap() { start_func
- // @TODO: remove our textures from globalTextures
- // @TODO: deallocate our textures
- // when deallocating, clear lastTexture if matches
- delete[] graphics;
- removeFromUpdates();
- }
- void TextureMap::removeFromUpdates() { start_func
- if (nextUpdate) {
- // (safe even if we're pointing to ourselves)
- nextUpdate->prevUpdate = prevUpdate;
- prevUpdate->nextUpdate = nextUpdate;
- if (headUpdate == this) {
- if (nextUpdate == this) headUpdate = NULL;
- else headUpdate = nextUpdate;
- }
- }
- }
- void TextureMap::store(int graphic, const SDL_Surface* src, int sx, int sy, int dx, int dy, int dw, int dh) { start_func
- // @TODO: doesn't actually cache anything
- if (dw == -1) dw = dispWidth;
- if (dh == -1) dh = dispHeight;
- assert(dx >= 0);
- assert(dy >= 0);
- assert(dx + dw <= dispWidth);
- assert(dy + dh <= dispHeight);
- assert(dw >= 0);
- assert(dh >= 0);
- // Create texture data
- Uint8* data = new Uint8[dw * dh * 4];
- matrixCopy((Uint8*)src->pixels + sx * 4 + sy * src->pitch, data,
- dw * 4, dh,
- src->pitch, dw * 4);
- // @TODO: Error checking?
- glBindTexture(GL_TEXTURE_2D, graphics[graphic].tex);
- glTexSubImage2D(GL_TEXTURE_2D, 0, graphics[graphic].x + dx, graphics[graphic].y + dy,
- dw, dh, GL_RGBA, GL_UNSIGNED_BYTE, data);
- delete[] data;
- }
- void TextureMap::updateTexture() { start_func
- // @TODO:
- removeFromUpdates();
- }
- void TextureMap::updateAllTextures() { start_func
- // @TODO:
- }
- void TextureMap::draw(int graphic, GLint x, GLint y) const { start_func
- assert(graphic > 0);
- assert(graphic <= graphicCount);
- // @TODO: Entire function- error checking?
- if (graphics[graphic].tex != lastTexture) {
- lastTexture = graphics[graphic].tex;
- glBindTexture(GL_TEXTURE_2D, lastTexture);
- }
- glBegin(GL_QUADS);
- glTexCoord2f(graphics[graphic].x1, graphics[graphic].y1); glVertex3i(x, y, 0);
- glTexCoord2f(graphics[graphic].x2, graphics[graphic].y1); glVertex3i(x + dispWidth, y, 0);
- glTexCoord2f(graphics[graphic].x2, graphics[graphic].y2); glVertex3i(x + dispWidth, y + dispHeight, 0);
- glTexCoord2f(graphics[graphic].x1, graphics[graphic].y2); glVertex3i(x, y + dispHeight, 0);
- glEnd();
- }
- void TextureMap::drawScale(int graphic, GLfloat x, GLfloat y, GLfloat scale) const { start_func
- assert(graphic > 0);
- assert(graphic <= graphicCount);
- // @TODO: Entire function- error checking?
- if (graphics[graphic].tex != lastTexture) {
- lastTexture = graphics[graphic].tex;
- glBindTexture(GL_TEXTURE_2D, lastTexture);
- }
- glBegin(GL_QUADS);
- GLfloat w = scale * dispWidth;
- GLfloat h = scale * dispHeight;
-
- glTexCoord2f(graphics[graphic].x1, graphics[graphic].y1); glVertex3f(x, y, 0);
- glTexCoord2f(graphics[graphic].x2, graphics[graphic].y1); glVertex3f(x + w, y, 0);
- glTexCoord2f(graphics[graphic].x2, graphics[graphic].y2); glVertex3f(x + w, y + h, 0);
- glTexCoord2f(graphics[graphic].x1, graphics[graphic].y2); glVertex3f(x, y + h, 0);
- glEnd();
- }
- void TextureMap::draw(int graphic, GLint x, GLint y, int orient) const { start_func
- assert(graphic > 0);
- assert(graphic <= graphicCount);
- // @TODO: Entire function- error checking?
- if (graphics[graphic].tex != lastTexture) {
- lastTexture = graphics[graphic].tex;
- glBindTexture(GL_TEXTURE_2D, lastTexture);
- }
- GLfloat x1 = graphics[graphic].x1;
- GLfloat x2 = graphics[graphic].x2;
- GLfloat y1 = graphics[graphic].y1;
- GLfloat y3 = graphics[graphic].y2;
- if (orient & TEXTURE_FLIP) swap(y1, y3);
- if (orient & TEXTURE_MIRROR) swap(x1, x2);
- GLfloat x3 = x2, x4, y2 = y1, y4 = y3;
- if (orient & TEXTURE_ROTATE) {
- x4 = x2;
- x2 = x1;
- swap(y1, y3);
- }
- else {
- x4 = x1;
- }
- glBegin(GL_QUADS);
- glTexCoord2f(x1, y1); glVertex3i(x, y, 0);
- glTexCoord2f(x2, y2); glVertex3i(x + dispWidth, y, 0);
- glTexCoord2f(x3, y3); glVertex3i(x + dispWidth, y + dispHeight, 0);
- glTexCoord2f(x4, y4); glVertex3i(x, y + dispHeight, 0);
- glEnd();
- }
- void TextureMap::draw(int graphic, GLint x, GLint y, Rect clip) const { start_func
- assert(graphic > 0);
- assert(graphic <= graphicCount);
- // @TODO:
- assert(0);
- }
|