- Texture offsets: models are now decoded for each individual texture offset state and texture coordinates are adjusted while generating the vertex data.

- Model LUT now capable of differentiating between texture offset states (linked list of different texture offsets for each model address).
- Removed texOffset uniform attribute from vertex shader -- no longer needed.
This commit is contained in:
Bart Trzynadlowski 2011-07-21 08:12:16 +00:00
parent c5c518390a
commit a033058deb
4 changed files with 129 additions and 83 deletions

View file

@ -23,6 +23,11 @@
* Models.cpp
*
* Model parsing, caching, and drawing.
*
* TO-DO List:
* -----------
* - More should be predecoded into the polygon structures, so that things like
* texture base coordinates are not re-decoded in two different places!
*/
#include <math.h>
@ -132,7 +137,6 @@ void CRender3D::DrawDisplayList(ModelCache *Cache, POLY_STATE state)
else
{
glUniformMatrix4fv(modelViewMatrixLoc, 1, GL_FALSE, D->Data.Model.modelViewMatrix);
glUniform2fv(texOffsetLoc, 1, D->Data.Model.texOffset);
glDrawArrays(GL_TRIANGLES, D->Data.Model.index, D->Data.Model.numVerts);
}
@ -141,7 +145,7 @@ void CRender3D::DrawDisplayList(ModelCache *Cache, POLY_STATE state)
}
// Appends an instance of a model or viewport to the display list, copying over the required state information
BOOL CRender3D::AppendDisplayList(ModelCache *Cache, BOOL isViewport, int modelNum)
BOOL CRender3D::AppendDisplayList(ModelCache *Cache, BOOL isViewport, const struct VBORef *Model)
{
int lm, i;
@ -173,21 +177,17 @@ BOOL CRender3D::AppendDisplayList(ModelCache *Cache, BOOL isViewport, int modelN
// Copy projection matrix
glGetFloatv(GL_PROJECTION_MATRIX, Cache->List[lm].Data.Viewport.projectionMatrix);
}
else if (Cache->Models[modelNum].numVerts[i] > 0) // vertices exist for this state
else if (Model->numVerts[i] > 0) // vertices exist for this state
{
// Get index for new display list item and advance to next one
lm = Cache->listSize++;
// Point to VBO for current model and state
Cache->List[lm].Data.Model.index = Cache->Models[modelNum].index[i];
Cache->List[lm].Data.Model.numVerts = Cache->Models[modelNum].numVerts[i];
Cache->List[lm].Data.Model.index = Model->index[i];
Cache->List[lm].Data.Model.numVerts = Model->numVerts[i];
// Copy modelview matrix
glGetFloatv(GL_MODELVIEW_MATRIX, Cache->List[lm].Data.Model.modelViewMatrix);
// Texture offset
Cache->List[lm].Data.Model.texOffset[0] = texOffset[0];
Cache->List[lm].Data.Model.texOffset[1] = texOffset[1];
}
else // nothing to do, continue loop
continue;
@ -252,8 +252,8 @@ void CRender3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P,
texWidth = (GLfloat) (32<<((P->header[3]>>3)&7));
texHeight = (GLfloat) (32<<((P->header[3]>>0)&7));
texPage = (P->header[4]&0x40) ? 1024 : 0; // treat texture page as Y coordinate
texBaseX = (GLfloat) (32*(((P->header[4]&0x1F)<<1)|((P->header[5]>>7)&1)));
texBaseY = (GLfloat) (32*(P->header[5]&0x1F)+texPage);
texBaseX = (GLfloat) (32*(((P->header[4]&0x1F)<<1)|((P->header[5]>>7)&1))) + texOffsetXY[0];
texBaseY = (GLfloat) (32*(P->header[5]&0x1F)+texPage) + texOffsetXY[1];
/*
* Lighting and Color Modulation:
@ -494,43 +494,55 @@ BOOL CRender3D::BeginModel(ModelCache *Cache)
}
// Uploads all vertices from the local vertex buffer to the VBO, sets up the VBO reference, updates the LUT
void CRender3D::EndModel(ModelCache *Cache, int lutIdx)
struct VBORef *CRender3D::EndModel(ModelCache *Cache, int lutIdx, UINT16 texOffset)
{
int m;
struct VBORef *Model;
int m;
m = Cache->numModels++;
Model = &(Cache->Models[m]);
// Record the number of vertices, completing the VBORef
for (int i = 0; i < 2; i++)
Cache->Models[m].numVerts[i] = Cache->curVertIdx[i];
Model->numVerts[i] = Cache->curVertIdx[i];
// First alpha polygon immediately follows the normal polygons
Cache->Models[m].index[POLY_STATE_ALPHA] = Cache->Models[m].index[POLY_STATE_NORMAL]+Cache->Models[m].numVerts[POLY_STATE_NORMAL];
Model->index[POLY_STATE_ALPHA] = Model->index[POLY_STATE_NORMAL] + Model->numVerts[POLY_STATE_NORMAL];
// Upload from local vertex buffer to real VBO
glBindBuffer(GL_ARRAY_BUFFER, Cache->vboID);
if (Cache->Models[m].numVerts[POLY_STATE_NORMAL] > 0)
glBufferSubData(GL_ARRAY_BUFFER, Cache->Models[m].index[POLY_STATE_NORMAL]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->curVertIdx[POLY_STATE_NORMAL]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->verts[POLY_STATE_NORMAL]);
if (Cache->Models[m].numVerts[POLY_STATE_ALPHA] > 0)
glBufferSubData(GL_ARRAY_BUFFER, Cache->Models[m].index[POLY_STATE_ALPHA]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->curVertIdx[POLY_STATE_ALPHA]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->verts[POLY_STATE_ALPHA]);
// Update the LUT
Cache->lut[lutIdx] = m;
if (Model->numVerts[POLY_STATE_NORMAL] > 0)
glBufferSubData(GL_ARRAY_BUFFER, Model->index[POLY_STATE_NORMAL]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->curVertIdx[POLY_STATE_NORMAL]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->verts[POLY_STATE_NORMAL]);
if (Model->numVerts[POLY_STATE_ALPHA] > 0)
glBufferSubData(GL_ARRAY_BUFFER, Model->index[POLY_STATE_ALPHA]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->curVertIdx[POLY_STATE_ALPHA]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->verts[POLY_STATE_ALPHA]);
// Record LUT index in the model VBORef
Cache->Models[m].lutIdx = lutIdx;
Model->lutIdx = lutIdx;
// Texture offset of this model state
Model->texOffset = texOffset;
// Update the LUT and link up to any existing model that already exists here
if (Cache->lut[lutIdx] >= 0) // another texture offset state already cached
Model->nextTexOffset = &(Cache->Models[Cache->lut[lutIdx]]);
Cache->lut[lutIdx] = m;
// Return a pointer to the cached model's VBO reference
return Model;
}
/*
* CacheModel():
*
* Decodes and caches a complete model. Returns FAIL if any sort of overflow in
* Decodes and caches a complete model. Returns NULL if any sort of overflow in
* the cache occurred. In this case, the model cache should be cleared before
* being used again because an incomplete model will be stored, wasting vertex
* buffer space.
*
* A pointer to the VBO reference for the cached model is returned when
* successful.
*/
BOOL CRender3D::CacheModel(ModelCache *Cache, int lutIdx, const UINT32 *data)
struct VBORef *CRender3D::CacheModel(ModelCache *Cache, int lutIdx, UINT16 texOffset, const UINT32 *data)
{
Vertex Prev[4]; // previous vertices
int numPolys = 0;
@ -541,11 +553,11 @@ BOOL CRender3D::CacheModel(ModelCache *Cache, int lutIdx, const UINT32 *data)
// return FAIL;
if (data == NULL)
return FAIL;
return NULL;
// Start constructing a new model
if (FAIL == BeginModel(Cache))
return FAIL; // too many models!
return NULL; // too many models!
// Cache all polygons
while (!done)
@ -572,8 +584,10 @@ BOOL CRender3D::CacheModel(ModelCache *Cache, int lutIdx, const UINT32 *data)
texWidth = (32<<((P.header[3]>>3)&7));
texHeight = (32<<((P.header[3]>>0)&7));
texPage = (P.header[4]&0x40) ? 1024 : 0; // treat texture page as Y coordinate
texBaseX = (32*(((P.header[4]&0x1F)<<1)|((P.header[5]>>7)&1)));
texBaseY = (32*(P.header[5]&0x1F)+texPage);
texBaseX = (32*(((P.header[4]&0x1F)<<1)|((P.header[5]>>7)&1))) + (int)texOffsetXY[0];
texBaseY = (32*(P.header[5]&0x1F)+texPage) + (int)texOffsetXY[1];
texBaseX &= 2047;
texBaseY &= 2047;
uvScale = (P.header[1]&0x40)?1.0f:(1.0f/8.0f);
// Determine whether this is an alpha polygon
@ -599,7 +613,7 @@ BOOL CRender3D::CacheModel(ModelCache *Cache, int lutIdx, const UINT32 *data)
}
// Decode the texture
DecodeTexture(texFormat, texBaseX+(int)texOffset[0], texBaseY+(int)texOffset[1], texWidth, texHeight);
DecodeTexture(texFormat, texBaseX, texBaseY, texWidth, texHeight);
// Polygon normal is in upper 24 bits: sign + 1.22 fixed point
P.n[0] = (GLfloat) (((INT32)P.header[1])>>8) * (1.0f/4194304.0f);
@ -661,16 +675,13 @@ BOOL CRender3D::CacheModel(ModelCache *Cache, int lutIdx, const UINT32 *data)
// Copy this polygon into the model buffer
if (OKAY != InsertPolygon(Cache,&P))
return FAIL;
return NULL;
++numPolys;
}
StopDecoding:
// Finish model and enter it into the LUT
EndModel(Cache,lutIdx);
return OKAY;
return EndModel(Cache,lutIdx,texOffset);
}
@ -678,12 +689,26 @@ StopDecoding:
Cache Management
******************************************************************************/
// Use this to determine if a model needs to be cached (returns TRUE if so)
BOOL CRender3D::NeedToCache(ModelCache *Cache, int lutIdx)
/*
* Look up a model. Use this to determine if a model needs to be cached
* (returns NULL if so).
*/
struct VBORef *CRender3D::LookUpModel(ModelCache *Cache, int lutIdx, UINT16 texOffset)
{
//if (Cache->dynamic) // never permanently store models in dynamic caches
// return TRUE;
return (Cache->lut[lutIdx]<0) ? TRUE : FALSE;
int m = Cache->lut[lutIdx];
// Has any state associated with this model LUT index been cached at all?
if (m < 0)
return NULL;
// Has the specified texture offset been cached?
for (struct VBORef *Model = &(Cache->Models[m]); Model != NULL; Model = Model->nextTexOffset)
{
if (Model->texOffset == texOffset)
return Model;
}
return NULL; // no match found, we must cache this new model state
}
// Discard all models in the cache and the display list

