Added a wide screen hack that can be enabled with the -'wide-screen' argument or WideScreen in the config file.

This commit is contained in:
Bart Trzynadlowski 2012-02-10 19:53:51 +00:00
parent 40a51287df
commit 1b8740165c
6 changed files with 155 additions and 68 deletions

View file

@ -22,15 +22,35 @@
/*
* Render2D.cpp
*
* To-Do List
* ----------
* - Fix color offsets: they should probably be applied to layers A/A' and B/B'
* rather than to the top and bottom surfaces (an artifact left over from
* when layer priorities were fixed as B/B' -> bottom, A/A' -> top). This can
* no longer be performed by the shaders, unfortunately, because of arbitrary
* layer priorities. Rather, three palettes should be maintained: master (the
* actual palette data), A, and B. Color offset writes should recompute
* these and the tile renderer should use either A or B palette (depending on
* the layer being drawn).
* - Is there a better way to handle the overscan regions in wide screen mode
* than using palette entry 0 as the fill color? Is clearing two thin
* viewports better than one big clear?
* - Layer priorities in Spikeout attract mode might not be totally correct.
* - Are v-scroll values 9 or 10 bits? (Does it matter?) Lost World seems to
* have some scrolling issues.
* - A proper shut-down function is needed! OpenGL might not be available when
* the destructor for this class is called.
*
* Implementation of the CRender2D class: OpenGL tile generator graphics.
*
* Tile Generator Hardware Overview
* --------------------------------
*
* Model 3's medium resolution tile generator hardware appears to be derived
* from the Model 2 and System 24 chipset. It consists of four 64x64 tile
* layers, comprised of 8x8 pixel tiles, with configurable priorities. There
* may be additional features but so far, no known Model 3 games use them.
* from the Model 2 and System 24 chipset, but is much simpler. It consists of
* four 64x64 tile layers, comprised of 8x8 pixel tiles, with configurable
* priorities. There may be additional features but so far, no known Model 3
* games use them.
*
* VRAM is comprised of 1 MB for tile data and an additional 128 KB for the
* palette. The four tilemap layers are referred to as: A (0), A' (1), B (2),
@ -65,8 +85,9 @@
* ???? ???? ???? ???? pqrs tuvw ???? ????
*
* Bits 'pqrs' control the color depth of layers B', B, A', and A,
* respectively, and 'tuvw' form a 4-bit priority code. The other bits are
* unused or unknown.
* respectively. If set, the layer's pattern data is encoded as 4 bits,
* otherwise the pixels are 8 bits. Bits 'tuvw' form a 4-bit priority code. The
* other bits are unused or unknown.
*
* The remaining registers are described where appropriate further below.
*
@ -257,17 +278,6 @@
* Where 'r', 'g', and 'b' appear to be signed 8-bit color offsets. Because
* they exceed the color resolution of the palette, they must be scaled
* appropriately.
*
* To-Do List
* ----------
* - Fix color offsets: they should probably be applied to layers A/A' and B/B'
* rather than to the top and bottom surfaces (an artifact left over from
* when layer priorities were fixed as B/B' -> bottom, A/A' -> top). This can
* no longer be performed by the shaders, unfortunately, because of arbitrary
* layer priorities.
* - Are v-scroll values 9 or 10 bits?
* - A proper shut-down function is needed! OpenGL might not be available when
* the destructor for this class is called.
*/
#include <string.h>
@ -358,8 +368,7 @@ void CRender2D::DrawTileLine8BitNoClip(UINT32 *buf, UINT16 tile, int tileLine)
* scrolling is applied here.
*
* Parametes:
* dest Destination of 512-pixel wide output buffer to draw
* to.
* dest Destination of 512-pixel output buffer to draw to.
* layerNum Layer number:
* 0 = Layer A (@ 0xF8000)
* 1 = Layer A' (@ 0xFA000)
@ -571,6 +580,13 @@ void CRender2D::DrawTilemaps(UINT32 *destBottom, UINT32 *destTop)
MixLine(destTop, lineBuffer[0], 0, y, false);
MixLine(destTop, lineBuffer[1], 1, y, false);
break;
case 0x9: // ? all layers on top but relative order unknown (Spikeout Final Edition, after first boss)
memset(destBottom, 0, 496*sizeof(UINT32)); //TODO: use glClear(GL_COLOR_BUFFER_BIT) if there is no bottom layer
MixLine(destTop, lineBuffer[2], 2, y, true);
MixLine(destTop, lineBuffer[3], 3, y, false);
MixLine(destTop, lineBuffer[1], 1, y, false);
MixLine(destTop, lineBuffer[0], 0, y, false);
break;
case 0xF: // all on top
memset(destBottom, 0, 496*sizeof(UINT32)); //TODO: use glClear(GL_COLOR_BUFFER_BIT) if there is no bottom layer
MixLine(destTop, lineBuffer[2], 2, y, true);
@ -606,6 +622,29 @@ void CRender2D::DrawTilemaps(UINT32 *destBottom, UINT32 *destTop)
// Draws a surface to the screen (0 is top and 1 is bottom)
void CRender2D::DisplaySurface(int surface, GLfloat z)
{
// If bottom surface and wide screen, clear overscan areas
if (surface && g_Config.wideScreen)
{
UINT32 c = pal[0]; // just use palette color 0 for now (not the best solution, it's usually black)
GLfloat r = (GLfloat)(c&0xFF) / 255.0f;
GLfloat g = (GLfloat)((c>>8)&0xFF) / 255.0f;
GLfloat b = (GLfloat)((c>>16)&0xFF) / 255.0f;
glClearColor(r, g, b, 0.0);
glViewport(0, 0, xOffs, totalYPixels);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(xOffs+xPixels, 0, totalXPixels, totalYPixels);
glClear(GL_COLOR_BUFFER_BIT);
}
// Set up the viewport and orthogonal projection
glViewport(xOffs, yOffs, xPixels, yPixels);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 1.0, 1.0, 0.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Draw the surface
glBindTexture(GL_TEXTURE_2D, texID[surface]);
glBegin(GL_QUADS);
glTexCoord2f(0.0f/512.0f, 0.0f); glVertex3f(0.0f, 0.0f, z);
@ -618,14 +657,6 @@ void CRender2D::DisplaySurface(int surface, GLfloat z)
// Set up viewport and OpenGL state for 2D rendering (sets up blending function but disables blending)
void CRender2D::Setup2D(void)
{
// Set up the viewport and orthogonal projection
glViewport(xOffs, yOffs, xPixels, yPixels);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 1.0, 1.0, 0.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Enable texture mapping and blending
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
@ -731,7 +762,7 @@ void CRender2D::AttachVRAM(const UINT8 *vramPtr)
#define OFFSET_BOTTOM_SURFACE (512*384*4) // 512*384*4
#define OFFSET_LINE_BUFFERS (2*512*384*4) // 4*512*4 (4 lines)
bool CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes)
bool CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes)
{
float memSizeMB = (float)MEMORY_POOL_SIZE/(float)0x100000;
@ -762,6 +793,8 @@ bool CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned
yPixels = yRes;
xOffs = xOffset;
yOffs = yOffset;
totalXPixels = totalXRes;
totalYPixels = totalYRes;
// Create textures
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

View file

@ -102,23 +102,26 @@ public:
void AttachVRAM(const UINT8 *vramPtr);
/*
* Init(xOffset, yOffset, xRes, yRes);
* Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes);
*
* One-time initialization of the context. Must be called before any other
* members (meaning it should be called even before being attached to any
* other objects that want to use it).
*
* Parameters:
* xOffset X offset within OpenGL display surface in pixels.
* xOffset X offset of the viewable area within OpenGL display
* surface, in pixels.
* yOffset Y offset.
* xRes Horizontal resolution of display surface in pixels.
* xRes Horizontal resolution of the viewable area.
* yRes Vertical resolution.
* totalXRes Horizontal resolution of the complete display area.
* totalYRes Vertical resolution.
*
* Returns:
* OKAY is successful, otherwise FAILED if a non-recoverable error
* occurred. Prints own error messages.
*/
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes);
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes);
/*
* CRender2D(void):
@ -153,6 +156,7 @@ private:
GLuint texID[2]; // IDs for the 2 layer textures (top and bottom)
unsigned xPixels, yPixels; // display surface resolution
unsigned xOffs, yOffs; // offset
unsigned totalXPixels, totalYPixels; // totay display surface resolution
// Shader programs and input data locations
GLuint shaderProgram; // shader program object

View file

@ -210,7 +210,20 @@ void CRender3D::DecodeTexture(int format, int x, int y, int width, int height)
i = 0;
switch (format)
{
default:
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++)
{
@ -262,7 +275,6 @@ void CRender3D::DecodeTexture(int format, int x, int y, int width, int height)
break;
case 4: // 8-bit, L4A4
for (yi = y; yi < (y+height); yi++)
@ -826,13 +838,26 @@ void CRender3D::RenderViewport(UINT32 addr, int pri)
// 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) && (vpY==0) && (vpWidth>=495) && (vpHeight >= 383))
{
// 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);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fovYDegrees,(GLfloat)vpWidth/(GLfloat)vpHeight,0.1f,1e5);
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
@ -1034,7 +1059,7 @@ void CRender3D::SetStep(int stepID)
DebugLog("Render3D set to Step %d.%d\n", (step>>4)&0xF, step&0xF);
}
bool CRender3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes)
bool CRender3D::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];
@ -1074,6 +1099,8 @@ bool CRender3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned
yRatio = (GLfloat) yRes / 384.0f;
xOffs = xOffset;
yOffs = yOffset;
totalXRes = totalXResParam;
totalYRes = totalYResParam;
// Load shaders
const char *vsFile = g_Config.vertexShaderFile.size() ? g_Config.vertexShaderFile.c_str() : NULL;

View file

@ -263,7 +263,7 @@ public:
void SetStep(int stepID);
/*
* Init(xOffset, yOffset, xRes, yRes):
* Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes):
*
* One-time initialization of the context. Must be called before any other
* members (meaning it should be called even before being attached to any
@ -272,18 +272,20 @@ public:
* External shader files are loaded according to configuration settings.
*
* Parameters:
* xOffset X offset of display surface in pixels (in case resolution
* is smaller than the true display surface).
* xOffset X offset of the viewable area within OpenGL display
* surface, in pixels.
* yOffset Y offset.
* xRes Horizontal resolution of display surface in pixels.
* xRes Horizontal resolution of the viewable area.
* yRes Vertical resolution.
* totalXRes Horizontal resolution of the complete display area.
* totalYRes Vertical resolution.
*
* Returns:
* OKAY is successful, otherwise FAILED if a non-recoverable error
* occurred. Any allocated memory will not be freed until the
* destructor is called. Prints own error messages.
*/
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes);
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes);
/*
* CRender3D(void):
@ -375,9 +377,10 @@ private:
GLfloat texOffsetXY[2]; // decoded X, Y offsets
UINT16 texOffset; // raw texture offset data as it appears in culling node
// Resolution scaling factors (to support resolutions higher than 496x384) and offsets
// Resolution and scaling factors (to support resolutions higher than 496x384) and offsets
GLfloat xRatio, yRatio;
unsigned xOffs, yOffs;
unsigned totalXRes, totalYRes;
// Texture ID for complete 2048x2048 texture map
GLuint texID;

View file

@ -121,6 +121,7 @@ bool ErrorLog(const char *fmt, ...)
*/
unsigned xOffset, yOffset; // offset of renderer output within OpenGL viewport
unsigned xRes, yRes; // renderer output resolution (can be smaller than GL viewport)
unsigned totalXRes, totalYRes; // total resolution (the whole GL viewport)
/*
* CreateGLScreen():
@ -128,10 +129,13 @@ unsigned xRes, yRes; // renderer output resolution (can be smaller than GL vi
* Creates an OpenGL display surface of the requested size. xOffset and yOffset
* are used to return a display surface offset (for OpenGL viewport commands)
* because the actual drawing area may need to be adjusted to preserve the
* Model 3 aspect ratio. The new resolution will be passed back as well.
* Model 3 aspect ratio. The new resolution will be passed back as well -- both
* the adjusted viewable area resolution and the total resolution.
*
* NOTE: keepAspectRatio should always be true. It has not yet been tested with
* the wide screen hack.
*/
static bool CreateGLScreen(const char *caption, unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *xResPtr, unsigned *yResPtr,
bool keepAspectRatio, bool fullScreen)
static bool CreateGLScreen(const char *caption, unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *xResPtr, unsigned *yResPtr, unsigned *totalXResPtr, unsigned *totalYResPtr, bool keepAspectRatio, bool fullScreen)
{
const SDL_VideoInfo *VideoInfo;
GLenum err;
@ -158,6 +162,8 @@ static bool CreateGLScreen(const char *caption, unsigned *xOffsetPtr, unsigned *
// What resolution did we actually get?
VideoInfo = SDL_GetVideoInfo();
*totalXResPtr = VideoInfo->current_w;
*totalYResPtr = VideoInfo->current_h;
// If required, fix the aspect ratio of the resolution that the user passed to match Model 3 ratio
xRes = (float) *xResPtr;
@ -207,7 +213,7 @@ static bool CreateGLScreen(const char *caption, unsigned *xOffsetPtr, unsigned *
gluPerspective(90.0,(GLfloat)xRes/(GLfloat)yRes,0.1,1e5);
glMatrixMode(GL_MODELVIEW);
// Clear the screen to ensure black border
// Clear both buffers to ensure a black border
for (int i = 0; i < 2; i++)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
@ -219,11 +225,14 @@ static bool CreateGLScreen(const char *caption, unsigned *xOffsetPtr, unsigned *
*yResPtr = (unsigned) yRes;
// Scissor box (to clip visible area)
if (!g_Config.wideScreen)
{
if (VideoInfo->current_w > *xResPtr || VideoInfo->current_h > *yResPtr)
{
glEnable(GL_SCISSOR_TEST);
glScissor(*xOffsetPtr, *yOffsetPtr, *xResPtr, *yResPtr);
}
}
return 0;
}
@ -239,11 +248,11 @@ static void PrintGLInfo(bool createScreen, bool infoLog, bool printExtensions)
const GLubyte *str;
char *strLocal;
GLint value;
unsigned xOffset, yOffset, xRes=496, yRes=384;
unsigned xOffset, yOffset, xRes=496, yRes=384, totalXRes, totalYRes;
if (createScreen)
{
if (OKAY != CreateGLScreen("Supermodel - Querying OpenGL Information...",&xOffset,&yOffset,&xRes,&yRes,false,false))
if (OKAY != CreateGLScreen("Supermodel - Querying OpenGL Information...",&xOffset,&yOffset,&xRes,&yRes,&totalXRes,&totalYRes,false,false))
{
ErrorLog("Unable to query OpenGL.\n");
return;
@ -339,7 +348,7 @@ static bool ConfigureInputs(CInputs *Inputs, bool configure)
{
// Open an SDL window
unsigned xOffset, yOffset, xRes=496, yRes=384;
if (OKAY != CreateGLScreen("Supermodel - Configuring Inputs...",&xOffset,&yOffset,&xRes,&yRes,false,false))
if (OKAY != CreateGLScreen("Supermodel - Configuring Inputs...",&xOffset,&yOffset,&xRes,&yRes,&totalXRes,&totalYRes,false,false))
return (bool) ErrorLog("Unable to start SDL to configure inputs.\n");
// Configure the inputs
@ -404,6 +413,8 @@ static void ApplySettings(CINIFile *INI, const char *section)
INI->Get(section, "YResolution", g_Config.yRes);
if (OKAY == INI->Get(section, "FullScreen", x))
g_Config.fullScreen = x ? true : false;
if (OKAY == INI->Get(section, "WideScreen", x))
g_Config.wideScreen = x ? true : false;
if (OKAY == INI->Get(section, "Throttle", x))
g_Config.throttle = x ? true : false;
if (OKAY == INI->Get(section, "ShowFrameRate", x))
@ -449,6 +460,7 @@ static void LogConfig(void)
InfoLog("\tXResolution = %d", g_Config.xRes);
InfoLog("\tYResolution = %d", g_Config.yRes);
InfoLog("\tFullScreen = %d", g_Config.fullScreen);
InfoLog("\tWideScreen = %d", g_Config.wideScreen);
InfoLog("\tThrottle = %d", g_Config.throttle);
InfoLog("\tShowFrameRate = %d", g_Config.showFPS);
#ifdef SUPERMODEL_DEBUGGER
@ -745,10 +757,10 @@ int Supermodel(const char *zipFile, CInputs *Inputs, CINIFile *CmdLine)
LoadNVRAM(Model3);
// Start up SDL and open a GL window
xRes = g_Config.xRes;
yRes = g_Config.yRes;
totalXRes = xRes = g_Config.xRes;
totalYRes = yRes = g_Config.yRes;
sprintf(baseTitleStr, "Supermodel - %s", Model3->GetGameInfo()->title);
if (OKAY != CreateGLScreen(baseTitleStr,&xOffset,&yOffset,&xRes,&yRes,true,g_Config.fullScreen))
if (OKAY != CreateGLScreen(baseTitleStr,&xOffset,&yOffset,&xRes,&yRes,&totalXRes,&totalYRes,true,g_Config.fullScreen))
return 1;
// Info log GL information and user options
@ -769,9 +781,9 @@ int Supermodel(const char *zipFile, CInputs *Inputs, CINIFile *CmdLine)
Model3->AttachInputs(Inputs);
// Initialize the renderer
if (OKAY != Render2D->Init(xOffset, yOffset, xRes, yRes))
if (OKAY != Render2D->Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes))
goto QuitError;
if (OKAY != Render3D->Init(xOffset, yOffset, xRes, yRes))
if (OKAY != Render3D->Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes))
goto QuitError;
Model3->AttachRenderers(Render2D,Render3D);
@ -1226,6 +1238,7 @@ static void Help(void)
puts("Video Options:");
puts(" -res=<x>,<y> Resolution");
puts(" -fullscreen Full screen mode");
puts(" -wide-screen Expand 3D field of view to screen width");
puts(" -no-throttle Disable 60 Hz frame rate lock");
puts(" -show-fps Display frame rate in window title bar");
puts(" -vert-shader=<file> Load 3D vertex shader from external file");
@ -1423,6 +1436,11 @@ int main(int argc, char **argv)
n = 1;
CmdLine.Set("Global", "FullScreen", n);
}
else if (!strcmp(argv[i],"-wide-screen"))
{
n = 1;
CmdLine.Set("Global", "WideScreen", n);
}
else if (!strcmp(argv[i],"-no-throttle"))
{
n = 0;

View file

@ -45,6 +45,7 @@ class COSDConfig
public:
unsigned xRes, yRes; // X and Y resolution, in pixels
bool fullScreen; // Full screen mode (if true)
bool wideScreen; // Wide screen hack
bool throttle; // 60 Hz frame limiting
bool showFPS; // Show frame rate
bool flipStereo; // Flip stereo channels
@ -107,6 +108,7 @@ public:
xRes = 496;
yRes = 384;
fullScreen = false;
wideScreen = false;
throttle = true;
showFPS = false;
flipStereo = false;