diff --git a/Src/Graphics/Models.cpp b/Src/Graphics/Models.cpp index 21db8fa..993f0e0 100644 --- a/Src/Graphics/Models.cpp +++ b/Src/Graphics/Models.cpp @@ -68,7 +68,7 @@ #define VBO_VERTEX_OFFSET_TEXPARAMS_TRANS 20 // texture parameter: >=0 use transparency bit, <0 no transparency (per-polygon) #define VBO_VERTEX_OFFSET_TEXPARAMS_UWRAP 21 // texture parameters: U wrap mode: ==1 mirrored repeat, ==0 normal repeat #define VBO_VERTEX_OFFSET_TEXPARAMS_VWRAP 22 // "" V wrap mode "" -#define VBO_VERTEX_OFFSET_TEXFORMAT_CONTOUR 23 // contour texture: >0 indicates contour texture (see also texParams.trans) +#define VBO_VERTEX_OFFSET_TEXFORMAT 23 // texture format 0-7 (also ==0 indicates contour texture - see also texParams.trans) #define VBO_VERTEX_SIZE 24 // total size (may include padding for alignment) @@ -154,7 +154,7 @@ void CRender3D::DrawDisplayList(ModelCache *Cache, POLY_STATE state) glColorPointer(3, GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_R*sizeof(GLfloat))); glVertexAttribPointer(subTextureLoc, 4, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXTURE_X*sizeof(GLfloat))); glVertexAttribPointer(texParamsLoc, 4, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXPARAMS_EN*sizeof(GLfloat))); - glVertexAttribPointer(texFormatLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXFORMAT_CONTOUR*sizeof(GLfloat))); + glVertexAttribPointer(texFormatLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXFORMAT*sizeof(GLfloat))); glVertexAttribPointer(transLevelLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TRANSLUCENCE*sizeof(GLfloat))); glVertexAttribPointer(lightEnableLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_LIGHTENABLE*sizeof(GLfloat))); glVertexAttribPointer(shininessLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_SHININESS*sizeof(GLfloat))); @@ -485,8 +485,8 @@ void CRender3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P, Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXPARAMS_TRANS] = contourProcessing; Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXPARAMS_UWRAP] = (P->header[2]&2) ? 1.0f : 0.0f; Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXPARAMS_VWRAP] = (P->header[2]&1) ? 1.0f : 0.0f; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXFORMAT_CONTOUR] = (texFormat==0) ? 1.0f : 0.0f; - + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXFORMAT] = (float)texFormat; + Cache->curVertIdx[s]++; Cache->vboCurOffset += VBO_VERTEX_SIZE*sizeof(GLfloat); } diff --git a/Src/Graphics/Render2D.cpp b/Src/Graphics/Render2D.cpp index f6d9647..e05a9e4 100644 --- a/Src/Graphics/Render2D.cpp +++ b/Src/Graphics/Render2D.cpp @@ -550,7 +550,6 @@ void CRender2D::MixLine(UINT32 *dest, const UINT32 *src, int layerNum, int y, bo } } - void CRender2D::DrawTilemaps(UINT32 *destBottom, UINT32 *destTop) { // Base address of all 4 name tables @@ -645,6 +644,7 @@ void CRender2D::DisplaySurface(int surface, GLfloat z) glLoadIdentity(); // Draw the surface + glActiveTexture(GL_TEXTURE0); // texture unit 0 glBindTexture(GL_TEXTURE_2D, texID[surface]); glBegin(GL_QUADS); glTexCoord2f(0.0f/512.0f, 0.0f); glVertex3f(0.0f, 0.0f, z); @@ -697,6 +697,7 @@ void CRender2D::BeginFrame(void) // Update all layers DrawTilemaps(surfBottom, surfTop); + glActiveTexture(GL_TEXTURE0); // texture unit 0 glBindTexture(GL_TEXTURE_2D, texID[0]); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 496, 384, GL_RGBA, GL_UNSIGNED_BYTE, surfTop); glBindTexture(GL_TEXTURE_2D, texID[1]); @@ -801,6 +802,7 @@ bool CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned glGenTextures(2, texID); for (int i = 0; i < 2; i++) { + glActiveTexture(GL_TEXTURE0); // texture unit 0 glBindTexture(GL_TEXTURE_2D, texID[i]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); diff --git a/Src/Graphics/Render3D.cpp b/Src/Graphics/Render3D.cpp index 26f5fb5..84a98ab 100644 --- a/Src/Graphics/Render3D.cpp +++ b/Src/Graphics/Render3D.cpp @@ -202,10 +202,16 @@ void CRender3D::DecodeTexture(int format, int x, int y, int width, int height) return; } - // Check to see if ALL texture tiles have been properly decoded - if ((textureFormat[y/32][x/32]==format) && (textureWidth[y/32][x/32]>=width) && (textureHeight[y/32][x/32]>=height)) + // Map Model3 format to texture unit and texture unit to texture sheet number + unsigned texUnit = fmtToTexUnit[format]; + unsigned texNum = texUnit % numTexIDs; // there may be less texture sheets than texture units (due to lack of video memory) + + // Check to see if ALL texture tiles have been properly decoded on current texture sheet + if ((textureFormat[texNum][y/32][x/32]==format) && (textureWidth[texNum][y/32][x/32]>=width) && (textureHeight[texNum][y/32][x/32]>=height)) return; + //printf("Decoding texture format %u: %u x %u @ (%u, %u) sheet %u\n", format, width, height, x, y, texNum); + // Copy and decode i = 0; switch (format) @@ -382,19 +388,20 @@ void CRender3D::DecodeTexture(int format, int x, int y, int width, int height) // Upload the texture glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glBindTexture(GL_TEXTURE_2D, texID); + glActiveTexture(GL_TEXTURE0 + texUnit); // activate correct texture unit + glBindTexture(GL_TEXTURE_2D, texIDs[texNum]); // bind correct texture sheet glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_FLOAT, textureBuffer); // Mark as decoded - textureFormat[y/32][x/32] = format; - textureWidth[y/32][x/32] = width; - textureHeight[y/32][x/32] = height; + textureFormat[texNum][y/32][x/32] = format; + textureWidth[texNum][y/32][x/32] = width; + textureHeight[texNum][y/32][x/32] = height; } // Signals that new textures have been uploaded. Flushes model caches. Be careful not to exceed bounds! void CRender3D::UploadTextures(unsigned x, unsigned y, unsigned width, unsigned height) { - unsigned xi, yi; + unsigned texNum, xi, yi; // Make everything red #ifdef DEBUG @@ -407,13 +414,19 @@ void CRender3D::UploadTextures(unsigned x, unsigned y, unsigned width, unsigned } #endif - for (xi = x/32; xi < (x+width)/32; xi++) - for (yi = y/32; yi < (y+height)/32; yi++) + // Update all texture sheets + for (texNum = 0; texNum < numTexIDs; texNum++) + { + for (xi = x/32; xi < (x+width)/32; xi++) { - textureFormat[yi][xi] = -1; - textureWidth[yi][xi] = -1; - textureHeight[yi][xi] = -1; + for (yi = y/32; yi < (y+height)/32; yi++) + { + textureFormat[texNum][yi][xi] = -1; + textureWidth[texNum][yi][xi] = -1; + textureHeight[texNum][yi][xi] = -1; + } } + } ClearModelCache(&VROMCache); ClearModelCache(&PolyCache); @@ -962,11 +975,18 @@ void CRender3D::RenderFrame(void) glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); - // Bind Real3D shader program and texture map + // Bind Real3D shader program and texture maps glUseProgram(shaderProgram); - glBindTexture(GL_TEXTURE_2D, texID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // fragment shader performs its own interpolation - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + for (unsigned fmt = 0; fmt < 8; fmt++) + { + // Map Model3 format to texture unit and texture unit to texture sheet number + unsigned texUnit = fmtToTexUnit[fmt]; + unsigned texNum = texUnit % numTexIDs; // there may be less texture sheets than texture units (due to lack of video memory) + glActiveTexture(GL_TEXTURE0 + texUnit); // activate correct texture unit + glBindTexture(GL_TEXTURE_2D, texIDs[texNum]); // bind correct texture sheet + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // fragment shader performs its own interpolation + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } // Enable VBO client states glEnableClientState(GL_VERTEX_ARRAY); @@ -1066,19 +1086,7 @@ bool CRender3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned if (NULL == textureBuffer) return ErrorLog("Insufficient memory for texture decode buffer."); - // Create texture map - glGetError(); // clear error flag - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glGenTextures(1, &texID); - glActiveTexture(GL_TEXTURE0); // texture unit 0 - glBindTexture(GL_TEXTURE_2D, texID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // fragment shader performs its own interpolation - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2048, 2048, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 0); - if (glGetError() != GL_NO_ERROR) - return ErrorLog("OpenGL was unable to provide a 2048x2048-texel texture map."); + glGetError(); // clear error flag // Create model caches and VBOs if (CreateModelCache(&VROMCache, NUM_STATIC_VERTS, NUM_LOCAL_VERTS, NUM_STATIC_MODELS, 0x4000000/4, NUM_DISPLAY_LIST_ITEMS, false)) @@ -1108,11 +1116,77 @@ bool CRender3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned if (OKAY != LoadShaderProgram(&shaderProgram,&vertexShader,&fragmentShader,vsFile,fsFile,vertexShaderSource,fragmentShaderSource)) return FAIL; - // Bind the texture to the "textureMap" uniform so fragment shader can access it - textureMapLoc = glGetUniformLocation(shaderProgram, "textureMap"); - glUseProgram(shaderProgram); // bind program - glUniform1i(textureMapLoc,0); // attach it to texture unit 0 + // Query max number of texture units supported by video card to use as upper limit + GLint glMaxTexUnits; + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &glMaxTexUnits); + unsigned maxTexUnits = max(1, min(g_Config.maxTexUnits, glMaxTexUnits)); + // Try locating default "textureMap" uniform in shader program + glUseProgram(shaderProgram); // bind program + textureMapLoc = glGetUniformLocation(shaderProgram, "textureMap"); + unsigned unitCount = 0; + if (textureMapLoc != -1) + { + // If exists, then bind to first texture unit + unsigned texUnit = unitCount % maxTexUnits; + glUniform1i(textureMapLoc, texUnit++); + unitCount++; + } + + // Try locating "textureMap[0-7]" uniforms in shader program + for (unsigned fmt = 0; fmt < 8; fmt++) + { + char uniformName[12]; + sprintf(uniformName, "textureMap%u", fmt); + textureMapLocs[fmt] = glGetUniformLocation(shaderProgram, uniformName); + if (textureMapLocs[fmt] != -1) + { + // If exists, then bind to next texture unit + unsigned texUnit = unitCount % maxTexUnits; + glUniform1i(textureMapLocs[fmt], texUnit); + fmtToTexUnit[fmt] = texUnit; + unitCount++; + } + else + { + // Otherwise bind to first texture unit by default + fmtToTexUnit[fmt] = 0; + } + } + numTexUnits = min(unitCount, maxTexUnits); + + // Check located at least one uniform to bind to a texture unit + if (numTexUnits == 0) + return ErrorLog("Could not locate any textureMap uniforms in shader script."); + InfoLog("Located and bound %u uniform(s) in GL shader script to %u texture unit(s).", numTexUnits, unitCount); + + // Now try creating texture sheets, one for each texture unit (memory permitting) + numTexIDs = numTexUnits; + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glGenTextures(numTexIDs, texIDs); + for (unsigned texNum = 0; texNum < numTexIDs; texNum++) + { + glActiveTexture(GL_TEXTURE0 + texNum); // activate correct texture unit + glBindTexture(GL_TEXTURE_2D, texIDs[texNum]); // bind correct texture sheet + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // fragment shader performs its own interpolation + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2048, 2048, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 0); + if (glGetError() != GL_NO_ERROR) + { + // Probably ran out of video memory, so don't try creating any more texture sheets + numTexIDs = texNum; + glGetError(); // clear error flag + break; + } + } + + // Check created at least one texture sheet + if (numTexIDs == 0) + return ErrorLog("OpenGL was unable to provide any 2048x2048-texel texture maps."); + InfoLog("Created %u 2048x2048-texel GL texture map(s)", numTexIDs); + // Get location of the rest of the uniforms modelViewMatrixLoc = glGetUniformLocation(shaderProgram,"modelViewMatrix"); projectionMatrixLoc = glGetUniformLocation(shaderProgram,"projectionMatrix"); @@ -1180,7 +1254,7 @@ CRender3D::~CRender3D(void) DestroyShaderProgram(shaderProgram,vertexShader,fragmentShader); if (glBindBuffer != NULL) // we may have failed earlier due to lack of OpenGL 2.0 functions glBindBuffer(GL_ARRAY_BUFFER, 0); // disable VBOs by binding to 0 - glDeleteTextures(1,&texID); + glDeleteTextures(numTexIDs, texIDs); DestroyModelCache(&VROMCache); DestroyModelCache(&PolyCache); diff --git a/Src/Graphics/Render3D.h b/Src/Graphics/Render3D.h index 6a691eb..20d5b60 100644 --- a/Src/Graphics/Render3D.h +++ b/Src/Graphics/Render3D.h @@ -177,11 +177,13 @@ class CRender3DConfig public: string vertexShaderFile; // path to vertex shader or "" to use internal shader string fragmentShaderFile; // fragment shader - + unsigned maxTexUnits; // maximum number of texture units to use (1-9) + // Defaults CRender3DConfig(void) { - // nothing to do, strings will be clear to begin with + // strings will be clear to begin with + maxTexUnits = 9; } }; @@ -382,27 +384,31 @@ private: unsigned xOffs, yOffs; unsigned totalXRes, totalYRes; - // Texture ID for complete 2048x2048 texture map - GLuint texID; + // Texture details + unsigned numTexUnits; // number of texture units + int fmtToTexUnit[8]; // mapping from Model3 texture format to texture unit + unsigned numTexIDs; // number of 2048x2048 texture sheets (maximum 8, one for each Model3 texture format) + GLuint texIDs[8]; // texture IDs of texture sheets // Shader programs and input data locations GLuint shaderProgram; // shader program object GLuint vertexShader; // vertex shader handle GLuint fragmentShader; // fragment shader - GLuint textureMapLoc; // location of "textureMap" uniform - GLuint modelViewMatrixLoc; // uniform - GLuint projectionMatrixLoc; // uniform - GLuint lightingLoc; // uniform - GLuint spotEllipseLoc; // uniform - GLuint spotRangeLoc; // uniform - GLuint spotColorLoc; // uniform - GLuint subTextureLoc; // attribute - GLuint texParamsLoc; // attribute - GLuint texFormatLoc; // attribute - GLuint transLevelLoc; // attribute - GLuint lightEnableLoc; // attribute - GLuint shininessLoc; // attribute - GLuint fogIntensityLoc; // attribute + GLint textureMapLoc; // location of "textureMap" uniform (default combined sheet for all Model3 textures formats) + GLint textureMapLocs[8]; // location of "textureMap[0-7]" uniforms (one sheet per Model3 texture format) + GLint modelViewMatrixLoc; // uniform + GLint projectionMatrixLoc; // uniform + GLint lightingLoc; // uniform + GLint spotEllipseLoc; // uniform + GLint spotRangeLoc; // uniform + GLint spotColorLoc; // uniform + GLint subTextureLoc; // attribute + GLint texParamsLoc; // attribute + GLint texFormatLoc; // attribute + GLint transLevelLoc; // attribute + GLint lightEnableLoc; // attribute + GLint shininessLoc; // attribute + GLint fogIntensityLoc; // attribute // Model caching ModelCache VROMCache; // VROM (static) models @@ -417,9 +423,9 @@ private: * correspond to the texture format bits in the polygon headers. They can * be used to determine whether a texture needs to be updated. */ - int textureWidth[2048/32][2048/32]; - int textureHeight[2048/32][2048/32]; - INT8 textureFormat[2048/32][2048/32]; + int textureWidth[8][2048/32][2048/32]; + int textureHeight[8][2048/32][2048/32]; + INT8 textureFormat[8][2048/32][2048/32]; /* * Texture Decode Buffer diff --git a/Src/Graphics/Shaders/Fragment.glsl b/Src/Graphics/Shaders/Fragment.glsl index 022387a..383af9d 100644 --- a/Src/Graphics/Shaders/Fragment.glsl +++ b/Src/Graphics/Shaders/Fragment.glsl @@ -167,7 +167,7 @@ void main(void) } // If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency - if (fsTexFormat > 0.0) // contour (T1RGB5) texture map + if (fsTexFormat < 0.5) // contour (T1RGB5) texture map fragColor.a = 1.0; } diff --git a/Src/Graphics/Shaders3D.h b/Src/Graphics/Shaders3D.h index d83e02b..420fce1 100644 --- a/Src/Graphics/Shaders3D.h +++ b/Src/Graphics/Shaders3D.h @@ -384,7 +384,7 @@ static const char fragmentShaderSource[] = "\t\t}\n" "\t\t\n" "\t\t// If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency\n" -"\t\tif (fsTexFormat > 0.0)\t\t// contour (T1RGB5) texture map\n" +"\t\tif (fsTexFormat < 0.5)\t\t// contour (T1RGB5) texture map\n" "\t\t\tfragColor.a = 1.0;\n" "\t}\n" "\n"