#include #include #include #include #include #include #include #define GL_GLEXT_PROTOTYPES #include #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__