2009-04-22 20:04:28 +02:00

413 lines
11 KiB
C

/*
Copyright (C) 1997-2007 ZSNES Team ( zsKnight, _Demo_, pagefault, Nach )
http://www.zsnes.com
http://sourceforge.net/projects/zsnes
https://zsnes.bountysource.com
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "../cfg.h"
#include "../gblhdr.h"
#include "../asm_call.h"
#define BYTE unsigned char
#define WORD unsigned short
#define DWORD unsigned long
//C++ style code in C
#define bool unsigned char
#define true 1
#define false 0
// FUNCTIONS
void hq2x_16b(void);
// VIDEO VARIABLES
extern SDL_Surface *surface;
extern int SurfaceX, SurfaceY;
extern int SurfaceLocking;
extern DWORD BitDepth;
// OPENGL VARIABLES
static unsigned short *glvidbuffer = 0;
static GLuint gltextures[4];
static int gltexture256, gltexture512;
static int glfilters = GL_NEAREST;
static int glscanready = 0;
extern Uint8 GUIOn2;
extern unsigned int vidbuffer;
extern unsigned char curblank;
extern BYTE GUIRESIZE[];
void gl_clearwin();
void UpdateVFrame();
void gl_scanlines();
bool OGLModeCheck();
int gl_start(int width, int height, int req_depth, int FullScreen)
{
Uint32 flags = SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_HWPALETTE | SDL_OPENGL;
int i;
flags |= (GUIRESIZE[cvidmode] ? SDL_RESIZABLE : 0);
flags |= (FullScreen ? SDL_FULLSCREEN : 0);
SurfaceX = width; SurfaceY = height;
surface = SDL_SetVideoMode(SurfaceX, SurfaceY, req_depth, flags);
if (surface == NULL)
{
fprintf(stderr, "Could not set %dx%d-GL video mode.\n",SurfaceX, SurfaceY);
return false;
}
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
#if (SDL_MAJOR_VERSION > 1) || ((SDL_MINOR_VERSION > 2) || ((SDL_MINOR_VERSION == 2) && (SDL_PATCHLEVEL >= 10)))
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
#endif
if (!glvidbuffer)
{
glvidbuffer = (unsigned short *) malloc(512 * 512 * sizeof(short));
}
gl_clearwin();
SDL_WarpMouse(SurfaceX / 4, SurfaceY / 4);
// Grab mouse in fullscreen mode
FullScreen ? SDL_WM_GrabInput(SDL_GRAB_ON) :
SDL_WM_GrabInput(SDL_GRAB_OFF);
SDL_WM_SetCaption("ZSNES", "ZSNES");
SDL_ShowCursor(0);
/* Setup some GL stuff */
glEnable(GL_TEXTURE_1D);
glEnable(GL_TEXTURE_2D);
glViewport(0, 0, SurfaceX, SurfaceY);
/*
* gltextures[0]: 2D texture, 256x224
* gltextures[1]: 2D texture, 512x224
* gltextures[3]: 1D texture, 256 lines of alternating alpha
*/
glGenTextures(4, gltextures);
for (i = 0; i < 3; i++) {
glBindTexture(GL_TEXTURE_2D, gltextures[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glfilters);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glfilters);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
}
if (scanlines) gl_scanlines();
return true;
}
void gl_end()
{
if (glvidbuffer)
{
glDeleteTextures(4, gltextures);
free(glvidbuffer);
glvidbuffer = 0;
}
}
extern DWORD AddEndBytes;
extern DWORD NumBytesPerLine;
extern unsigned char *WinVidMemStart;
extern unsigned char NGNoTransp;
void copy640x480x16bwin(void);
extern unsigned char SpecialLine[224]; /* 0 if lo-res, > 0 if hi-res */
void gl_clearwin()
{
glClear(GL_COLOR_BUFFER_BIT);
memset(glvidbuffer, 0, 512 * 448 * 2);
}
/* gl_drawspan:
* Puts a quad on the screen for hires/lores portions, starting at line start,
* and ending at line end..
* Builds the 256x256/512x256 textures if gltexture256 or gltexture512 == 0
*/
static void gl_drawspan(int hires, int start, int end)
{
int i, j;
switch (hires)
{
case 0:
break;
case 3:
case 7:
hires = 2;
break;
default:
hires = 1;
break;
}
if (hires)
{
if (hires != gltexture512)
{
unsigned short *vbuf1 = &((unsigned short *) vidbuffer)[16];
unsigned short *vbuf2 = &((unsigned short *) vidbuffer)[75036 * 2 + 16];
unsigned short *vbuf = &glvidbuffer[0];
if (hires>1) // mode 7
{
for (j = 224; j--;)
{
for (i = 256; i--;)
*vbuf++ = *vbuf1++;
for (i = 256; i--;)
*vbuf++ = *vbuf2++;
vbuf1 += 32;
vbuf2 += 32;
}
glBindTexture(GL_TEXTURE_2D, gltextures[1]);
glTexImage2D(GL_TEXTURE_2D, 0, 3, 256, 512, 0,
GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
glvidbuffer);
gltexture512 = 2;
}
else
{
for (j = 224; j--;)
{
for (i = 256; i--;)
{
*vbuf++ = *vbuf1++;
*vbuf++ = *vbuf2++;
}
vbuf1 += 32;
vbuf2 += 32; // skip the two 16-pixel-wide columns
}
glBindTexture(GL_TEXTURE_2D, gltextures[1]);
glTexImage2D(GL_TEXTURE_2D, 0, 3, 512, 256, 0,
GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
glvidbuffer);
gltexture512 = 1;
}
}
glBindTexture(GL_TEXTURE_2D, gltextures[1]);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, (224.0 / 256.0) * (start / 224.0));
glVertex2f(-1.0f, (112 - start) / 112.0);
glTexCoord2f(1.0f, (224.0 / 256.0) * (start / 224.0));
glVertex2f(1.0f, (112 - start) / 112.0);
glTexCoord2f(1.0f, (224.0 / 256.0) * (end / 224.0));
glVertex2f(1.0f, (112 - end) / 112.0);
glTexCoord2f(0.0f, (224.0 / 256.0) * (end / 224.0));
glVertex2f(-1.0f, (112 - end) / 112.0);
glEnd();
}
else
{
glBindTexture(GL_TEXTURE_2D, gltextures[0]);
if (!gltexture256)
{
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 16);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 288);
glTexImage2D(GL_TEXTURE_2D, 0, 3, 256, 256, 0,
GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
((unsigned short *) vidbuffer) + 288);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
gltexture256 = 1;
}
glBegin(GL_QUADS);
glTexCoord2f(0.0f, (224.0 / 256.0) * (start / 224.0));
glVertex2f(-1.0f, (112 - start) / 112.0);
glTexCoord2f(1.0f, (224.0 / 256.0) * (start / 224.0));
glVertex2f(1.0f, (112 - start) / 112.0);
glTexCoord2f(1.0f, (224.0 / 256.0) * (end / 224.0));
glVertex2f(1.0f, (112 - end) / 112.0);
glTexCoord2f(0.0f, (224.0 / 256.0) * (end / 224.0));
glVertex2f(-1.0f, (112 - end) / 112.0);
glEnd();
}
}
void gl_drawwin()
{
int i;
NGNoTransp = 0; // Set this value to 1 within the appropriate
// video mode if you want to add a custom
// transparency routine or hardware
// transparency. This only works if
// the value of newengen is equal to 1.
// (see ProcessTransparencies in newgfx16.asm
// for ZSNES' current transparency code)
UpdateVFrame();
if (curblank || !OGLModeCheck())
return;
if (BilinearFilter)
{
glfilters = GL_LINEAR;
if (GUIOn2 && !FilteredGUI)
glfilters = GL_NEAREST;
}
else
{
glfilters = GL_NEAREST;
}
if (SurfaceX >= 512 && (hqFilter || En2xSaI))
{
AddEndBytes = 0;
NumBytesPerLine = 1024;
WinVidMemStart = (void *) glvidbuffer;
if (hqFilter) hq2x_16b();
else asm_call(copy640x480x16bwin);
/* Display 1 512x448 quad for the 512x448 buffer */
glBindTexture(GL_TEXTURE_2D, gltextures[1]);
glTexEnvi(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_DECAL);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0,
GL_RGB, GL_UNSIGNED_SHORT_5_6_5, glvidbuffer);
glDisable (GL_DEPTH_TEST);
glDisable (GL_LIGHTING);
glDisable (GL_BLEND);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(1.0f, 1.0f, -1.0f);
glTexCoord2f(1.0f, 448.0f / 512.0f);
glVertex3f(1.0f, -1.0f, -1.0f);
glTexCoord2f(0.0f, 448.0f / 512.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
glEnd();
}
else
{
/*
* This code splits the hires/lores portions up, and draws
* them with gl_drawspan
*/
int lasthires, lasthires_line = 0;
gltexture256 = gltexture512 = 0;
lasthires = SpecialLine[1];
for (i = 0; i < 224; i++)
{
if (SpecialLine[i + 1])
{
if (lasthires)
continue;
gl_drawspan(lasthires, lasthires_line, i);
lasthires = SpecialLine[i + 1];
lasthires_line = i;
}
else
{
if (!lasthires)
continue;
gl_drawspan(lasthires, lasthires_line, i);
lasthires = SpecialLine[i + 1];
lasthires_line = i;
}
}
if (i - lasthires_line > 1)
gl_drawspan(lasthires, lasthires_line, i);
/*
* This is here rather than right outside this if because the
* GUI doesn't allow scanlines to be selected while filters are
* on.. There is no technical reason they can't be on while
* filters are on, however. Feel free to change the GUI, and
* move this outside the if (En2xSaI) {}, if you do.
*/
if (scanlines)
{
glDisable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
if (scanlines != glscanready) gl_scanlines();
glBlendFunc(GL_DST_COLOR, GL_ZERO);
glBindTexture(GL_TEXTURE_1D, gltextures[3]);
glBegin(GL_QUADS);
for (i = 0; i < SurfaceY; i += 256)
{
glTexCoord1f(0.0f);
glVertex3f(-1.0f, (SurfaceY - i * 2.0) / SurfaceY, -1.0f);
glTexCoord1f(0.0f);
glVertex3f(1.0f, (SurfaceY - i * 2.0) / SurfaceY, -1.0f);
glTexCoord1f(1.0f);
glVertex3f(1.0f, (SurfaceY - (i + 256) * 2.0) / SurfaceY, -1.0f);
glTexCoord1f(1.0f);
glVertex3f(-1.0f, (SurfaceY - (i + 256) * 2.0) / SurfaceY, -1.0f);
}
glEnd();
glDisable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
}
}
SDL_GL_SwapBuffers();
}
void gl_scanlines(void)
{
GLubyte scanbuffer[256][4];
int i, j = scanlines==1 ? 0 : (scanlines==2 ? 192 : 128);
for (i = 0; i < 256; i += 2)
{
scanbuffer[i][0] = scanbuffer[i][1] = scanbuffer[i][2] = j;
scanbuffer[i][3] = 0xFF;
scanbuffer[i+1][0] = scanbuffer[i+1][1] = scanbuffer[i+1][2] = 0xFF;
scanbuffer[i+1][3] = 0xFF;
}
glBindTexture(GL_TEXTURE_1D, gltextures[3]);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA,
GL_UNSIGNED_BYTE, scanbuffer);
glscanready = scanlines;
}