mupen64plus-oldsvn/glN64/OpenGL.cpp

1239 lines
45 KiB
C++

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <png.h>
#include <SDL.h>
#define GL_GLEXT_PROTOTYPES
#include <SDL_opengl.h>
#ifndef min
#define min(a,b) ((a) < (b) ? (a) : (b))
#endif
#ifndef max
#define max(a,b) ((a) > (b) ? (a) : (b))
#endif
#define timeGetTime() time(NULL)
#include "../main/winlnxdefs.h"
#include "glN64.h"
#include "OpenGL.h"
#include "Types.h"
#include "N64.h"
#include "gSP.h"
#include "gDP.h"
#include "Textures.h"
#include "Combiner.h"
#include "VI.h"
#ifndef GL_BGR
#define GL_BGR GL_BGR_EXT
#endif
GLInfo OGL;
#ifndef __LINUX__
// NV_register_combiners functions
PFNGLCOMBINERPARAMETERFVNVPROC glCombinerParameterfvNV;
PFNGLCOMBINERPARAMETERFNVPROC glCombinerParameterfNV;
PFNGLCOMBINERPARAMETERIVNVPROC glCombinerParameterivNV;
PFNGLCOMBINERPARAMETERINVPROC glCombinerParameteriNV;
PFNGLCOMBINERINPUTNVPROC glCombinerInputNV;
PFNGLCOMBINEROUTPUTNVPROC glCombinerOutputNV;
PFNGLFINALCOMBINERINPUTNVPROC glFinalCombinerInputNV;
PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC glGetCombinerInputParameterfvNV;
PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC glGetCombinerInputParameterivNV;
PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC glGetCombinerOutputParameterfvNV;
PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC glGetCombinerOutputParameterivNV;
PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC glGetFinalCombinerInputParameterfvNV;
PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC glGetFinalCombinerInputParameterivNV;
// ARB_multitexture functions
PFNGLACTIVETEXTUREARBPROC glActiveTextureARB;
PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB;
PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2fARB;
// EXT_fog_coord functions
PFNGLFOGCOORDFEXTPROC glFogCoordfEXT;
PFNGLFOGCOORDFVEXTPROC glFogCoordfvEXT;
PFNGLFOGCOORDDEXTPROC glFogCoorddEXT;
PFNGLFOGCOORDDVEXTPROC glFogCoorddvEXT;
PFNGLFOGCOORDPOINTEREXTPROC glFogCoordPointerEXT;
// EXT_secondary_color functions
PFNGLSECONDARYCOLOR3BEXTPROC glSecondaryColor3bEXT;
PFNGLSECONDARYCOLOR3BVEXTPROC glSecondaryColor3bvEXT;
PFNGLSECONDARYCOLOR3DEXTPROC glSecondaryColor3dEXT;
PFNGLSECONDARYCOLOR3DVEXTPROC glSecondaryColor3dvEXT;
PFNGLSECONDARYCOLOR3FEXTPROC glSecondaryColor3fEXT;
PFNGLSECONDARYCOLOR3FVEXTPROC glSecondaryColor3fvEXT;
PFNGLSECONDARYCOLOR3IEXTPROC glSecondaryColor3iEXT;
PFNGLSECONDARYCOLOR3IVEXTPROC glSecondaryColor3ivEXT;
PFNGLSECONDARYCOLOR3SEXTPROC glSecondaryColor3sEXT;
PFNGLSECONDARYCOLOR3SVEXTPROC glSecondaryColor3svEXT;
PFNGLSECONDARYCOLOR3UBEXTPROC glSecondaryColor3ubEXT;
PFNGLSECONDARYCOLOR3UBVEXTPROC glSecondaryColor3ubvEXT;
PFNGLSECONDARYCOLOR3UIEXTPROC glSecondaryColor3uiEXT;
PFNGLSECONDARYCOLOR3UIVEXTPROC glSecondaryColor3uivEXT;
PFNGLSECONDARYCOLOR3USEXTPROC glSecondaryColor3usEXT;
PFNGLSECONDARYCOLOR3USVEXTPROC glSecondaryColor3usvEXT;
PFNGLSECONDARYCOLORPOINTEREXTPROC glSecondaryColorPointerEXT;
#endif // !__LINUX__
BOOL isExtensionSupported( const char *extension )
{
const GLubyte *extensions = NULL;
const GLubyte *start;
GLubyte *where, *terminator;
where = (GLubyte *) strchr(extension, ' ');
if (where || *extension == '\0')
return 0;
extensions = glGetString(GL_EXTENSIONS);
start = extensions;
for (;;)
{
where = (GLubyte *) strstr((const char *) start, extension);
if (!where)
break;
terminator = where + strlen(extension);
if (where == start || *(where - 1) == ' ')
if (*terminator == ' ' || *terminator == '\0')
return TRUE;
start = terminator;
}
return FALSE;
}
void OGL_InitExtensions()
{
if ((OGL.NV_register_combiners = isExtensionSupported( "GL_NV_register_combiners" )))
{
#ifndef __LINUX__
glCombinerParameterfvNV = (PFNGLCOMBINERPARAMETERFVNVPROC)wglGetProcAddress( "glCombinerParameterfvNV" );
glCombinerParameterfNV = (PFNGLCOMBINERPARAMETERFNVPROC)wglGetProcAddress( "glCombinerParameterfNV" );
glCombinerParameterivNV = (PFNGLCOMBINERPARAMETERIVNVPROC)wglGetProcAddress( "glCombinerParameterivNV" );
glCombinerParameteriNV = (PFNGLCOMBINERPARAMETERINVPROC)wglGetProcAddress( "glCombinerParameteriNV" );
glCombinerInputNV = (PFNGLCOMBINERINPUTNVPROC)wglGetProcAddress( "glCombinerInputNV" );
glCombinerOutputNV = (PFNGLCOMBINEROUTPUTNVPROC)wglGetProcAddress( "glCombinerOutputNV" );
glFinalCombinerInputNV = (PFNGLFINALCOMBINERINPUTNVPROC)wglGetProcAddress( "glFinalCombinerInputNV" );
glGetCombinerInputParameterfvNV = (PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC)wglGetProcAddress( "glGetCombinerInputParameterfvNV" );
glGetCombinerInputParameterivNV = (PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC)wglGetProcAddress( "glGetCombinerInputParameterivNV" );
glGetCombinerOutputParameterfvNV = (PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC)wglGetProcAddress( "glGetCombinerOutputParameterfvNV" );
glGetCombinerOutputParameterivNV = (PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC)wglGetProcAddress( "glGetCombinerOutputParameterivNV" );
glGetFinalCombinerInputParameterfvNV = (PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC)wglGetProcAddress( "glGetFinalCombinerInputParameterfvNV" );
glGetFinalCombinerInputParameterivNV = (PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC)wglGetProcAddress( "glGetFinalCombinerInputParameterivNV" );
#endif // !__LINUX__
glGetIntegerv( GL_MAX_GENERAL_COMBINERS_NV, &OGL.maxGeneralCombiners );
}
if ((OGL.ARB_multitexture = isExtensionSupported( "GL_ARB_multitexture" )))
{
#ifndef __LINUX__
glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC)wglGetProcAddress( "glActiveTextureARB" );
glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC)wglGetProcAddress( "glClientActiveTextureARB" );
glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC)wglGetProcAddress( "glMultiTexCoord2fARB" );
#endif // !__LINUX__
glGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &OGL.maxTextureUnits );
OGL.maxTextureUnits = min( 8, OGL.maxTextureUnits ); // The plugin only supports 8, and 4 is really enough
}
if ((OGL.EXT_fog_coord = isExtensionSupported( "GL_EXT_fog_coord" )))
{
#ifndef __LINUX__
glFogCoordfEXT = (PFNGLFOGCOORDFEXTPROC)wglGetProcAddress( "glFogCoordfEXT" );
glFogCoordfvEXT = (PFNGLFOGCOORDFVEXTPROC)wglGetProcAddress( "glFogCoordfvEXT" );
glFogCoorddEXT = (PFNGLFOGCOORDDEXTPROC)wglGetProcAddress( "glFogCoorddEXT" );
glFogCoorddvEXT = (PFNGLFOGCOORDDVEXTPROC)wglGetProcAddress( "glFogCoorddvEXT" );
glFogCoordPointerEXT = (PFNGLFOGCOORDPOINTEREXTPROC)wglGetProcAddress( "glFogCoordPointerEXT" );
#endif // !__LINUX__
}
if ((OGL.EXT_secondary_color = isExtensionSupported( "GL_EXT_secondary_color" )))
{
#ifndef __LINUX__
glSecondaryColor3bEXT = (PFNGLSECONDARYCOLOR3BEXTPROC)wglGetProcAddress( "glSecondaryColor3bEXT" );
glSecondaryColor3bvEXT = (PFNGLSECONDARYCOLOR3BVEXTPROC)wglGetProcAddress( "glSecondaryColor3bvEXT" );
glSecondaryColor3dEXT = (PFNGLSECONDARYCOLOR3DEXTPROC)wglGetProcAddress( "glSecondaryColor3dEXT" );
glSecondaryColor3dvEXT = (PFNGLSECONDARYCOLOR3DVEXTPROC)wglGetProcAddress( "glSecondaryColor3dvEXT" );
glSecondaryColor3fEXT = (PFNGLSECONDARYCOLOR3FEXTPROC)wglGetProcAddress( "glSecondaryColor3fEXT" );
glSecondaryColor3fvEXT = (PFNGLSECONDARYCOLOR3FVEXTPROC)wglGetProcAddress( "glSecondaryColor3fvEXT" );
glSecondaryColor3iEXT = (PFNGLSECONDARYCOLOR3IEXTPROC)wglGetProcAddress( "glSecondaryColor3iEXT" );
glSecondaryColor3ivEXT = (PFNGLSECONDARYCOLOR3IVEXTPROC)wglGetProcAddress( "glSecondaryColor3ivEXT" );
glSecondaryColor3sEXT = (PFNGLSECONDARYCOLOR3SEXTPROC)wglGetProcAddress( "glSecondaryColor3sEXT" );
glSecondaryColor3svEXT = (PFNGLSECONDARYCOLOR3SVEXTPROC)wglGetProcAddress( "glSecondaryColor3svEXT" );
glSecondaryColor3ubEXT = (PFNGLSECONDARYCOLOR3UBEXTPROC)wglGetProcAddress( "glSecondaryColor3ubEXT" );
glSecondaryColor3ubvEXT = (PFNGLSECONDARYCOLOR3UBVEXTPROC)wglGetProcAddress( "glSecondaryColor3ubvEXT" );
glSecondaryColor3uiEXT = (PFNGLSECONDARYCOLOR3UIEXTPROC)wglGetProcAddress( "glSecondaryColor3uiEXT" );
glSecondaryColor3uivEXT = (PFNGLSECONDARYCOLOR3UIVEXTPROC)wglGetProcAddress( "glSecondaryColor3uivEXT" );
glSecondaryColor3usEXT = (PFNGLSECONDARYCOLOR3USEXTPROC)wglGetProcAddress( "glSecondaryColor3usEXT" );
glSecondaryColor3usvEXT = (PFNGLSECONDARYCOLOR3USVEXTPROC)wglGetProcAddress( "glSecondaryColor3usvEXT" );
glSecondaryColorPointerEXT = (PFNGLSECONDARYCOLORPOINTEREXTPROC)wglGetProcAddress( "glSecondaryColorPointerEXT" );
#endif // !__LINUX__
}
OGL.ARB_texture_env_combine = isExtensionSupported( "GL_ARB_texture_env_combine" );
OGL.ARB_texture_env_crossbar = isExtensionSupported( "GL_ARB_texture_env_crossbar" );
OGL.EXT_texture_env_combine = isExtensionSupported( "GL_EXT_texture_env_combine" );
OGL.ATI_texture_env_combine3 = isExtensionSupported( "GL_ATI_texture_env_combine3" );
OGL.ATIX_texture_env_route = isExtensionSupported( "GL_ATIX_texture_env_route" );
OGL.NV_texture_env_combine4 = isExtensionSupported( "GL_NV_texture_env_combine4" );;
}
void OGL_InitStates()
{
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glVertexPointer( 4, GL_FLOAT, sizeof( GLVertex ), &OGL.vertices[0].x );
glEnableClientState( GL_VERTEX_ARRAY );
glColorPointer( 4, GL_FLOAT, sizeof( GLVertex ), &OGL.vertices[0].color.r );
glEnableClientState( GL_COLOR_ARRAY );
if (OGL.EXT_secondary_color)
{
glSecondaryColorPointerEXT( 3, GL_FLOAT, sizeof( GLVertex ), &OGL.vertices[0].secondaryColor.r );
glEnableClientState( GL_SECONDARY_COLOR_ARRAY_EXT );
}
if (OGL.ARB_multitexture)
{
glClientActiveTextureARB( GL_TEXTURE0_ARB );
glTexCoordPointer( 2, GL_FLOAT, sizeof( GLVertex ), &OGL.vertices[0].s0 );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glClientActiveTextureARB( GL_TEXTURE1_ARB );
glTexCoordPointer( 2, GL_FLOAT, sizeof( GLVertex ), &OGL.vertices[0].s1 );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
}
else
{
glTexCoordPointer( 2, GL_FLOAT, sizeof( GLVertex ), &OGL.vertices[0].s0 );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
}
if (OGL.EXT_fog_coord)
{
glFogi( GL_FOG_COORDINATE_SOURCE_EXT, GL_FOG_COORDINATE_EXT );
glFogi( GL_FOG_MODE, GL_LINEAR );
glFogf( GL_FOG_START, 0.0f );
glFogf( GL_FOG_END, 255.0f );
glFogCoordPointerEXT( GL_FLOAT, sizeof( GLVertex ), &OGL.vertices[0].fog );
glEnableClientState( GL_FOG_COORDINATE_ARRAY_EXT );
}
glPolygonOffset( -3.0f, -3.0f );
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
glClear( GL_COLOR_BUFFER_BIT );
srand( timeGetTime() );
for (int i = 0; i < 32; i++)
{
for (int j = 0; j < 8; j++)
for (int k = 0; k < 128; k++)
OGL.stipplePattern[i][j][k] =((i > (rand() >> 10)) << 7) |
((i > (rand() >> 10)) << 6) |
((i > (rand() >> 10)) << 5) |
((i > (rand() >> 10)) << 4) |
((i > (rand() >> 10)) << 3) |
((i > (rand() >> 10)) << 2) |
((i > (rand() >> 10)) << 1) |
((i > (rand() >> 10)) << 0);
}
#ifndef __LINUX__
SwapBuffers( wglGetCurrentDC() );
#else
OGL_SwapBuffers();
#endif
}
void OGL_UpdateScale()
{
OGL.scaleX = (float)OGL.width / (float)VI.width;
OGL.scaleY = (float)OGL.height / (float)VI.height;
}
void OGL_ResizeWindow()
{
#ifndef __LINUX__
RECT windowRect, statusRect, toolRect;
if (OGL.fullscreen)
{
OGL.width = OGL.fullscreenWidth;
OGL.height = OGL.fullscreenHeight;
OGL.heightOffset = 0;
SetWindowPos( hWnd, NULL, 0, 0, OGL.width, OGL.height, SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW );
}
else
{
OGL.width = OGL.windowedWidth;
OGL.height = OGL.windowedHeight;
GetClientRect( hWnd, &windowRect );
GetWindowRect( hStatusBar, &statusRect );
if (hToolBar)
GetWindowRect( hToolBar, &toolRect );
else
toolRect.bottom = toolRect.top = 0;
OGL.heightOffset = (statusRect.bottom - statusRect.top);
windowRect.right = windowRect.left + OGL.windowedWidth - 1;
windowRect.bottom = windowRect.top + OGL.windowedHeight - 1 + OGL.heightOffset;
AdjustWindowRect( &windowRect, GetWindowLong( hWnd, GWL_STYLE ), GetMenu( hWnd ) != NULL );
SetWindowPos( hWnd, NULL, 0, 0, windowRect.right - windowRect.left + 1,
windowRect.bottom - windowRect.top + 1 + toolRect.bottom - toolRect.top + 1, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE );
}
#else // !__LINUX__
#endif // __LINUX__
}
bool OGL_Start()
{
#ifndef __LINUX__
int pixelFormat;
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1, // version number
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
32, // color depth
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buffer
0, 0, 0, 0, // accum bits ignored
32, // z-buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
if ((OGL.hDC = GetDC( hWnd )) == NULL)
{
MessageBox( hWnd, "Error while getting a device context!", pluginName, MB_ICONERROR | MB_OK );
return FALSE;
}
if ((pixelFormat = ChoosePixelFormat( OGL.hDC, &pfd )) == 0)
{
MessageBox( hWnd, "Unable to find a suitable pixel format!", pluginName, MB_ICONERROR | MB_OK );
OGL_Stop();
return FALSE;
}
if ((SetPixelFormat( OGL.hDC, pixelFormat, &pfd )) == FALSE)
{
MessageBox( hWnd, "Error while setting pixel format!", pluginName, MB_ICONERROR | MB_OK );
OGL_Stop();
return FALSE;
}
if ((OGL.hRC = wglCreateContext( OGL.hDC )) == NULL)
{
MessageBox( hWnd, "Error while creating OpenGL context!", pluginName, MB_ICONERROR | MB_OK );
OGL_Stop();
return FALSE;
}
if ((wglMakeCurrent( OGL.hDC, OGL.hRC )) == FALSE)
{
MessageBox( hWnd, "Error while making OpenGL context current!", pluginName, MB_ICONERROR | MB_OK );
OGL_Stop();
return FALSE;
}
#else // !__LINUX__
// init sdl & gl
const SDL_VideoInfo *videoInfo;
Uint32 videoFlags = 0;
if (OGL.fullscreen)
{
OGL.width = OGL.fullscreenWidth;
OGL.height = OGL.fullscreenHeight;
}
else
{
OGL.width = OGL.windowedWidth;
OGL.height = OGL.windowedHeight;
}
/* Initialize SDL */
printf( "[glN64]: (II) Initializing SDL video subsystem...\n" );
if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1)
{
printf( "[glN64]: (EE) Error initializing SDL video subsystem: %s\n", SDL_GetError() );
return FALSE;
}
/* Video Info */
printf( "[glN64]: (II) Getting video info...\n" );
if (!(videoInfo = SDL_GetVideoInfo()))
{
printf( "[glN64]: (EE) Video query failed: %s\n", SDL_GetError() );
SDL_QuitSubSystem( SDL_INIT_VIDEO );
return FALSE;
}
/* Set the video mode */
videoFlags |= SDL_OPENGL | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE;
if (videoInfo->hw_available)
videoFlags |= SDL_HWSURFACE;
else
videoFlags |= SDL_SWSURFACE;
if (videoInfo->blit_hw)
videoFlags |= SDL_HWACCEL;
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
/* SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );*/
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 ); // 32 bit z-buffer
#if !defined(SDL_PRE_1_2_11)
SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 1); // only swap buffers on vertical sync
#endif
printf( "[glN64]: (II) Setting video mode %dx%d...\n", (int)OGL.width, (int)OGL.height );
if (!(OGL.hScreen = SDL_SetVideoMode( OGL.width, OGL.height, 0, videoFlags )))
{
printf( "[glN64]: (EE) Error setting videomode %dx%d: %s\n", (int)OGL.width, (int)OGL.height, SDL_GetError() );
SDL_QuitSubSystem( SDL_INIT_VIDEO );
return FALSE;
}
SDL_WM_SetCaption( pluginName, pluginName );
#endif // __LINUX__
OGL_InitExtensions();
OGL_InitStates();
TextureCache_Init();
FrameBuffer_Init();
Combiner_Init();
gSP.changed = gDP.changed = 0xFFFFFFFF;
OGL_UpdateScale();
return TRUE;
}
void OGL_Stop()
{
Combiner_Destroy();
FrameBuffer_Destroy();
TextureCache_Destroy();
#ifndef __LINUX__
wglMakeCurrent( NULL, NULL );
if (OGL.hRC)
{
wglDeleteContext( OGL.hRC );
OGL.hRC = NULL;
}
if (OGL.hDC)
{
ReleaseDC( hWnd, OGL.hDC );
OGL.hDC = NULL;
}
#else // !__LINUX__
SDL_QuitSubSystem( SDL_INIT_VIDEO );
OGL.hScreen = NULL;
#endif // __LINUX__
}
void OGL_UpdateCullFace()
{
if (gSP.geometryMode & G_CULL_BOTH)
{
glEnable( GL_CULL_FACE );
if (gSP.geometryMode & G_CULL_BACK)
glCullFace( GL_BACK );
else
glCullFace( GL_FRONT );
}
else
glDisable( GL_CULL_FACE );
}
void OGL_UpdateViewport()
{
glViewport( (int)(gSP.viewport.x * OGL.scaleX), (int)((VI.height - (gSP.viewport.y + gSP.viewport.height)) * OGL.scaleY + OGL.heightOffset),
(int)(gSP.viewport.width * OGL.scaleX), (int)(gSP.viewport.height * OGL.scaleY) );
glDepthRange( 0.0f, 1.0f );//gSP.viewport.nearz, gSP.viewport.farz );
}
void OGL_UpdateDepthUpdate()
{
if (gDP.otherMode.depthUpdate)
glDepthMask( TRUE );
else
glDepthMask( FALSE );
}
void OGL_UpdateStates()
{
if (gSP.changed & CHANGED_GEOMETRYMODE)
{
OGL_UpdateCullFace();
if ((gSP.geometryMode & G_FOG) && OGL.EXT_fog_coord && OGL.fog)
glEnable( GL_FOG );
else
glDisable( GL_FOG );
gSP.changed &= ~CHANGED_GEOMETRYMODE;
}
if (gSP.geometryMode & G_ZBUFFER)
glEnable( GL_DEPTH_TEST );
else
glDisable( GL_DEPTH_TEST );
if (gDP.changed & CHANGED_RENDERMODE)
{
if (gDP.otherMode.depthCompare)
glDepthFunc( GL_LEQUAL );
else
glDepthFunc( GL_ALWAYS );
OGL_UpdateDepthUpdate();
if (gDP.otherMode.depthMode == ZMODE_DEC)
glEnable( GL_POLYGON_OFFSET_FILL );
else
{
// glPolygonOffset( -3.0f, -3.0f );
glDisable( GL_POLYGON_OFFSET_FILL );
}
}
if ((gDP.changed & CHANGED_ALPHACOMPARE) || (gDP.changed & CHANGED_RENDERMODE))
{
// Enable alpha test for threshold mode
if ((gDP.otherMode.alphaCompare == G_AC_THRESHOLD) && !(gDP.otherMode.alphaCvgSel))
{
glEnable( GL_ALPHA_TEST );
glAlphaFunc( (gDP.blendColor.a > 0.0f) ? GL_GEQUAL : GL_GREATER, gDP.blendColor.a );
}
// Used in TEX_EDGE and similar render modes
else if (gDP.otherMode.cvgXAlpha)
{
glEnable( GL_ALPHA_TEST );
// Arbitrary number -- gives nice results though
glAlphaFunc( GL_GEQUAL, 0.5f );
}
else
glDisable( GL_ALPHA_TEST );
if (OGL.usePolygonStipple && (gDP.otherMode.alphaCompare == G_AC_DITHER) && !(gDP.otherMode.alphaCvgSel))
glEnable( GL_POLYGON_STIPPLE );
else
glDisable( GL_POLYGON_STIPPLE );
}
if (gDP.changed & CHANGED_SCISSOR)
{
glScissor( (int)(gDP.scissor.ulx * OGL.scaleX), (int)((VI.height - gDP.scissor.lry) * OGL.scaleY + OGL.heightOffset),
(int)((gDP.scissor.lrx - gDP.scissor.ulx) * OGL.scaleX), (int)((gDP.scissor.lry - gDP.scissor.uly) * OGL.scaleY) );
}
if (gSP.changed & CHANGED_VIEWPORT)
{
OGL_UpdateViewport();
}
if ((gDP.changed & CHANGED_COMBINE) || (gDP.changed & CHANGED_CYCLETYPE))
{
if (gDP.otherMode.cycleType == G_CYC_COPY)
Combiner_SetCombine( EncodeCombineMode( 0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0 ) );
else if (gDP.otherMode.cycleType == G_CYC_FILL)
Combiner_SetCombine( EncodeCombineMode( 0, 0, 0, SHADE, 0, 0, 0, 1, 0, 0, 0, SHADE, 0, 0, 0, 1 ) );
else
Combiner_SetCombine( gDP.combine.mux );
}
if (gDP.changed & CHANGED_COMBINE_COLORS)
{
Combiner_UpdateCombineColors();
}
if ((gSP.changed & CHANGED_TEXTURE) || (gDP.changed & CHANGED_TILE) || (gDP.changed & CHANGED_TMEM))
{
Combiner_BeginTextureUpdate();
if (combiner.usesT0)
{
TextureCache_Update( 0 );
gSP.changed &= ~CHANGED_TEXTURE;
gDP.changed &= ~CHANGED_TILE;
gDP.changed &= ~CHANGED_TMEM;
}
else
{
TextureCache_ActivateDummy( 0 );
}
if (combiner.usesT1)
{
TextureCache_Update( 1 );
gSP.changed &= ~CHANGED_TEXTURE;
gDP.changed &= ~CHANGED_TILE;
gDP.changed &= ~CHANGED_TMEM;
}
else
{
TextureCache_ActivateDummy( 1 );
}
Combiner_EndTextureUpdate();
}
if ((gDP.changed & CHANGED_FOGCOLOR) && OGL.fog)
glFogfv( GL_FOG_COLOR, &gDP.fogColor.r );
if ((gDP.changed & CHANGED_RENDERMODE) || (gDP.changed & CHANGED_CYCLETYPE))
{
if ((gDP.otherMode.forceBlender) &&
(gDP.otherMode.cycleType != G_CYC_COPY) &&
(gDP.otherMode.cycleType != G_CYC_FILL) &&
!(gDP.otherMode.alphaCvgSel))
{
glEnable( GL_BLEND );
switch (gDP.otherMode.l >> 16)
{
case 0x0448: // Add
case 0x055A:
glBlendFunc( GL_ONE, GL_ONE );
break;
case 0x0C08: // 1080 Sky
case 0x0F0A: // Used LOTS of places
glBlendFunc( GL_ONE, GL_ZERO );
break;
case 0xC810: // Blends fog
case 0xC811: // Blends fog
case 0x0C18: // Standard interpolated blend
case 0x0C19: // Used for antialiasing
case 0x0050: // Standard interpolated blend
case 0x0055: // Used for antialiasing
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
break;
case 0x0FA5: // Seems to be doing just blend color - maybe combiner can be used for this?
case 0x5055: // Used in Paper Mario intro, I'm not sure if this is right...
glBlendFunc( GL_ZERO, GL_ONE );
break;
default:
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
break;
}
}
else
glDisable( GL_BLEND );
if (gDP.otherMode.cycleType == G_CYC_FILL)
{
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glEnable( GL_BLEND );
}
}
gDP.changed &= CHANGED_TILE | CHANGED_TMEM;
gSP.changed &= CHANGED_TEXTURE | CHANGED_MATRIX;
}
void OGL_AddTriangle( SPVertex *vertices, int v0, int v1, int v2 )
{
int v[] = { v0, v1, v2 };
if (gSP.changed || gDP.changed)
OGL_UpdateStates();
// Playing around with lod fraction junk...
// float ds = max( max( fabs( vertices[v0].s - vertices[v1].s ), fabs( vertices[v0].s - vertices[v2].s ) ), fabs( vertices[v1].s - vertices[v2].s ) ) * cache.current[0]->shiftScaleS * gSP.texture.scales;
// float dx = max( max( fabs( vertices[v0].x / vertices[v0].w - vertices[v1].x / vertices[v1].w ), fabs( vertices[v0].x / vertices[v0].w - vertices[v2].x / vertices[v2].w ) ), fabs( vertices[v1].x / vertices[v1].w - vertices[v2].x / vertices[v2].w ) ) * gSP.viewport.vscale[0];
// float lod = ds / dx;
// float lod_fraction = min( 1.0f, max( 0.0f, lod - 1.0f ) / max( 1.0f, gSP.texture.level ) );
for (int i = 0; i < 3; i++)
{
OGL.vertices[OGL.numVertices].x = vertices[v[i]].x;
OGL.vertices[OGL.numVertices].y = vertices[v[i]].y;
OGL.vertices[OGL.numVertices].z = gDP.otherMode.depthSource == G_ZS_PRIM ? gDP.primDepth.z * vertices[v[i]].w : vertices[v[i]].z;
OGL.vertices[OGL.numVertices].w = vertices[v[i]].w;
OGL.vertices[OGL.numVertices].color.r = vertices[v[i]].r;
OGL.vertices[OGL.numVertices].color.g = vertices[v[i]].g;
OGL.vertices[OGL.numVertices].color.b = vertices[v[i]].b;
OGL.vertices[OGL.numVertices].color.a = vertices[v[i]].a;
SetConstant( OGL.vertices[OGL.numVertices].color, combiner.vertex.color, combiner.vertex.alpha );
//SetConstant( OGL.vertices[OGL.numVertices].secondaryColor, combiner.vertex.secondaryColor, ONE );
if (OGL.EXT_secondary_color)
{
OGL.vertices[OGL.numVertices].secondaryColor.r = 0.0f;//lod_fraction; //vertices[v[i]].r;
OGL.vertices[OGL.numVertices].secondaryColor.g = 0.0f;//lod_fraction; //vertices[v[i]].g;
OGL.vertices[OGL.numVertices].secondaryColor.b = 0.0f;//lod_fraction; //vertices[v[i]].b;
OGL.vertices[OGL.numVertices].secondaryColor.a = 1.0f;
SetConstant( OGL.vertices[OGL.numVertices].secondaryColor, combiner.vertex.secondaryColor, ONE );
}
if ((gSP.geometryMode & G_FOG) && OGL.EXT_fog_coord && OGL.fog)
{
if (vertices[v[i]].z < -vertices[v[i]].w)
OGL.vertices[OGL.numVertices].fog = max( 0.0f, -(float)gSP.fog.multiplier + (float)gSP.fog.offset );
else
OGL.vertices[OGL.numVertices].fog = max( 0.0f, vertices[v[i]].z / vertices[v[i]].w * (float)gSP.fog.multiplier + (float)gSP.fog.offset );
}
if (combiner.usesT0)
{
if (cache.current[0]->frameBufferTexture)
{
/* OGL.vertices[OGL.numVertices].s0 = (cache.current[0]->offsetS + (vertices[v[i]].s * cache.current[0]->shiftScaleS * gSP.texture.scales - gSP.textureTile[0]->fuls)) * cache.current[0]->scaleS;
OGL.vertices[OGL.numVertices].t0 = (cache.current[0]->offsetT - (vertices[v[i]].t * cache.current[0]->shiftScaleT * gSP.texture.scalet - gSP.textureTile[0]->fult)) * cache.current[0]->scaleT;*/
if (gSP.textureTile[0]->masks)
OGL.vertices[OGL.numVertices].s0 = (cache.current[0]->offsetS + (vertices[v[i]].s * cache.current[0]->shiftScaleS * gSP.texture.scales - fmod( gSP.textureTile[0]->fuls, 1 << gSP.textureTile[0]->masks ))) * cache.current[0]->scaleS;
else
OGL.vertices[OGL.numVertices].s0 = (cache.current[0]->offsetS + (vertices[v[i]].s * cache.current[0]->shiftScaleS * gSP.texture.scales - gSP.textureTile[0]->fuls)) * cache.current[0]->scaleS;
if (gSP.textureTile[0]->maskt)
OGL.vertices[OGL.numVertices].t0 = (cache.current[0]->offsetT - (vertices[v[i]].t * cache.current[0]->shiftScaleT * gSP.texture.scalet - fmod( gSP.textureTile[0]->fult, 1 << gSP.textureTile[0]->maskt ))) * cache.current[0]->scaleT;
else
OGL.vertices[OGL.numVertices].t0 = (cache.current[0]->offsetT - (vertices[v[i]].t * cache.current[0]->shiftScaleT * gSP.texture.scalet - gSP.textureTile[0]->fult)) * cache.current[0]->scaleT;
}
else
{
OGL.vertices[OGL.numVertices].s0 = (vertices[v[i]].s * cache.current[0]->shiftScaleS * gSP.texture.scales - gSP.textureTile[0]->fuls + cache.current[0]->offsetS) * cache.current[0]->scaleS;
OGL.vertices[OGL.numVertices].t0 = (vertices[v[i]].t * cache.current[0]->shiftScaleT * gSP.texture.scalet - gSP.textureTile[0]->fult + cache.current[0]->offsetT) * cache.current[0]->scaleT;
}
}
if (combiner.usesT1)
{
if (cache.current[0]->frameBufferTexture)
{
OGL.vertices[OGL.numVertices].s1 = (cache.current[1]->offsetS + (vertices[v[i]].s * cache.current[1]->shiftScaleS * gSP.texture.scales - gSP.textureTile[1]->fuls)) * cache.current[1]->scaleS;
OGL.vertices[OGL.numVertices].t1 = (cache.current[1]->offsetT - (vertices[v[i]].t * cache.current[1]->shiftScaleT * gSP.texture.scalet - gSP.textureTile[1]->fult)) * cache.current[1]->scaleT;
}
else
{
OGL.vertices[OGL.numVertices].s1 = (vertices[v[i]].s * cache.current[1]->shiftScaleS * gSP.texture.scales - gSP.textureTile[1]->fuls + cache.current[1]->offsetS) * cache.current[1]->scaleS;
OGL.vertices[OGL.numVertices].t1 = (vertices[v[i]].t * cache.current[1]->shiftScaleT * gSP.texture.scalet - gSP.textureTile[1]->fult + cache.current[1]->offsetT) * cache.current[1]->scaleT;
}
}
OGL.numVertices++;
}
OGL.numTriangles++;
if (OGL.numVertices >= 255)
OGL_DrawTriangles();
}
void OGL_DrawTriangles()
{
if (OGL.usePolygonStipple && (gDP.otherMode.alphaCompare == G_AC_DITHER) && !(gDP.otherMode.alphaCvgSel))
{
OGL.lastStipple = (OGL.lastStipple + 1) & 0x7;
glPolygonStipple( OGL.stipplePattern[(BYTE)(gDP.envColor.a * 255.0f) >> 3][OGL.lastStipple] );
}
glDrawArrays( GL_TRIANGLES, 0, OGL.numVertices );
OGL.numTriangles = OGL.numVertices = 0;
}
void OGL_DrawLine( SPVertex *vertices, int v0, int v1, float width )
{
int v[] = { v0, v1 };
GLcolor color;
if (gSP.changed || gDP.changed)
OGL_UpdateStates();
glLineWidth( width * OGL.scaleX );
glBegin( GL_LINES );
for (int i = 0; i < 2; i++)
{
color.r = vertices[v[i]].r;
color.g = vertices[v[i]].g;
color.b = vertices[v[i]].b;
color.a = vertices[v[i]].a;
SetConstant( color, combiner.vertex.color, combiner.vertex.alpha );
glColor4fv( &color.r );
if (OGL.EXT_secondary_color)
{
color.r = vertices[v[i]].r;
color.g = vertices[v[i]].g;
color.b = vertices[v[i]].b;
color.a = vertices[v[i]].a;
SetConstant( color, combiner.vertex.secondaryColor, combiner.vertex.alpha );
glSecondaryColor3fvEXT( &color.r );
}
glVertex4f( vertices[v[i]].x, vertices[v[i]].y, vertices[v[i]].z, vertices[v[i]].w );
}
glEnd();
}
void OGL_DrawRect( int ulx, int uly, int lrx, int lry, float *color )
{
OGL_UpdateStates();
glDisable( GL_SCISSOR_TEST );
glDisable( GL_CULL_FACE );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho( 0, VI.width, VI.height, 0, 1.0f, -1.0f );
glViewport( 0, OGL.heightOffset, OGL.width, OGL.height );
glDepthRange( 0.0f, 1.0f );
glColor4f( color[0], color[1], color[2], color[3] );
glBegin( GL_QUADS );
glVertex4f( ulx, uly, (gDP.otherMode.depthSource == G_ZS_PRIM) ? gDP.primDepth.z : gSP.viewport.nearz, 1.0f );
glVertex4f( lrx, uly, (gDP.otherMode.depthSource == G_ZS_PRIM) ? gDP.primDepth.z : gSP.viewport.nearz, 1.0f );
glVertex4f( lrx, lry, (gDP.otherMode.depthSource == G_ZS_PRIM) ? gDP.primDepth.z : gSP.viewport.nearz, 1.0f );
glVertex4f( ulx, lry, (gDP.otherMode.depthSource == G_ZS_PRIM) ? gDP.primDepth.z : gSP.viewport.nearz, 1.0f );
glEnd();
glLoadIdentity();
OGL_UpdateCullFace();
OGL_UpdateViewport();
glEnable( GL_SCISSOR_TEST );
}
void OGL_DrawTexturedRect( float ulx, float uly, float lrx, float lry, float uls, float ult, float lrs, float lrt, bool flip )
{
GLVertex rect[2] =
{
{ ulx, uly, gDP.otherMode.depthSource == G_ZS_PRIM ? gDP.primDepth.z : gSP.viewport.nearz, 1.0f, { /*gDP.blendColor.r, gDP.blendColor.g, gDP.blendColor.b, gDP.blendColor.a */1.0f, 1.0f, 1.0f, 0.0f }, { 1.0f, 1.0f, 1.0f, 1.0f }, uls, ult, uls, ult, 0.0f },
{ lrx, lry, gDP.otherMode.depthSource == G_ZS_PRIM ? gDP.primDepth.z : gSP.viewport.nearz, 1.0f, { /*gDP.blendColor.r, gDP.blendColor.g, gDP.blendColor.b, gDP.blendColor.a*/1.0f, 1.0f, 1.0f, 0.0f }, { 1.0f, 1.0f, 1.0f, 1.0f }, lrs, lrt, lrs, lrt, 0.0f },
};
OGL_UpdateStates();
glDisable( GL_CULL_FACE );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho( 0, VI.width, VI.height, 0, 1.0f, -1.0f );
glViewport( 0, OGL.heightOffset, OGL.width, OGL.height );
if (combiner.usesT0)
{
rect[0].s0 = rect[0].s0 * cache.current[0]->shiftScaleS - gSP.textureTile[0]->fuls;
rect[0].t0 = rect[0].t0 * cache.current[0]->shiftScaleT - gSP.textureTile[0]->fult;
rect[1].s0 = (rect[1].s0 + 1.0f) * cache.current[0]->shiftScaleS - gSP.textureTile[0]->fuls;
rect[1].t0 = (rect[1].t0 + 1.0f) * cache.current[0]->shiftScaleT - gSP.textureTile[0]->fult;
if ((cache.current[0]->maskS) && (fmod( rect[0].s0, cache.current[0]->width ) == 0.0f) && !(cache.current[0]->mirrorS))
{
rect[1].s0 -= rect[0].s0;
rect[0].s0 = 0.0f;
}
if ((cache.current[0]->maskT) && (fmod( rect[0].t0, cache.current[0]->height ) == 0.0f) && !(cache.current[0]->mirrorT))
{
rect[1].t0 -= rect[0].t0;
rect[0].t0 = 0.0f;
}
if (cache.current[0]->frameBufferTexture)
{
rect[0].s0 = cache.current[0]->offsetS + rect[0].s0;
rect[0].t0 = cache.current[0]->offsetT - rect[0].t0;
rect[1].s0 = cache.current[0]->offsetS + rect[1].s0;
rect[1].t0 = cache.current[0]->offsetT - rect[1].t0;
}
if (OGL.ARB_multitexture)
glActiveTextureARB( GL_TEXTURE0_ARB );
if ((rect[0].s0 >= 0.0f) && (rect[1].s0 <= cache.current[0]->width))
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
if ((rect[0].t0 >= 0.0f) && (rect[1].t0 <= cache.current[0]->height))
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
// GLint height;
// glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height );
rect[0].s0 *= cache.current[0]->scaleS;
rect[0].t0 *= cache.current[0]->scaleT;
rect[1].s0 *= cache.current[0]->scaleS;
rect[1].t0 *= cache.current[0]->scaleT;
}
if (combiner.usesT1 && OGL.ARB_multitexture)
{
rect[0].s1 = rect[0].s1 * cache.current[1]->shiftScaleS - gSP.textureTile[1]->fuls;
rect[0].t1 = rect[0].t1 * cache.current[1]->shiftScaleT - gSP.textureTile[1]->fult;
rect[1].s1 = (rect[1].s1 + 1.0f) * cache.current[1]->shiftScaleS - gSP.textureTile[1]->fuls;
rect[1].t1 = (rect[1].t1 + 1.0f) * cache.current[1]->shiftScaleT - gSP.textureTile[1]->fult;
if ((cache.current[1]->maskS) && (fmod( rect[0].s1, cache.current[1]->width ) == 0.0f) && !(cache.current[1]->mirrorS))
{
rect[1].s1 -= rect[0].s1;
rect[0].s1 = 0.0f;
}
if ((cache.current[1]->maskT) && (fmod( rect[0].t1, cache.current[1]->height ) == 0.0f) && !(cache.current[1]->mirrorT))
{
rect[1].t1 -= rect[0].t1;
rect[0].t1 = 0.0f;
}
if (cache.current[1]->frameBufferTexture)
{
rect[0].s1 = cache.current[1]->offsetS + rect[0].s1;
rect[0].t1 = cache.current[1]->offsetT - rect[0].t1;
rect[1].s1 = cache.current[1]->offsetS + rect[1].s1;
rect[1].t1 = cache.current[1]->offsetT - rect[1].t1;
}
glActiveTextureARB( GL_TEXTURE1_ARB );
if ((rect[0].s1 == 0.0f) && (rect[1].s1 <= cache.current[1]->width))
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
if ((rect[0].t1 == 0.0f) && (rect[1].t1 <= cache.current[1]->height))
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
rect[0].s1 *= cache.current[1]->scaleS;
rect[0].t1 *= cache.current[1]->scaleT;
rect[1].s1 *= cache.current[1]->scaleS;
rect[1].t1 *= cache.current[1]->scaleT;
}
if ((gDP.otherMode.cycleType == G_CYC_COPY) && !OGL.forceBilinear)
{
if (OGL.ARB_multitexture)
glActiveTextureARB( GL_TEXTURE0_ARB );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
}
SetConstant( rect[0].color, combiner.vertex.color, combiner.vertex.alpha );
if (OGL.EXT_secondary_color)
SetConstant( rect[0].secondaryColor, combiner.vertex.secondaryColor, combiner.vertex.alpha );
glBegin( GL_QUADS );
glColor4f( rect[0].color.r, rect[0].color.g, rect[0].color.b, rect[0].color.a );
if (OGL.EXT_secondary_color)
glSecondaryColor3fEXT( rect[0].secondaryColor.r, rect[0].secondaryColor.g, rect[0].secondaryColor.b );
if (OGL.ARB_multitexture)
{
glMultiTexCoord2fARB( GL_TEXTURE0_ARB, rect[0].s0, rect[0].t0 );
glMultiTexCoord2fARB( GL_TEXTURE1_ARB, rect[0].s1, rect[0].t1 );
glVertex4f( rect[0].x, rect[0].y, rect[0].z, 1.0f );
glMultiTexCoord2fARB( GL_TEXTURE0_ARB, rect[1].s0, rect[0].t0 );
glMultiTexCoord2fARB( GL_TEXTURE1_ARB, rect[1].s1, rect[0].t1 );
glVertex4f( rect[1].x, rect[0].y, rect[0].z, 1.0f );
glMultiTexCoord2fARB( GL_TEXTURE0_ARB, rect[1].s0, rect[1].t0 );
glMultiTexCoord2fARB( GL_TEXTURE1_ARB, rect[1].s1, rect[1].t1 );
glVertex4f( rect[1].x, rect[1].y, rect[0].z, 1.0f );
glMultiTexCoord2fARB( GL_TEXTURE0_ARB, rect[0].s0, rect[1].t0 );
glMultiTexCoord2fARB( GL_TEXTURE1_ARB, rect[0].s1, rect[1].t1 );
glVertex4f( rect[0].x, rect[1].y, rect[0].z, 1.0f );
}
else
{
glTexCoord2f( rect[0].s0, rect[0].t0 );
glVertex4f( rect[0].x, rect[0].y, rect[0].z, 1.0f );
if (flip)
glTexCoord2f( rect[1].s0, rect[0].t0 );
else
glTexCoord2f( rect[0].s0, rect[1].t0 );
glVertex4f( rect[1].x, rect[0].y, rect[0].z, 1.0f );
glTexCoord2f( rect[1].s0, rect[1].t0 );
glVertex4f( rect[1].x, rect[1].y, rect[0].z, 1.0f );
if (flip)
glTexCoord2f( rect[1].s0, rect[0].t0 );
else
glTexCoord2f( rect[1].s0, rect[0].t0 );
glVertex4f( rect[0].x, rect[1].y, rect[0].z, 1.0f );
}
glEnd();
glLoadIdentity();
OGL_UpdateCullFace();
OGL_UpdateViewport();
}
void OGL_ClearDepthBuffer()
{
glDisable( GL_SCISSOR_TEST );
OGL_UpdateStates();
glDepthMask( TRUE );
glClear( GL_DEPTH_BUFFER_BIT );
OGL_UpdateDepthUpdate();
glEnable( GL_SCISSOR_TEST );
}
void OGL_ClearColorBuffer( float *color )
{
glDisable( GL_SCISSOR_TEST );
glClearColor( color[0], color[1], color[2], color[3] );
glClear( GL_COLOR_BUFFER_BIT );
glEnable( GL_SCISSOR_TEST );
}
static void OGL_png_error(png_structp png_write, const char *message)
{
printf("PNG Error: %s\n", message);
}
static void OGL_png_warn(png_structp png_write, const char *message)
{
printf("PNG Warning: %s\n", message);
}
void OGL_SaveScreenshot()
{
#ifndef __LINUX__
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
HANDLE hBitmapFile;
char *pixelData = (char*)malloc( OGL.width * OGL.height * 3 );
glReadBuffer( GL_FRONT );
glReadPixels( 0, OGL.heightOffset, OGL.width, OGL.height, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixelData );
glReadBuffer( GL_BACK );
infoHeader.biSize = sizeof( BITMAPINFOHEADER );
infoHeader.biWidth = OGL.width;
infoHeader.biHeight = OGL.height;
infoHeader.biPlanes = 1;
infoHeader.biBitCount = 24;
infoHeader.biCompression = BI_RGB;
infoHeader.biSizeImage = OGL.width * OGL.height * 3;
infoHeader.biXPelsPerMeter = 0;
infoHeader.biYPelsPerMeter = 0;
infoHeader.biClrUsed = 0;
infoHeader.biClrImportant = 0;
fileHeader.bfType = 19778;
fileHeader.bfSize = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ) + infoHeader.biSizeImage;
fileHeader.bfReserved1 = fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER );
char filename[256];
CreateDirectory( screenDirectory, NULL );
int i = 0;
do
{
sprintf( filename, "%sscreen%02i.bmp", screenDirectory, i );
i++;
if (i > 99)
return;
hBitmapFile = CreateFile( filename, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
}
while (hBitmapFile == INVALID_HANDLE_VALUE);
DWORD written;
WriteFile( hBitmapFile, &fileHeader, sizeof( BITMAPFILEHEADER ), &written, NULL );
WriteFile( hBitmapFile, &infoHeader, sizeof( BITMAPINFOHEADER ), &written, NULL );
WriteFile( hBitmapFile, pixelData, infoHeader.biSizeImage, &written, NULL );
CloseHandle( hBitmapFile );
free( pixelData );
#else // !__LINUX__
// start by getting the base file path
char filepath[2048], filename[2048];
filepath[0] = 0;
filename[0] = 0;
strcpy(filepath, screenDirectory);
if (strlen(filepath) > 0 && filepath[strlen(filepath)-1] != '/')
strcat(filepath, "/");
strcat(filepath, "mupen64");
// look for a file
int i;
for (i = 0; i < 100; i++)
{
sprintf(filename, "%s_%03i.png", filepath, i);
FILE *pFile = fopen(filename, "r");
if (pFile == NULL)
break;
fclose(pFile);
}
if (i == 100) return;
// allocate PNG structures
png_structp png_write = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, OGL_png_error, OGL_png_warn);
if (!png_write)
{
printf("Error creating PNG write struct.\n");
return;
}
png_infop png_info = png_create_info_struct(png_write);
if (!png_info)
{
png_destroy_write_struct(&png_write, (png_infopp)NULL);
printf("Error creating PNG info struct.\n");
return;
}
// Set the jumpback
if (setjmp(png_jmpbuf(png_write)))
{
png_destroy_write_struct(&png_write, &png_info);
printf("Error calling setjmp()\n");
return;
}
// open the file to write
FILE *savefile = fopen(filename, "wb");
if (savefile == NULL)
{
printf("Error opening '%s' to save screenshot.\n", filename);
return;
}
// give the file handle to the PNG compressor
png_init_io(png_write, savefile);
// read pixel data from OpenGL
char *pixels = (char*)malloc( OGL.width * OGL.height * 3 );
glReadBuffer( GL_FRONT );
glReadPixels( 0, OGL.heightOffset, OGL.width, OGL.height, GL_RGB, GL_UNSIGNED_BYTE, pixels );
glReadBuffer( GL_BACK );
// set the info
int width = OGL.width;
int height = OGL.height;
png_set_IHDR(png_write, png_info, width, height, 8, PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
// lock the screen, get a pointer and row pitch
long pitch = width * 3;
// allocate row pointers and scale each row to 24-bit color
png_byte **row_pointers;
row_pointers = (png_byte **) malloc(height * sizeof(png_bytep));
for (int i = 0; i < height; i++)
{
row_pointers[i] = (png_byte *) (pixels + (height - 1 - i) * pitch);
}
// set the row pointers
png_set_rows(png_write, png_info, row_pointers);
// write the picture to disk
png_write_png(png_write, png_info, 0, NULL);
// free memory
free(row_pointers);
png_destroy_write_struct(&png_write, &png_info);
free(pixels);
// all done
#endif // __LINUX__
}
#ifdef __LINUX__
void
OGL_SwapBuffers()
{
static int frames[5] = { 0, 0, 0, 0, 0 };
static int framesIndex = 0;
static Uint32 lastTicks = 0;
Uint32 ticks = SDL_GetTicks();
frames[framesIndex]++;
if (ticks >= (lastTicks + 1000))
{
char caption[500];
float fps = 0.0;
for (int i = 0; i < 5; i++)
fps += frames[i];
fps /= 5.0;
snprintf( caption, 500, "%s - %.2f fps", pluginName, fps );
SDL_WM_SetCaption( caption, pluginName );
framesIndex = (framesIndex + 1) % 5;
frames[framesIndex] = 0;
lastTicks = ticks;
}
// if emulator defined a render callback function, call it before buffer swap
if(renderCallback)
(*renderCallback)();
SDL_GL_SwapBuffers();
}
void OGL_ReadScreen( void **dest, int *width, int *height )
{
*width = OGL.width;
*height = OGL.height;
*dest = malloc( OGL.height * OGL.width * 3 );
if (*dest == 0)
return;
GLint oldMode;
glGetIntegerv( GL_READ_BUFFER, &oldMode );
glReadBuffer( GL_FRONT );
// glReadBuffer( GL_BACK );
glReadPixels( 0, 0, OGL.width, OGL.height,
GL_RGB, GL_UNSIGNED_BYTE, *dest );
glReadBuffer( oldMode );
}
#endif // __LINUX__