From 3627213577fb76035bde3163daddc0558820f00a Mon Sep 17 00:00:00 2001 From: Bart Trzynadlowski Date: Tue, 22 Mar 2016 03:43:31 +0000 Subject: [PATCH] Fixed compiler warnings, whitespace, and moved local variable declarations to site of assignment --- Src/Graphics/Legacy3D/Legacy3D.cpp | 2053 ++++++++++++------------- Src/Graphics/Legacy3D/Legacy3D.h | 2 +- Src/Graphics/Legacy3D/Models.cpp | 1671 ++++++++++---------- Src/Graphics/Legacy3D/TextureRefs.cpp | 2 +- 4 files changed, 1815 insertions(+), 1913 deletions(-) diff --git a/Src/Graphics/Legacy3D/Legacy3D.cpp b/Src/Graphics/Legacy3D/Legacy3D.cpp index ea0b726..67cae92 100644 --- a/Src/Graphics/Legacy3D/Legacy3D.cpp +++ b/Src/Graphics/Legacy3D/Legacy3D.cpp @@ -47,12 +47,12 @@ * * The parameters that describe the ellipse in display coordinates are: * - * cx,cy Center point. - * a,b Width (or rather, half-width) and height of spotlight. + * cx,cy Center point. + * a,b Width (or rather, half-width) and height of spotlight. * * These correspond to the standard form of the ellipse equation: * - * ((x-cx)/a)^2 + ((y-cy)/b)^2 = 1 + * ((x-cx)/a)^2 + ((y-cy)/b)^2 = 1 * * It is trivial to test whether a point lies inside an ellipse by plugging * it into the equation and checking to see if it is less than or equal to @@ -61,8 +61,8 @@ * may be up to 16 bits (this has not been observed). They are already * inverted, scaled by the screen size, and squared. * - * w = (496/a)^2 -> a = 496/sqrt(w) - * h = (384/b)^2 -> b = 384/sqrt(h) + * w = (496/a)^2 -> a = 496/sqrt(w) + * h = (384/b)^2 -> b = 384/sqrt(h) * * This is mostly a guess. It is almost certain, however, based on * observations of the Scud Race backfire effect that w and h are related @@ -70,16 +70,16 @@ * view 3 should be smaller than in view 4, but the values are actually * larger. Here is some data: * - * View 3: - * X,Y=247,342 - * W,H=24,16 - * N,F=1e-9,200 - * Car translation length: 4.93 - * View 4: - * X,Y=247,317 - * W,H=48,32 - * N,F=1e-9,200 - * Car translation length: 7.5 + * View 3: + * X,Y=247,342 + * W,H=24,16 + * N,F=1e-9,200 + * Car translation length: 4.93 + * View 4: + * X,Y=247,317 + * W,H=48,32 + * N,F=1e-9,200 + * Car translation length: 7.5 * * The translation length is the total translation vector for the car model * extracted by applying the scene matrices. Note that sqrt(48/24) = 1.4 @@ -114,14 +114,14 @@ * -------------------- * The 32-level polygon translucency appears to be applied as follows * - * 1. If polygon is untextured, fragment color is the polygon color and - * the translucency level becomes the alpha channel. - * 2. If contour textures are used, the translucency level becomes the - * alpha channel regardless of the contour bit. I assume that contour - * bit processing is still carried out, if enabled, however. - * 3. If the texture format is RGBA4, translucency is multiplied by texel - * alpha. - * 4. Other texture formats: ??? + * 1. If polygon is untextured, fragment color is the polygon color and + * the translucency level becomes the alpha channel. + * 2. If contour textures are used, the translucency level becomes the + * alpha channel regardless of the contour bit. I assume that contour + * bit processing is still carried out, if enabled, however. + * 3. If the texture format is RGBA4, translucency is multiplied by texel + * alpha. + * 4. Other texture formats: ??? * * A simple way to handle this is to force alpha to 1.0 for polygon colors, * discard fragments if required by the contour setting (forcing alpha to 1.0 @@ -135,9 +135,9 @@ * for example. This is currently being handled by attempting to detect the * defective scenes. * - * 1. Scud Race: the coordinate system matrix is checked for vectors whose - * magnitudes are not 1.0. - * 2. Virtual On 2: model 0x200000 is not rendered. + * 1. Scud Race: the coordinate system matrix is checked for vectors whose + * magnitudes are not 1.0. + * 2. Virtual On 2: model 0x200000 is not rendered. * * There are probably better ways of doing it. * @@ -146,20 +146,22 @@ * - Can some of the floating point flag attribs be replaced with ints? */ -#include #include "Supermodel.h" -#include "Graphics/Legacy3D/Shaders3D.h" // fragment and vertex shaders +#include "Graphics/Legacy3D/Shaders3D.h" // fragment and vertex shaders +#include +#include +#include namespace Legacy3D { // Microsoft doesn't provide isnan() and isinf() #ifdef _MSC_VER - #include - #define ISNAN(x) (_isnan(x)) - #define ISINF(x) (!_finite(x)) + #include + #define ISNAN(x) (_isnan(x)) + #define ISINF(x) (!_finite(x)) #else - #define ISNAN(x) (std::isnan(x)) - #define ISINF(x) (std::isinf(x)) + #define ISNAN(x) (std::isnan(x)) + #define ISINF(x) (std::isinf(x)) #endif /****************************************************************************** @@ -167,19 +169,16 @@ namespace Legacy3D { ******************************************************************************/ // Shader program files -#define VERTEX_SHADER_FILE "Src/Graphics/Vertex.glsl" -#define FRAGMENT_SHADER_FILE "Src/Graphics/Fragment.glsl" +#define VERTEX_SHADER_FILE "Src/Graphics/Vertex.glsl" +#define FRAGMENT_SHADER_FILE "Src/Graphics/Fragment.glsl" // Model cache settings -#define NUM_STATIC_VERTS 700000 // suggested maximum number of static vertices -#define NUM_DYNAMIC_VERTS 64000 // "" dynamic vertices -#define NUM_LOCAL_VERTS 32768 // size of local vertex buffer -#define NUM_STATIC_MODELS 10000 // maximum number of unique static models to cache -#define NUM_DYNAMIC_MODELS 1024 // maximum number of unique dynamic models to cache -#define NUM_DISPLAY_LIST_ITEMS 10000 // maximum number of model instances displayed per frame - -// Scene traversal stack -#define STACK_SIZE 1024 +#define NUM_STATIC_VERTS 700000 // suggested maximum number of static vertices +#define NUM_DYNAMIC_VERTS 64000 // "" dynamic vertices +#define NUM_LOCAL_VERTS 32768 // size of local vertex buffer +#define NUM_STATIC_MODELS 10000 // maximum number of unique static models to cache +#define NUM_DYNAMIC_MODELS 1024 // maximum number of unique dynamic models to cache +#define NUM_DISPLAY_LIST_ITEMS 10000 // maximum number of model instances displayed per frame /****************************************************************************** @@ -187,259 +186,240 @@ namespace Legacy3D { ******************************************************************************/ // Default mapping from Model3 texture format to texture sheet. -// Currently this is just a simple 1-to-1 mapping but if/when more formats get added, sheets will start to get reused. +// Currently this is just a simple 1-to-1 mapping but if/when more formats get +// added, sheets will start to get reused. int CLegacy3D::defaultFmtToTexSheetNum[] = { - 0, // Fmt 0 -> 0 - 1, // 1 -> 1 - 2, // 2 -> 2 - 3, // 3 -> 3 - 4, // 4 -> 4 - 5, // 5 -> 5 - 6, // 6 -> 6 - 7 // 7 -> 7 - }; + 0, // Fmt 0 -> 0 + 1, // 1 -> 1 + 2, // 2 -> 2 + 3, // 3 -> 3 + 4, // 4 -> 4 + 5, // 5 -> 5 + 6, // 6 -> 6 + 7 // 7 -> 7 +}; void CLegacy3D::DecodeTexture(int format, int x, int y, int width, int height) -{ - int xi, yi, i; - UINT16 texel; - GLfloat c, a; - - x &= 2047; - y &= 2047; - - if ((x+width)>2048 || (y+height)>2048) - return; - if (width > 512 || height > 512) - { - //ErrorLog("Encountered a texture that is too large (%d,%d,%d,%d)", x, y, width, height); - return; - } - - // Map Model3 format to texture sheet - TexSheet *texSheet = fmtToTexSheet[format]; - - // Check to see if ALL texture tiles have been properly decoded on texture sheet - if ((texSheet->texFormat[y/32][x/32] == format) && (texSheet->texWidth[y/32][x/32] >= width) && (texSheet->texHeight[y/32][x/32] >= height)) - return; +{ + x &= 2047; + y &= 2047; + + if ((x+width)>2048 || (y+height)>2048) + return; + if (width > 512 || height > 512) + { + //ErrorLog("Encountered a texture that is too large (%d,%d,%d,%d)", x, y, width, height); + return; + } + + // Map Model3 format to texture sheet + TexSheet *texSheet = fmtToTexSheet[format]; + + // Check to see if ALL texture tiles have been properly decoded on texture sheet + if ((texSheet->texFormat[y/32][x/32] == format) && (texSheet->texWidth[y/32][x/32] >= width) && (texSheet->texHeight[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); + //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) - { - default: // Unknown - - for (yi = y; yi < (y+height); yi++) - { - for (xi = x; xi < (x+width); xi++) - { - textureBuffer[i++] = 0.0; // R - textureBuffer[i++] = 0.0; // G - textureBuffer[i++] = 1.0f; // B - textureBuffer[i++] = 1.0f; // A - } - } - break; - - case 0: // T1RGB5 - for (yi = y; yi < (y+height); yi++) - { - for (xi = x; xi < (x+width); xi++) - { - textureBuffer[i++] = (GLfloat) ((textureRAM[yi*2048+xi]>>10)&0x1F) * (1.0f/31.0f); // R - textureBuffer[i++] = (GLfloat) ((textureRAM[yi*2048+xi]>>5)&0x1F) * (1.0f/31.0f); // G - textureBuffer[i++] = (GLfloat) ((textureRAM[yi*2048+xi]>>0)&0x1F) * (1.0f/31.0f); // B - textureBuffer[i++] = ((textureRAM[yi*2048+xi]&0x8000)?0.0f:1.0f); // T - } - } - break; - - case 7: // RGBA4 - for (yi = y; yi < (y+height); yi++) - { - for (xi = x; xi < (x+width); xi++) - { - textureBuffer[i++] = (GLfloat) ((textureRAM[yi*2048+xi]>>12)&0xF) * (1.0f/15.0f); // R - textureBuffer[i++] = (GLfloat) ((textureRAM[yi*2048+xi]>>8)&0xF) * (1.0f/15.0f); // G - textureBuffer[i++] = (GLfloat) ((textureRAM[yi*2048+xi]>>4)&0xF) * (1.0f/15.0f); // B - textureBuffer[i++] = (GLfloat) ((textureRAM[yi*2048+xi]>>0)&0xF) * (1.0f/15.0f); // A - } - } - break; - - case 5: // 8-bit grayscale - for (yi = y; yi < (y+height); yi++) - { - for (xi = x; xi < (x+width); xi++) - { - /* - texel = textureRAM[yi*2048+xi]; - c = (GLfloat) (texel&0xFF) * (1.0f/255.0f); - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = 1.0; - */ - // Interpret as 8-bit grayscale - texel = textureRAM[yi*2048+xi]; - c = (GLfloat) texel * (1.0f/255.0f); - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = 1.0f; - } - } - - break; - - case 4: // 8-bit, L4A4 - - for (yi = y; yi < (y+height); yi++) - { - for (xi = x; xi < (x+width); xi++) - { - texel = textureRAM[yi*2048+xi]; - //c = (GLfloat) (~texel&0x0F) * (1.0f/15.0f); - //a = (GLfloat) ((texel>>4)&0xF) * (1.0f/15.0f); - c = (GLfloat) ((texel>>4)&0xF) * (1.0f/15.0f); // seems to work better in Lost World (raptor shadows) - a = (GLfloat) (texel&0xF) * (1.0f/15.0f); - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = a; - } - } - - break; - - case 6: // 8-bit grayscale? (How does this differ from format 5? Alpha values?) - for (yi = y; yi < (y+height); yi++) - { - for (xi = x; xi < (x+width); xi++) - { - /* - texel = textureRAM[yi*2048+xi]; - c = (GLfloat) ((texel>>4)&0xF) * (1.0f/15.0f); - a = (GLfloat) (texel&0xF) * (1.0f/15.0f); - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = a; - */ - texel = textureRAM[yi*2048+xi]&0xFF; - c = (GLfloat) texel * (1.0f/255.0f); - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = 1.0f; - - } - } - break; - - case 2: // Unknown (all 16 bits appear present in Daytona 2, but only lower 8 bits in Le Mans 24) - for (yi = y; yi < (y+height); yi++) - { - for (xi = x; xi < (x+width); xi++) - { - texel = textureRAM[yi*2048+xi]; - a = (GLfloat) ((texel>>4)&0xF) * (1.0f/15.0f); - c = (GLfloat) (texel&0xF) * (1.0f/15.0f); - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = a; - - //printf("%04X\n", textureRAM[yi*2048+xi]); - /* - texel = textureRAM[yi*2048+xi]&0xFF; - c = (GLfloat) texel * (1.0f/255.0f); - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = 1.0f; - */ - } - } - break; - - case 3: // Interleaved A4L4 (high byte) - for (yi = y; yi < (y+height); yi++) - { - for (xi = x; xi < (x+width); xi++) - { - texel = textureRAM[yi*2048+xi]>>8; - c = (GLfloat) (texel&0xF) * (1.0f/15.0f); - a = (GLfloat) (texel>>4) * (1.0f/15.0f); - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = a; - } - } - break; - - case 1: // Interleaved A4L4 (low byte) - for (yi = y; yi < (y+height); yi++) - { - for (xi = x; xi < (x+width); xi++) - { - // Interpret as A4L4 - texel = textureRAM[yi*2048+xi]&0xFF; - c = (GLfloat) (texel&0xF) * (1.0f/15.0f); - a = (GLfloat) (texel>>4) * (1.0f/15.0f); - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = c; - textureBuffer[i++] = a; - } - } - break; - } - - // Upload texture to correct position within texture map - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glActiveTexture(GL_TEXTURE0 + texSheet->mapNum); // activate correct texture unit - glBindTexture(GL_TEXTURE_2D, texMapIDs[texSheet->mapNum]); // bind correct texture map - glTexSubImage2D(GL_TEXTURE_2D, 0, texSheet->xOffset + x, texSheet->yOffset + y, width, height, GL_RGBA, GL_FLOAT, textureBuffer); - - // Mark texture as decoded - texSheet->texFormat[y/32][x/32] = format; - texSheet->texWidth[y/32][x/32] = width; - texSheet->texHeight[y/32][x/32] = height; + // Copy and decode + int i = 0; + switch (format) + { + default: // Unknown + for (int yi = y; yi < (y+height); yi++) + { + for (int xi = x; xi < (x+width); xi++) + { + textureBuffer[i++] = 0.0; // R + textureBuffer[i++] = 0.0; // G + textureBuffer[i++] = 1.0f; // B + textureBuffer[i++] = 1.0f; // A + } + } + break; + case 0: // T1RGB5 + for (int yi = y; yi < (y+height); yi++) + { + for (int xi = x; xi < (x+width); xi++) + { + textureBuffer[i++] = (GLfloat) ((textureRAM[yi*2048+xi]>>10)&0x1F) * (1.0f/31.0f); // R + textureBuffer[i++] = (GLfloat) ((textureRAM[yi*2048+xi]>>5)&0x1F) * (1.0f/31.0f); // G + textureBuffer[i++] = (GLfloat) ((textureRAM[yi*2048+xi]>>0)&0x1F) * (1.0f/31.0f); // B + textureBuffer[i++] = ((textureRAM[yi*2048+xi]&0x8000)?0.0f:1.0f); // T + } + } + break; + case 7: // RGBA4 + for (int yi = y; yi < (y+height); yi++) + { + for (int xi = x; xi < (x+width); xi++) + { + textureBuffer[i++] = (GLfloat) ((textureRAM[yi*2048+xi]>>12)&0xF) * (1.0f/15.0f); // R + textureBuffer[i++] = (GLfloat) ((textureRAM[yi*2048+xi]>>8)&0xF) * (1.0f/15.0f); // G + textureBuffer[i++] = (GLfloat) ((textureRAM[yi*2048+xi]>>4)&0xF) * (1.0f/15.0f); // B + textureBuffer[i++] = (GLfloat) ((textureRAM[yi*2048+xi]>>0)&0xF) * (1.0f/15.0f); // A + } + } + break; + case 5: // 8-bit grayscale + for (int yi = y; yi < (y+height); yi++) + { + for (int xi = x; xi < (x+width); xi++) + { + /* + UINT16 texel = textureRAM[yi*2048+xi]; + c = (GLfloat) (texel&0xFF) * (1.0f/255.0f); + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = 1.0; + */ + // Interpret as 8-bit grayscale + UINT16 texel = textureRAM[yi*2048+xi]; + GLfloat c = texel * (1.0f/255.0f); + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = 1.0f; + } + } + break; + case 4: // 8-bit, L4A4 + for (int yi = y; yi < (y+height); yi++) + { + for (int xi = x; xi < (x+width); xi++) + { + UINT16 texel = textureRAM[yi*2048+xi]; + //GLfloat c = (~texel&0x0F) * (1.0f/15.0f); + //GLfloat a = ((texel>>4)&0xF) * (1.0f/15.0f); + GLfloat c = ((texel>>4)&0xF) * (1.0f/15.0f); // seems to work better in Lost World (raptor shadows) + GLfloat a = (texel&0xF) * (1.0f/15.0f); + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = a; + } + } + break; + case 6: // 8-bit grayscale? (How does this differ from format 5? Alpha values?) + for (int yi = y; yi < (y+height); yi++) + { + for (int xi = x; xi < (x+width); xi++) + { + /* + UINT16 texel = textureRAM[yi*2048+xi]; + GLfloat c = ((texel>>4)&0xF) * (1.0f/15.0f); + GLfloat a = (texel&0xF) * (1.0f/15.0f); + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = a; + */ + UINT16 texel = textureRAM[yi*2048+xi]&0xFF; + GLfloat c = texel * (1.0f/255.0f); + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = 1.0f; + } + } + break; + case 2: // Unknown (all 16 bits appear present in Daytona 2, but only lower 8 bits in Le Mans 24) + for (int yi = y; yi < (y+height); yi++) + { + for (int xi = x; xi < (x+width); xi++) + { + UINT16 texel = textureRAM[yi*2048+xi]; + GLfloat a = ((texel>>4)&0xF) * (1.0f/15.0f); + GLfloat c = (texel&0xF) * (1.0f/15.0f); + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = a; + //printf("%04X\n", textureRAM[yi*2048+xi]); + /* + UINT16 texel = textureRAM[yi*2048+xi]&0xFF; + GLfloat c = texel * (1.0f/255.0f); + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = 1.0f; + */ + } + } + break; + case 3: // Interleaved A4L4 (high byte) + for (int yi = y; yi < (y+height); yi++) + { + for (int xi = x; xi < (x+width); xi++) + { + UINT16 texel = textureRAM[yi*2048+xi]>>8; + GLfloat c = (texel&0xF) * (1.0f/15.0f); + GLfloat a = (texel>>4) * (1.0f/15.0f); + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = a; + } + } + break; + case 1: // Interleaved A4L4 (low byte) + for (int yi = y; yi < (y+height); yi++) + { + for (int xi = x; xi < (x+width); xi++) + { + // Interpret as A4L4 + UINT16 texel = textureRAM[yi*2048+xi]&0xFF; + GLfloat c = (texel&0xF) * (1.0f/15.0f); + GLfloat a = (texel>>4) * (1.0f/15.0f); + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = c; + textureBuffer[i++] = a; + } + } + break; + } + + // Upload texture to correct position within texture map + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glActiveTexture(GL_TEXTURE0 + texSheet->mapNum); // activate correct texture unit + glBindTexture(GL_TEXTURE_2D, texMapIDs[texSheet->mapNum]); // bind correct texture map + glTexSubImage2D(GL_TEXTURE_2D, 0, texSheet->xOffset + x, texSheet->yOffset + y, width, height, GL_RGBA, GL_FLOAT, textureBuffer); + + // Mark texture as decoded + texSheet->texFormat[y/32][x/32] = format; + texSheet->texWidth[y/32][x/32] = width; + texSheet->texHeight[y/32][x/32] = height; } // Signals that new textures have been uploaded. Flushes model caches. Be careful not to exceed bounds! void CLegacy3D::UploadTextures(unsigned x, unsigned y, unsigned width, unsigned height) { - unsigned texSheet, xi, yi; - - // Make everything red #ifdef DEBUG - for (int i = 0; i < 512*512; ) - { - textureBuffer[i++] = 1.0f; - textureBuffer[i++] = 0.0f; - textureBuffer[i++] = 0.0f; - textureBuffer[i++] = 1.0f; - } + // Make everything red + for (int i = 0; i < 512*512; ) + { + textureBuffer[i++] = 1.0f; + textureBuffer[i++] = 0.0f; + textureBuffer[i++] = 0.0f; + textureBuffer[i++] = 1.0f; + } #endif - // Update all texture sheets - for (texSheet = 0; texSheet < numTexSheets; texSheet++) - { - for (xi = x/32; xi < (x+width)/32; xi++) - { - for (yi = y/32; yi < (y+height)/32; yi++) - { - texSheets[texSheet].texFormat[yi][xi] = -1; - texSheets[texSheet].texWidth[yi][xi] = -1; - texSheets[texSheet].texHeight[yi][xi] = -1; - } - } - } + // Update all texture sheets + for (size_t texSheet = 0; texSheet < numTexSheets; texSheet++) + { + for (size_t xi = x/32; xi < (x+width)/32; xi++) + { + for (size_t yi = y/32; yi < (y+height)/32; yi++) + { + texSheets[texSheet].texFormat[yi][xi] = -1; + texSheets[texSheet].texWidth[yi][xi] = -1; + texSheets[texSheet].texHeight[yi][xi] = -1; + } + } + } } /****************************************************************************** @@ -451,28 +431,28 @@ void CLegacy3D::UploadTextures(unsigned x, unsigned y, unsigned width, unsigned // Translates 24-bit culling RAM addresses const UINT32 *CLegacy3D::TranslateCullingAddress(UINT32 addr) { - addr &= 0x00FFFFFF; // caller should have done this already - - if ((addr>=0x800000) && (addr<0x840000)) - return &cullingRAMHi[addr&0x3FFFF]; - else if (addr < 0x100000) - return &cullingRAMLo[addr]; - + addr &= 0x00FFFFFF; // caller should have done this already + + if ((addr>=0x800000) && (addr<0x840000)) + return &cullingRAMHi[addr&0x3FFFF]; + else if (addr < 0x100000) + return &cullingRAMLo[addr]; + #ifdef DEBUG - ErrorLog("TranslateCullingAddress(): invalid address: %06X", addr); + ErrorLog("TranslateCullingAddress(): invalid address: %06X", addr); #endif - return NULL; + return NULL; } // Translates model references const UINT32 *CLegacy3D::TranslateModelAddress(UINT32 modelAddr) { - modelAddr &= 0x00FFFFFF; // caller should have done this already - - if (modelAddr < 0x100000) - return &polyRAM[modelAddr]; - else - return &vrom[modelAddr]; + modelAddr &= 0x00FFFFFF; // caller should have done this already + + if (modelAddr < 0x100000) + return &polyRAM[modelAddr]; + else + return &vrom[modelAddr]; } @@ -481,7 +461,7 @@ const UINT32 *CLegacy3D::TranslateModelAddress(UINT32 modelAddr) ******************************************************************************/ // Macro to generate column-major (OpenGL) index from y,x subscripts -#define CMINDEX(y,x) (x*4+y) +#define CMINDEX(y,x) (x*4+y) /* * MultMatrix(): @@ -492,29 +472,27 @@ const UINT32 *CLegacy3D::TranslateModelAddress(UINT32 modelAddr) */ void CLegacy3D::MultMatrix(UINT32 matrixOffset) { - GLfloat m[4*4]; - const float *src = &matrixBasePtr[matrixOffset*12]; - - if (matrixBasePtr==NULL) // LA Machineguns - return; - m[CMINDEX(0, 0)] = src[3]; - m[CMINDEX(0, 1)] = src[4]; - m[CMINDEX(0, 2)] = src[5]; - m[CMINDEX(0, 3)] = src[0]; - m[CMINDEX(1, 0)] = src[6]; - m[CMINDEX(1, 1)] = src[7]; - m[CMINDEX(1, 2)] = src[8]; - m[CMINDEX(1, 3)] = src[1]; - m[CMINDEX(2, 0)] = src[9]; - m[CMINDEX(2, 1)] = src[10]; - m[CMINDEX(2, 2)] = src[11]; - m[CMINDEX(2, 3)] = src[2]; - m[CMINDEX(3, 0)] = 0.0; - m[CMINDEX(3, 1)] = 0.0; - m[CMINDEX(3, 2)] = 0.0; - m[CMINDEX(3, 3)] = 1.0; - - glMultMatrixf(m); + GLfloat m[4*4]; + const float *src = &matrixBasePtr[matrixOffset*12]; + if (matrixBasePtr==NULL) // LA Machineguns + return; + m[CMINDEX(0, 0)] = src[3]; + m[CMINDEX(0, 1)] = src[4]; + m[CMINDEX(0, 2)] = src[5]; + m[CMINDEX(0, 3)] = src[0]; + m[CMINDEX(1, 0)] = src[6]; + m[CMINDEX(1, 1)] = src[7]; + m[CMINDEX(1, 2)] = src[8]; + m[CMINDEX(1, 3)] = src[1]; + m[CMINDEX(2, 0)] = src[9]; + m[CMINDEX(2, 1)] = src[10]; + m[CMINDEX(2, 2)] = src[11]; + m[CMINDEX(2, 3)] = src[2]; + m[CMINDEX(3, 0)] = 0.0; + m[CMINDEX(3, 1)] = 0.0; + m[CMINDEX(3, 2)] = 0.0; + m[CMINDEX(3, 3)] = 1.0; + glMultMatrixf(m); } /* @@ -526,17 +504,17 @@ void CLegacy3D::MultMatrix(UINT32 matrixOffset) * * Model 3 games tend to define the following unusual base matrix: * - * 0 0 -1 0 - * 1 0 0 0 - * 0 -1 0 0 - * 0 0 0 1 + * 0 0 -1 0 + * 1 0 0 0 + * 0 -1 0 0 + * 0 0 0 1 * * When this is multiplied by a column vector, the output is: * - * -Z - * X - * -Y - * 1 + * -Z + * X + * -Y + * 1 * * My theory is that the Real3D GPU accepts vectors in Z,X,Y order. The games * store everything as X,Y,Z and perform the translation at the end. The Real3D @@ -548,29 +526,29 @@ void CLegacy3D::MultMatrix(UINT32 matrixOffset) void CLegacy3D::InitMatrixStack(UINT32 matrixBaseAddr) { - GLfloat m[4*4]; + GLfloat m[4*4]; - // This matrix converts vectors back from the weird Model 3 Z,X,Y ordering - // and also into OpenGL viewspace (-Y,-Z) - m[CMINDEX(0,0)]=0.0; m[CMINDEX(0,1)]=1.0; m[CMINDEX(0,2)]=0.0; m[CMINDEX(0,3)]=0.0; - m[CMINDEX(1,0)]=0.0; m[CMINDEX(1,1)]=0.0; m[CMINDEX(1,2)]=-1.0; m[CMINDEX(1,3)]=0.0; - m[CMINDEX(2,0)]=-1.0; m[CMINDEX(2,1)]=0.0; m[CMINDEX(2,2)]=0.0; m[CMINDEX(2,3)]=0.0; - m[CMINDEX(3,0)]=0.0; m[CMINDEX(3,1)]=0.0; m[CMINDEX(3,2)]=0.0; m[CMINDEX(3,3)]=1.0; - - if (step > 0x10) - glLoadMatrixf(m); - else - { - // Scaling seems to help w/ Step 1.0's extremely large coordinates - GLfloat s = 1.0f/2048.0f; - glLoadIdentity(); - glScalef(s,s,s); - glMultMatrixf(m); - } - - // Set matrix base address and apply matrix #0 (coordinate system matrix) - matrixBasePtr = (float *) TranslateCullingAddress(matrixBaseAddr); - MultMatrix(0); + // This matrix converts vectors back from the weird Model 3 Z,X,Y ordering + // and also into OpenGL viewspace (-Y,-Z) + m[CMINDEX(0,0)]=0.0; m[CMINDEX(0,1)]=1.0; m[CMINDEX(0,2)]=0.0; m[CMINDEX(0,3)]=0.0; + m[CMINDEX(1,0)]=0.0; m[CMINDEX(1,1)]=0.0; m[CMINDEX(1,2)]=-1.0; m[CMINDEX(1,3)]=0.0; + m[CMINDEX(2,0)]=-1.0; m[CMINDEX(2,1)]=0.0; m[CMINDEX(2,2)]=0.0; m[CMINDEX(2,3)]=0.0; + m[CMINDEX(3,0)]=0.0; m[CMINDEX(3,1)]=0.0; m[CMINDEX(3,2)]=0.0; m[CMINDEX(3,3)]=1.0; + + if (step > 0x10) + glLoadMatrixf(m); + else + { + // Scaling seems to help w/ Step 1.0's extremely large coordinates + GLfloat s = 1.0f/2048.0f; + glLoadIdentity(); + glScalef(s,s,s); + glMultMatrixf(m); + } + + // Set matrix base address and apply matrix #0 (coordinate system matrix) + matrixBasePtr = (float *) TranslateCullingAddress(matrixBaseAddr); + MultMatrix(0); } @@ -589,26 +567,26 @@ static bool IsDynamicModel(const UINT32 *data) { if (data == NULL) return false; - unsigned sharedVerts[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 }; - // VROM models are only dynamic if they reference polygon RAM via color palette indices - bool done = false; - do - { - // Check if polygon has color palette reference, if so polygon is dynamic and can return here - if ((data[1]&2) == 0) - return true; - if (data[6] == 0) - break; - // Get number of vertices - unsigned numVerts = (data[0]&0x40 ? 4 : 3); - // Deduct number of reused verts + unsigned sharedVerts[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 }; + // VROM models are only dynamic if they reference polygon RAM via color palette indices + bool done = false; + do + { + // Check if polygon has color palette reference, if so polygon is dynamic and can return here + if ((data[1]&2) == 0) + return true; + if (data[6] == 0) + break; + // Get number of vertices + unsigned numVerts = (data[0]&0x40 ? 4 : 3); + // Deduct number of reused verts numVerts -= sharedVerts[data[0]&0xf]; - done = data[1]&4; - // Skip header and vertices to next polygon - data += 7 + numVerts * 4; - } - while (!done); - return false; + done = data[1]&4; + // Skip header and vertices to next polygon + data += 7 + numVerts * 4; + } + while (!done); + return false; } /* @@ -624,26 +602,18 @@ static bool IsDynamicModel(const UINT32 *data) */ bool CLegacy3D::DrawModel(UINT32 modelAddr) { - ModelCache *Cache; - const UINT32 *model; - int lutIdx; - struct VBORef *ModelRef; - - //if (modelAddr==0x7FFF00) // Fighting Vipers (this is not polygon data!) - // return; - if (modelAddr == 0x200000) // Virtual On 2 (during boot-up, causes slow-down) - return OKAY; - model = TranslateModelAddress(modelAddr); - - // Determine whether model is in polygon RAM or VROM - if (IsVROMModel(modelAddr)) - Cache = &VROMCache; - else - Cache = &PolyCache; - - // Look up the model in the LUT and cache it if necessary - lutIdx = modelAddr&0xFFFFFF; - ModelRef = LookUpModel(Cache, lutIdx, texOffset); + //if (modelAddr==0x7FFF00) // Fighting Vipers (this is not polygon data!) + // return; + if (modelAddr == 0x200000) // Virtual On 2 (during boot-up, causes slow-down) + return OKAY; + const UINT32 *model = TranslateModelAddress(modelAddr); + + // Determine whether model is in polygon RAM or VROM + ModelCache *Cache = IsVROMModel(modelAddr) ? &VROMCache : &PolyCache; + + // Look up the model in the LUT and cache it if necessary + int lutIdx = modelAddr&0xFFFFFF; + struct VBORef *ModelRef = LookUpModel(Cache, lutIdx, texOffset); if (NULL == ModelRef && Cache == &VROMCache) { // If the model was a VROM model, it may be dynamic, so we need to try @@ -653,182 +623,168 @@ bool CLegacy3D::DrawModel(UINT32 modelAddr) Cache = &PolyCache; } - if (NULL == ModelRef) - { - // Attempt to cache the model, and perform a final check to determine - // whether VROM model is in fact dynamic (this should be fixed -- models - // should be decoded to a common buffer and the cache determined - // afterwards) - if (Cache == &VROMCache && IsDynamicModel(model)) - Cache = &PolyCache; - 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); - DrawDisplayList(&PolyCache, POLY_STATE_NORMAL); - DrawDisplayList(&VROMCache, POLY_STATE_ALPHA); - DrawDisplayList(&PolyCache, POLY_STATE_ALPHA); - ClearModelCache(&VROMCache); - ClearModelCache(&PolyCache); - - // Try caching again... - ModelRef = CacheModel(Cache, lutIdx, texOffset, model); - if (NULL == ModelRef) - return ErrorUnableToCacheModel(modelAddr); // nothing we can do :( - } - } + if (NULL == ModelRef) + { + // Attempt to cache the model, and perform a final check to determine + // whether VROM model is in fact dynamic (this should be fixed -- models + // should be decoded to a common buffer and the cache determined + // afterwards) + if (Cache == &VROMCache && IsDynamicModel(model)) + Cache = &PolyCache; + 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); + DrawDisplayList(&PolyCache, POLY_STATE_NORMAL); + DrawDisplayList(&VROMCache, POLY_STATE_ALPHA); + DrawDisplayList(&PolyCache, POLY_STATE_ALPHA); + ClearModelCache(&VROMCache); + ClearModelCache(&PolyCache); + + // Try caching again... + ModelRef = CacheModel(Cache, lutIdx, texOffset, model); + if (NULL == ModelRef) + return ErrorUnableToCacheModel(modelAddr); // nothing we can do :( + } + } - // If cache is static then decode all the texture references contained in the cached model - // before rendering (models in dynamic cache will have been decoded already in CacheModel) - if (!Cache->dynamic) - ModelRef->texRefs.DecodeAllTextures(this); + // If cache is static then decode all the texture references contained in the cached model + // before rendering (models in dynamic cache will have been decoded already in CacheModel) + if (!Cache->dynamic) + ModelRef->texRefs.DecodeAllTextures(this); - // Add to display list - return AppendDisplayList(Cache, false, ModelRef); + // Add to display list + return AppendDisplayList(Cache, false, ModelRef); } // Descends into a 10-word culling node void CLegacy3D::DescendCullingNode(UINT32 addr) -{ - const UINT32 *node, *lodTable; - 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) - if (stackDepth>=(512+64)) // safety (prevent overflows -- OpenGL matrix stack will still overflow by this point) - { - --stackDepth; - return; - } +{ + ++stackDepth; + // Stack depth of 64 is too small for Star Wars Trilogy (Hoth) + if (stackDepth>=(512+64)) // safety (prevent overflows -- OpenGL matrix stack will still overflow by this point) + { + --stackDepth; + return; + } - node = TranslateCullingAddress(addr); - if (NULL == node) - { - --stackDepth; - return; - } + const UINT32 *node = TranslateCullingAddress(addr); + if (NULL == node) + { + --stackDepth; + return; + } //printf("%08x NODE %d\n", addr, stackDepth); //for (int i = 0; i < 8; i++) // printf(" %08x\n", node[i]); - // Debug: texture offset? (NOTE: offsets 1 and 2 don't exist on step 1.0) - //if (node[0x02]&0xFFFF) - // printf("%X -> %02X %04X\n", addr, node[0x00]&0xFF, node[0x02]&0xFFFF); - - // Extract known fields - node1Ptr = node[0x07-offset]; - node2Ptr = node[0x08-offset]; - matrixOffset = node[0x03-offset]&0xFFF; - x = *(float *) &node[0x04-offset]; - y = *(float *) &node[0x05-offset]; - z = *(float *) &node[0x06-offset]; - - // Texture offset? - oldTexOffsetX = texOffsetXY[0]; // save old offsets - oldTexOffsetY = texOffsetXY[1]; - oldTexOffset = texOffset; - if (!offset) // Step 1.5+ - { - 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 - glPushMatrix(); - if ((node[0x00]&0x10)) // apply translation vector - glTranslatef(x,y,z); - else if (matrixOffset) // multiply matrix, if specified - MultMatrix(matrixOffset); - - // Descend down first link - if ((node[0x00]&0x08)) // 4-element LOD table - { - lodTable = TranslateCullingAddress(node1Ptr); - if (NULL != lodTable) - { - if ((node[0x03-offset]&0x20000000)) - DescendCullingNode(lodTable[0]&0xFFFFFF); - else - DrawModel(lodTable[0]&0xFFFFFF); - } - } - else - DescendNodePtr(node1Ptr); - - // Proceed to second link - glPopMatrix(); - if ((node[0x00] & 0x07) != 0x06) // seems to indicate second link is invalid (fixes circular references) - DescendNodePtr(node2Ptr); - --stackDepth; - - // Restore old texture offsets - texOffsetXY[0] = oldTexOffsetX; - texOffsetXY[1] = oldTexOffsetY; - texOffset = oldTexOffset; + // Debug: texture offset? (NOTE: offsets 1 and 2 don't exist on step 1.0) + //if (node[0x02]&0xFFFF) + // printf("%X -> %02X %04X\n", addr, node[0x00]&0xFF, node[0x02]&0xFFFF); + + // Extract known fields + const UINT32 node1Ptr = node[0x07-offset]; + const UINT32 node2Ptr = node[0x08-offset]; + const UINT32 matrixOffset = node[0x03-offset]&0xFFF; + const float x = *(float *) &node[0x04-offset]; + const float y = *(float *) &node[0x05-offset]; + const float z = *(float *) &node[0x06-offset]; + + // Texture offset? + const float oldTexOffsetX = texOffsetXY[0]; // save old offsets + const float oldTexOffsetY = texOffsetXY[1]; + const UINT16 oldTexOffset = texOffset; + if (!offset) // Step 1.5+ + { + int tx = 32*((node[0x02]>>7)&0x3F); + int 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 + glPushMatrix(); + if ((node[0x00]&0x10)) // apply translation vector + glTranslatef(x,y,z); + else if (matrixOffset) // multiply matrix, if specified + MultMatrix(matrixOffset); + + // Descend down first link + if ((node[0x00]&0x08)) // 4-element LOD table + { + const UINT32 *lodTable = TranslateCullingAddress(node1Ptr); + if (NULL != lodTable) + { + if ((node[0x03-offset]&0x20000000)) + DescendCullingNode(lodTable[0]&0xFFFFFF); + else + DrawModel(lodTable[0]&0xFFFFFF); + } + } + else + DescendNodePtr(node1Ptr); + + // Proceed to second link + glPopMatrix(); + if ((node[0x00] & 0x07) != 0x06) // seems to indicate second link is invalid (fixes circular references) + DescendNodePtr(node2Ptr); + --stackDepth; + + // Restore old texture offsets + texOffsetXY[0] = oldTexOffsetX; + texOffsetXY[1] = oldTexOffsetY; + texOffset = oldTexOffset; } // A list of pointers. MAME assumes that these may only point to culling nodes. void CLegacy3D::DescendPointerList(UINT32 addr) { - const UINT32 *list; - UINT32 nodeAddr; - int listEnd; - - if (listDepth > 2) // several Step 2.1 games require this safeguard - return; - - list = TranslateCullingAddress(addr); - if (NULL == list) - return; - - ++listDepth; -//printf("%08x LIST\n", addr); - // Traverse the list forward and print it out - listEnd = 0; - while (1) - { -//printf(" %08x\n", list[listEnd]); - if ((list[listEnd] & 0x02000000)) // end of list (?) - break; - - if ((list[listEnd] == 0) || (((list[listEnd])>>24) != 0)) - { - //printf("ATTENTION: Unknown list termination: %08X.\n", list[listEnd]); - listEnd--; // back up to last valid list element - break; - } - - ++listEnd; - } + if (listDepth > 2) // several Step 2.1 games require this safeguard + return; + + const UINT32 *list = TranslateCullingAddress(addr); + if (NULL == list) + return; + + ++listDepth; + // Traverse the list forward and print it out + int listEnd = 0; + while (1) + { + if ((list[listEnd] & 0x02000000)) // end of list (?) + break; + if ((list[listEnd] == 0) || (((list[listEnd])>>24) != 0)) + { + //printf("ATTENTION: Unknown list termination: %08X.\n", list[listEnd]); + listEnd--; // back up to last valid list element + break; + } + ++listEnd; + } - // Traverse the list backward and descend into each pointer - while (listEnd >= 0) - { - nodeAddr = list[listEnd]&0x00FFFFFF; // clear upper 8 bits to ensure this is processed as a culling node - if (!(list[listEnd]&0x01000000))//Fighting Vipers - { - if ((nodeAddr != 0) && (nodeAddr != 0x800800)) - { - DescendCullingNode(nodeAddr); - } - //else - // printf("Strange pointers encountered\n"); - } - --listEnd; - } - - --listDepth; + // Traverse the list backward and descend into each pointer + while (listEnd >= 0) + { + UINT32 nodeAddr = list[listEnd]&0x00FFFFFF; // clear upper 8 bits to ensure this is processed as a culling node + if (!(list[listEnd]&0x01000000))//Fighting Vipers + { + if ((nodeAddr != 0) && (nodeAddr != 0x800800)) + { + DescendCullingNode(nodeAddr); + } + //else + // printf("Strange pointers encountered\n"); + } + --listEnd; + } + + --listDepth; } /* @@ -837,259 +793,250 @@ void CLegacy3D::DescendPointerList(UINT32 addr) * The old scene traversal engine. Recursively descends into a node pointer. */ void CLegacy3D::DescendNodePtr(UINT32 nodeAddr) -{ - // Ignore null links - if ((nodeAddr&0x00FFFFFF) == 0) - return; - - switch ((nodeAddr>>24)&0xFF) // pointer type encoded in upper 8 bits - { - case 0x00: // culling node - DescendCullingNode(nodeAddr&0xFFFFFF); - break; - case 0x01: // model (perhaps bit 1 is a flag in this case?) - case 0x03: - DrawModel(nodeAddr&0xFFFFFF); - break; - case 0x04: // pointer list - DescendPointerList(nodeAddr&0xFFFFFF); - break; - default: - //printf("ATTENTION: Unknown pointer format: %08X\n\n", nodeAddr); - break; - } +{ + // Ignore null links + if ((nodeAddr&0x00FFFFFF) == 0) + return; + + switch ((nodeAddr>>24)&0xFF) // pointer type encoded in upper 8 bits + { + case 0x00: // culling node + DescendCullingNode(nodeAddr&0xFFFFFF); + break; + case 0x01: // model (perhaps bit 1 is a flag in this case?) + case 0x03: + DrawModel(nodeAddr&0xFFFFFF); + break; + case 0x04: // pointer list + DescendPointerList(nodeAddr&0xFFFFFF); + break; + default: + //printf("ATTENTION: Unknown pointer format: %08X\n\n", nodeAddr); + break; + } } // Draws viewports of the given priority void CLegacy3D::RenderViewport(UINT32 addr, int pri) { - GLfloat color[8][3] = // RGB1 translation - { - { 0.0, 0.0, 0.0 }, // off - { 0.0, 0.0, 1.0 }, // blue - { 0.0, 1.0, 0.0 }, // green - { 0.0, 1.0, 1.0 }, // cyan - { 1.0, 0.0, 0.0 }, // red - { 1.0, 0.0, 1.0 }, // purple - { 1.0, 1.0, 0.0 }, // yellow - { 1.0, 1.0, 1.0 } // white - }; - const UINT32 *vpnode; - UINT32 nextAddr, nodeAddr, matrixBase; - int curPri; - int vpX, vpY, vpWidth, vpHeight; - int spotColorIdx; - GLfloat vpTopAngle, vpBotAngle, fovYDegrees; - GLfloat scrollFog, scrollAtt; - - // Translate address and obtain pointer - vpnode = TranslateCullingAddress(addr); - if (NULL == vpnode) - return; - - curPri = (vpnode[0x00] >> 3) & 3; // viewport priority - nextAddr = vpnode[0x01] & 0xFFFFFF; // next viewport - nodeAddr = vpnode[0x02]; // scene database node pointer + static const GLfloat color[8][3] = { + { 0.0, 0.0, 0.0 }, // off + { 0.0, 0.0, 1.0 }, // blue + { 0.0, 1.0, 0.0 }, // green + { 0.0, 1.0, 1.0 }, // cyan + { 1.0, 0.0, 0.0 }, // red + { 1.0, 0.0, 1.0 }, // purple + { 1.0, 1.0, 0.0 }, // yellow + { 1.0, 1.0, 1.0 } // white + }; - // Recursively process next viewport - if (vpnode[0x01] == 0) // memory probably hasn't been set up yet, abort - return; - if (vpnode[0x01] != 0x01000000) - RenderViewport(vpnode[0x01],pri); + // Translate address and obtain pointer + const UINT32 *vpnode = TranslateCullingAddress(addr); + if (NULL == vpnode) + return; - // If the priority doesn't match, do not process - if (curPri != pri) - return; - - // Fetch viewport parameters (TO-DO: would rounding make a difference?) - vpX = (vpnode[0x1A]&0xFFFF)>>4; // viewport X (12.4 fixed point) - vpY = (vpnode[0x1A]>>20)&0xFFF; // viewport Y (12.4) - vpWidth = (vpnode[0x14]&0xFFFF)>>2; // width (14.2) - vpHeight = (vpnode[0x14]>>18)&0x3FFF; // height (14.2) - matrixBase = vpnode[0x16]&0xFFFFFF; // matrix base address - - // Field of view and clipping - vpTopAngle = (float) asin(*(float *)&vpnode[0x0E]); // FOV Y upper half-angle (radians) - vpBotAngle = (float) asin(*(float *)&vpnode[0x12]); // FOV Y lower half-angle - fovYDegrees = (vpTopAngle+vpBotAngle)*(float)(180.0/3.14159265358979323846); - // TO-DO: investigate clipping planes - - // Set up viewport and projection (TO-DO: near and far clipping) - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - if (g_Config.wideScreen && (vpX==0) && (vpWidth>=495) && (vpY==0) && (vpHeight >= 383)) // only expand viewports that occupy whole screen - { - // Wide screen hack only modifies X axis and not the Y FOV - viewportX = 0; - viewportY = yOffs + (GLint) ((float)(384-(vpY+vpHeight))*yRatio); - viewportWidth = totalXRes; - viewportHeight = (GLint) ((float)vpHeight*yRatio); - gluPerspective(fovYDegrees,(GLfloat)viewportWidth/(GLfloat)viewportHeight,0.1f,1e5); // use actual full screen ratio to get proper X FOV - //printf("viewportX=%d, viewportY=%d, viewportWidth=%d, viewportHeight=%d\tvpY=%d vpHeight=%d\n", viewportX, viewportY, viewportWidth, viewportHeight, vpY,vpHeight); - } - else - { - viewportX = xOffs + (GLint) ((float)vpX*xRatio); - viewportY = yOffs + (GLint) ((float)(384-(vpY+vpHeight))*yRatio); - viewportWidth = (GLint) ((float)vpWidth*xRatio); - viewportHeight = (GLint) ((float)vpHeight*yRatio); - gluPerspective(fovYDegrees,(GLfloat)vpWidth/(GLfloat)vpHeight,0.1f,1e5); // use Model 3 viewport ratio - } - - // Lighting (note that sun vector points toward sun -- away from vertex) - lightingParams[0] = *(float *) &vpnode[0x05]; // sun X - lightingParams[1] = *(float *) &vpnode[0x06]; // sun Y - lightingParams[2] = *(float *) &vpnode[0x04]; // sun Z - lightingParams[3] = *(float *) &vpnode[0x07]; // sun intensity - lightingParams[4] = (float) ((vpnode[0x24]>>8)&0xFF) * (1.0f/255.0f); // ambient intensity - lightingParams[5] = 0.0; // reserved - - // Spotlight - spotColorIdx = (vpnode[0x20]>>11)&7; // spotlight color index - spotEllipse[0] = (float) ((vpnode[0x1E]>>3)&0x1FFF); // spotlight X position (fractional component?) - spotEllipse[1] = (float) ((vpnode[0x1D]>>3)&0x1FFF); // spotlight Y - spotEllipse[2] = (float) ((vpnode[0x1E]>>16)&0xFFFF); // spotlight X size (16-bit? May have fractional component below bit 16) - spotEllipse[3] = (float) ((vpnode[0x1D]>>16)&0xFFFF); // spotlight Y size - spotRange[0] = 1.0f/(*(float *) &vpnode[0x21]); // spotlight start - spotRange[1] = *(float *) &vpnode[0x1F]; // spotlight extent - spotColor[0] = color[spotColorIdx][0]; // spotlight color - spotColor[1] = color[spotColorIdx][1]; - spotColor[2] = color[spotColorIdx][2]; - //printf("(%g,%g),(%g,%g),(%g,%g) -> \n", spotEllipse[0], spotEllipse[1], spotEllipse[2], spotEllipse[3], spotRange[0], spotRange[1]); - - // Spotlight is applied on a per pixel basis, must scale its position and size to screen - spotEllipse[1] = 384.0f-spotEllipse[1]; - spotRange[1] += spotRange[0]; // limit - spotEllipse[2] = 496.0f/sqrt(spotEllipse[2]); // spotlight appears to be specified in terms of physical resolution (unconfirmed) - spotEllipse[3] = 384.0f/sqrt(spotEllipse[3]); + // Recursively process next viewport + UINT32 nextAddr = vpnode[0x01]; // next viewport + UINT32 nodeAddr = vpnode[0x02]; // scene database node pointer + if (nextAddr == 0) // memory probably hasn't been set up yet, abort + return; + if (nextAddr != 0x01000000) + RenderViewport(nextAddr, pri); - // Scale the spotlight to the OpenGL viewport - spotEllipse[0] = spotEllipse[0]*xRatio + xOffs; - spotEllipse[1] = spotEllipse[1]*yRatio + yOffs; - spotEllipse[2] *= xRatio; - spotEllipse[3] *= yRatio; + // If the priority doesn't match, do not process + int curPri = (vpnode[0x00] >> 3) & 3; // viewport priority + if (curPri != pri) + return; + + // Fetch viewport parameters (TO-DO: would rounding make a difference?) + int vpX = (vpnode[0x1A]&0xFFFF)>>4; // viewport X (12.4 fixed point) + int vpY = (vpnode[0x1A]>>20)&0xFFF; // viewport Y (12.4) + int vpWidth = (vpnode[0x14]&0xFFFF)>>2; // width (14.2) + int vpHeight = (vpnode[0x14]>>18)&0x3FFF; // height (14.2) + + // Field of view and clipping + GLfloat vpTopAngle = (float) asin(*(float *)&vpnode[0x0E]); // FOV Y upper half-angle (radians) + GLfloat vpBotAngle = (float) asin(*(float *)&vpnode[0x12]); // FOV Y lower half-angle + GLfloat fovYDegrees = (vpTopAngle+vpBotAngle)*(float)(180.0/3.14159265358979323846); + // TO-DO: investigate clipping planes + + // Set up viewport and projection (TO-DO: near and far clipping) + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + if (g_Config.wideScreen && (vpX==0) && (vpWidth>=495) && (vpY==0) && (vpHeight >= 383)) // only expand viewports that occupy whole screen + { + // Wide screen hack only modifies X axis and not the Y FOV + viewportX = 0; + viewportY = yOffs + (GLint) ((float)(384-(vpY+vpHeight))*yRatio); + viewportWidth = totalXRes; + viewportHeight = (GLint) ((float)vpHeight*yRatio); + gluPerspective(fovYDegrees,(GLfloat)viewportWidth/(GLfloat)viewportHeight,0.1f,1e5); // use actual full screen ratio to get proper X FOV + //printf("viewportX=%d, viewportY=%d, viewportWidth=%d, viewportHeight=%d\tvpY=%d vpHeight=%d\n", viewportX, viewportY, viewportWidth, viewportHeight, vpY,vpHeight); + } + else + { + viewportX = xOffs + (GLint) ((float)vpX*xRatio); + viewportY = yOffs + (GLint) ((float)(384-(vpY+vpHeight))*yRatio); + viewportWidth = (GLint) ((float)vpWidth*xRatio); + viewportHeight = (GLint) ((float)vpHeight*yRatio); + gluPerspective(fovYDegrees,(GLfloat)vpWidth/(GLfloat)vpHeight,0.1f,1e5); // use Model 3 viewport ratio + } + + // Lighting (note that sun vector points toward sun -- away from vertex) + lightingParams[0] = *(float *) &vpnode[0x05]; // sun X + lightingParams[1] = *(float *) &vpnode[0x06]; // sun Y + lightingParams[2] = *(float *) &vpnode[0x04]; // sun Z + lightingParams[3] = *(float *) &vpnode[0x07]; // sun intensity + lightingParams[4] = (float) ((vpnode[0x24]>>8)&0xFF) * (1.0f/255.0f); // ambient intensity + lightingParams[5] = 0.0; // reserved + + // Spotlight + int spotColorIdx = (vpnode[0x20]>>11)&7; // spotlight color index + spotEllipse[0] = (float) ((vpnode[0x1E]>>3)&0x1FFF); // spotlight X position (fractional component?) + spotEllipse[1] = (float) ((vpnode[0x1D]>>3)&0x1FFF); // spotlight Y + spotEllipse[2] = (float) ((vpnode[0x1E]>>16)&0xFFFF); // spotlight X size (16-bit? May have fractional component below bit 16) + spotEllipse[3] = (float) ((vpnode[0x1D]>>16)&0xFFFF); // spotlight Y size + spotRange[0] = 1.0f/(*(float *) &vpnode[0x21]); // spotlight start + spotRange[1] = *(float *) &vpnode[0x1F]; // spotlight extent + spotColor[0] = color[spotColorIdx][0]; // spotlight color + spotColor[1] = color[spotColorIdx][1]; + spotColor[2] = color[spotColorIdx][2]; + //printf("(%g,%g),(%g,%g),(%g,%g) -> \n", spotEllipse[0], spotEllipse[1], spotEllipse[2], spotEllipse[3], spotRange[0], spotRange[1]); + + // Spotlight is applied on a per pixel basis, must scale its position and size to screen + spotEllipse[1] = 384.0f-spotEllipse[1]; + spotRange[1] += spotRange[0]; // limit + spotEllipse[2] = 496.0f/sqrt(spotEllipse[2]); // spotlight appears to be specified in terms of physical resolution (unconfirmed) + spotEllipse[3] = 384.0f/sqrt(spotEllipse[3]); - // Fog - fogParams[0] = (float) ((vpnode[0x22]>>16)&0xFF) * (1.0f/255.0f); // fog color R - fogParams[1] = (float) ((vpnode[0x22]>>8)&0xFF) * (1.0f/255.0f); // fog color G - fogParams[2] = (float) ((vpnode[0x22]>>0)&0xFF) * (1.0f/255.0f); // fog color B - fogParams[3] = *(float *) &vpnode[0x23]; // fog density - fogParams[4] = (float) (INT16) (vpnode[0x25]&0xFFFF)*(1.0f/255.0f); // fog start - if (ISINF(fogParams[3]) || ISNAN(fogParams[3]) || ISINF(fogParams[4]) || ISNAN(fogParams[4])) // Star Wars Trilogy - fogParams[3] = fogParams[4] = 0.0f; - - // Unknown light/fog parameters - scrollFog = (float) (vpnode[0x20]&0xFF) * (1.0f/255.0f); // scroll fog - scrollAtt = (float) (vpnode[0x24]&0xFF) * (1.0f/255.0f); // scroll attenuation - //printf("scrollFog = %g, scrollAtt = %g\n", scrollFog, scrollAtt); - //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 - texOffsetXY[0] = 0.0; - texOffsetXY[1] = 0.0; - texOffset = 0x0000; - - // Set up coordinate system and base matrix - glMatrixMode(GL_MODELVIEW); - InitMatrixStack(matrixBase); - - // Safeguard: weird coordinate system matrices usually indicate scenes that will choke the renderer - if (NULL != matrixBasePtr) - { - float m21, m32, m13; - - // Get the three elements that are usually set and see if their magnitudes are 1 - m21 = matrixBasePtr[6]; - m32 = matrixBasePtr[10]; - m13 = matrixBasePtr[5]; - - m21 *= m21; - m32 *= m32; - m13 *= m13; + // Scale the spotlight to the OpenGL viewport + spotEllipse[0] = spotEllipse[0]*xRatio + xOffs; + spotEllipse[1] = spotEllipse[1]*yRatio + yOffs; + spotEllipse[2] *= xRatio; + spotEllipse[3] *= yRatio; - if ((m21>1.05) || (m21<0.95)) - return; - if ((m32>1.05) || (m32<0.95)) - return; - if ((m13>1.05) || (m13<0.95)) - return; - } - - // Render - AppendDisplayList(&VROMCache, true, 0); // add a viewport display list node - AppendDisplayList(&PolyCache, true, 0); - stackDepth = 0; - listDepth = 0; - - // Descend down the node link: Use recursive traversal - DescendNodePtr(nodeAddr); + // Fog + fogParams[0] = (float) ((vpnode[0x22]>>16)&0xFF) * (1.0f/255.0f); // fog color R + fogParams[1] = (float) ((vpnode[0x22]>>8)&0xFF) * (1.0f/255.0f); // fog color G + fogParams[2] = (float) ((vpnode[0x22]>>0)&0xFF) * (1.0f/255.0f); // fog color B + fogParams[3] = *(float *) &vpnode[0x23]; // fog density + fogParams[4] = (float) (INT16) (vpnode[0x25]&0xFFFF)*(1.0f/255.0f); // fog start + if (ISINF(fogParams[3]) || ISNAN(fogParams[3]) || ISINF(fogParams[4]) || ISNAN(fogParams[4])) // Star Wars Trilogy + fogParams[3] = fogParams[4] = 0.0f; + + // Unknown light/fog parameters + //GLfloat scrollFog = (float) (vpnode[0x20]&0xFF) * (1.0f/255.0f); // scroll fog + //GLfloat scrollAtt = (float) (vpnode[0x24]&0xFF) * (1.0f/255.0f); // scroll attenuation + //printf("scrollFog = %g, scrollAtt = %g\n", scrollFog, scrollAtt); + //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 + texOffsetXY[0] = 0.0; + texOffsetXY[1] = 0.0; + texOffset = 0x0000; + + // Set up coordinate system and base matrix + UINT32 matrixBase = vpnode[0x16] & 0xFFFFFF; + glMatrixMode(GL_MODELVIEW); + InitMatrixStack(matrixBase); + + // Safeguard: weird coordinate system matrices usually indicate scenes that will choke the renderer + if (NULL != matrixBasePtr) + { + float m21, m32, m13; + + // Get the three elements that are usually set and see if their magnitudes are 1 + m21 = matrixBasePtr[6]; + m32 = matrixBasePtr[10]; + m13 = matrixBasePtr[5]; + + m21 *= m21; + m32 *= m32; + m13 *= m13; + + if ((m21>1.05) || (m21<0.95)) + return; + if ((m32>1.05) || (m32<0.95)) + return; + if ((m13>1.05) || (m13<0.95)) + return; + } + + // Render + AppendDisplayList(&VROMCache, true, 0); // add a viewport display list node + AppendDisplayList(&PolyCache, true, 0); + stackDepth = 0; + listDepth = 0; + + // Descend down the node link: Use recursive traversal + DescendNodePtr(nodeAddr); } void CLegacy3D::RenderFrame(void) { - // Begin frame - ClearErrors(); // must be cleared each frame - //printf("BEGIN FRAME\n"); - - // Z buffering (Z buffer is cleared by display list viewport nodes) - glDepthFunc(GL_LESS); - glEnable(GL_DEPTH_TEST); - - // Bind Real3D shader program and texture maps - glUseProgram(shaderProgram); - for (unsigned mapNum = 0; mapNum < numTexMaps; mapNum++) - { - // Map Model3 format to texture unit and texture unit to texture sheet number - glActiveTexture(GL_TEXTURE0 + mapNum); // activate correct texture unit - glBindTexture(GL_TEXTURE_2D, texMapIDs[mapNum]); // 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); - glEnableClientState(GL_NORMAL_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - if (subTextureLoc != -1) glEnableVertexAttribArray(subTextureLoc); - if (texParamsLoc != -1) glEnableVertexAttribArray(texParamsLoc); - if (texFormatLoc != -1) glEnableVertexAttribArray(texFormatLoc); - if (texMapLoc != -1) glEnableVertexAttribArray(texMapLoc); - if (transLevelLoc != -1) glEnableVertexAttribArray(transLevelLoc); - if (lightEnableLoc != -1) glEnableVertexAttribArray(lightEnableLoc); - if (shininessLoc != -1) glEnableVertexAttribArray(shininessLoc); - if (fogIntensityLoc != -1) glEnableVertexAttribArray(fogIntensityLoc); - - // Draw - //ClearModelCache(&VROMCache); // debug - ClearModelCache(&PolyCache); - for (int pri = 0; pri <= 3; pri++) - { - glClear(GL_DEPTH_BUFFER_BIT); - //ClearModelCache(&PolyCache); - ClearDisplayList(&PolyCache); - ClearDisplayList(&VROMCache); - RenderViewport(0x800000,pri); - DrawDisplayList(&VROMCache, POLY_STATE_NORMAL); - DrawDisplayList(&PolyCache, POLY_STATE_NORMAL); - DrawDisplayList(&VROMCache, POLY_STATE_ALPHA); - DrawDisplayList(&PolyCache, POLY_STATE_ALPHA); - } - glFrontFace(GL_CW); // restore front face - - // Disable VBO client states - if (fogIntensityLoc != -1) glDisableVertexAttribArray(fogIntensityLoc); - if (shininessLoc != -1) glDisableVertexAttribArray(shininessLoc); - if (lightEnableLoc != -1) glDisableVertexAttribArray(lightEnableLoc); - if (transLevelLoc != -1) glDisableVertexAttribArray(transLevelLoc); - if (texMapLoc != -1) glDisableVertexAttribArray(texMapLoc); - if (texFormatLoc != -1) glDisableVertexAttribArray(texFormatLoc); - if (texParamsLoc != -1) glDisableVertexAttribArray(texParamsLoc); - if (subTextureLoc != -1) glDisableVertexAttribArray(subTextureLoc); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); + // Begin frame + ClearErrors(); // must be cleared each frame + //printf("BEGIN FRAME\n"); + + // Z buffering (Z buffer is cleared by display list viewport nodes) + glDepthFunc(GL_LESS); + glEnable(GL_DEPTH_TEST); + + // Bind Real3D shader program and texture maps + glUseProgram(shaderProgram); + for (unsigned mapNum = 0; mapNum < numTexMaps; mapNum++) + { + // Map Model3 format to texture unit and texture unit to texture sheet number + glActiveTexture(GL_TEXTURE0 + mapNum); // activate correct texture unit + glBindTexture(GL_TEXTURE_2D, texMapIDs[mapNum]); // 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); + glEnableClientState(GL_NORMAL_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + if (subTextureLoc != -1) glEnableVertexAttribArray(subTextureLoc); + if (texParamsLoc != -1) glEnableVertexAttribArray(texParamsLoc); + if (texFormatLoc != -1) glEnableVertexAttribArray(texFormatLoc); + if (texMapLoc != -1) glEnableVertexAttribArray(texMapLoc); + if (transLevelLoc != -1) glEnableVertexAttribArray(transLevelLoc); + if (lightEnableLoc != -1) glEnableVertexAttribArray(lightEnableLoc); + if (shininessLoc != -1) glEnableVertexAttribArray(shininessLoc); + if (fogIntensityLoc != -1) glEnableVertexAttribArray(fogIntensityLoc); + + // Draw + //ClearModelCache(&VROMCache); // debug + ClearModelCache(&PolyCache); + for (int pri = 0; pri <= 3; pri++) + { + glClear(GL_DEPTH_BUFFER_BIT); + //ClearModelCache(&PolyCache); + ClearDisplayList(&PolyCache); + ClearDisplayList(&VROMCache); + RenderViewport(0x800000,pri); + DrawDisplayList(&VROMCache, POLY_STATE_NORMAL); + DrawDisplayList(&PolyCache, POLY_STATE_NORMAL); + DrawDisplayList(&VROMCache, POLY_STATE_ALPHA); + DrawDisplayList(&PolyCache, POLY_STATE_ALPHA); + } + glFrontFace(GL_CW); // restore front face + + // Disable VBO client states + if (fogIntensityLoc != -1) glDisableVertexAttribArray(fogIntensityLoc); + if (shininessLoc != -1) glDisableVertexAttribArray(shininessLoc); + if (lightEnableLoc != -1) glDisableVertexAttribArray(lightEnableLoc); + if (transLevelLoc != -1) glDisableVertexAttribArray(transLevelLoc); + if (texMapLoc != -1) glDisableVertexAttribArray(texMapLoc); + if (texFormatLoc != -1) glDisableVertexAttribArray(texFormatLoc); + if (texParamsLoc != -1) glDisableVertexAttribArray(texParamsLoc); + if (subTextureLoc != -1) glDisableVertexAttribArray(subTextureLoc); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); } void CLegacy3D::EndFrame(void) @@ -1108,300 +1055,300 @@ void CLegacy3D::BeginFrame(void) void CLegacy3D::AttachMemory(const UINT32 *cullingRAMLoPtr, const UINT32 *cullingRAMHiPtr, const UINT32 *polyRAMPtr, const UINT32 *vromPtr, const UINT16 *textureRAMPtr) { - cullingRAMLo = cullingRAMLoPtr; - cullingRAMHi = cullingRAMHiPtr; - polyRAM = polyRAMPtr; - vrom = vromPtr; - textureRAM = textureRAMPtr; - DebugLog("Legacy3D attached Real3D memory regions\n"); + cullingRAMLo = cullingRAMLoPtr; + cullingRAMHi = cullingRAMHiPtr; + polyRAM = polyRAMPtr; + vrom = vromPtr; + textureRAM = textureRAMPtr; + DebugLog("Legacy3D attached Real3D memory regions\n"); } void CLegacy3D::SetStep(int stepID) { - step = stepID; - - if ((step!=0x10) && (step!=0x15) && (step!=0x20) && (step!=0x21)) - { - DebugLog("Legacy3D: Unrecognized stepping: %d.%d\n", (step>>4)&0xF, step&0xF); - step = 0x10; - } - - if (step > 0x10) - { - offset = 0; // culling nodes are 10 words - vertexFactor = (1.0f/2048.0f); // vertices are in 13.11 format - } - else - { - offset = 2; // 8 words - vertexFactor = (1.0f/128.0f); // 17.7 - } - - DebugLog("Legacy3D set to Step %d.%d\n", (step>>4)&0xF, step&0xF); + step = stepID; + + if ((step!=0x10) && (step!=0x15) && (step!=0x20) && (step!=0x21)) + { + DebugLog("Legacy3D: Unrecognized stepping: %d.%d\n", (step>>4)&0xF, step&0xF); + step = 0x10; + } + + if (step > 0x10) + { + offset = 0; // culling nodes are 10 words + vertexFactor = (1.0f/2048.0f); // vertices are in 13.11 format + } + else + { + offset = 2; // 8 words + vertexFactor = (1.0f/128.0f); // 17.7 + } + + DebugLog("Legacy3D set to Step %d.%d\n", (step>>4)&0xF, step&0xF); } - + bool CLegacy3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXResParam, unsigned totalYResParam) { - // Allocate memory for texture buffer - textureBuffer = new(std::nothrow) GLfloat[512*512*4]; - if (NULL == textureBuffer) - return ErrorLog("Insufficient memory for texture decode buffer."); - - 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)) - return FAIL; - if (CreateModelCache(&PolyCache, NUM_DYNAMIC_VERTS, NUM_LOCAL_VERTS, NUM_DYNAMIC_MODELS, 0x4000000/4, NUM_DISPLAY_LIST_ITEMS, true)) - return FAIL; + // Allocate memory for texture buffer + textureBuffer = new(std::nothrow) GLfloat[512*512*4]; + if (NULL == textureBuffer) + return ErrorLog("Insufficient memory for texture decode buffer."); + + 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)) + return FAIL; + if (CreateModelCache(&PolyCache, NUM_DYNAMIC_VERTS, NUM_LOCAL_VERTS, NUM_DYNAMIC_MODELS, 0x4000000/4, NUM_DISPLAY_LIST_ITEMS, true)) + return FAIL; - // Initialize lighting parameters (updated as viewports are traversed) - lightingParams[0] = 0.0; - lightingParams[1] = 0.0; - lightingParams[2] = 0.0; - lightingParams[3] = 0.0; - lightingParams[4] = 1.0; // full ambient intensity in case we want to render a standalone model - lightingParams[5] = 0.0; + // Initialize lighting parameters (updated as viewports are traversed) + lightingParams[0] = 0.0; + lightingParams[1] = 0.0; + lightingParams[2] = 0.0; + lightingParams[3] = 0.0; + lightingParams[4] = 1.0; // full ambient intensity in case we want to render a standalone model + lightingParams[5] = 0.0; - // Resolution and offset within physical display area - xRatio = (GLfloat) xRes / 496.0f; - yRatio = (GLfloat) yRes / 384.0f; - xOffs = xOffset; - yOffs = yOffset; - totalXRes = totalXResParam; - totalYRes = totalYResParam; + // Resolution and offset within physical display area + xRatio = (GLfloat) xRes / 496.0f; + yRatio = (GLfloat) yRes / 384.0f; + xOffs = xOffset; + yOffs = yOffset; + totalXRes = totalXResParam; + totalYRes = totalYResParam; - // Get ideal number of texture sheets required by default mapping from Model3 texture format to texture sheet - unsigned idealTexSheets = 0; - for (unsigned fmt = 0; fmt < 8; fmt++) - { - int sheetNum = defaultFmtToTexSheetNum[fmt]; - idealTexSheets = max(idealTexSheets, sheetNum + 1); - } + // Get ideal number of texture sheets required by default mapping from Model3 texture format to texture sheet + int idealTexSheets = 0; + for (size_t fmt = 0; fmt < 8; fmt++) + { + int sheetNum = defaultFmtToTexSheetNum[fmt]; + idealTexSheets = std::max(idealTexSheets, sheetNum + 1); + } - // Get upper limit for number of texture maps to use from max number of texture units supported by video card - GLint glMaxTexUnits; - glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &glMaxTexUnits); - unsigned maxTexMaps = max(1, min(g_Config.maxTexMaps, glMaxTexUnits)); - - // Get upper limit for extent of texture maps to use from max texture size supported by video card - GLint maxTexSize; - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); - unsigned mapExtent = max(1, min(g_Config.maxTexMapExtent, maxTexSize / 2048)); - unsigned mapSize = 2048 * mapExtent; - while (mapExtent > 1) - { - if ((mapExtent - 1) * (mapExtent - 1) < idealTexSheets) - { - // Use a GL proxy texture to double check max texture size returned above - glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA8, mapSize, mapSize, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, NULL); - GLint glTexWidth; - glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &glTexWidth); - if (glTexWidth == mapSize) - break; - } - mapExtent--; - mapSize -= 2048; - } + // Get upper limit for number of texture maps to use from max number of texture units supported by video card + GLint glMaxTexUnits; + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &glMaxTexUnits); + int maxTexMaps = std::max(1, std::min(g_Config.maxTexMaps, glMaxTexUnits)); + + // Get upper limit for extent of texture maps to use from max texture size supported by video card + GLint maxTexSize; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); + int mapExtent = std::max(1, std::min(g_Config.maxTexMapExtent, maxTexSize / 2048)); + int mapSize = 2048 * mapExtent; + while (mapExtent > 1) + { + if ((mapExtent - 1) * (mapExtent - 1) < idealTexSheets) + { + // Use a GL proxy texture to double check max texture size returned above + glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA8, mapSize, mapSize, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, NULL); + GLint glTexWidth; + glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &glTexWidth); + if (glTexWidth == mapSize) + break; + } + mapExtent--; + mapSize -= 2048; + } - // Load shaders, using multi-sheet shader if requested. - const char *vsFile = g_Config.vertexShaderFile.size() ? g_Config.vertexShaderFile.c_str() : NULL; - const char *fsFile = g_Config.fragmentShaderFile.size() ? g_Config.fragmentShaderFile.c_str() : NULL; - const char *fragmentShaderSource = (g_Config.multiTexture ? fragmentShaderMultiSheetSource : fragmentShaderSingleSheetSource); // single texture shader - if (OKAY != LoadShaderProgram(&shaderProgram,&vertexShader,&fragmentShader,vsFile,fsFile,vertexShaderSource,fragmentShaderSource)) - return FAIL; - - // Try locating default "textureMap" uniform in shader program - glUseProgram(shaderProgram); // bind program - textureMapLoc = glGetUniformLocation(shaderProgram, "textureMap"); - - // If exists, bind to first texture unit - unsigned mapCount = 0; - if (textureMapLoc != -1) - glUniform1i(textureMapLoc, mapCount++); - - // Try locating "textureMap[0-7]" uniforms in shader program - for (unsigned mapNum = 0; mapNum < 8 && mapCount < maxTexMaps; mapNum++) - { - char uniformName[12]; - sprintf(uniformName, "textureMap%u", mapNum); - textureMapLocs[mapNum] = glGetUniformLocation(shaderProgram, uniformName); - // If exist, bind to remaining texture units - if (textureMapLocs[mapNum] != -1) - glUniform1i(textureMapLocs[mapNum], mapCount++); - } - - // Check sucessully located at least one "textureMap" uniform in shader program - if (mapCount == 0) - return ErrorLog("Fragment shader must contain at least one 'textureMap' uniform."); - InfoLog("Located and bound %u 'textureMap' uniform(s) in fragment shader.", mapCount); + // Load shaders, using multi-sheet shader if requested. + const char *vsFile = g_Config.vertexShaderFile.size() ? g_Config.vertexShaderFile.c_str() : NULL; + const char *fsFile = g_Config.fragmentShaderFile.size() ? g_Config.fragmentShaderFile.c_str() : NULL; + const char *fragmentShaderSource = (g_Config.multiTexture ? fragmentShaderMultiSheetSource : fragmentShaderSingleSheetSource); // single texture shader + if (OKAY != LoadShaderProgram(&shaderProgram,&vertexShader,&fragmentShader,vsFile,fsFile,vertexShaderSource,fragmentShaderSource)) + return FAIL; + + // Try locating default "textureMap" uniform in shader program + glUseProgram(shaderProgram); // bind program + textureMapLoc = glGetUniformLocation(shaderProgram, "textureMap"); + + // If exists, bind to first texture unit + int mapCount = 0; + if (textureMapLoc != -1) + glUniform1i(textureMapLoc, mapCount++); + + // Try locating "textureMap[0-7]" uniforms in shader program + for (int mapNum = 0; mapNum < 8 && mapCount < maxTexMaps; mapNum++) + { + char uniformName[12]; + sprintf(uniformName, "textureMap%u", mapNum); + textureMapLocs[mapNum] = glGetUniformLocation(shaderProgram, uniformName); + // If exist, bind to remaining texture units + if (textureMapLocs[mapNum] != -1) + glUniform1i(textureMapLocs[mapNum], mapCount++); + } + + // Check sucessully located at least one "textureMap" uniform in shader program + if (mapCount == 0) + return ErrorLog("Fragment shader must contain at least one 'textureMap' uniform."); + InfoLog("Located and bound %u 'textureMap' uniform(s) in fragment shader.", mapCount); - // Readjust map extent so as to utilise as many texture maps found in shader program as possible - while (mapExtent > 1 && mapCount * (mapExtent - 1) * (mapExtent - 1) >= idealTexSheets) - { - mapExtent--; - mapSize -= 2048; - } - - // Create required number of GL textures for texture maps, decreasing map extent if memory is insufficent - unsigned sheetsPerMap = mapExtent * mapExtent; - while (true) - { - numTexMaps = min(mapCount, 1 + (idealTexSheets - 1) / sheetsPerMap); + // Readjust map extent so as to utilise as many texture maps found in shader program as possible + while (mapExtent > 1 && mapCount * (mapExtent - 1) * (mapExtent - 1) >= idealTexSheets) + { + mapExtent--; + mapSize -= 2048; + } + + // Create required number of GL textures for texture maps, decreasing map extent if memory is insufficent + unsigned sheetsPerMap = mapExtent * mapExtent; + while (true) + { + numTexMaps = std::min(mapCount, 1 + (idealTexSheets - 1) / sheetsPerMap); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glGenTextures(numTexMaps, texMapIDs); - bool okay = true; - for (unsigned mapNum = 0; mapNum < numTexMaps; mapNum++) - { - glActiveTexture(GL_TEXTURE0 + mapNum); // activate correct texture unit - glBindTexture(GL_TEXTURE_2D, texMapIDs[mapNum]); // 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, mapSize, mapSize, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 0); - if (glGetError() != GL_NO_ERROR) - { - // Ran out of video memory or texture size is too large - numTexMaps = mapNum; - okay = false; - break; - } - } - if (okay || mapExtent == 1) - break; + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glGenTextures(numTexMaps, texMapIDs); + bool okay = true; + for (unsigned mapNum = 0; mapNum < numTexMaps; mapNum++) + { + glActiveTexture(GL_TEXTURE0 + mapNum); // activate correct texture unit + glBindTexture(GL_TEXTURE_2D, texMapIDs[mapNum]); // 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, mapSize, mapSize, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 0); + if (glGetError() != GL_NO_ERROR) + { + // Ran out of video memory or texture size is too large + numTexMaps = mapNum; + okay = false; + break; + } + } + if (okay || mapExtent == 1) + break; - // Delete textures, decrease extent and try again - glDeleteTextures(numTexMaps, texMapIDs); - mapExtent--; - mapSize -= 2048; - sheetsPerMap = mapExtent * mapExtent; - } - - // Check successfully created at least one texture map - if (numTexMaps == 0) - return ErrorLog("OpenGL was unable to provide any 2048x2048-texel texture maps."); - InfoLog("Created %u %ux%u-texel GL texture map(s).", numTexMaps, mapSize, mapSize); + // Delete textures, decrease extent and try again + glDeleteTextures(numTexMaps, texMapIDs); + mapExtent--; + mapSize -= 2048; + sheetsPerMap = mapExtent * mapExtent; + } + + // Check successfully created at least one texture map + if (numTexMaps == 0) + return ErrorLog("OpenGL was unable to provide any 2048x2048-texel texture maps."); + InfoLog("Created %u %ux%u-texel GL texture map(s).", numTexMaps, mapSize, mapSize); - // Create texture sheet objects and assign them to texture maps - numTexSheets = min(numTexMaps * sheetsPerMap, idealTexSheets); - texSheets = new(std::nothrow) TexSheet[numTexSheets]; - if (texSheets == NULL) - return ErrorLog("Unable to assign memory for %u texture sheet objects.", numTexSheets); - for (unsigned sheetNum = 0; sheetNum < numTexSheets; sheetNum++) - { - unsigned mapNum = sheetNum / sheetsPerMap; - unsigned posInMap = sheetNum % sheetsPerMap; - texSheets[sheetNum].sheetNum = sheetNum; - texSheets[sheetNum].mapNum = mapNum; - texSheets[sheetNum].xOffset = 2048 * (posInMap % mapExtent); - texSheets[sheetNum].yOffset = 2048 * (posInMap / mapExtent); - } + // Create texture sheet objects and assign them to texture maps + numTexSheets = std::min(numTexMaps * sheetsPerMap, idealTexSheets); + texSheets = new(std::nothrow) TexSheet[numTexSheets]; + if (texSheets == NULL) + return ErrorLog("Unable to assign memory for %u texture sheet objects.", numTexSheets); + for (unsigned sheetNum = 0; sheetNum < numTexSheets; sheetNum++) + { + unsigned mapNum = sheetNum / sheetsPerMap; + unsigned posInMap = sheetNum % sheetsPerMap; + texSheets[sheetNum].sheetNum = sheetNum; + texSheets[sheetNum].mapNum = mapNum; + texSheets[sheetNum].xOffset = 2048 * (posInMap % mapExtent); + texSheets[sheetNum].yOffset = 2048 * (posInMap / mapExtent); + } - // Assign Model3 texture formats to texture sheets (cannot just use default mapping as may have ended up with fewer - // texture sheets than anticipated) - for (unsigned fmt = 0; fmt < 8; fmt++) - { - int sheetNum = defaultFmtToTexSheetNum[fmt] % numTexSheets; - fmtToTexSheet[fmt] = &texSheets[sheetNum]; - } + // Assign Model3 texture formats to texture sheets (cannot just use default mapping as may have ended up with fewer + // texture sheets than anticipated) + for (unsigned fmt = 0; fmt < 8; fmt++) + { + int sheetNum = defaultFmtToTexSheetNum[fmt] % numTexSheets; + fmtToTexSheet[fmt] = &texSheets[sheetNum]; + } - InfoLog("Mapped %u Model3 texture formats to %u texture sheet(s) in %u %ux%u-texel texture map(s).", 8, numTexSheets, numTexMaps, mapSize, mapSize); + InfoLog("Mapped %u Model3 texture formats to %u texture sheet(s) in %u %ux%u-texel texture map(s).", 8, numTexSheets, numTexMaps, mapSize, mapSize); - // Get location of the rest of the uniforms - modelViewMatrixLoc = glGetUniformLocation(shaderProgram,"modelViewMatrix"); - projectionMatrixLoc = glGetUniformLocation(shaderProgram,"projectionMatrix"); - lightingLoc = glGetUniformLocation(shaderProgram, "lighting"); - mapSizeLoc = glGetUniformLocation(shaderProgram, "mapSize"); - spotEllipseLoc = glGetUniformLocation(shaderProgram, "spotEllipse"); - spotRangeLoc = glGetUniformLocation(shaderProgram, "spotRange"); - spotColorLoc = glGetUniformLocation(shaderProgram, "spotColor"); - - // Get locations of custom vertex attributes - subTextureLoc = glGetAttribLocation(shaderProgram,"subTexture"); - texParamsLoc = glGetAttribLocation(shaderProgram,"texParams"); - texFormatLoc = glGetAttribLocation(shaderProgram,"texFormat"); - texMapLoc = glGetAttribLocation(shaderProgram,"texMap"); - transLevelLoc = glGetAttribLocation(shaderProgram,"transLevel"); - lightEnableLoc = glGetAttribLocation(shaderProgram,"lightEnable"); - shininessLoc = glGetAttribLocation(shaderProgram,"shininess"); - fogIntensityLoc = glGetAttribLocation(shaderProgram,"fogIntensity"); - - // Set map size - if (mapSizeLoc != -1) - glUniform1f(mapSizeLoc, (GLfloat)mapSize); + // Get location of the rest of the uniforms + modelViewMatrixLoc = glGetUniformLocation(shaderProgram,"modelViewMatrix"); + projectionMatrixLoc = glGetUniformLocation(shaderProgram,"projectionMatrix"); + lightingLoc = glGetUniformLocation(shaderProgram, "lighting"); + mapSizeLoc = glGetUniformLocation(shaderProgram, "mapSize"); + spotEllipseLoc = glGetUniformLocation(shaderProgram, "spotEllipse"); + spotRangeLoc = glGetUniformLocation(shaderProgram, "spotRange"); + spotColorLoc = glGetUniformLocation(shaderProgram, "spotColor"); + + // Get locations of custom vertex attributes + subTextureLoc = glGetAttribLocation(shaderProgram,"subTexture"); + texParamsLoc = glGetAttribLocation(shaderProgram,"texParams"); + texFormatLoc = glGetAttribLocation(shaderProgram,"texFormat"); + texMapLoc = glGetAttribLocation(shaderProgram,"texMap"); + transLevelLoc = glGetAttribLocation(shaderProgram,"transLevel"); + lightEnableLoc = glGetAttribLocation(shaderProgram,"lightEnable"); + shininessLoc = glGetAttribLocation(shaderProgram,"shininess"); + fogIntensityLoc = glGetAttribLocation(shaderProgram,"fogIntensity"); + + // Set map size + if (mapSizeLoc != -1) + glUniform1f(mapSizeLoc, (GLfloat)mapSize); - // Additional OpenGL stuff - glFrontFace(GL_CW); // polygons are uploaded w/ clockwise winding - glCullFace(GL_BACK); - glEnable(GL_CULL_FACE); - glClearDepth(1.0); - glEnable(GL_TEXTURE_2D); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + // Additional OpenGL stuff + glFrontFace(GL_CW); // polygons are uploaded w/ clockwise winding + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + glClearDepth(1.0); + glEnable(GL_TEXTURE_2D); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); - // Mark all textures as dirty - UploadTextures(0,0,2048,2048); + // Mark all textures as dirty + UploadTextures(0,0,2048,2048); - DebugLog("Legacy3D initialized\n"); - return OKAY; + DebugLog("Legacy3D initialized\n"); + return OKAY; } CLegacy3D::CLegacy3D(void) -{ - cullingRAMLo = NULL; - cullingRAMHi = NULL; - polyRAM = NULL; - vrom = NULL; - textureRAM = NULL; - textureBuffer = NULL; - texSheets = NULL; - - // Clear model cache pointers so we can safely destroy them if init fails - for (int i = 0; i < 2; i++) - { - VROMCache.verts[i] = NULL; - PolyCache.verts[i] = NULL; - VROMCache.Models = NULL; - PolyCache.Models = NULL; - VROMCache.lut = NULL; - PolyCache.lut = NULL; - VROMCache.List = NULL; - PolyCache.List = NULL; - VROMCache.ListHead[i] = NULL; - PolyCache.ListHead[i] = NULL; - VROMCache.ListTail[i] = NULL; - PolyCache.ListTail[i] = NULL; - } - - DebugLog("Built Legacy3D\n"); +{ + cullingRAMLo = NULL; + cullingRAMHi = NULL; + polyRAM = NULL; + vrom = NULL; + textureRAM = NULL; + textureBuffer = NULL; + texSheets = NULL; + + // Clear model cache pointers so we can safely destroy them if init fails + for (int i = 0; i < 2; i++) + { + VROMCache.verts[i] = NULL; + PolyCache.verts[i] = NULL; + VROMCache.Models = NULL; + PolyCache.Models = NULL; + VROMCache.lut = NULL; + PolyCache.lut = NULL; + VROMCache.List = NULL; + PolyCache.List = NULL; + VROMCache.ListHead[i] = NULL; + PolyCache.ListHead[i] = NULL; + VROMCache.ListTail[i] = NULL; + PolyCache.ListTail[i] = NULL; + } + + DebugLog("Built Legacy3D\n"); } CLegacy3D::~CLegacy3D(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(numTexMaps, texMapIDs); - - DestroyModelCache(&VROMCache); - DestroyModelCache(&PolyCache); - - cullingRAMLo = NULL; - cullingRAMHi = NULL; - polyRAM = NULL; - vrom = NULL; - textureRAM = NULL; - - if (texSheets != NULL) - delete [] texSheets; + 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(numTexMaps, texMapIDs); + + DestroyModelCache(&VROMCache); + DestroyModelCache(&PolyCache); + + cullingRAMLo = NULL; + cullingRAMHi = NULL; + polyRAM = NULL; + vrom = NULL; + textureRAM = NULL; + + if (texSheets != NULL) + delete [] texSheets; - if (textureBuffer != NULL) - delete [] textureBuffer; - textureBuffer = NULL; + if (textureBuffer != NULL) + delete [] textureBuffer; + textureBuffer = NULL; - DebugLog("Destroyed Legacy3D\n"); + DebugLog("Destroyed Legacy3D\n"); } } // Legacy3D diff --git a/Src/Graphics/Legacy3D/Legacy3D.h b/Src/Graphics/Legacy3D/Legacy3D.h index 2e3a904..c67cfac 100644 --- a/Src/Graphics/Legacy3D/Legacy3D.h +++ b/Src/Graphics/Legacy3D/Legacy3D.h @@ -1,4 +1,4 @@ -/** + /** ** Supermodel ** A Sega Model 3 Arcade Emulator. ** Copyright 2011 Bart Trzynadlowski, Nik Henson diff --git a/Src/Graphics/Legacy3D/Models.cpp b/Src/Graphics/Legacy3D/Models.cpp index 1de4964..3301853 100644 --- a/Src/Graphics/Legacy3D/Models.cpp +++ b/Src/Graphics/Legacy3D/Models.cpp @@ -28,8 +28,6 @@ * ----------- * - If vertex normals aren't offset from polygon normals, would that improve * specular lighting? - * - Check to see if vertices in LA Machineguns and Dirt Devils contain color - * values rather than normals. * - More should be predecoded into the polygon structures, so that things like * texture base coordinates are not re-decoded in two different places! */ @@ -50,32 +48,32 @@ namespace Legacy3D { * All vertex information is stored in an array of GLfloats. Offset and size * information is defined here for now. */ -#define VBO_VERTEX_OFFSET_X 0 // vertex X -#define VBO_VERTEX_OFFSET_Y 1 // vertex Y -#define VBO_VERTEX_OFFSET_Z 2 // vertex Z -#define VBO_VERTEX_OFFSET_NX 3 // normal X -#define VBO_VERTEX_OFFSET_NY 4 // normal Y -#define VBO_VERTEX_OFFSET_NZ 5 // normal Z -#define VBO_VERTEX_OFFSET_R 6 // color (untextured polys) and material (textured polys) R -#define VBO_VERTEX_OFFSET_G 7 // color and material G -#define VBO_VERTEX_OFFSET_B 8 // color and material B -#define VBO_VERTEX_OFFSET_TRANSLUCENCE 9 // translucence level (0.0 fully transparent, 1.0 opaque) -#define VBO_VERTEX_OFFSET_LIGHTENABLE 10 // lighting enabled (0.0 luminous, 1.0 light enabled) -#define VBO_VERTEX_OFFSET_SHININESS 11 // shininess (if negative, disables specular lighting) -#define VBO_VERTEX_OFFSET_FOGINTENSITY 12 // fog intensity (0.0 no fog applied, 1.0 all fog applied) -#define VBO_VERTEX_OFFSET_U 13 // texture U coordinate (in texels, relative to sub-texture) -#define VBO_VERTEX_OFFSET_V 14 // texture V coordinate -#define VBO_VERTEX_OFFSET_TEXTURE_X 15 // sub-texture parameters, X (position in overall texture map, in texels) -#define VBO_VERTEX_OFFSET_TEXTURE_Y 16 // "" Y "" -#define VBO_VERTEX_OFFSET_TEXTURE_W 17 // sub-texture parameters, width of texture in texels -#define VBO_VERTEX_OFFSET_TEXTURE_H 18 // "" height of texture in texels -#define VBO_VERTEX_OFFSET_TEXPARAMS_EN 19 // texture parameter: ==1 texturing enabled, ==0 disabled (per-polygon) -#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 23 // texture format 0-7 (also ==0 indicates contour texture - see also texParams.trans) -#define VBO_VERTEX_OFFSET_TEXMAP 24 // texture map number -#define VBO_VERTEX_SIZE 25 // total size (may include padding for alignment) +#define VBO_VERTEX_OFFSET_X 0 // vertex X +#define VBO_VERTEX_OFFSET_Y 1 // vertex Y +#define VBO_VERTEX_OFFSET_Z 2 // vertex Z +#define VBO_VERTEX_OFFSET_NX 3 // normal X +#define VBO_VERTEX_OFFSET_NY 4 // normal Y +#define VBO_VERTEX_OFFSET_NZ 5 // normal Z +#define VBO_VERTEX_OFFSET_R 6 // color (untextured polys) and material (textured polys) R +#define VBO_VERTEX_OFFSET_G 7 // color and material G +#define VBO_VERTEX_OFFSET_B 8 // color and material B +#define VBO_VERTEX_OFFSET_TRANSLUCENCE 9 // translucence level (0.0 fully transparent, 1.0 opaque) +#define VBO_VERTEX_OFFSET_LIGHTENABLE 10 // lighting enabled (0.0 luminous, 1.0 light enabled) +#define VBO_VERTEX_OFFSET_SHININESS 11 // shininess (if negative, disables specular lighting) +#define VBO_VERTEX_OFFSET_FOGINTENSITY 12 // fog intensity (0.0 no fog applied, 1.0 all fog applied) +#define VBO_VERTEX_OFFSET_U 13 // texture U coordinate (in texels, relative to sub-texture) +#define VBO_VERTEX_OFFSET_V 14 // texture V coordinate +#define VBO_VERTEX_OFFSET_TEXTURE_X 15 // sub-texture parameters, X (position in overall texture map, in texels) +#define VBO_VERTEX_OFFSET_TEXTURE_Y 16 // "" Y "" +#define VBO_VERTEX_OFFSET_TEXTURE_W 17 // sub-texture parameters, width of texture in texels +#define VBO_VERTEX_OFFSET_TEXTURE_H 18 // "" height of texture in texels +#define VBO_VERTEX_OFFSET_TEXPARAMS_EN 19 // texture parameter: ==1 texturing enabled, ==0 disabled (per-polygon) +#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 23 // texture format 0-7 (also ==0 indicates contour texture - see also texParams.trans) +#define VBO_VERTEX_OFFSET_TEXMAP 24 // texture map number +#define VBO_VERTEX_SIZE 25 // total size (may include padding for alignment) /****************************************************************************** @@ -83,56 +81,58 @@ namespace Legacy3D { ******************************************************************************/ // Macro to generate column-major (OpenGL) index from y,x subscripts -#define CMINDEX(y,x) (x*4+y) +#define CMINDEX(y,x) (x*4+y) -static void CrossProd(GLfloat out[3], GLfloat a[3], GLfloat b[3]) +static void CrossProd(GLfloat out[3], const GLfloat a[3], const GLfloat b[3]) { - out[0] = a[1]*b[2]-a[2]*b[1]; - out[1] = a[2]*b[0]-a[0]*b[2]; - out[2] = a[0]*b[1]-a[1]*b[0]; + out[0] = a[1]*b[2]-a[2]*b[1]; + out[1] = a[2]*b[0]-a[0]*b[2]; + out[2] = a[0]*b[1]-a[1]*b[0]; } // 3x3 matrix used (upper-left of m[]) -static void MultMat3Vec3(GLfloat out[3], GLfloat m[4*4], GLfloat v[3]) +static void MultMat3Vec3(GLfloat out[3], const GLfloat m[4*4], const GLfloat v[3]) { - out[0] = m[CMINDEX(0,0)]*v[0]+m[CMINDEX(0,1)]*v[1]+m[CMINDEX(0,2)]*v[2]; - out[1] = m[CMINDEX(1,0)]*v[0]+m[CMINDEX(1,1)]*v[1]+m[CMINDEX(1,2)]*v[2]; - out[2] = m[CMINDEX(2,0)]*v[0]+m[CMINDEX(2,1)]*v[1]+m[CMINDEX(2,2)]*v[2]; + out[0] = m[CMINDEX(0,0)]*v[0]+m[CMINDEX(0,1)]*v[1]+m[CMINDEX(0,2)]*v[2]; + out[1] = m[CMINDEX(1,0)]*v[0]+m[CMINDEX(1,1)]*v[1]+m[CMINDEX(1,2)]*v[2]; + out[2] = m[CMINDEX(2,0)]*v[0]+m[CMINDEX(2,1)]*v[1]+m[CMINDEX(2,2)]*v[2]; } static GLfloat Sign(GLfloat x) { - if (x > 0.0f) - return 1.0f; - else if (x < 0.0f) - return -1.0f; - return 0.0f; + if (x > 0.0f) + return 1.0f; + else if (x < 0.0f) + return -1.0f; + return 0.0f; } // Inverts and transposes a 3x3 matrix (upper-left of the 4x4), returning a // 4x4 matrix with the extra components undefined (do not use them!) static void InvertTransposeMat3(GLfloat out[4*4], GLfloat m[4*4]) { - GLfloat invDet; - GLfloat a00 = m[CMINDEX(0,0)], a01 = m[CMINDEX(0,1)], a02 = m[CMINDEX(0,2)]; - GLfloat a10 = m[CMINDEX(1,0)], a11 = m[CMINDEX(1,1)], a12 = m[CMINDEX(1,2)]; - GLfloat a20 = m[CMINDEX(2,0)], a21 = m[CMINDEX(2,1)], a22 = m[CMINDEX(2,2)]; - - invDet = 1.0f/(a00*(a22*a11-a21*a12)-a10*(a22*a01-a21*a02)+a20*(a12*a01-a11*a02)); - out[CMINDEX(0,0)] = invDet*(a22*a11-a21*a12); out[CMINDEX(1,0)] = invDet*(-(a22*a01-a21*a02)); out[CMINDEX(2,0)] = invDet*(a12*a01-a11*a02); - out[CMINDEX(0,1)] = invDet*(-(a22*a10-a20*a12)); out[CMINDEX(1,1)] = invDet*(a22*a00-a20*a02); out[CMINDEX(2,1)] = invDet*(-(a12*a00-a10*a02)); - out[CMINDEX(0,2)] = invDet*(a21*a10-a20*a11); out[CMINDEX(1,2)] = invDet*(-(a21*a00-a20*a01)); out[CMINDEX(2,2)] = invDet*(a11*a00-a10*a01); + GLfloat invDet; + GLfloat a00 = m[CMINDEX(0,0)], a01 = m[CMINDEX(0,1)], a02 = m[CMINDEX(0,2)]; + GLfloat a10 = m[CMINDEX(1,0)], a11 = m[CMINDEX(1,1)], a12 = m[CMINDEX(1,2)]; + GLfloat a20 = m[CMINDEX(2,0)], a21 = m[CMINDEX(2,1)], a22 = m[CMINDEX(2,2)]; + + invDet = 1.0f/(a00*(a22*a11-a21*a12)-a10*(a22*a01-a21*a02)+a20*(a12*a01-a11*a02)); + out[CMINDEX(0,0)] = invDet*(a22*a11-a21*a12); out[CMINDEX(1,0)] = invDet*(-(a22*a01-a21*a02)); out[CMINDEX(2,0)] = invDet*(a12*a01-a11*a02); + out[CMINDEX(0,1)] = invDet*(-(a22*a10-a20*a12)); out[CMINDEX(1,1)] = invDet*(a22*a00-a20*a02); out[CMINDEX(2,1)] = invDet*(-(a12*a00-a10*a02)); + out[CMINDEX(0,2)] = invDet*(a21*a10-a20*a11); out[CMINDEX(1,2)] = invDet*(-(a21*a00-a20*a01)); out[CMINDEX(2,2)] = invDet*(a11*a00-a10*a01); } +/* static void PrintMatrix(GLfloat m[4*4]) { - for (int i = 0; i < 3; i++) - { - for (int j = 0; j < 3; j++) - printf("%g\t", m[CMINDEX(i,j)]); - printf("\n"); - } + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + printf("%g\t", m[CMINDEX(i,j)]); + printf("\n"); + } } +*/ /****************************************************************************** @@ -145,210 +145,206 @@ static void PrintMatrix(GLfloat m[4*4]) Binding display lists to model caches may cause priority problems among alpha polygons. Therefore, it may be necessary in the future to decouple them. -******************************************************************************/ - +******************************************************************************/ + // Draws the display list void CLegacy3D::DrawDisplayList(ModelCache *Cache, POLY_STATE state) { - DisplayList *D; - - // Bind and activate VBO (pointers activate currently bound VBO) - glBindBuffer(GL_ARRAY_BUFFER, Cache->vboID); - glVertexPointer(3, GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_X*sizeof(GLfloat))); - glNormalPointer(GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_NX*sizeof(GLfloat))); - glTexCoordPointer(2, GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_U*sizeof(GLfloat))); - glColorPointer(3, GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_R*sizeof(GLfloat))); - if (subTextureLoc != -1) glVertexAttribPointer(subTextureLoc, 4, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXTURE_X*sizeof(GLfloat))); - if (texParamsLoc != -1) glVertexAttribPointer(texParamsLoc, 4, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXPARAMS_EN*sizeof(GLfloat))); - if (texFormatLoc != -1) glVertexAttribPointer(texFormatLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXFORMAT*sizeof(GLfloat))); - if (texMapLoc != -1) glVertexAttribPointer(texMapLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXMAP*sizeof(GLfloat))); - if (transLevelLoc != -1) glVertexAttribPointer(transLevelLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TRANSLUCENCE*sizeof(GLfloat))); - if (lightEnableLoc != -1) glVertexAttribPointer(lightEnableLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_LIGHTENABLE*sizeof(GLfloat))); - if (shininessLoc != -1) glVertexAttribPointer(shininessLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_SHININESS*sizeof(GLfloat))); - if (fogIntensityLoc != -1) glVertexAttribPointer(fogIntensityLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_FOGINTENSITY*sizeof(GLfloat))); - - // Set up state - if (state == POLY_STATE_ALPHA) - { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - else - { - glDisable(GL_BLEND); - } - - // Draw if there are items in the list - D = Cache->ListHead[state]; - while (D != NULL) - { - if (D->isViewport) - { - if (D->next != NULL) // if nothing follows, no point in doing this - { - if (!D->next->isViewport) - { - if (lightingLoc != -1) glUniform3fv(lightingLoc, 2, D->Data.Viewport.lightingParams); - if (projectionMatrixLoc != -1) glUniformMatrix4fv(projectionMatrixLoc, 1, GL_FALSE, D->Data.Viewport.projectionMatrix); - glFogf(GL_FOG_DENSITY, D->Data.Viewport.fogParams[3]); - glFogf(GL_FOG_START, D->Data.Viewport.fogParams[4]); - glFogfv(GL_FOG_COLOR, &(D->Data.Viewport.fogParams[0])); - if (spotEllipseLoc != -1) glUniform4fv(spotEllipseLoc, 1, D->Data.Viewport.spotEllipse); - if (spotRangeLoc != -1) glUniform2fv(spotRangeLoc, 1, D->Data.Viewport.spotRange); - if (spotColorLoc != -1) glUniform3fv(spotColorLoc, 1, D->Data.Viewport.spotColor); - glViewport(D->Data.Viewport.x, D->Data.Viewport.y, D->Data.Viewport.width, D->Data.Viewport.height); - } - } - } - else - { - GLint frontFace; - - if (D->Data.Model.frontFace == -GL_CW) // no backface culling (all normals have lost their Z component) - glDisable(GL_CULL_FACE); - else // use appropriate winding convention - { - glGetIntegerv(GL_FRONT_FACE, &frontFace); - if (frontFace != D->Data.Model.frontFace) - glFrontFace(D->Data.Model.frontFace); - } - - if (modelViewMatrixLoc != -1) - glUniformMatrix4fv(modelViewMatrixLoc, 1, GL_FALSE, D->Data.Model.modelViewMatrix); - glDrawArrays(GL_TRIANGLES, D->Data.Model.index, D->Data.Model.numVerts); - - if (D->Data.Model.frontFace == -GL_CW) - glEnable(GL_CULL_FACE); - } - - D = D->next; - } + // Bind and activate VBO (pointers activate currently bound VBO) + glBindBuffer(GL_ARRAY_BUFFER, Cache->vboID); + glVertexPointer(3, GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_X*sizeof(GLfloat))); + glNormalPointer(GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_NX*sizeof(GLfloat))); + glTexCoordPointer(2, GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_U*sizeof(GLfloat))); + glColorPointer(3, GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_R*sizeof(GLfloat))); + if (subTextureLoc != -1) glVertexAttribPointer(subTextureLoc, 4, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXTURE_X*sizeof(GLfloat))); + if (texParamsLoc != -1) glVertexAttribPointer(texParamsLoc, 4, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXPARAMS_EN*sizeof(GLfloat))); + if (texFormatLoc != -1) glVertexAttribPointer(texFormatLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXFORMAT*sizeof(GLfloat))); + if (texMapLoc != -1) glVertexAttribPointer(texMapLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXMAP*sizeof(GLfloat))); + if (transLevelLoc != -1) glVertexAttribPointer(transLevelLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TRANSLUCENCE*sizeof(GLfloat))); + if (lightEnableLoc != -1) glVertexAttribPointer(lightEnableLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_LIGHTENABLE*sizeof(GLfloat))); + if (shininessLoc != -1) glVertexAttribPointer(shininessLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_SHININESS*sizeof(GLfloat))); + if (fogIntensityLoc != -1) glVertexAttribPointer(fogIntensityLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_FOGINTENSITY*sizeof(GLfloat))); + + // Set up state + if (state == POLY_STATE_ALPHA) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + else + { + glDisable(GL_BLEND); + } + + // Draw if there are items in the list + const DisplayList *D = Cache->ListHead[state]; + while (D != NULL) + { + if (D->isViewport) + { + if (D->next != NULL) // if nothing follows, no point in doing this + { + if (!D->next->isViewport) + { + if (lightingLoc != -1) glUniform3fv(lightingLoc, 2, D->Data.Viewport.lightingParams); + if (projectionMatrixLoc != -1) glUniformMatrix4fv(projectionMatrixLoc, 1, GL_FALSE, D->Data.Viewport.projectionMatrix); + glFogf(GL_FOG_DENSITY, D->Data.Viewport.fogParams[3]); + glFogf(GL_FOG_START, D->Data.Viewport.fogParams[4]); + glFogfv(GL_FOG_COLOR, &(D->Data.Viewport.fogParams[0])); + if (spotEllipseLoc != -1) glUniform4fv(spotEllipseLoc, 1, D->Data.Viewport.spotEllipse); + if (spotRangeLoc != -1) glUniform2fv(spotRangeLoc, 1, D->Data.Viewport.spotRange); + if (spotColorLoc != -1) glUniform3fv(spotColorLoc, 1, D->Data.Viewport.spotColor); + glViewport(D->Data.Viewport.x, D->Data.Viewport.y, D->Data.Viewport.width, D->Data.Viewport.height); + } + } + } + else + { + if (D->Data.Model.frontFace == -GL_CW) // no backface culling (all normals have lost their Z component) + glDisable(GL_CULL_FACE); + else // use appropriate winding convention + { + GLint frontFace; + glGetIntegerv(GL_FRONT_FACE, &frontFace); + if (frontFace != D->Data.Model.frontFace) + glFrontFace(D->Data.Model.frontFace); + } + + if (modelViewMatrixLoc != -1) + glUniformMatrix4fv(modelViewMatrixLoc, 1, GL_FALSE, D->Data.Model.modelViewMatrix); + glDrawArrays(GL_TRIANGLES, D->Data.Model.index, D->Data.Model.numVerts); + + if (D->Data.Model.frontFace == -GL_CW) + glEnable(GL_CULL_FACE); + } + + D = D->next; + } } // Appends an instance of a model or viewport to the display list, copying over the required state information bool CLegacy3D::AppendDisplayList(ModelCache *Cache, bool isViewport, const struct VBORef *Model) { - int lm, i; - - if ((Cache->listSize+2) > Cache->maxListSize) // a model may have 2 states (viewports are added to both display lists) - return FAIL; - //return ErrorLog("Display list is full."); - - // Insert states into the display list - for (i = 0; i < 2; i++) - { - if (isViewport) - { - // Get index for new display list item and advance to next one - lm = Cache->listSize++; - - // Viewport parameters - Cache->List[lm].Data.Viewport.x = viewportX; - Cache->List[lm].Data.Viewport.y = viewportY; - Cache->List[lm].Data.Viewport.width = viewportWidth; - Cache->List[lm].Data.Viewport.height = viewportHeight; - - // Copy over lighting and fog state - memcpy(Cache->List[lm].Data.Viewport.lightingParams, lightingParams, sizeof(lightingParams)); - memcpy(Cache->List[lm].Data.Viewport.fogParams, fogParams, sizeof(fogParams)); - memcpy(Cache->List[lm].Data.Viewport.spotEllipse, spotEllipse, sizeof(spotEllipse)); - memcpy(Cache->List[lm].Data.Viewport.spotRange, spotRange, sizeof(spotRange)); - memcpy(Cache->List[lm].Data.Viewport.spotColor, spotColor, sizeof(spotColor)); - - // Copy projection matrix - glGetFloatv(GL_PROJECTION_MATRIX, Cache->List[lm].Data.Viewport.projectionMatrix); - } - 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 = 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); - - /* - * Determining if winding was reversed (but not polygon normal): - * - * Real3D performs backface culling in view space based on the - * polygon normal unlike OpenGL, which uses the computed normal - * from the edges (in screen space) of the polygon. Consequently, - * it is possible to create a matrix that mirrors an axis without - * rotating the normal, which in turn flips the polygon winding and - * makes it invisible in OpenGL but not on Real3D, because the - * normal is still facing the right way. - * - * To detect such a situation, we create a fictitious polygon with - * edges X = [1 0 0] and Y = [0 1 0], with normal Z = [0 0 1]. We - * rotate the edges by the matrix then compute a normal P, which is - * what OpenGL would use for culling. We transform the normal Z by - * the normal matrix (normals are special and must be multiplied by - * Transpose(Inverse(M)), not M). If the Z components of P and the - * transformed Z vector have opposite signs, the OpenGL winding - * mode must be switched in order to draw correctly. The X axis may - * have been flipped, for example, changing the winding mode while - * leaving the polygon normal unaffected. OpenGL would erroneously - * discard these polygons, so we flip the winding convention, - * ensuring they are drawn correctly. - * - * We have to adjust the Z vector (fictitious normal) by the sign - * of the Z axis specified by the coordinate system matrix (#0). - * This is described further in InsertPolygon(), where the vertices - * are ordered in clockwise fashion. - */ - GLfloat x[3] = { 1.0f, 0.0f, 0.0f }; - GLfloat y[3] = { 0.0f, 1.0f, 0.0f }; - GLfloat z[3] = { 0.0f, 0.0f, -1.0f*matrixBasePtr[0x5] }; - GLfloat m[4*4]; - GLfloat xT[3], yT[3], zT[3], pT[3]; + if ((Cache->listSize+2) > Cache->maxListSize) // a model may have 2 states (viewports are added to both display lists) + return FAIL; + //return ErrorLog("Display list is full."); + + // Insert states into the display list + size_t lm = 0; + for (size_t i = 0; i < 2; i++) + { + if (isViewport) + { + // Get index for new display list item and advance to next one + lm = Cache->listSize++; + + // Viewport parameters + Cache->List[lm].Data.Viewport.x = viewportX; + Cache->List[lm].Data.Viewport.y = viewportY; + Cache->List[lm].Data.Viewport.width = viewportWidth; + Cache->List[lm].Data.Viewport.height = viewportHeight; + + // Copy over lighting and fog state + memcpy(Cache->List[lm].Data.Viewport.lightingParams, lightingParams, sizeof(lightingParams)); + memcpy(Cache->List[lm].Data.Viewport.fogParams, fogParams, sizeof(fogParams)); + memcpy(Cache->List[lm].Data.Viewport.spotEllipse, spotEllipse, sizeof(spotEllipse)); + memcpy(Cache->List[lm].Data.Viewport.spotRange, spotRange, sizeof(spotRange)); + memcpy(Cache->List[lm].Data.Viewport.spotColor, spotColor, sizeof(spotColor)); + + // Copy projection matrix + glGetFloatv(GL_PROJECTION_MATRIX, Cache->List[lm].Data.Viewport.projectionMatrix); + } + 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 = 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); + + /* + * Determining if winding was reversed (but not polygon normal): + * + * Real3D performs backface culling in view space based on the + * polygon normal unlike OpenGL, which uses the computed normal + * from the edges (in screen space) of the polygon. Consequently, + * it is possible to create a matrix that mirrors an axis without + * rotating the normal, which in turn flips the polygon winding and + * makes it invisible in OpenGL but not on Real3D, because the + * normal is still facing the right way. + * + * To detect such a situation, we create a fictitious polygon with + * edges X = [1 0 0] and Y = [0 1 0], with normal Z = [0 0 1]. We + * rotate the edges by the matrix then compute a normal P, which is + * what OpenGL would use for culling. We transform the normal Z by + * the normal matrix (normals are special and must be multiplied by + * Transpose(Inverse(M)), not M). If the Z components of P and the + * transformed Z vector have opposite signs, the OpenGL winding + * mode must be switched in order to draw correctly. The X axis may + * have been flipped, for example, changing the winding mode while + * leaving the polygon normal unaffected. OpenGL would erroneously + * discard these polygons, so we flip the winding convention, + * ensuring they are drawn correctly. + * + * We have to adjust the Z vector (fictitious normal) by the sign + * of the Z axis specified by the coordinate system matrix (#0). + * This is described further in InsertPolygon(), where the vertices + * are ordered in clockwise fashion. + */ + static const GLfloat x[3] = { 1.0f, 0.0f, 0.0f }; + static const GLfloat y[3] = { 0.0f, 1.0f, 0.0f }; + const GLfloat z[3] = { 0.0f, 0.0f, -1.0f*matrixBasePtr[0x5] }; + GLfloat m[4*4]; + GLfloat xT[3], yT[3], zT[3], pT[3]; - InvertTransposeMat3(m,Cache->List[lm].Data.Model.modelViewMatrix); - MultMat3Vec3(xT,Cache->List[lm].Data.Model.modelViewMatrix,x); - MultMat3Vec3(yT,Cache->List[lm].Data.Model.modelViewMatrix,y); - MultMat3Vec3(zT,m,z); - CrossProd(pT,xT,yT); - - float s = Sign(zT[2]*pT[2]); - if (s < 0.0f) - Cache->List[lm].Data.Model.frontFace = GL_CCW; - else if (s > 0.0f) - Cache->List[lm].Data.Model.frontFace = GL_CW; - else - Cache->List[lm].Data.Model.frontFace = -GL_CW; - } - else // nothing to do, continue loop - continue; - - // Update list pointers and set list node type - Cache->List[lm].isViewport = isViewport; - Cache->List[lm].next = NULL; // current end of list - if (Cache->ListHead[i] == NULL) - { - Cache->ListHead[i] = &(Cache->List[lm]); - Cache->ListTail[i] = Cache->ListHead[i]; - } - else - { - Cache->ListTail[i]->next = &(Cache->List[lm]); - Cache->ListTail[i] = &(Cache->List[lm]); - } - } - - return OKAY; + InvertTransposeMat3(m,Cache->List[lm].Data.Model.modelViewMatrix); + MultMat3Vec3(xT,Cache->List[lm].Data.Model.modelViewMatrix,x); + MultMat3Vec3(yT,Cache->List[lm].Data.Model.modelViewMatrix,y); + MultMat3Vec3(zT,m,z); + CrossProd(pT,xT,yT); + + float s = Sign(zT[2]*pT[2]); + if (s < 0.0f) + Cache->List[lm].Data.Model.frontFace = GL_CCW; + else if (s > 0.0f) + Cache->List[lm].Data.Model.frontFace = GL_CW; + else + Cache->List[lm].Data.Model.frontFace = -GL_CW; + } + else // nothing to do, continue loop + continue; + + // Update list pointers and set list node type + Cache->List[lm].isViewport = isViewport; + Cache->List[lm].next = NULL; // current end of list + if (Cache->ListHead[i] == NULL) + { + Cache->ListHead[i] = &(Cache->List[lm]); + Cache->ListTail[i] = Cache->ListHead[i]; + } + else + { + Cache->ListTail[i]->next = &(Cache->List[lm]); + Cache->ListTail[i] = &(Cache->List[lm]); + } + } + + return OKAY; } // Clears the display list in preparation for a new frame void CLegacy3D::ClearDisplayList(ModelCache *Cache) { - Cache->listSize = 0; - for (int i = 0; i < 2; i++) - { - Cache->ListHead[i] = NULL; - Cache->ListTail[i] = NULL; - } + Cache->listSize = 0; + for (size_t i = 0; i < 2; i++) + { + Cache->ListHead[i] = NULL; + Cache->ListTail[i] = NULL; + } } @@ -364,356 +360,351 @@ void CLegacy3D::ClearDisplayList(ModelCache *Cache) // Inserts a vertex into the local vertex buffer, incrementing both the local and VBO pointers. The normal is scaled by normFlip. void CLegacy3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P, float normFlip) { - GLfloat r, g, b; - GLfloat translucence, fogIntensity, texWidth, texHeight, texBaseX, texBaseY, contourProcessing; - unsigned baseIdx, texFormat, texEnable, lightEnable, modulate, colorIdx; - TexSheet *texSheet; - int s, texPage, shininess; - - // Texture selection - texEnable = P->header[6]&0x04000000; - texFormat = (P->header[6]>>7)&7; - 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 - texSheet = fmtToTexSheet[texFormat]; // get X&Y offset of texture sheet within texture map - texBaseX = (GLfloat) (texSheet->xOffset + (((32*(((P->header[4]&0x1F)<<1)|((P->header[5]>>7)&1))) + (int)texOffsetXY[0])&2047)); - texBaseY = (GLfloat) (texSheet->yOffset + (((32*(P->header[5]&0x1F)+texPage) + (int)texOffsetXY[1])&2047)); - - /* - * Lighting and Color Modulation: - * - * It appears that there is a modulate bit which causes the polygon color - * to be multiplied by texel colors. However, if polygons are luminous, - * this appears to be disabled (not quite correct yet, though). - * - * Color Table - * ----------- - * 1. Color table base is definitely at 0x400 for most games. - * 2. There are two color indexes in header[4]. One between bits 31-20 and - * the other between bits 19-8. Sometimes they are set the same, sometimes - * they differ by 1. They must either be selectable or apply to different - * sides of the polygon. Indexed colors appear to be enabled by - * !(header[1]&2). - * 3. Bits 19-8 are needed to make Daytona 2 lights blink. They also seem to - * work well for Scud Race. - * 4. Two bits, header[4]&0x80 and header[3]&0x80, seem to affect color - * modulation (multiplication of RGB or indexed color value by texels). - * header[4] works best in Sega Rally 2 but header[3] works a bit better - * elsewhere. - * 5. !(header[4]&0x80) is sufficient to get blinking lights to work in - * Daytona and also fixes shadows under the overpass (spiral turn) on the - * expert course. But, it makes the waterfalls on Scud's medium course too - * dark. The waterfalls have !(header[1]&2), which seems to indicate they - * use indexed colors, but they are too dark when used. header[3]&0x80 is - * 0, which if interpreted as modulation off, makes waterfalls appear - * correctly. If !(header[4]&0x80) is used instead, it is enabled, and - * modulation fails. Blinking lights in Scud Race (medium, expert courses) - * seem to work with both. - * 6. Forcing modulation to be enabled in color index mode does not seem to - * work because of the Scud Race waterfalls (they seem to dislike being - * modulated). - * 7. A possibly important test case, in addition to waterfalls, are the red - * traffic cones at the start of the Desert course in Sega Rally 2's - * championship mode. When !header[4]&0x80 is used, colors are mostly - * correct, but cones are too dark. Need to investigate further. - */ - - lightEnable = !(P->header[6]&0x00010000); - modulate = !(P->header[4]&0x80); // makes traffic lights blink in Daytona and works best in Sega Rally 2 - //modulate = P->header[3]&0x80; // seems to work better overall (TODO: are header[3]&0x80 and header[4]&0x80 ever both set?) - - // Material color - if ((P->header[1]&2) == 0) - { - colorIdx = ((P->header[4]>>20)&0x7FF) - 0; // works for Scud - colorIdx = ((P->header[4]>>8)&0x7FF) - 0; // works for Daytona2 lights - unsigned base = 0x400; - b = (GLfloat) (polyRAM[base+colorIdx]&0xFF) * (1.0f/255.0f); - g = (GLfloat) ((polyRAM[base+colorIdx]>>8)&0xFF) * (1.0f/255.0f); - r = (GLfloat) ((polyRAM[base+colorIdx]>>16)&0xFF) * (1.0f/255.0f); -//modulate=true; - } - else if ((P->header[6] & 0x02000000)) + // Texture selection + unsigned texEnable = P->header[6]&0x04000000; + unsigned texFormat = (P->header[6]>>7)&7; + GLfloat texWidth = (GLfloat) (32<<((P->header[3]>>3)&7)); + GLfloat texHeight = (GLfloat) (32<<((P->header[3]>>0)&7)); + int texPage = (P->header[4]&0x40) ? 1024 : 0; // treat texture page as Y coordinate + TexSheet *texSheet = fmtToTexSheet[texFormat]; // get X&Y offset of texture sheet within texture map + GLfloat texBaseX = (GLfloat) (texSheet->xOffset + (((32*(((P->header[4]&0x1F)<<1)|((P->header[5]>>7)&1))) + (int)texOffsetXY[0])&2047)); + GLfloat texBaseY = (GLfloat) (texSheet->yOffset + (((32*(P->header[5]&0x1F)+texPage) + (int)texOffsetXY[1])&2047)); + + /* + * Lighting and Color Modulation: + * + * It appears that there is a modulate bit which causes the polygon color + * to be multiplied by texel colors. However, if polygons are luminous, + * this appears to be disabled (not quite correct yet, though). + * + * Color Table + * ----------- + * 1. Color table base is definitely at 0x400 for most games. + * 2. There are two color indexes in header[4]. One between bits 31-20 and + * the other between bits 19-8. Sometimes they are set the same, sometimes + * they differ by 1. They must either be selectable or apply to different + * sides of the polygon. Indexed colors appear to be enabled by + * !(header[1]&2). + * 3. Bits 19-8 are needed to make Daytona 2 lights blink. They also seem to + * work well for Scud Race. + * 4. Two bits, header[4]&0x80 and header[3]&0x80, seem to affect color + * modulation (multiplication of RGB or indexed color value by texels). + * header[4] works best in Sega Rally 2 but header[3] works a bit better + * elsewhere. + * 5. !(header[4]&0x80) is sufficient to get blinking lights to work in + * Daytona and also fixes shadows under the overpass (spiral turn) on the + * expert course. But, it makes the waterfalls on Scud's medium course too + * dark. The waterfalls have !(header[1]&2), which seems to indicate they + * use indexed colors, but they are too dark when used. header[3]&0x80 is + * 0, which if interpreted as modulation off, makes waterfalls appear + * correctly. If !(header[4]&0x80) is used instead, it is enabled, and + * modulation fails. Blinking lights in Scud Race (medium, expert courses) + * seem to work with both. + * 6. Forcing modulation to be enabled in color index mode does not seem to + * work because of the Scud Race waterfalls (they seem to dislike being + * modulated). + * 7. A possibly important test case, in addition to waterfalls, are the red + * traffic cones at the start of the Desert course in Sega Rally 2's + * championship mode. When !header[4]&0x80 is used, colors are mostly + * correct, but cones are too dark. Need to investigate further. + */ + + unsigned lightEnable = !(P->header[6]&0x00010000); + unsigned modulate = !(P->header[4]&0x80); // makes traffic lights blink in Daytona and works best in Sega Rally 2 + //unsigned modulate = P->header[3]&0x80; // seems to work better overall (TODO: are header[3]&0x80 and header[4]&0x80 ever both set?) + + // Material color + GLfloat r = 1.0; + GLfloat g = 1.0; + GLfloat b = 1.0; + if ((P->header[1]&2) == 0) + { + size_t base = 0x400; + //size_t colorIdx = ((P->header[4]>>20)&0x7FF) - 0; // works for Scud + size_t colorIdx = ((P->header[4]>>8)&0x7FF) - 0; // works for Daytona2 lights and Scud + b = (GLfloat) (polyRAM[base+colorIdx]&0xFF) * (1.0f/255.0f); + g = (GLfloat) ((polyRAM[base+colorIdx]>>8)&0xFF) * (1.0f/255.0f); + r = (GLfloat) ((polyRAM[base+colorIdx]>>16)&0xFF) * (1.0f/255.0f); + //modulate=true; + } + /* + else if ((P->header[6] & 0x02000000)) { r = g = b = (GLfloat) ((P->header[6]>>26)&0x1f) * (1.0f/31.0f); } - else - { - // Colors are 8-bit (almost certainly true, see Star Wars) - r = (GLfloat) (P->header[4]>>24) * (1.0f/255.0f); - g = (GLfloat) ((P->header[4]>>16)&0xFF) * (1.0f/255.0f); - b = (GLfloat) ((P->header[4]>>8)&0xFF) * (1.0f/255.0f); - } + */ + else + { + // Colors are 8-bit (almost certainly true, see Star Wars) + r = (GLfloat) (P->header[4]>>24) * (1.0f/255.0f); + g = (GLfloat) ((P->header[4]>>16)&0xFF) * (1.0f/255.0f); + b = (GLfloat) ((P->header[4]>>8)&0xFF) * (1.0f/255.0f); + } - // Determine modulation settings - if (texEnable) - { - //if (!lightEnable|| !modulate) - if (!modulate) - r = g = b = 1.0f; - } -if ((P->header[6] & 0x02000000)) { lightEnable=false;r=b=0;g=1.0;texEnable=false;modulate=false;} - // Specular shininess - shininess = (P->header[0]>>26)&0x3F; - //shininess = (P->header[0]>>28)&0xF; - //if (shininess) - // printf("%X\n", shininess); - if (!(P->header[0]&0x80) || (shininess == 0)) // bit 0x80 seems to enable specular lighting - shininess = -1; // disable + // Determine modulation settings + if (texEnable) + { + //if (!lightEnable|| !modulate) + if (!modulate) + r = g = b = 1.0f; + } -#if 0 - if (texFormat==5)//texFormat==6||texFormat==2) - { - //printf("%03X\n", P->header[4]>>8); - //texEnable=0; - g=b=1.0; - r=1.0f; - } + // Specular shininess + int shininess = (P->header[0]>>26)&0x3F; + //shininess = (P->header[0]>>28)&0xF; + //if (shininess) + // printf("%X\n", shininess); + if (!(P->header[0]&0x80) || (shininess == 0)) // bit 0x80 seems to enable specular lighting + shininess = -1; // disable + +#if 0 + if (texFormat==5)//texFormat==6||texFormat==2) + { + //printf("%03X\n", P->header[4]>>8); + //texEnable=0; + g=b=1.0; + r=1.0f; + } #endif #if 0 - int testWord = 0; - int testBit = 7; - //if ((P->header[testWord]&(1<header[0]>>24) & 0x3) != 0) - //if (!((P->header[0]>>26) & 0x3F) && (P->header[0]&0x80)) - { - texEnable = 0; - r=b=0; - g=1.0f; - g = ((P->header[0]>>26)&0x3F) * (1.0f/64.0f); - //if (!lightEnable) - // b=1.0f; - lightEnable=0; - } + int testWord = 0; + int testBit = 7; + //if ((P->header[testWord]&(1<header[0]>>24) & 0x3) != 0) + //if (!((P->header[0]>>26) & 0x3F) && (P->header[0]&0x80)) + { + texEnable = 0; + r=b=0; + g=1.0f; + g = ((P->header[0]>>26)&0x3F) * (1.0f/64.0f); + //if (!lightEnable) + // b=1.0f; + lightEnable=0; + } #endif - // Determine whether polygon is translucent - translucence = (GLfloat) ((P->header[6]>>18)&0x1F) * (1.0f/31.0f); - if ((P->header[6]&0x00800000)) // if set, polygon is opaque - translucence = 1.0f; - - // Fog intensity (for luminous polygons) - fogIntensity = (GLfloat) ((P->header[6]>>11)&0x1F) * (1.0f/31.0f); - if (!(P->header[6]&0x00010000)) // if not luminous, always use full fog intensity - fogIntensity = 1.0f; - - /* - * Contour processing. Any alpha value sufficiently close to 0 seems to - * cause pixels to be discarded entirely on Model 3 (no modification of the - * depth buffer). Strictly speaking, only T1RGB5 format textures are - * "contour textures" (in Real3D lingo), we enable contour processing for - * alpha blended texture formats as well in order to discard fully - * transparent pixels. - */ - if ((P->header[6]&0x80000000) || (texFormat==7) || // contour processing enabled or RGBA4 texture - ((texFormat==1) && (P->header[6]&2)) || // A4L4 interleaved (these formats are not being interpreted correctly, see Scud Race clock tower) - ((texFormat==3) && (P->header[6]&4))) // A4L4 interleaved - contourProcessing = 1.0f; - else - contourProcessing = -1.0f; + // Determine whether polygon is translucent + GLfloat translucence = (GLfloat) ((P->header[6]>>18)&0x1F) * (1.0f/31.0f); + if ((P->header[6]&0x00800000)) // if set, polygon is opaque + translucence = 1.0f; + + // Fog intensity (for luminous polygons) + GLfloat fogIntensity = (GLfloat) ((P->header[6]>>11)&0x1F) * (1.0f/31.0f); + if (!(P->header[6]&0x00010000)) // if not luminous, always use full fog intensity + fogIntensity = 1.0f; + + /* + * Contour processing. Any alpha value sufficiently close to 0 seems to + * cause pixels to be discarded entirely on Model 3 (no modification of the + * depth buffer). Strictly speaking, only T1RGB5 format textures are + * "contour textures" (in Real3D lingo), we enable contour processing for + * alpha blended texture formats as well in order to discard fully + * transparent pixels. + */ + GLfloat contourProcessing = -1.0f; + if ((P->header[6]&0x80000000) || (texFormat==7) || // contour processing enabled or RGBA4 texture + ((texFormat==1) && (P->header[6]&2)) || // A4L4 interleaved (these formats are not being interpreted correctly, see Scud Race clock tower) + ((texFormat==3) && (P->header[6]&4))) // A4L4 interleaved + contourProcessing = 1.0f; - // Store to local vertex buffer - s = P->state; - baseIdx = Cache->curVertIdx[s]*VBO_VERTEX_SIZE; - - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_X] = V->x; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_Y] = V->y; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_Z] = V->z; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_R] = r; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_G] = g; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_B] = b; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TRANSLUCENCE] = translucence; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_LIGHTENABLE] = lightEnable ? 1.0f : 0.0f; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_SHININESS] = (GLfloat) shininess; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_FOGINTENSITY] = fogIntensity; - - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_NX] = V->n[0]*normFlip; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_NY] = V->n[1]*normFlip; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_NZ] = V->n[2]*normFlip; - - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_U] = V->u; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_V] = V->v; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_X] = texBaseX; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_Y] = texBaseY; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_W] = texWidth; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_H] = texHeight; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXPARAMS_EN] = texEnable ? 1.0f : 0.0f; - 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] = (float)texFormat; - Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXMAP] = (float)texSheet->mapNum; + // Store to local vertex buffer + size_t s = P->state; + size_t baseIdx = Cache->curVertIdx[s]*VBO_VERTEX_SIZE; - Cache->curVertIdx[s]++; - Cache->vboCurOffset += VBO_VERTEX_SIZE*sizeof(GLfloat); + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_X] = V->x; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_Y] = V->y; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_Z] = V->z; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_R] = r; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_G] = g; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_B] = b; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TRANSLUCENCE] = translucence; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_LIGHTENABLE] = lightEnable ? 1.0f : 0.0f; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_SHININESS] = (GLfloat) shininess; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_FOGINTENSITY] = fogIntensity; + + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_NX] = V->n[0]*normFlip; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_NY] = V->n[1]*normFlip; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_NZ] = V->n[2]*normFlip; + + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_U] = V->u; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_V] = V->v; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_X] = texBaseX; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_Y] = texBaseY; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_W] = texWidth; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_H] = texHeight; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXPARAMS_EN] = texEnable ? 1.0f : 0.0f; + 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] = (float)texFormat; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXMAP] = (float)texSheet->mapNum; + + Cache->curVertIdx[s]++; + Cache->vboCurOffset += VBO_VERTEX_SIZE*sizeof(GLfloat); } bool CLegacy3D::InsertPolygon(ModelCache *Cache, const Poly *P) { - GLfloat n[3], v1[3], v2[3], normZFlip; - int i; - bool doubleSided; - - // Bounds testing: up to 12 triangles will be inserted (worst case: double sided quad is 6 triangles) - if ((Cache->curVertIdx[P->state]+6*2) >= Cache->maxVertIdx) - return ErrorLocalVertexOverflow(); // local buffers are not expected to overflow - if ((Cache->vboCurOffset+6*2*VBO_VERTEX_SIZE*sizeof(GLfloat)) >= Cache->vboMaxOffset) - return FAIL; // this just indicates we may need to re-cache - - // Is the polygon double sided? - doubleSided = (P->header[1]&0x10) ? true : false; - - /* - * Determine polygon winding by taking cross product of vectors formed from - * 3 polygon vertices (the middle one being the origin). In reality, back- - * face culling is determined by the polygon normal and two-sided polygons - * exist. This is just a temporary hack. - * - * If the cross product points the same way as the normal, the winding is - * clockwise and can be kept, otherwise it must be reversed. - * - * NOTE: This assumes that the Model 3 base coordinate system's Z axis - * (into the screen) is -1, like OpenGL. For some games (eg., Lost World), - * this is not the case. Assuming games consistently use the same type of - * coordinate system matrix, it seems that inverting the whole dot product - * when Z is positive helps. I don't understand exactly why... but it has - * to do with using the correct Z convention to identify a vector pointing - * toward or away from the screen. - */ - v1[0] = P->Vert[0].x-P->Vert[1].x; - v1[1] = P->Vert[0].y-P->Vert[1].y; - v1[2] = P->Vert[0].z-P->Vert[1].z; - v2[0] = P->Vert[2].x-P->Vert[1].x; - v2[1] = P->Vert[2].y-P->Vert[1].y; - v2[2] = P->Vert[2].z-P->Vert[1].z; - CrossProd(n,v1,v2); - - normZFlip = -1.0f*matrixBasePtr[0x5]; // coordinate system m13 component - - if (normZFlip*(n[0]*P->n[0]+n[1]*P->n[1]+n[2]*P->n[2]) >= 0.0) // clockwise winding confirmed - { - // Store the first triangle - for (i = 0; i < 3; i++) - { - InsertVertex(Cache, &(P->Vert[i]), P, 1.0f); - } - - if (doubleSided) // store backside as counter-clockwise - { - for (i = 2; i >=0; i--) - { - InsertVertex(Cache, &(P->Vert[i]), P, -1.0f); - } - } - - // If quad, second triangle will just be vertices 1, 3, 4 - if (P->numVerts == 4) - { - InsertVertex(Cache, &(P->Vert[0]), P, 1.0f); - InsertVertex(Cache, &(P->Vert[2]), P, 1.0f); - InsertVertex(Cache, &(P->Vert[3]), P, 1.0f); - - if (doubleSided) - { - InsertVertex(Cache, &(P->Vert[0]), P, -1.0f); - InsertVertex(Cache, &(P->Vert[3]), P, -1.0f); - InsertVertex(Cache, &(P->Vert[2]), P, -1.0f); - } - } - } - else // counterclockwise winding, reverse it - { - for (i = 2; i >=0; i--) - { - InsertVertex(Cache, &(P->Vert[i]), P, 1.0f); - } - - if (doubleSided) // store backside as clockwise - { - for (i = 0; i < 3; i++) - { - InsertVertex(Cache, &(P->Vert[i]), P, -1.0f); - } - } - - if (P->numVerts == 4) - { - InsertVertex(Cache, &(P->Vert[0]), P, 1.0f); - InsertVertex(Cache, &(P->Vert[3]), P, 1.0f); - InsertVertex(Cache, &(P->Vert[2]), P, 1.0f); - - if (doubleSided) - { - InsertVertex(Cache, &(P->Vert[0]), P, -1.0f); - InsertVertex(Cache, &(P->Vert[2]), P, -1.0f); - InsertVertex(Cache, &(P->Vert[3]), P, -1.0f); - } - } - } - - return OKAY; + // Bounds testing: up to 12 triangles will be inserted (worst case: double sided quad is 6 triangles) + if ((Cache->curVertIdx[P->state]+6*2) >= Cache->maxVertIdx) + return ErrorLocalVertexOverflow(); // local buffers are not expected to overflow + if ((Cache->vboCurOffset+6*2*VBO_VERTEX_SIZE*sizeof(GLfloat)) >= Cache->vboMaxOffset) + return FAIL; // this just indicates we may need to re-cache + + // Is the polygon double sided? + bool doubleSided = (P->header[1]&0x10) ? true : false; + + /* + * Determine polygon winding by taking cross product of vectors formed from + * 3 polygon vertices (the middle one being the origin). In reality, back- + * face culling is determined by the polygon normal and two-sided polygons + * exist. This is just a temporary hack. + * + * If the cross product points the same way as the normal, the winding is + * clockwise and can be kept, otherwise it must be reversed. + * + * NOTE: This assumes that the Model 3 base coordinate system's Z axis + * (into the screen) is -1, like OpenGL. For some games (eg., Lost World), + * this is not the case. Assuming games consistently use the same type of + * coordinate system matrix, it seems that inverting the whole dot product + * when Z is positive helps. I don't understand exactly why... but it has + * to do with using the correct Z convention to identify a vector pointing + * toward or away from the screen. + */ + GLfloat v1[3]; + GLfloat v2[3]; + GLfloat n[3]; + v1[0] = P->Vert[0].x-P->Vert[1].x; + v1[1] = P->Vert[0].y-P->Vert[1].y; + v1[2] = P->Vert[0].z-P->Vert[1].z; + v2[0] = P->Vert[2].x-P->Vert[1].x; + v2[1] = P->Vert[2].y-P->Vert[1].y; + v2[2] = P->Vert[2].z-P->Vert[1].z; + CrossProd(n,v1,v2); + + GLfloat normZFlip = -1.0f*matrixBasePtr[0x5]; // coordinate system m13 component + + if (normZFlip*(n[0]*P->n[0]+n[1]*P->n[1]+n[2]*P->n[2]) >= 0.0) // clockwise winding confirmed + { + // Store the first triangle + for (int i = 0; i < 3; i++) + { + InsertVertex(Cache, &(P->Vert[i]), P, 1.0f); + } + + if (doubleSided) // store backside as counter-clockwise + { + for (int i = 2; i >=0; i--) + { + InsertVertex(Cache, &(P->Vert[i]), P, -1.0f); + } + } + + // If quad, second triangle will just be vertices 1, 3, 4 + if (P->numVerts == 4) + { + InsertVertex(Cache, &(P->Vert[0]), P, 1.0f); + InsertVertex(Cache, &(P->Vert[2]), P, 1.0f); + InsertVertex(Cache, &(P->Vert[3]), P, 1.0f); + + if (doubleSided) + { + InsertVertex(Cache, &(P->Vert[0]), P, -1.0f); + InsertVertex(Cache, &(P->Vert[3]), P, -1.0f); + InsertVertex(Cache, &(P->Vert[2]), P, -1.0f); + } + } + } + else // counterclockwise winding, reverse it + { + for (int i = 2; i >=0; i--) + { + InsertVertex(Cache, &(P->Vert[i]), P, 1.0f); + } + + if (doubleSided) // store backside as clockwise + { + for (int i = 0; i < 3; i++) + { + InsertVertex(Cache, &(P->Vert[i]), P, -1.0f); + } + } + + if (P->numVerts == 4) + { + InsertVertex(Cache, &(P->Vert[0]), P, 1.0f); + InsertVertex(Cache, &(P->Vert[3]), P, 1.0f); + InsertVertex(Cache, &(P->Vert[2]), P, 1.0f); + + if (doubleSided) + { + InsertVertex(Cache, &(P->Vert[0]), P, -1.0f); + InsertVertex(Cache, &(P->Vert[2]), P, -1.0f); + InsertVertex(Cache, &(P->Vert[3]), P, -1.0f); + } + } + } + + return OKAY; } // Begins caching a new model by resetting to the start of the local vertex buffer struct VBORef *CLegacy3D::BeginModel(ModelCache *Cache) { - struct VBORef *Model; - - unsigned m = Cache->numModels; - - // Determine whether we've exceeded the model cache limits (caller will have to recache) - if (m >= Cache->maxModels) - { - //ErrorLog("Too many %s models.", Cache->dynamic?"dynamic":"static"); - return NULL; - } - - Model = &(Cache->Models[m]); - - // Reset to the beginning of the local vertex buffer - for (int i = 0; i < 2; i++) - Cache->curVertIdx[i] = 0; - - // Clear the VBO reference to 0 and clear texture references - Model->Clear(); - - // Record starting index of first opaque polygon in VBO (alpha poly index will be re-set in EndModel()) - Model->index[POLY_STATE_NORMAL] = Cache->vboCurOffset/(VBO_VERTEX_SIZE*sizeof(GLfloat)); - Model->index[POLY_STATE_ALPHA] = Model->index[POLY_STATE_NORMAL]; - - return Model; + size_t m = Cache->numModels; + + // Determine whether we've exceeded the model cache limits (caller will have to recache) + if (m >= Cache->maxModels) + { + //ErrorLog("Too many %s models.", Cache->dynamic?"dynamic":"static"); + return NULL; + } + + struct VBORef *Model = &(Cache->Models[m]); + + // Reset to the beginning of the local vertex buffer + for (size_t i = 0; i < 2; i++) + Cache->curVertIdx[i] = 0; + + // Clear the VBO reference to 0 and clear texture references + Model->Clear(); + + // Record starting index of first opaque polygon in VBO (alpha poly index will be re-set in EndModel()) + Model->index[POLY_STATE_NORMAL] = Cache->vboCurOffset/(VBO_VERTEX_SIZE*sizeof(GLfloat)); + Model->index[POLY_STATE_ALPHA] = Model->index[POLY_STATE_NORMAL]; + + return Model; } // Uploads all vertices from the local vertex buffer to the VBO, sets up the VBO reference, updates the LUT void CLegacy3D::EndModel(ModelCache *Cache, struct VBORef *Model, int lutIdx, UINT16 texOffset) { - int m = Cache->numModels++; + int m = Cache->numModels++; - // Record the number of vertices, completing the VBORef - for (int i = 0; i < 2; i++) - Model->numVerts[i] = Cache->curVertIdx[i]; + // Record the number of vertices, completing the VBORef + for (size_t i = 0; i < 2; i++) + Model->numVerts[i] = Cache->curVertIdx[i]; - // First alpha polygon immediately follows the normal polygons - Model->index[POLY_STATE_ALPHA] = Model->index[POLY_STATE_NORMAL] + Model->numVerts[POLY_STATE_NORMAL]; + // First alpha polygon immediately follows the normal polygons + 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 (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 - 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; + // Upload from local vertex buffer to real VBO + glBindBuffer(GL_ARRAY_BUFFER, Cache->vboID); + 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 + 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; } /* @@ -730,184 +721,152 @@ void CLegacy3D::EndModel(ModelCache *Cache, struct VBORef *Model, int lutIdx, UI struct VBORef *CLegacy3D::CacheModel(ModelCache *Cache, int lutIdx, UINT16 texOffset, const UINT32 *data) { - Vertex Prev[4]; // previous vertices - int numPolys = 0; - bool done = false; - - // Sega Rally 2 bad models - //if (lutIdx == 0x27a1 || lutIdx == 0x21e0) - // return FAIL; - - if (data == NULL) - return NULL; - - // Start constructing a new model - struct VBORef *Model = BeginModel(Cache); - if (NULL == Model) - return NULL; // too many models! - - // Cache all polygons - while (!done) - { - Poly P; // current polygon - GLfloat mag; - GLfloat uvScale; - int texEnable, texFormat, texWidth, texHeight, texPage, texBaseX, texBaseY; - unsigned i, j, vmask; - UINT32 ix, iy, iz, it; - bool validPoly; - - // Set current header pointer (header is 7 words) - P.header = data; - data += 7; // data will now point to first vertex - if (P.header[6]==0)// || P.header[0]==0) - break; + // Sega Rally 2 bad models + //if (lutIdx == 0x27a1 || lutIdx == 0x21e0) + // return FAIL; + + if (data == NULL) + return NULL; + + // Start constructing a new model + struct VBORef *Model = BeginModel(Cache); + if (NULL == Model) + return NULL; // too many models! + + // Cache all polygons + Vertex Prev[4]; // previous vertices + int numPolys = 0; + bool done = false; + while (!done) + { + // Set current header pointer (header is 7 words) + Poly P; // current polygon + P.header = data; + data += 7; // data will now point to first vertex + if (P.header[6]==0)// || P.header[0]==0) + break; // Sega Rally 2: dust trails often have polygons with seemingly invalid // vertices (very large values or 0). Ignoring polygons with these bits set // seems to fix the problem. Perhaps these polygons exist for alignment // purposes or are another type of entity altogether? - validPoly = (P.header[0] & 0x300) != 0x300; -// if (!validPoly) + bool validPoly = (P.header[0] & 0x300) != 0x300; + + // Obtain basic polygon parameters + done = P.header[1]&4; // last polygon? + P.numVerts = (P.header[0]&0x40)?4:3; + // Texture data + int texEnable = P.header[6]&0x04000000; + int texFormat = (P.header[6]>>7)&7; + int texWidth = (32<<((P.header[3]>>3)&7)); + int texHeight = (32<<((P.header[3]>>0)&7)); + int texPage = (P.header[4]&0x40) ? 1024 : 0; // treat texture page as Y coordinate + int texBaseX = ((32*(((P.header[4]&0x1F)<<1)|((P.header[5]>>7)&1))) + (int)texOffsetXY[0]) & 2047; + int texBaseY = ((32*(P.header[5]&0x1F)+texPage) + (int)texOffsetXY[1]) & 2047; + GLfloat uvScale = (P.header[1]&0x40)?1.0f:(1.0f/8.0f); + + // Determine whether this is an alpha polygon (TODO: when testing textures, test if texturing enabled? Might not matter) + if (((P.header[6]&0x00800000)==0) || // translucent polygon + (texFormat==7) || // RGBA4 texture + (texFormat==4)) // A4L4 texture + P.state = POLY_STATE_ALPHA; + else + P.state = POLY_STATE_NORMAL; + if (texFormat==1) // A4L4 interleaved { - //printf("Invalid poly:\n"); - //for (int i = 0; i < 7; i++) - // printf(" %d: %08x\n", i, P.header[i]); + if ((P.header[6]&2)) + P.state = POLY_STATE_ALPHA; + else + P.state = POLY_STATE_NORMAL; } - - // Obtain basic polygon parameters - done = P.header[1]&4; // last polygon? - P.numVerts = (P.header[0]&0x40)?4:3; - -#if 0 -if ((P.header[0] & 0xf) && numPolys == 0) -{ - validPoly=false; - printf("LNK=%x num=%d\n", P.header[0] & 0xf, P.numVerts); -} -if (lutIdx==0x8c0955) -{ - for (int i = 0; i < 7; i++) - printf("%d %d: %08x\n", numPolys, i, P.header[i]); - -} -#endif - - // Texture data - texEnable = P.header[6]&0x04000000; - texFormat = (P.header[6]>>7)&7; - 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))) + (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 (TODO: when testing textures, test if texturing enabled? Might not matter) - if (((P.header[6]&0x00800000)==0) || // translucent polygon - (texFormat==7) || // RGBA4 texture - (texFormat==4)) // A4L4 texture - P.state = POLY_STATE_ALPHA; - else - P.state = POLY_STATE_NORMAL; - if (texFormat==1) // A4L4 interleaved - { - if ((P.header[6]&2)) - P.state = POLY_STATE_ALPHA; - else - P.state = POLY_STATE_NORMAL; - } - if (texFormat==3) // A4L4 interleaved - { - if ((P.header[6]&4)) - P.state = POLY_STATE_ALPHA; - else - P.state = POLY_STATE_NORMAL; - } - - // Decode the texture - if (texEnable) - { - // If model cache is static, record texture reference in model cache entry for later decoding. - // If cache is dynamic, or if it's not possible to record the texture reference (due to lack of - // memory) then decode the texture now. - if (Cache->dynamic || !Model->texRefs.AddRef(texFormat, texBaseX, texBaseY, 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); - P.n[1] = (GLfloat) (((INT32)P.header[2])>>8) * (1.0f/4194304.0f); - P.n[2] = (GLfloat) (((INT32)P.header[3])>>8) * (1.0f/4194304.0f); - - // Fetch reused vertices according to bitfield, then new verts - i = 0; - j = 0; - vmask = 1; - for (i = 0; i < 4; i++) // up to 4 reused vertices - { - if ((P.header[0x00]&vmask)) - { - P.Vert[j] = Prev[i]; - ++j; - } - vmask <<= 1; - } - - for (; j < P.numVerts; j++) // remaining vertices are new and defined here - { - // Fetch vertices - ix = data[0]; - iy = data[1]; - iz = data[2]; - it = data[3]; - - /* - // Check for bad vertices (Sega Rally 2) - if (((ix>>28)==7) || ((iy>>28)==7) || ((iz>>28)==7)) - { - //printf("%X ix=%08X, iy=%08X, iz=%08X\n", lutIdx, ix, iy, iz); - goto StopDecoding; - } - */ - - // Decode vertices - P.Vert[j].x = (GLfloat) (((INT32)ix)>>8) * vertexFactor; - P.Vert[j].y = (GLfloat) (((INT32)iy)>>8) * vertexFactor; - P.Vert[j].z = (GLfloat) (((INT32)iz)>>8) * vertexFactor; - P.Vert[j].n[0] = P.n[0]+(GLfloat)(INT8)(ix&0xFF); // vertex normals are offset from polygon normal - P.Vert[j].n[1] = P.n[1]+(GLfloat)(INT8)(iy&0xFF); - P.Vert[j].n[2] = P.n[2]+(GLfloat)(INT8)(iz&0xFF); - P.Vert[j].u = (GLfloat) ((UINT16)(it>>16)) * uvScale; // TO-DO: might these be signed? - P.Vert[j].v = (GLfloat) ((UINT16)(it&0xFFFF)) * uvScale; - data += 4; - - // Normalize the vertex normal - mag = sqrt(P.Vert[j].n[0]*P.Vert[j].n[0]+P.Vert[j].n[1]*P.Vert[j].n[1]+P.Vert[j].n[2]*P.Vert[j].n[2]); - P.Vert[j].n[0] /= mag; - P.Vert[j].n[1] /= mag; - P.Vert[j].n[2] /= mag; - } - + if (texFormat==3) // A4L4 interleaved + { + if ((P.header[6]&4)) + P.state = POLY_STATE_ALPHA; + else + P.state = POLY_STATE_NORMAL; + } + + // Decode the texture + if (texEnable) + { + // If model cache is static, record texture reference in model cache entry for later decoding. + // If cache is dynamic, or if it's not possible to record the texture reference (due to lack of + // memory) then decode the texture now. + if (Cache->dynamic || !Model->texRefs.AddRef(texFormat, texBaseX, texBaseY, 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); + P.n[1] = (GLfloat) (((INT32)P.header[2])>>8) * (1.0f/4194304.0f); + P.n[2] = (GLfloat) (((INT32)P.header[3])>>8) * (1.0f/4194304.0f); + + // Fetch reused vertices according to bitfield, then new verts + size_t j = 0; + size_t vmask = 1; + for (size_t i = 0; i < 4; i++) // up to 4 reused vertices + { + if ((P.header[0x00]&vmask)) + { + P.Vert[j] = Prev[i]; + ++j; + } + vmask <<= 1; + } + + for (; j < P.numVerts; j++) // remaining vertices are new and defined here + { + // Fetch vertices + UINT32 ix = data[0]; + UINT32 iy = data[1]; + UINT32 iz = data[2]; + UINT32 it = data[3]; + + /* + // Check for bad vertices (Sega Rally 2) + if (((ix>>28)==7) || ((iy>>28)==7) || ((iz>>28)==7)) + { + //printf("%X ix=%08X, iy=%08X, iz=%08X\n", lutIdx, ix, iy, iz); + goto StopDecoding; + } + */ + + // Decode vertices + P.Vert[j].x = (GLfloat) (((INT32)ix)>>8) * vertexFactor; + P.Vert[j].y = (GLfloat) (((INT32)iy)>>8) * vertexFactor; + P.Vert[j].z = (GLfloat) (((INT32)iz)>>8) * vertexFactor; + P.Vert[j].n[0] = P.n[0]+(GLfloat)(INT8)(ix&0xFF); // vertex normals are offset from polygon normal + P.Vert[j].n[1] = P.n[1]+(GLfloat)(INT8)(iy&0xFF); + P.Vert[j].n[2] = P.n[2]+(GLfloat)(INT8)(iz&0xFF); + P.Vert[j].u = (GLfloat) ((UINT16)(it>>16)) * uvScale; // TO-DO: might these be signed? + P.Vert[j].v = (GLfloat) ((UINT16)(it&0xFFFF)) * uvScale; + data += 4; + + // Normalize the vertex normal + GLfloat mag = sqrt(P.Vert[j].n[0]*P.Vert[j].n[0]+P.Vert[j].n[1]*P.Vert[j].n[1]+P.Vert[j].n[2]*P.Vert[j].n[2]); + P.Vert[j].n[0] /= mag; + P.Vert[j].n[1] /= mag; + P.Vert[j].n[2] /= mag; + } + if (validPoly) { - // Copy current vertices into previous vertex array - for (i = 0; i < 4; i++) - Prev[i] = P.Vert[i]; - - // Copy this polygon into the model buffer - if (OKAY != InsertPolygon(Cache,&P)) - return NULL; - ++numPolys; + // Copy current vertices into previous vertex array + for (size_t i = 0; i < 4; i++) + Prev[i] = P.Vert[i]; + + // Copy this polygon into the model buffer + if (OKAY != InsertPolygon(Cache,&P)) + return NULL; + ++numPolys; } - } - - // Finish model and enter it into the LUT - EndModel(Cache,Model,lutIdx,texOffset); - return Model; + } + + // Finish model and enter it into the LUT + EndModel(Cache,Model,lutIdx,texOffset); + return Model; } @@ -921,144 +880,140 @@ if (lutIdx==0x8c0955) */ struct VBORef *CLegacy3D::LookUpModel(ModelCache *Cache, int lutIdx, UINT16 texOffset) { - 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 + 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 void CLegacy3D::ClearModelCache(ModelCache *Cache) { - Cache->vboCurOffset = 0; - for (int i = 0; i < 2; i++) - Cache->curVertIdx[i] = 0; - for (int i = 0; i < Cache->numModels; i++) - Cache->lut[Cache->Models[i].lutIdx] = -1; + Cache->vboCurOffset = 0; + for (size_t i = 0; i < 2; i++) + Cache->curVertIdx[i] = 0; + for (size_t i = 0; i < Cache->numModels; i++) + Cache->lut[Cache->Models[i].lutIdx] = -1; - Cache->numModels = 0; - ClearDisplayList(Cache); + Cache->numModels = 0; + ClearDisplayList(Cache); } bool CLegacy3D::CreateModelCache(ModelCache *Cache, unsigned vboMaxVerts, - unsigned localMaxVerts, unsigned maxNumModels, unsigned numLUTEntries, - unsigned displayListSize, bool isDynamic) + unsigned localMaxVerts, unsigned maxNumModels, unsigned numLUTEntries, + unsigned displayListSize, bool isDynamic) { - unsigned i; - int vboBytes, localBytes; - bool success; - - Cache->dynamic = isDynamic; - - /* - * VBO allocation: - * - * Progressively smaller VBOs, in steps of localMaxVerts are allocated - * until successful. If the size dips below localMaxVerts, localMaxVerts is - * attempted as the final try. - */ - - glGetError(); // clear error flag - glGenBuffers(1, &(Cache->vboID)); - glBindBuffer(GL_ARRAY_BUFFER, Cache->vboID); - - vboBytes = vboMaxVerts*VBO_VERTEX_SIZE*sizeof(GLfloat); - localBytes = localMaxVerts*VBO_VERTEX_SIZE*sizeof(GLfloat); - - // Try allocating until size is - success = false; - while (vboBytes >= localBytes) - { - glBufferData(GL_ARRAY_BUFFER, vboBytes, 0, isDynamic?GL_STREAM_DRAW:GL_STATIC_DRAW); - if (glGetError() == GL_NO_ERROR) - { - success = true; - break; - } - - vboBytes -= localBytes; - } - - if (!success) - { - // Last ditch attempt: try the local buffer size - vboBytes = localBytes; - glBufferData(GL_ARRAY_BUFFER, vboBytes, 0, isDynamic?GL_STREAM_DRAW:GL_STATIC_DRAW); - if (glGetError() != GL_NO_ERROR) - return ErrorLog("OpenGL was unable to provide a %s vertex buffer.", isDynamic?"dynamic":"static"); - } - - DebugLog("%s vertex buffer size: %1.2f MB", isDynamic?"Dynamic":"Static", (float)vboBytes/(float)0x100000); - InfoLog("%s vertex buffer size: %1.2f MB", isDynamic?"Dynamic":"Static", (float)vboBytes/(float)0x100000); - - // Set the VBO to the size we obtained - Cache->vboMaxOffset = vboBytes; - Cache->vboCurOffset = 0; - - // Attempt to allocate space for local VBO - for (i = 0; i < 2; i++) - { - Cache->verts[i] = new(std::nothrow) GLfloat[localMaxVerts*VBO_VERTEX_SIZE]; - Cache->curVertIdx[i] = 0; - } - Cache->maxVertIdx = localMaxVerts; - - // ... model array - Cache->Models = new(std::nothrow) VBORef[maxNumModels]; - Cache->maxModels = maxNumModels; - Cache->numModels = 0; - - // ... LUT - Cache->lut = new(std::nothrow) INT16[numLUTEntries]; - Cache->lutSize = numLUTEntries; - - // ... display list - Cache->List = new(std::nothrow) DisplayList[displayListSize]; - ClearDisplayList(Cache); - Cache->maxListSize = displayListSize; - - // Check if memory allocation succeeded - if ((Cache->verts[0]==NULL) || (Cache->verts[1]==NULL) || (Cache->Models==NULL) || (Cache->lut==NULL) || (Cache->List==NULL)) - { - DestroyModelCache(Cache); - return ErrorLog("Insufficient memory for model cache."); - } + Cache->dynamic = isDynamic; + + /* + * VBO allocation: + * + * Progressively smaller VBOs, in steps of localMaxVerts are allocated + * until successful. If the size dips below localMaxVerts, localMaxVerts is + * attempted as the final try. + */ + + glGetError(); // clear error flag + glGenBuffers(1, &(Cache->vboID)); + glBindBuffer(GL_ARRAY_BUFFER, Cache->vboID); + + size_t vboBytes = vboMaxVerts*VBO_VERTEX_SIZE*sizeof(GLfloat); + size_t localBytes = localMaxVerts*VBO_VERTEX_SIZE*sizeof(GLfloat); + + // Try allocating until size is + bool success = false; + while (vboBytes >= localBytes) + { + glBufferData(GL_ARRAY_BUFFER, vboBytes, 0, isDynamic?GL_STREAM_DRAW:GL_STATIC_DRAW); + if (glGetError() == GL_NO_ERROR) + { + success = true; + break; + } + + vboBytes -= localBytes; + } + + if (!success) + { + // Last ditch attempt: try the local buffer size + vboBytes = localBytes; + glBufferData(GL_ARRAY_BUFFER, vboBytes, 0, isDynamic?GL_STREAM_DRAW:GL_STATIC_DRAW); + if (glGetError() != GL_NO_ERROR) + return ErrorLog("OpenGL was unable to provide a %s vertex buffer.", isDynamic?"dynamic":"static"); + } + + DebugLog("%s vertex buffer size: %1.2f MB", isDynamic?"Dynamic":"Static", (float)vboBytes/(float)0x100000); + InfoLog("%s vertex buffer size: %1.2f MB", isDynamic?"Dynamic":"Static", (float)vboBytes/(float)0x100000); + + // Set the VBO to the size we obtained + Cache->vboMaxOffset = vboBytes; + Cache->vboCurOffset = 0; + + // Attempt to allocate space for local VBO + for (size_t i = 0; i < 2; i++) + { + Cache->verts[i] = new(std::nothrow) GLfloat[localMaxVerts*VBO_VERTEX_SIZE]; + Cache->curVertIdx[i] = 0; + } + Cache->maxVertIdx = localMaxVerts; + + // ... model array + Cache->Models = new(std::nothrow) VBORef[maxNumModels]; + Cache->maxModels = maxNumModels; + Cache->numModels = 0; + + // ... LUT + Cache->lut = new(std::nothrow) INT16[numLUTEntries]; + Cache->lutSize = numLUTEntries; + + // ... display list + Cache->List = new(std::nothrow) DisplayList[displayListSize]; + ClearDisplayList(Cache); + Cache->maxListSize = displayListSize; + + // Check if memory allocation succeeded + if ((Cache->verts[0]==NULL) || (Cache->verts[1]==NULL) || (Cache->Models==NULL) || (Cache->lut==NULL) || (Cache->List==NULL)) + { + DestroyModelCache(Cache); + return ErrorLog("Insufficient memory for model cache."); + } - // Clear LUT (MUST be done here because ClearModelCache() won't do it for dynamic models) - for (i = 0; i < numLUTEntries; i++) - Cache->lut[i] = -1; - - // All good! - return OKAY; + // Clear LUT (MUST be done here because ClearModelCache() won't do it for dynamic models) + for (size_t i = 0; i < numLUTEntries; i++) + Cache->lut[i] = -1; + + // All good! + return OKAY; } void CLegacy3D::DestroyModelCache(ModelCache *Cache) { - glDeleteBuffers(1, &(Cache->vboID)); + glDeleteBuffers(1, &(Cache->vboID)); - for (int i = 0; i < 2; i++) - { - if (Cache->verts[i] != NULL) - delete [] Cache->verts[i]; - } - if (Cache->Models != NULL) - delete [] Cache->Models; - if (Cache->lut != NULL) - delete [] Cache->lut; - if (Cache->List != NULL) - delete [] Cache->List; - - memset(Cache, 0, sizeof(ModelCache)); + for (size_t i = 0; i < 2; i++) + { + if (Cache->verts[i] != NULL) + delete [] Cache->verts[i]; + } + if (Cache->Models != NULL) + delete [] Cache->Models; + if (Cache->lut != NULL) + delete [] Cache->lut; + if (Cache->List != NULL) + delete [] Cache->List; + + memset(Cache, 0, sizeof(ModelCache)); } } // Legacy3D diff --git a/Src/Graphics/Legacy3D/TextureRefs.cpp b/Src/Graphics/Legacy3D/TextureRefs.cpp index 3f09c37..a5e96bd 100644 --- a/Src/Graphics/Legacy3D/TextureRefs.cpp +++ b/Src/Graphics/Legacy3D/TextureRefs.cpp @@ -206,7 +206,7 @@ bool CTextureRefs::UpdateHashCapacity(unsigned capacity) m_hashEntries = new(std::nothrow) HashEntry*[capacity]; if (!m_hashEntries) return false; - memset(m_hashEntries, NULL, capacity * sizeof(HashEntry*)); + memset(m_hashEntries, 0, capacity * sizeof(HashEntry*)); if (oldEntries) { // Redistribute entries into new entries array