Added support for multiple texture sheets (with up to one per Model 3 texture format) as a rather brute-force way to handle overlapping texture formats in the current 3D engine. This fixes some corrupt textures in Daytona 2 and Virtua Striker 2 (and possibly other games) and also offers a small speed increase when some scenes load multiple overlapping textures.

This feature only enables itself when a compatible shader script is loaded.  Since none have been checked in yet this means it is currently disabled.
This commit is contained in:
Nik Henson 2012-02-13 21:54:26 +00:00
parent 35a47bc7e3
commit 84eb017744
6 changed files with 144 additions and 62 deletions

View file

@ -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);
}

View file

@ -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);

View file

@ -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<int>(1, min<int>(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<int>(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);

View file

@ -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

View file

@ -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;
}

View file

@ -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"