View file

@ -188,8 +188,8 @@ void CRender3D::DecodeTexture(int format, int x, int y, int width, int height)
UINT16 texel;
GLfloat c, a;
x %= 2048;
y %= 2048;
x &= 2047;
y &= 2047;
if ((x+width)>2048 || (y+height)>2048)
return;
@ -198,7 +198,7 @@ void CRender3D::DecodeTexture(int format, int x, int y, int width, int height)
//ErrorLog("Encountered a texture that is too large (%d,%d,%d,%d)", x, y, width, 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))
return;
@ -616,12 +616,16 @@ void CRender3D::ClearStack(void)
* buffer overflows and display list overflows will be detected. An attempt is
* made to salvage the situation if this occurs, so if DrawModel() returns
* FAIL, it is a serious matter and rendering should be aborted for the frame.
*
* The current texture offset state, texOffset, is also used. Models are cached
* for each unique texOffset.
*/
BOOL CRender3D::DrawModel(UINT32 modelAddr)
{
ModelCache *Cache;
const UINT32 *model;
int lutIdx;
struct VBORef *ModelRef;
//if (modelAddr==0x7FFF00) // Fighting Vipers (this is not polygon data!)
// return;
@ -637,10 +641,12 @@ BOOL CRender3D::DrawModel(UINT32 modelAddr)
// Look up the model in the LUT and cache it if necessary
lutIdx = modelAddr&0xFFFFFF;
if (NeedToCache(Cache, lutIdx))
ModelRef = LookUpModel(Cache, lutIdx, texOffset);
if (NULL == ModelRef)
{
// Attempt to cache the model
if (CacheModel(Cache, lutIdx, model) != OKAY)
ModelRef = CacheModel(Cache, lutIdx, texOffset, model);
if (NULL == ModelRef)
{
// Model could not be cached. Render what we have so far and try again.
DrawDisplayList(&VROMCache, POLY_STATE_NORMAL);
@ -651,13 +657,14 @@ BOOL CRender3D::DrawModel(UINT32 modelAddr)
ClearModelCache(&PolyCache);
// Try caching again...
if (CacheModel(Cache, lutIdx, model) != OKAY)
ModelRef = CacheModel(Cache, lutIdx, texOffset, model);
if (NULL == ModelRef)
return ErrorUnableToCacheModel(modelAddr); // nothing we can do :(
}
}
// Add to display list
return AppendDisplayList(Cache, FALSE, Cache->lut[lutIdx]);
return AppendDisplayList(Cache, FALSE, ModelRef);
}
// Descends into a 10-word culling node
@ -667,6 +674,7 @@ void CRender3D::DescendCullingNode(UINT32 addr)
UINT32 matrixOffset, node1Ptr, node2Ptr;
float x, y, z, oldTexOffsetX, oldTexOffsetY;
int tx, ty;
UINT16 oldTexOffset;
++stackDepth;
// Stack depth of 64 is too small for Star Wars Trilogy (Hoth)
@ -696,15 +704,20 @@ void CRender3D::DescendCullingNode(UINT32 addr)
z = *(float *) &node[0x06-offset];
// Texture offset?
oldTexOffsetX = texOffset[0]; // save old offsets
oldTexOffsetY = texOffset[1];
tx = 32*((node[0x02]>>7)&0x3F);
ty = 32*(node[0x02]&0x3F) + ((node[0x02]&0x4000)?1024:0);
if ((node[0x02]&0x8000)) // apply texture offsets, else retain current ones
oldTexOffsetX = texOffsetXY[0]; // save old offsets
oldTexOffsetY = texOffsetXY[1];
oldTexOffset = texOffset;
if (!offset) // Step 1.5+
{
texOffset[0] = (GLfloat) tx;
texOffset[1] = (GLfloat) ty;
//printf("Tex Offset: %d, %d (%08X %08X)\n", tx, ty, node[0x02], node[0x00]);
tx = 32*((node[0x02]>>7)&0x3F);
ty = 32*(node[0x02]&0x3F) + ((node[0x02]&0x4000)?1024:0); // TODO: 5 or 6 bits for Y coord?
if ((node[0x02]&0x8000)) // apply texture offsets, else retain current ones
{
texOffsetXY[0] = (GLfloat) tx;
texOffsetXY[1] = (GLfloat) ty;
texOffset = node[0x02]&0x7FFF;
//printf("Tex Offset: %d, %d (%08X %08X)\n", tx, ty, node[0x02], node1Ptr);
}
}
// Apply matrix and translation
@ -735,8 +748,9 @@ void CRender3D::DescendCullingNode(UINT32 addr)
--stackDepth;
// Restore old texture offsets
texOffset[0] = oldTexOffsetX;
texOffset[1] = oldTexOffsetY;
texOffsetXY[0] = oldTexOffsetX;
texOffsetXY[1] = oldTexOffsetY;
texOffset = oldTexOffset;
}
// A list of pointers. MAME assumes that these may only point to culling nodes.
@ -1119,8 +1133,9 @@ void CRender3D::RenderViewport(UINT32 addr, int pri)
//printf("Fog: R=%02X G=%02X B=%02X density=%g (%X) %d start=%g\n", ((vpnode[0x22]>>16)&0xFF), ((vpnode[0x22]>>8)&0xFF), ((vpnode[0x22]>>0)&0xFF), fogParams[3], vpnode[0x23], (fogParams[3]==fogParams[3]), fogParams[4]);
// Clear texture offsets before proceeding
texOffset[0] = 0.0;
texOffset[1] = 0.0;
texOffsetXY[0] = 0.0;
texOffsetXY[1] = 0.0;
texOffset = 0x0000;
// Set up coordinate system and base matrix
glMatrixMode(GL_MODELVIEW);
@ -1329,7 +1344,6 @@ BOOL CRender3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned
spotEllipseLoc = glGetUniformLocation(shaderProgram, "spotEllipse");
spotRangeLoc = glGetUniformLocation(shaderProgram, "spotRange");
spotColorLoc = glGetUniformLocation(shaderProgram, "spotColor");
texOffsetLoc = glGetUniformLocation(shaderProgram, "texOffset");
// Get locations of custom vertex attributes
subTextureLoc = glGetAttribLocation(shaderProgram,"subTexture");

