mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2025-02-16 17:35:39 +00:00
- 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.
This commit is contained in:
parent
30130f2bd5
commit
79d24d403f
|
@ -252,6 +252,7 @@ void ppc_reset(void)
|
||||||
int ppc_execute(int cycles)
|
int ppc_execute(int cycles)
|
||||||
{
|
{
|
||||||
UINT32 opcode;
|
UINT32 opcode;
|
||||||
|
|
||||||
ppc_icount = cycles;
|
ppc_icount = cycles;
|
||||||
ppc_tb_base_icount = cycles;
|
ppc_tb_base_icount = cycles;
|
||||||
ppc_dec_base_icount = cycles + ppc.dec_frac;
|
ppc_dec_base_icount = cycles + ppc.dec_frac;
|
||||||
|
@ -290,8 +291,13 @@ int ppc_execute(int cycles)
|
||||||
ppc.pc = ppc.npc;
|
ppc.pc = ppc.npc;
|
||||||
|
|
||||||
// Debug breakpoints
|
// 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
|
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;
|
ppc.npc = ppc.pc + 4;
|
||||||
|
@ -314,6 +320,9 @@ int ppc_execute(int cycles)
|
||||||
}
|
}
|
||||||
|
|
||||||
ppc_icount--;
|
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)
|
if(ppc_icount == ppc_dec_trigger_cycle)
|
||||||
{
|
{
|
||||||
|
@ -332,6 +341,9 @@ int ppc_execute(int cycles)
|
||||||
|
|
||||||
// update timebase
|
// update timebase
|
||||||
// timebase is incremented once every four core clock cycles, so adjust the cycles accordingly
|
// 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);
|
ppc.tb += ((ppc_tb_base_icount - ppc_icount) / 4);
|
||||||
|
|
||||||
// update decrementer
|
// update decrementer
|
||||||
|
|
|
@ -1110,17 +1110,25 @@ bool CRender3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned
|
||||||
totalXRes = totalXResParam;
|
totalXRes = totalXResParam;
|
||||||
totalYRes = totalYResParam;
|
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
|
// Query max number of texture units supported by video card to use as upper limit
|
||||||
GLint glMaxTexUnits;
|
GLint glMaxTexUnits;
|
||||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &glMaxTexUnits);
|
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &glMaxTexUnits);
|
||||||
unsigned maxTexUnits = max<int>(1, min<int>(g_Config.maxTexUnits, glMaxTexUnits));
|
unsigned maxTexUnits = max<int>(1, min<int>(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
|
// Try locating default "textureMap" uniform in shader program
|
||||||
glUseProgram(shaderProgram); // bind program
|
glUseProgram(shaderProgram); // bind program
|
||||||
textureMapLoc = glGetUniformLocation(shaderProgram, "textureMap");
|
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
|
// Check located at least one uniform to bind to a texture unit
|
||||||
if (numTexUnits == 0)
|
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);
|
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)
|
// Now try creating texture sheets, one for each texture unit (memory permitting)
|
||||||
|
|
|
@ -175,15 +175,17 @@ struct ModelCache
|
||||||
class CRender3DConfig
|
class CRender3DConfig
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
string vertexShaderFile; // path to vertex shader or "" to use internal shader
|
string vertexShaderFile; // path to vertex shader or "" to use internal shader
|
||||||
string fragmentShaderFile; // fragment shader
|
string fragmentShaderFile; // fragment shader
|
||||||
unsigned maxTexUnits; // maximum number of texture units to use (1-9)
|
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
|
// Defaults
|
||||||
CRender3DConfig(void)
|
CRender3DConfig(void)
|
||||||
{
|
{
|
||||||
// strings will be clear to begin with
|
// strings will be clear to begin with
|
||||||
maxTexUnits = 9;
|
maxTexUnits = 9;
|
||||||
|
multiTexture = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
237
Src/Graphics/Shaders/Fragment_MultiSheet.glsl
Normal file
237
Src/Graphics/Shaders/Fragment_MultiSheet.glsl
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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<spotRange.y))
|
||||||
|
lightIntensity = fsLightIntensity+(1.0-insideSpot)*spotColor;
|
||||||
|
else
|
||||||
|
lightIntensity = fsLightIntensity;
|
||||||
|
fragColor.rgb *= lightIntensity;
|
||||||
|
fragColor.rgb += vec3(fsSpecularTerm,fsSpecularTerm,fsSpecularTerm);
|
||||||
|
|
||||||
|
// Translucency (modulates existing alpha channel for RGBA4 texels)
|
||||||
|
fragColor.a *= fsTransLevel;
|
||||||
|
|
||||||
|
// Apply fog under the control of fog factor setting from polygon header
|
||||||
|
fragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor);
|
||||||
|
|
||||||
|
// Store final color
|
||||||
|
gl_FragColor = fragColor;
|
||||||
|
}
|
|
@ -212,8 +212,8 @@ static const char vertexShaderSource[] =
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Fragment shader
|
// Fragment shader (single texture sheet)
|
||||||
static const char fragmentShaderSource[] =
|
static const char fragmentShaderSingleSheetSource[] =
|
||||||
{
|
{
|
||||||
"/**\n"
|
"/**\n"
|
||||||
" ** Supermodel\n"
|
" ** Supermodel\n"
|
||||||
|
@ -409,4 +409,248 @@ static const char fragmentShaderSource[] =
|
||||||
"}\n"
|
"}\n"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Fragment shader (8 texture sheets)
|
||||||
|
static const char fragmentShaderMultiSheetSource[] =
|
||||||
|
{
|
||||||
|
"/**\n"
|
||||||
|
" ** Supermodel\n"
|
||||||
|
" ** A Sega Model 3 Arcade Emulator.\n"
|
||||||
|
" ** Copyright 2011 Bart Trzynadlowski, Nik Henson \n"
|
||||||
|
" **\n"
|
||||||
|
" ** This file is part of Supermodel.\n"
|
||||||
|
" **\n"
|
||||||
|
" ** Supermodel is free software: you can redistribute it and/or modify it under\n"
|
||||||
|
" ** the terms of the GNU General Public License as published by the Free \n"
|
||||||
|
" ** Software Foundation, either version 3 of the License, or (at your option)\n"
|
||||||
|
" ** any later version.\n"
|
||||||
|
" **\n"
|
||||||
|
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n"
|
||||||
|
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n"
|
||||||
|
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n"
|
||||||
|
" ** more details.\n"
|
||||||
|
" **\n"
|
||||||
|
" ** You should have received a copy of the GNU General Public License along\n"
|
||||||
|
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>.\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) && (fsViewZ<spotRange.y))\n"
|
||||||
|
"\t\tlightIntensity = fsLightIntensity+(1.0-insideSpot)*spotColor;\n"
|
||||||
|
"\telse\n"
|
||||||
|
"\t\tlightIntensity = fsLightIntensity;\n"
|
||||||
|
"\tfragColor.rgb *= lightIntensity;\n"
|
||||||
|
"\tfragColor.rgb += vec3(fsSpecularTerm,fsSpecularTerm,fsSpecularTerm);\n"
|
||||||
|
"\t\n"
|
||||||
|
"\t// Translucency (modulates existing alpha channel for RGBA4 texels)\n"
|
||||||
|
"\tfragColor.a *= fsTransLevel;\n"
|
||||||
|
"\n"
|
||||||
|
"\t// Apply fog under the control of fog factor setting from polygon header\n"
|
||||||
|
"\tfragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor);\n"
|
||||||
|
"\n"
|
||||||
|
"\t// Store final color\n"
|
||||||
|
"\tgl_FragColor = fragColor;\n"
|
||||||
|
"}\n"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // INCLUDED_SHADERS3D_H
|
#endif // INCLUDED_SHADERS3D_H
|
||||||
|
|
|
@ -413,7 +413,9 @@ static void ApplySettings(CINIFile *INI, const char *section)
|
||||||
if (OKAY == INI->Get(section, "FullScreen", x))
|
if (OKAY == INI->Get(section, "FullScreen", x))
|
||||||
g_Config.fullScreen = x ? true : false;
|
g_Config.fullScreen = x ? true : false;
|
||||||
if (OKAY == INI->Get(section, "WideScreen", x))
|
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))
|
if (OKAY == INI->Get(section, "Throttle", x))
|
||||||
g_Config.throttle = x ? true : false;
|
g_Config.throttle = x ? true : false;
|
||||||
if (OKAY == INI->Get(section, "ShowFrameRate", x))
|
if (OKAY == INI->Get(section, "ShowFrameRate", x))
|
||||||
|
@ -460,6 +462,7 @@ static void LogConfig(void)
|
||||||
InfoLog("\tYResolution = %d", g_Config.yRes);
|
InfoLog("\tYResolution = %d", g_Config.yRes);
|
||||||
InfoLog("\tFullScreen = %d", g_Config.fullScreen);
|
InfoLog("\tFullScreen = %d", g_Config.fullScreen);
|
||||||
InfoLog("\tWideScreen = %d", g_Config.wideScreen);
|
InfoLog("\tWideScreen = %d", g_Config.wideScreen);
|
||||||
|
InfoLog("\tMultiTexture = %d", g_Config.multiTexture);
|
||||||
InfoLog("\tThrottle = %d", g_Config.throttle);
|
InfoLog("\tThrottle = %d", g_Config.throttle);
|
||||||
InfoLog("\tShowFrameRate = %d", g_Config.showFPS);
|
InfoLog("\tShowFrameRate = %d", g_Config.showFPS);
|
||||||
#ifdef SUPERMODEL_DEBUGGER
|
#ifdef SUPERMODEL_DEBUGGER
|
||||||
|
@ -1263,6 +1266,8 @@ static void Help(void)
|
||||||
puts(" -window Windowed mode [Default]");
|
puts(" -window Windowed mode [Default]");
|
||||||
puts(" -fullscreen Full screen mode");
|
puts(" -fullscreen Full screen mode");
|
||||||
puts(" -wide-screen Expand 3D field of view to screen width");
|
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(" -no-throttle Disable 60 Hz frame rate lock");
|
||||||
puts(" -show-fps Display frame rate in window title bar");
|
puts(" -show-fps Display frame rate in window title bar");
|
||||||
puts(" -vert-shader=<file> Load 3D vertex shader from external file");
|
puts(" -vert-shader=<file> Load 3D vertex shader from external file");
|
||||||
|
@ -1480,6 +1485,16 @@ int main(int argc, char **argv)
|
||||||
n = 1;
|
n = 1;
|
||||||
CmdLine.Set("Global", "WideScreen", n);
|
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"))
|
else if (!strcmp(argv[i],"-no-throttle"))
|
||||||
{
|
{
|
||||||
n = 0;
|
n = 0;
|
||||||
|
|
Loading…
Reference in a new issue