From 79d24d403f3757c1983eca6ae39546fbe71dd969 Mon Sep 17 00:00:00 2001 From: Bart Trzynadlowski Date: Tue, 14 Feb 2012 03:28:52 +0000 Subject: [PATCH] - NOTE: In this revision (possibly earlier), I've started to notice some intermittent geometry glitches in Spikeout and slowdown while playing certain characters. - Added multi-texture fragment shader to repo. - Added a multiTexture option (enabled by default) to use multi-texturing to decode textures. - Added some comments regarding timing to the PowerPC execution loop. --- Src/CPU/PowerPC/ppc603.c | 16 +- Src/Graphics/Render3D.cpp | 22 +- Src/Graphics/Render3D.h | 8 +- Src/Graphics/Shaders/Fragment_MultiSheet.glsl | 237 +++++++++++++++++ Src/Graphics/Shaders3D.h | 248 +++++++++++++++++- Src/OSD/SDL/Main.cpp | 17 +- 6 files changed, 533 insertions(+), 15 deletions(-) create mode 100644 Src/Graphics/Shaders/Fragment_MultiSheet.glsl diff --git a/Src/CPU/PowerPC/ppc603.c b/Src/CPU/PowerPC/ppc603.c index a2bbcc8..39c01e5 100644 --- a/Src/CPU/PowerPC/ppc603.c +++ b/Src/CPU/PowerPC/ppc603.c @@ -252,6 +252,7 @@ void ppc_reset(void) int ppc_execute(int cycles) { UINT32 opcode; + ppc_icount = cycles; ppc_tb_base_icount = cycles; ppc_dec_base_icount = cycles + ppc.dec_frac; @@ -290,8 +291,13 @@ int ppc_execute(int cycles) ppc.pc = ppc.npc; // Debug breakpoints - //if (ppc.pc == 0x9e4d4) - // printf("%X R0=%08X\n", ppc.pc, REG(0)); + /* + if (ppc.pc == 0x9d40) + { + printf("%X R3=%08X R4=%08X\n", ppc.pc, REG(3), REG(4)); + + } + */ opcode = *ppc.op++; // Supermodel byte reverses each aligned word (converting them to little endian) so they can be fetched directly ppc.npc = ppc.pc + 4; @@ -314,6 +320,9 @@ int ppc_execute(int cycles) } ppc_icount--; + + // Updating TB four times per core cycle fixes VF3 timing but breaks other games (Daytona 2 too fast, Spikeout has some geometry flickering) + //ppc.tb += 4; if(ppc_icount == ppc_dec_trigger_cycle) { @@ -332,6 +341,9 @@ int ppc_execute(int cycles) // update timebase // timebase is incremented once every four core clock cycles, so adjust the cycles accordingly + // NOTE: updating at the end of the time slice breaks things that try to wait on TBL. Performing + // the update inside the execution loop fixes VF3 and allows many decrementer patches to be + // removed but it adversely affects Spikeout and other games. ppc.tb += ((ppc_tb_base_icount - ppc_icount) / 4); // update decrementer diff --git a/Src/Graphics/Render3D.cpp b/Src/Graphics/Render3D.cpp index 84a98ab..729fc02 100644 --- a/Src/Graphics/Render3D.cpp +++ b/Src/Graphics/Render3D.cpp @@ -1110,17 +1110,25 @@ bool CRender3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned totalXRes = totalXResParam; totalYRes = totalYResParam; - // Load shaders - 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; - if (OKAY != LoadShaderProgram(&shaderProgram,&vertexShader,&fragmentShader,vsFile,fsFile,vertexShaderSource,fragmentShaderSource)) - return FAIL; - // Query max number of texture units supported by video card to use as upper limit GLint glMaxTexUnits; glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &glMaxTexUnits); unsigned maxTexUnits = max(1, min(g_Config.maxTexUnits, glMaxTexUnits)); + // Load shaders. Use multi-sheet shader if requested and possible. + 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 = fragmentShaderSingleSheetSource; // single texture shader + if (g_Config.multiTexture) + { + if (maxTexUnits >= 8) // can we use the multi-sheet shader? + fragmentShaderSource = fragmentShaderMultiSheetSource; + else + ErrorLog("Your system has too few texture units. Reverting to 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"); @@ -1157,7 +1165,7 @@ bool CRender3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned // Check located at least one uniform to bind to a texture unit if (numTexUnits == 0) - return ErrorLog("Could not locate any textureMap uniforms in shader script."); + return ErrorLog("Fragment shader must contain at least one 'textureMap' uniform."); InfoLog("Located and bound %u uniform(s) in GL shader script to %u texture unit(s).", numTexUnits, unitCount); // Now try creating texture sheets, one for each texture unit (memory permitting) diff --git a/Src/Graphics/Render3D.h b/Src/Graphics/Render3D.h index 20d5b60..f79160b 100644 --- a/Src/Graphics/Render3D.h +++ b/Src/Graphics/Render3D.h @@ -175,15 +175,17 @@ struct ModelCache class CRender3DConfig { public: - string vertexShaderFile; // path to vertex shader or "" to use internal shader - string fragmentShaderFile; // fragment shader - unsigned maxTexUnits; // maximum number of texture units to use (1-9) + string vertexShaderFile; // path to vertex shader or "" to use internal shader + string fragmentShaderFile; // fragment shader + unsigned maxTexUnits; // maximum number of texture units to use (1-9) + bool multiTexture; // if enabled and no external fragment shader, select internal shader w/ multiple texture sheet support // Defaults CRender3DConfig(void) { // strings will be clear to begin with maxTexUnits = 9; + multiTexture = true; } }; diff --git a/Src/Graphics/Shaders/Fragment_MultiSheet.glsl b/Src/Graphics/Shaders/Fragment_MultiSheet.glsl new file mode 100644 index 0000000..5b454d8 --- /dev/null +++ b/Src/Graphics/Shaders/Fragment_MultiSheet.glsl @@ -0,0 +1,237 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel is free software: you can redistribute it and/or modify it under + ** the terms of the GNU General Public License as published by the Free + ** Software Foundation, either version 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel is distributed in the hope that it will be useful, but WITHOUT + ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + ** more details. + ** + ** You should have received a copy of the GNU General Public License along + ** with Supermodel. If not, see . + **/ + +/* + * Fragment_MultiSheet.glsl + * + * Fragment shader for 3D rendering. Uses 8 texture sheets to decode the + * different possible formats. + */ + +#version 120 + +// Global uniforms +uniform sampler2D textureMap0; // complete texture map (fmt 0), 2048x2048 texels +uniform sampler2D textureMap1; // complete texture map (fmt 1), 2048x2048 texels +uniform sampler2D textureMap2; // complete texture map (fmt 2), 2048x2048 texels +uniform sampler2D textureMap3; // complete texture map (fmt 3), 2048x2048 texels +uniform sampler2D textureMap4; // complete texture map (fmt 4), 2048x2048 texels +uniform sampler2D textureMap5; // complete texture map (fmt 5), 2048x2048 texels +uniform sampler2D textureMap6; // complete texture map (fmt 6), 2048x2048 texels +uniform sampler2D textureMap7; // complete texture map (fmt 7), 2048x2048 texels +uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height) +uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit +uniform vec3 spotColor; // spotlight RGB color +uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0) + +// Inputs from vertex shader +varying vec4 fsSubTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels) +varying vec4 fsTexParams; // .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode +varying float fsTexFormat; // T1RGB5 contour texture (if > 0) +varying float fsTransLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque) +varying vec3 fsLightIntensity; // lighting intensity +varying float fsSpecularTerm; // specular highlight +varying float fsFogFactor; // fog factor +varying float fsViewZ; // Z distance to fragment from viewpoint at origin + +/* + * WrapTexelCoords(): + * + * Computes the normalized OpenGL S,T coordinates within the 2048x2048 texture + * sheet, taking into account wrapping behavior. + * + * Computing normalized OpenGL texture coordinates (0 to 1) within the + * Real3D texture sheet: + * + * If the texture is not mirrored, we simply have to clamp the + * coordinates to fit within the texture dimensions, add the texture + * X, Y position to select the appropriate one, and normalize by 2048 + * (the dimensions of the Real3D texture sheet). + * + * = [(u,v)%(w,h)+(x,y)]/(2048,2048) + * + * If mirroring is enabled, textures are mirrored every odd multiple of + * the original texture. To detect whether we are in an odd multiple, + * simply divide the coordinate by the texture dimension and check + * whether the result is odd. Then, clamp the coordinates as before but + * subtract from the last texel to mirror them: + * + * = [M*((w-1,h-1)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048) + * where M is 1.0 if the texture must be mirrored. + * + * As an optimization, this function computes TWO texture coordinates + * simultaneously. The first is texCoord.xy, the second is in .zw. The other + * parameters must have .xy = .zw. + */ +vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEnable) +{ + vec4 clampedCoord, mirror, glTexCoord; + + clampedCoord = mod(texCoord,texSize); // clamp coordinates to within texture size + mirror = mirrorEnable * mod(floor(texCoord/texSize),2.0); // whether this texel needs to be mirrored + + glTexCoord = ( mirror*(texSize-clampedCoord) + + (vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord + + texOffset + ) / 2048.0; + return glTexCoord; +} + +/* + * main(): + * + * Fragment shader entry point. + */ + +void main(void) +{ + vec4 uv_top, uv_bot, c[4]; + vec2 r; + vec4 fragColor; + vec2 ellipse; + vec3 lightIntensity; + float insideSpot; + int x; + + // Get polygon color for untextured polygons (textured polygons will overwrite) + if (fsTexParams.x < 0.5) + fragColor = gl_Color; + else + // Textured polygons: set fragment color to texel value + { + /* + * Bilinear Filtering + * + * In order to get this working on ATI, the number of operations is + * reduced by putting everything into vec4s. uv_top holds the UV + * coordinates for the top two texels (.xy=left, .zw=right) and uv_bot + * is for the lower two. + */ + + // Compute fractional blending factor, r, and lower left corner of texel 0 + uv_bot.xy = gl_TexCoord[0].st-vec2(0.5,0.5); // move into the lower left blending texel + r = uv_bot.xy-floor(uv_bot.xy); // fractional part + uv_bot.xy = floor(uv_bot.xy); // integral part + + // Compute texel coordinates + uv_bot.xy += vec2(0.5,0.5); // offset to center of pixel (should not be needed but it fixes a lot of glitches, esp. on Nvidia) + uv_bot.zw = uv_bot.xy + vec2(1.0,0.0); // compute coordinates of the other three neighbors + uv_top = uv_bot + vec4(0.0,1.0,0.0,1.0); + + // Compute the properly wrapped texel coordinates + uv_top = WrapTexelCoords(uv_top,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw)); + uv_bot = WrapTexelCoords(uv_bot,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw)); + + // Fetch the texels from the texture map that corresponds to the current texture format + if (fsTexFormat < 0.5f) { + c[0]=texture2D(textureMap0, uv_bot.xy); // bottom-left (base texel) + c[1]=texture2D(textureMap0, uv_bot.zw); // bottom-right + c[2]=texture2D(textureMap0, uv_top.xy); // top-left + c[3]=texture2D(textureMap0, uv_top.zw); // top-right + } else if (fsTexFormat < 1.5f) { + c[0]=texture2D(textureMap1, uv_bot.xy); // bottom-left (base texel) + c[1]=texture2D(textureMap1, uv_bot.zw); // bottom-right + c[2]=texture2D(textureMap1, uv_top.xy); // top-left + c[3]=texture2D(textureMap1, uv_top.zw); // top-right + } else if (fsTexFormat < 2.5f) { + c[0]=texture2D(textureMap2, uv_bot.xy); // bottom-left (base texel) + c[1]=texture2D(textureMap2, uv_bot.zw); // bottom-right + c[2]=texture2D(textureMap2, uv_top.xy); // top-left + c[3]=texture2D(textureMap2, uv_top.zw); // top-right + } else if (fsTexFormat < 3.5f) { + c[0]=texture2D(textureMap3, uv_bot.xy); // bottom-left (base texel) + c[1]=texture2D(textureMap3, uv_bot.zw); // bottom-right + c[2]=texture2D(textureMap3, uv_top.xy); // top-left + c[3]=texture2D(textureMap3, uv_top.zw); // top-right + } else if (fsTexFormat < 4.5f) { + c[0]=texture2D(textureMap4, uv_bot.xy); // bottom-left (base texel) + c[1]=texture2D(textureMap4, uv_bot.zw); // bottom-right + c[2]=texture2D(textureMap4, uv_top.xy); // top-left + c[3]=texture2D(textureMap4, uv_top.zw); // top-right + } else if (fsTexFormat < 5.5f) { + c[0]=texture2D(textureMap5, uv_bot.xy); // bottom-left (base texel) + c[1]=texture2D(textureMap5, uv_bot.zw); // bottom-right + c[2]=texture2D(textureMap5, uv_top.xy); // top-left + c[3]=texture2D(textureMap5, uv_top.zw); // top-right + } else if (fsTexFormat < 6.5f) { + c[0]=texture2D(textureMap6, uv_bot.xy); // bottom-left (base texel) + c[1]=texture2D(textureMap6, uv_bot.zw); // bottom-right + c[2]=texture2D(textureMap6, uv_top.xy); // top-left + c[3]=texture2D(textureMap6, uv_top.zw); // top-right + } else { + c[0]=texture2D(textureMap7, uv_bot.xy); // bottom-left (base texel) + c[1]=texture2D(textureMap7, uv_bot.zw); // bottom-right + c[2]=texture2D(textureMap7, uv_top.xy); // top-left + c[3]=texture2D(textureMap7, uv_top.zw); // top-right + } + + // Interpolate texels and blend result with material color to determine final (unlit) fragment color + // fragColor = (c[0]*(1.0-r.s)*(1.0-r.t) + c[1]*r.s*(1.0-r.t) + c[2]*(1.0-r.s)*r.t + c[3]*r.s*r.t); + // Faster method: + c[0] += (c[1]-c[0])*r.s; // 2 alu + c[2] += (c[3]-c[2])*r.s; // 2 alu + fragColor = c[0]+(c[2]-c[0])*r.t; // 2 alu + + /* + * T1RGB5: + * + * The transparency bit determines whether to discard pixels (if set). + * What is unknown is how this bit behaves when interpolated. OpenGL + * processes it as an alpha value, so it might concievably be blended + * with neighbors. Here, an arbitrary threshold is chosen. + * + * To-do: blending could probably enabled and this would work even + * better with a hard threshold. + * + * Countour processing also seems to be enabled for RGBA4 textures. + * When the alpha value is 0.0 (or close), pixels are discarded + * entirely. + */ + if (fsTexParams.y > 0.5) // contour processing enabled + { + if (fragColor.a < 0.01) // discard anything with alpha == 0 + discard; + } + + // If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency + if (fsTexFormat < 0.5) // contour (T1RGB5) texture map + fragColor.a = 1.0; + } + + // Compute spotlight and apply lighting + ellipse = (gl_FragCoord.xy-spotEllipse.xy)/spotEllipse.zw; + insideSpot = dot(ellipse,ellipse); + if ((insideSpot <= 1.0) && (fsViewZ>=spotRange.x) && (fsViewZ.\n" +" **/\n" +" \n" +"/*\n" +" * Fragment_MultiSheet.glsl\n" +" *\n" +" * Fragment shader for 3D rendering. Uses 8 texture sheets to decode the \n" +" * different possible formats.\n" +" */\n" +"\n" +"#version 120\n" +"\n" +"// Global uniforms\n" +"uniform sampler2D\ttextureMap0;\t\t// complete texture map (fmt 0), 2048x2048 texels\n" +"uniform sampler2D\ttextureMap1;\t\t// complete texture map (fmt 1), 2048x2048 texels\n" +"uniform sampler2D\ttextureMap2;\t\t// complete texture map (fmt 2), 2048x2048 texels\n" +"uniform sampler2D\ttextureMap3;\t\t// complete texture map (fmt 3), 2048x2048 texels\n" +"uniform sampler2D\ttextureMap4;\t\t// complete texture map (fmt 4), 2048x2048 texels\n" +"uniform sampler2D\ttextureMap5;\t\t// complete texture map (fmt 5), 2048x2048 texels\n" +"uniform sampler2D\ttextureMap6;\t\t// complete texture map (fmt 6), 2048x2048 texels\n" +"uniform sampler2D\ttextureMap7;\t\t// complete texture map (fmt 7), 2048x2048 texels\n" +"uniform vec4\t\tspotEllipse;\t\t// spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)\n" +"uniform vec2\t\tspotRange;\t\t\t// spotlight Z range: .x=start (viewspace coordinates), .y=limit\n" +"uniform vec3\t\tspotColor;\t\t\t// spotlight RGB color\n" +"uniform vec3\t\tlighting[2];\t\t// lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)\n" +"\n" +"// Inputs from vertex shader \n" +"varying vec4\t\tfsSubTexture;\t// .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)\n" +"varying vec4\t\tfsTexParams;\t// .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode\n" +"varying float\t\tfsTexFormat;\t// T1RGB5 contour texture (if > 0)\n" +"varying float\t\tfsTransLevel;\t// translucence level, 0.0 (transparent) to 1.0 (opaque)\n" +"varying vec3\t\tfsLightIntensity;\t// lighting intensity \n" +"varying float\t\tfsSpecularTerm;\t// specular highlight\n" +"varying float\t\tfsFogFactor;\t// fog factor\n" +"varying float\t\tfsViewZ;\t\t// Z distance to fragment from viewpoint at origin\n" +"\n" +"/*\n" +" * WrapTexelCoords():\n" +" *\n" +" * Computes the normalized OpenGL S,T coordinates within the 2048x2048 texture\n" +" * sheet, taking into account wrapping behavior.\n" +" *\n" +" * Computing normalized OpenGL texture coordinates (0 to 1) within the \n" +" * Real3D texture sheet:\n" +" *\n" +" * If the texture is not mirrored, we simply have to clamp the\n" +" * coordinates to fit within the texture dimensions, add the texture\n" +" * X, Y position to select the appropriate one, and normalize by 2048\n" +" * (the dimensions of the Real3D texture sheet).\n" +" *\n" +" *\t\t= [(u,v)%(w,h)+(x,y)]/(2048,2048)\n" +" *\n" +" * If mirroring is enabled, textures are mirrored every odd multiple of\n" +" * the original texture. To detect whether we are in an odd multiple, \n" +" * simply divide the coordinate by the texture dimension and check \n" +" * whether the result is odd. Then, clamp the coordinates as before but\n" +" * subtract from the last texel to mirror them:\n" +" *\n" +" * \t\t= [M*((w-1,h-1)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048)\n" +" *\t\twhere M is 1.0 if the texture must be mirrored.\n" +" *\n" +" * As an optimization, this function computes TWO texture coordinates\n" +" * simultaneously. The first is texCoord.xy, the second is in .zw. The other\n" +" * parameters must have .xy = .zw.\n" +" */\n" +"vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEnable)\n" +"{\n" +"\tvec4\tclampedCoord, mirror, glTexCoord;\n" +"\t\n" +"\tclampedCoord = mod(texCoord,texSize);\t\t\t\t\t\t// clamp coordinates to within texture size\n" +"\tmirror = mirrorEnable * mod(floor(texCoord/texSize),2.0);\t// whether this texel needs to be mirrored\n" +"\n" +"\tglTexCoord = (\tmirror*(texSize-clampedCoord) +\n" +"\t\t\t\t\t(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +\n" +"\t\t\t\t\ttexOffset\n" +"\t\t\t\t ) / 2048.0;\n" +"\treturn glTexCoord;\n" +"}\n" +"\n" +"/*\n" +" * main():\n" +" *\n" +" * Fragment shader entry point.\n" +" */\n" +"\n" +"void main(void)\n" +"{\t\n" +"\tvec4\tuv_top, uv_bot, c[4];\n" +"\tvec2\tr;\n" +"\tvec4\tfragColor;\n" +"\tvec2\tellipse;\n" +"\tvec3\tlightIntensity;\n" +"\tfloat\tinsideSpot;\n" +"\tint\t\tx;\n" +"\t\n" +"\t// Get polygon color for untextured polygons (textured polygons will overwrite)\n" +"\tif (fsTexParams.x < 0.5)\n" +"\t\tfragColor = gl_Color;\t\t\n" +"\telse\n" +"\t// Textured polygons: set fragment color to texel value\n" +"\t{\t\t\t\n" +"\t\t/*\n" +"\t\t * Bilinear Filtering\n" +"\t\t *\n" +"\t\t * In order to get this working on ATI, the number of operations is\n" +"\t\t * reduced by putting everything into vec4s. uv_top holds the UV \n" +"\t\t * coordinates for the top two texels (.xy=left, .zw=right) and uv_bot\n" +"\t\t * is for the lower two.\n" +"\t\t */\n" +"\n" +"\t\t// Compute fractional blending factor, r, and lower left corner of texel 0\n" +"\t\tuv_bot.xy = gl_TexCoord[0].st-vec2(0.5,0.5);\t// move into the lower left blending texel \n" +"\t\tr = uv_bot.xy-floor(uv_bot.xy);\t\t\t\t\t// fractional part\n" +"\t\tuv_bot.xy = floor(uv_bot.xy);\t\t\t\t\t// integral part\n" +"\t\t\n" +"\t\t// Compute texel coordinates\n" +"\t\tuv_bot.xy += vec2(0.5,0.5);\t// offset to center of pixel (should not be needed but it fixes a lot of glitches, esp. on Nvidia)\n" +"\t\tuv_bot.zw = uv_bot.xy + vec2(1.0,0.0);\t\t\t// compute coordinates of the other three neighbors\n" +"\t\tuv_top = uv_bot + vec4(0.0,1.0,0.0,1.0);\n" +"\n" +"\t\t// Compute the properly wrapped texel coordinates\n" +"\t\tuv_top = WrapTexelCoords(uv_top,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));\n" +"\t\tuv_bot = WrapTexelCoords(uv_bot,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));\n" +"\n" +"\t\t// Fetch the texels from the texture map that corresponds to the current texture format\n" +"\t\tif (fsTexFormat < 0.5f)\t{\n" +"\t\t\tc[0]=texture2D(textureMap0, uv_bot.xy); // bottom-left (base texel)\n" +"\t\t\tc[1]=texture2D(textureMap0, uv_bot.zw); // bottom-right\n" +"\t\t\tc[2]=texture2D(textureMap0, uv_top.xy); // top-left\n" +"\t\t\tc[3]=texture2D(textureMap0, uv_top.zw); // top-right\n" +"\t\t} else if (fsTexFormat < 1.5f) {\n" +" c[0]=texture2D(textureMap1, uv_bot.xy); // bottom-left (base texel)\n" +"\t\t\tc[1]=texture2D(textureMap1, uv_bot.zw); // bottom-right\n" +"\t\t\tc[2]=texture2D(textureMap1, uv_top.xy); // top-left\n" +"\t\t\tc[3]=texture2D(textureMap1, uv_top.zw); // top-right\n" +"\t\t} else if (fsTexFormat < 2.5f) {\n" +" c[0]=texture2D(textureMap2, uv_bot.xy); // bottom-left (base texel)\n" +"\t\t\tc[1]=texture2D(textureMap2, uv_bot.zw); // bottom-right\n" +"\t\t\tc[2]=texture2D(textureMap2, uv_top.xy); // top-left\n" +"\t\t\tc[3]=texture2D(textureMap2, uv_top.zw); // top-right\n" +"\t\t} else if (fsTexFormat < 3.5f) {\n" +" c[0]=texture2D(textureMap3, uv_bot.xy); // bottom-left (base texel)\n" +"\t\t\tc[1]=texture2D(textureMap3, uv_bot.zw); // bottom-right\n" +"\t\t\tc[2]=texture2D(textureMap3, uv_top.xy); // top-left\n" +"\t\t\tc[3]=texture2D(textureMap3, uv_top.zw); // top-right\n" +"\t\t} else if (fsTexFormat < 4.5f) {\n" +" c[0]=texture2D(textureMap4, uv_bot.xy); // bottom-left (base texel)\n" +"\t\t\tc[1]=texture2D(textureMap4, uv_bot.zw); // bottom-right\n" +"\t\t\tc[2]=texture2D(textureMap4, uv_top.xy); // top-left\n" +"\t\t\tc[3]=texture2D(textureMap4, uv_top.zw); // top-right\n" +"\t\t} else if (fsTexFormat < 5.5f) {\n" +" c[0]=texture2D(textureMap5, uv_bot.xy); // bottom-left (base texel)\n" +"\t\t\tc[1]=texture2D(textureMap5, uv_bot.zw); // bottom-right\n" +"\t\t\tc[2]=texture2D(textureMap5, uv_top.xy); // top-left\n" +"\t\t\tc[3]=texture2D(textureMap5, uv_top.zw); // top-right\n" +"\t\t} else if (fsTexFormat < 6.5f) {\n" +"\t\t\tc[0]=texture2D(textureMap6, uv_bot.xy); // bottom-left (base texel)\n" +"\t\t\tc[1]=texture2D(textureMap6, uv_bot.zw); // bottom-right\n" +"\t\t\tc[2]=texture2D(textureMap6, uv_top.xy); // top-left\n" +"\t\t\tc[3]=texture2D(textureMap6, uv_top.zw); // top-right\n" +"\t\t} else {\n" +" c[0]=texture2D(textureMap7, uv_bot.xy); // bottom-left (base texel)\n" +"\t\t\tc[1]=texture2D(textureMap7, uv_bot.zw); // bottom-right\n" +"\t\t\tc[2]=texture2D(textureMap7, uv_top.xy); // top-left\n" +"\t\t\tc[3]=texture2D(textureMap7, uv_top.zw); // top-right\n" +"\t\t} \n" +"\n" +"\t\t// Interpolate texels and blend result with material color to determine final (unlit) fragment color\n" +"\t\t// fragColor = (c[0]*(1.0-r.s)*(1.0-r.t) + c[1]*r.s*(1.0-r.t) + c[2]*(1.0-r.s)*r.t + c[3]*r.s*r.t);\n" +"\t\t// Faster method:\n" +"\t\tc[0] += (c[1]-c[0])*r.s;\t\t\t// 2 alu\n" +"\t\tc[2] += (c[3]-c[2])*r.s;\t\t\t// 2 alu\n" +"\t\tfragColor = c[0]+(c[2]-c[0])*r.t;\t// 2 alu\n" +"\t\n" +"\t\t/*\n" +"\t\t * T1RGB5:\n" +"\t\t *\n" +"\t\t * The transparency bit determines whether to discard pixels (if set).\n" +"\t\t * What is unknown is how this bit behaves when interpolated. OpenGL\n" +"\t\t * processes it as an alpha value, so it might concievably be blended\n" +"\t\t * with neighbors. Here, an arbitrary threshold is chosen.\n" +"\t\t *\n" +"\t\t * To-do: blending could probably enabled and this would work even\n" +"\t\t * better with a hard threshold.\n" +"\t\t *\n" +"\t\t * Countour processing also seems to be enabled for RGBA4 textures.\n" +"\t\t * When the alpha value is 0.0 (or close), pixels are discarded \n" +"\t\t * entirely.\n" +"\t\t */\n" +"\t\tif (fsTexParams.y > 0.5)\t// contour processing enabled\n" +"\t\t{\n" +"\t\t\tif (fragColor.a < 0.01)\t// discard anything with alpha == 0\n" +"\t\t\t\tdiscard;\n" +"\t\t}\n" +"\t\t\n" +"\t\t// If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency\n" +"\t\tif (fsTexFormat < 0.5)\t\t// contour (T1RGB5) texture map\n" +"\t\t\tfragColor.a = 1.0;\n" +"\t}\n" +"\n" +"\t// Compute spotlight and apply lighting\n" +"\tellipse = (gl_FragCoord.xy-spotEllipse.xy)/spotEllipse.zw;\n" +"\tinsideSpot = dot(ellipse,ellipse);\n" +"\tif ((insideSpot <= 1.0) && (fsViewZ>=spotRange.x) && (fsViewZGet(section, "FullScreen", x)) g_Config.fullScreen = x ? true : false; if (OKAY == INI->Get(section, "WideScreen", x)) - g_Config.wideScreen = x ? true : false; + g_Config.wideScreen = x ? true : false; + if (OKAY == INI->Get(section, "MultiTexture", x)) + g_Config.multiTexture = x ? true : false; if (OKAY == INI->Get(section, "Throttle", x)) g_Config.throttle = x ? true : false; if (OKAY == INI->Get(section, "ShowFrameRate", x)) @@ -460,6 +462,7 @@ static void LogConfig(void) InfoLog("\tYResolution = %d", g_Config.yRes); InfoLog("\tFullScreen = %d", g_Config.fullScreen); InfoLog("\tWideScreen = %d", g_Config.wideScreen); + InfoLog("\tMultiTexture = %d", g_Config.multiTexture); InfoLog("\tThrottle = %d", g_Config.throttle); InfoLog("\tShowFrameRate = %d", g_Config.showFPS); #ifdef SUPERMODEL_DEBUGGER @@ -1263,6 +1266,8 @@ static void Help(void) puts(" -window Windowed mode [Default]"); puts(" -fullscreen Full screen mode"); puts(" -wide-screen Expand 3D field of view to screen width"); + puts(" -multi-texture Use 8 texture maps for accurate decoding [Default]"); + puts(" -no-multi-texture Decode to a single texture map"); puts(" -no-throttle Disable 60 Hz frame rate lock"); puts(" -show-fps Display frame rate in window title bar"); puts(" -vert-shader= Load 3D vertex shader from external file"); @@ -1480,6 +1485,16 @@ int main(int argc, char **argv) n = 1; CmdLine.Set("Global", "WideScreen", n); } + else if (!strcmp(argv[i],"-multi-texture")) + { + n = 1; + CmdLine.Set("Global", "MultiTexture", n); + } + else if (!strcmp(argv[i],"-no-multi-texture")) + { + n = 0; + CmdLine.Set("Global", "MultiTexture", n); + } else if (!strcmp(argv[i],"-no-throttle")) { n = 0;