View file

@ -60,12 +60,20 @@ struct Poly
const UINT32 *header; // pointer to Real3D 7-word polygon header
};
// References to model polygons stored in a VBO
/*
* VBORef:
*
* Reference to model polygons stored in a VBO. Each reference has two sets of
* vertices: normal and alpha. Copies of the model with different texture
* offsets applied are searchable via the linked list of texture offset states.
*/
struct VBORef
{
unsigned index[2]; // index of model polygons in VBO
unsigned numVerts[2]; // number of vertices
unsigned lutIdx; // LUT index associated with this model (for fast LUT clearing)
unsigned index[2]; // index of model polygons in VBO
unsigned numVerts[2];// number of vertices
unsigned lutIdx; // LUT index associated with this model (for fast LUT clearing)
struct VBORef *nextTexOffset; // linked list of models with different texture offset states
UINT16 texOffset; // texture offset data for this model
};
// Display list items: model instances and viewport settings
@ -92,7 +100,6 @@ struct DisplayList
struct
{
GLfloat modelViewMatrix[4*4]; // model-view matrix
GLfloat texOffset[2]; // texture offset (X, Y)
unsigned index; // index in VBO
unsigned numVerts; // number of vertices
} Model;
@ -281,18 +288,18 @@ private:
const UINT32 *TranslateModelAddress(UINT32 addr);
// Model caching and display list management
void DrawDisplayList(ModelCache *Cache, POLY_STATE state);
BOOL AppendDisplayList(ModelCache *Cache, BOOL isViewport, int modelNum);
void ClearDisplayList(ModelCache *Cache);
BOOL InsertPolygon(ModelCache *cache, const Poly *p);
void InsertVertex(ModelCache *cache, const Vertex *v, const Poly *p, float normFlip);
BOOL BeginModel(ModelCache *cache);
void EndModel(ModelCache *cache, int lutIdx);
BOOL CacheModel(ModelCache *cache, int lutIdx, const UINT32 *data);
BOOL NeedToCache(ModelCache *cache, int lutIdx);
void ClearModelCache(ModelCache *cache);
BOOL CreateModelCache(ModelCache *cache, unsigned vboMaxVerts, unsigned localMaxVerts, unsigned maxNumModels, unsigned numLUTEntries, unsigned displayListSize, BOOL isDynamic);
void DestroyModelCache(ModelCache *cache);
void DrawDisplayList(ModelCache *Cache, POLY_STATE state);
BOOL AppendDisplayList(ModelCache *Cache, BOOL isViewport, const struct VBORef *Model);
void ClearDisplayList(ModelCache *Cache);
BOOL InsertPolygon(ModelCache *cache, const Poly *p);
void InsertVertex(ModelCache *cache, const Vertex *v, const Poly *p, float normFlip);
BOOL BeginModel(ModelCache *cache);
struct VBORef *EndModel(ModelCache *cache, int lutIdx, UINT16 texOffset);
struct VBORef *CacheModel(ModelCache *cache, int lutIdx, UINT16 texOffset, const UINT32 *data);
struct VBORef *LookUpModel(ModelCache *cache, int lutIdx, UINT16 texOffset);
void ClearModelCache(ModelCache *cache);
BOOL CreateModelCache(ModelCache *cache, unsigned vboMaxVerts, unsigned localMaxVerts, unsigned maxNumModels, unsigned numLUTEntries, unsigned displayListSize, BOOL isDynamic);
void DestroyModelCache(ModelCache *cache);
// Texture management
void DecodeTexture(int format, int x, int y, int width, int height);
@ -359,7 +366,8 @@ private:
int stackDepth; // for debugging and error handling purposes
// Texture offset (during scene graph processing)
GLfloat texOffset[2]; // X, Y
GLfloat texOffsetXY[2]; // decoded X, Y offsets
UINT16 texOffset; // raw texture offset data as it appears in culling node
// Resolution scaling factors (to support resolutions higher than 496x384) and offsets
GLfloat xRatio, yRatio;
@ -379,7 +387,6 @@ private:
GLuint spotEllipseLoc; // uniform
GLuint spotRangeLoc; // uniform
GLuint spotColorLoc; // uniform
GLuint texOffsetLoc; // uniform
GLuint subTextureLoc; // attribute
GLuint texParamsLoc; // attribute
GLuint texFormatLoc; // attribute

View file

@ -34,7 +34,7 @@ uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, light
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (normalized device coordinates), .y=Y position, .z=half-width, .w=half-height)
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
uniform vec3 spotColor; // spotlight RGB color
uniform vec2 texOffset; // offset (within 2048x2048 texture sheet) to apply to texture base coordinates
//uniform vec2 texOffset; // offset (within 2048x2048 texture sheet) to apply to texture base coordinates
// Custom vertex attributes
attribute vec4 subTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
@ -139,7 +139,7 @@ void main(void)
// Pass remaining parameters to fragment shader
gl_TexCoord[0] = gl_MultiTexCoord0;
fsSubTexture = subTexture;
fsSubTexture.xy += texOffset; // apply texture offset
//fsSubTexture.xy += texOffset; // apply texture offset
fsTexParams = texParams;
fsTransLevel = transLevel;
fsTexFormat = texFormat;