//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "platformWin32/platformGL.h" #include "platformWin32/platformWin32.h" #include "platform/platformAudio.h" #include "platformWin32/winV2Video.h" #include "platform/3Dfx.h" #include "console/console.h" #include "math/mPoint.h" #include "platform/event.h" #include "platform/gameInterface.h" #include "console/consoleInternal.h" #include "console/ast.h" #include "core/fileStream.h" static U8 sResCode; // Used for initializing the resolution list only static void execScript(const char *scriptFile) { // execute the script FileStream str; if (!str.open(scriptFile, FileStream::Read)) return; U32 size = str.getStreamSize(); char *script = new char[size + 1]; str.read(size, script); str.close(); script[size] = 0; Con::executef(2, "eval", script); delete[] script; } //------------------------------------------------------------------------------ Voodoo2Device::Voodoo2Device() { initDevice(); } //------------------------------------------------------------------------------ void Voodoo2Device::initDevice() { // Set the device name: mDeviceName = "Voodoo2"; // Set some initial conditions: mResolutionList.clear(); mFullScreenOnly = true; // Enumerate all available resolutions: Resolution newRes; newRes = Resolution( 640, 480, 16 ); mResolutionList.push_back( newRes ); if ( sResCode & 1 ) { newRes = Resolution( 800, 600, 16 ); mResolutionList.push_back( newRes ); } if ( sResCode & 2 ) { Con::printf( "SLI detected." ); newRes = Resolution( 960, 720, 16 ); mResolutionList.push_back( newRes ); newRes = Resolution( 1024, 768, 16 ); mResolutionList.push_back( newRes ); } } //------------------------------------------------------------------------------ bool Voodoo2Device::activate( U32 width, U32 height, U32 /*bpp*/, bool /*fullScreen*/ ) { bool needResurrect = false; // If the rendering context exists, delete it: if ( winState.hGLRC ) { Con::printf( "Killing the texture manager..." ); Game->textureKill(); needResurrect = true; Con::printf( "Making the rendering context not current..." ); if ( !dwglMakeCurrent( NULL, NULL ) ) { AssertFatal( false, "Voodoo2Device::activate\ndwglMakeCurrent( NULL, NULL ) failed!" ); return false; } Con::printf( "Deleting the rendering context..." ); if ( !dwglDeleteContext( winState.hGLRC ) ) { AssertFatal( false, "Voodoo2Device::activate\ndwglDeleteContext failed!" ); return false; } winState.hGLRC = NULL; } // If window already exists, kill it so we can start fresh: if ( winState.appWindow ) { Con::printf( "Releasing the device context..." ); ReleaseDC( winState.appWindow, winState.appDC ); winState.appDC = NULL; Con::printf( "Destroying the window..." ); DestroyWindow( winState.appWindow ); winState.appWindow = NULL; } // If OpenGL library already loaded, shut it down and reload the 3Dfx standalone driver: if ( winState.hinstOpenGL ) GL_Shutdown(); GL_Init( "3dfxvgl", "glu32" ); // This device only supports full-screen, so force it: smIsFullScreen = true; Con::setVariable( "$pref::Video::fullScreen", "true" ); // Create the new window: Con::printf( "Creating a new full-screen window..." ); winState.appWindow = CreateOpenGLWindow( width, height, true ); if ( !winState.appWindow ) { AssertFatal( false, "Voodoo2Device::activate\nFailed to create a window!" ); return false; } // Get a device context from the new window: HDC tempDC = GetDC( winState.appWindow ); if ( !tempDC ) { AssertFatal( false, "Voodoo2Device::activate\nFailed to get a device context!" ); return false; } // Set the pixel format of the new window: PIXELFORMATDESCRIPTOR pfd; CreatePixelFormat( &pfd, 16, 16, 8, false ); S32 chosenFormat = ChooseBestPixelFormat( tempDC, &pfd ); dwglDescribePixelFormat( tempDC, chosenFormat, sizeof( pfd ), &pfd ); bool test = dwglSetPixelFormat( tempDC, chosenFormat, &pfd ); ReleaseDC( winState.appWindow, tempDC ); if ( !test ) { AssertFatal( false, "Voodoo2Device::activate\nFailed to set the pixel format of the device context!" ); return false; } Con::printf( "Pixel format set:" ); Con::printf( " %d color bits, %d depth bits, %d stencil bits", pfd.cColorBits, pfd.cDepthBits, pfd.cStencilBits ); // Set the resolution: smCurrentRes.bpp = 16; // Force 16-bit color. if ( !setScreenMode( width, height, 16, true, true, false ) ) return false; // Output some driver info to the console: const char* vendorString = (const char*) glGetString( GL_VENDOR ); const char* rendererString = (const char*) glGetString( GL_RENDERER ); const char* versionString = (const char*) glGetString( GL_VERSION ); Con::printf( "OpenGL driver information:" ); if ( vendorString ) Con::printf( " Vendor: %s", vendorString ); if ( rendererString ) Con::printf( " Renderer: %s", rendererString ); if ( versionString ) Con::printf( " Version: %s", versionString ); // Set a few variables: if ( Con::getIntVariable( "$pref::OpenGL::mipReduction" ) < 1 ) Con::setIntVariable( "$pref::OpenGL::mipReduction", 1 ); if ( Con::getIntVariable( "$pref::OpenGL::interiorMipReduction" ) < 1 ) Con::setIntVariable( "$pref::OpenGL::interiorMipReduction", 1 ); if ( Con::getIntVariable( "$pref::OpenGL::skyMipReduction" ) < 1 ) Con::setIntVariable( "$pref::OpenGL::skyMipReduction", 1 ); if (dStrcmp(vendorString, Con::getVariable("$pref::Video::profiledVendor")) || dStrcmp(rendererString, Con::getVariable("$pref::Video::profiledRenderer"))) { // Voodoo2 defaults Con::setBoolVariable("$pref::OpenGL::disableEXTCompiledVertexArray", false); Con::setBoolVariable("$pref::OpenGL::disableSubImage", false); Con::setBoolVariable("$pref::OpenGL::noEnvColor", true); Con::setBoolVariable("$pref::OpenGL::disableARBTextureCompression", false); Con::setBoolVariable("$pref::Interior::lockArrays", true); Con::setBoolVariable("$pref::TS::skipFirstFog", false); Con::setBoolVariable("$pref::OpenGL::noDrawArraysAlpha", true); } if ( needResurrect ) { // Reload the textures: Con::printf( "Resurrecting the texture manager..." ); Game->textureResurrect(); } // Set the new window to the foreground: ShowWindow( winState.appWindow, SW_SHOW ); SetForegroundWindow( winState.appWindow ); SetFocus( winState.appWindow ); GL_EXT_Init(); Con::setBoolVariable( "$SwapIntervalSupported", false ); Con::setVariable( "$pref::Video::displayDevice", mDeviceName ); Con::setBoolVariable("$pref::OpenGL::allowTexGen", true); return true; } //------------------------------------------------------------------------------ void Voodoo2Device::shutdown() { if ( winState.hGLRC ) { dwglMakeCurrent( NULL, NULL ); dwglDeleteContext( winState.hGLRC ); winState.hGLRC = NULL; } if ( winState.appDC ) { ReleaseDC( winState.appWindow, winState.appDC ); winState.appDC = NULL; } ChangeDisplaySettings( NULL, 0 ); } //------------------------------------------------------------------------------ bool Voodoo2Device::setScreenMode( U32 width, U32 height, U32 bpp, bool fullScreen, bool forceIt, bool repaint ) { HWND curtain = NULL; bool needResurrect = false; Resolution newRes( width, height, bpp ); if ( !fullScreen ) { // The 3Dfx Voodoo2 OpenGL driver only runs in full-screen, so ignore: Con::warnf( ConsoleLogEntry::General, "Sorry, the Voodoo 2 display device only supports full-screen!" ); } // Force the res to be one of the ones in the supported list: U32 resIndex = 0; U32 bestScore = 0, thisScore = 0; for ( U32 i = 0; i < mResolutionList.size(); i++ ) { if ( newRes == mResolutionList[i] ) { resIndex = i; break; } else { thisScore = abs( S32( newRes.w ) - S32( mResolutionList[i].w ) ) + abs( S32( newRes.h ) - S32( mResolutionList[i].h ) ); if ( !bestScore || thisScore < bestScore ) { bestScore = thisScore; resIndex = i; } } } newRes = mResolutionList[resIndex]; // Return if we aren't forcing it and we are already in the desired resolution: if ( !forceIt && newRes == smCurrentRes ) return true; Con::printf( "Setting screen mode to %dx%dx16 (fs)...", newRes.w, newRes.h ); // Delete the rendering context: if ( winState.hGLRC ) { Con::printf( "Killing the texture manager..." ); Game->textureKill(); needResurrect = true; Con::printf( "Making the rendering context not current..." ); if ( !dwglMakeCurrent( NULL, NULL ) ) { AssertFatal( false, "Voodoo2Device::setScreenMode\ndwglMakeCurrent( NULL, NULL ) failed!" ); return false; } Con::printf( "Deleting the rendering context..." ); if ( !dwglDeleteContext( winState.hGLRC ) ) { AssertFatal( false, "Voodoo2Device::setScreenMode\ndwglDeleteContext failed!" ); return false; } winState.hGLRC = NULL; } // Release the device context: if ( winState.appDC ) { Con::printf( "Releasing the device context..." ); ReleaseDC( winState.appWindow, winState.appDC ); winState.appDC = NULL; } // Change the display settings (shouldn't really be necessary, but is): U32 test; DEVMODE devMode; dMemset( &devMode, 0, sizeof( devMode ) ); devMode.dmSize = sizeof( devMode ); devMode.dmPelsWidth = newRes.w; devMode.dmPelsHeight = newRes.h; devMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; Con::printf( "Changing the display settings to %dx%dx16...", newRes.w, newRes.h ); curtain = CreateCurtain( newRes.w, newRes.h ); test = ChangeDisplaySettings( &devMode, CDS_FULLSCREEN ); if ( test != DISP_CHANGE_SUCCESSFUL ) { ChangeDisplaySettings( NULL, 0 ); switch( test ) { case DISP_CHANGE_RESTART: Platform::AlertOK( "Display Change Failed", "You must restart your machine to get the specified mode." ); break; case DISP_CHANGE_BADMODE: Platform::AlertOK( "Display Change Failed", "The specified mode is not supported by this device." ); break; default: Platform::AlertOK( "Display Change Failed", "Hardware failed to switch to the specified mode." ); break; }; return false; } if( winState.appWindow && !fullScreen ) ShowWindow( winState.appWindow, SW_NORMAL ); else ShowWindow( winState.appWindow, SW_MAXIMIZE ); // Resize the window: if ( !SetWindowPos( winState.appWindow, NULL, 0, 0, newRes.w, newRes.h, SWP_NOZORDER | SWP_NOMOVE ) ) { Con::printf( "Voodoo2Device::setScreenMode - SetWindowPos sizing to %dx%d failed.", newRes.w, newRes.h ); return false; } // Get a new device context: Con::printf( "Acquiring a new device context..." ); winState.appDC = GetDC( winState.appWindow ); if ( !winState.appDC ) { AssertFatal( false, "Voodoo2Device::setScreenMode\nFailed to get a valid device context!" ); return false; } // Create a new rendering context: Con::printf( "Creating a new rendering context..." ); winState.hGLRC = dwglCreateContext( winState.appDC ); if ( !winState.hGLRC ) { AssertFatal( false, "Voodoo2Device::setScreenMode\nFailed to create a GL rendering context!" ); return false; } // Make the new rendering context current: Con::printf( "Making the new rendering context current..." ); if ( !dwglMakeCurrent( winState.appDC, winState.hGLRC ) ) { AssertFatal( false, "Voodoo2Device::setScreenMode\nFailed to make the rendering context current!" ); return false; } if ( needResurrect ) { // Reload the textures: Con::printf( "Resurrecting the texture manager..." ); Game->textureResurrect(); } smCurrentRes = newRes; Platform::setWindowSize( newRes.w, newRes.h ); char tempBuf[15]; dSprintf( tempBuf, sizeof( tempBuf ), "%d %d %d", smCurrentRes.w, smCurrentRes.h, smCurrentRes.bpp ); Con::setVariable( "$pref::Video::resolution", tempBuf ); if ( curtain ) DestroyWindow( curtain ); if ( repaint ) Con::evaluate( "resetCanvas();" ); return true; } //------------------------------------------------------------------------------ void Voodoo2Device::swapBuffers() { dwglSwapBuffers( winState.appDC ); } //------------------------------------------------------------------------------ const char* Voodoo2Device::getDriverInfo() { // Output some driver info to the console: const char* vendorString = (const char*) glGetString( GL_VENDOR ); const char* rendererString = (const char*) glGetString( GL_RENDERER ); const char* versionString = (const char*) glGetString( GL_VERSION ); const char* extensionsString = (const char*) glGetString( GL_EXTENSIONS ); // TODO - move strings out of code and into script... U32 bufferLen = ( vendorString ? dStrlen( vendorString ) : 0 ) + ( rendererString ? dStrlen( rendererString ) : 0 ) + ( versionString ? dStrlen( versionString ) : 0 ) + ( extensionsString ? dStrlen( extensionsString ) : 0 ) + 4; char* returnString = Con::getReturnBuffer( bufferLen ); dSprintf( returnString, bufferLen, "%s\t%s\t%s\t%s", ( vendorString ? vendorString : "" ), ( rendererString ? rendererString : "" ), ( versionString ? versionString : "" ), ( extensionsString ? extensionsString : "" ) ); return( returnString ); } //------------------------------------------------------------------------------ bool Voodoo2Device::getGammaCorrection(F32 &g) { // apparently the qwlGetDeviceGammaRamp3DFX doesn't actually work. So, this // is a good default. We don't need to worry about restoring the original // gamma. Anything we do is wiped out once we destroy the Voodoo2 window. g = 0.8; return true; U16 ramp[256*3]; if (!dwglGetDeviceGammaRamp3DFX(winState.appDC, ramp)) return false; F32 csum = 0.0; U32 ccount = 0; for (U16 i = 0; i < 256; ++i) { if (i != 0 && ramp[i] != 0 && ramp[i] != 65535) { F64 b = (F64) i/256.0; F64 a = (F64) ramp[i]/65535.0; F32 c = (F32) (mLog(a)/mLog(b)); csum += c; ++ccount; } } g = csum/ccount; return true; } //------------------------------------------------------------------------------ bool Voodoo2Device::setGammaCorrection(F32 g) { U16 ramp[256*3]; for (U16 i = 0; i < 256; ++i) ramp[i] = mPow((F32) i/256.0f, g) * 65535.0f; dMemcpy(&ramp[256],ramp,256*sizeof(U16)); dMemcpy(&ramp[512],ramp,256*sizeof(U16)); return dwglSetDeviceGammaRamp3DFX(winState.appDC, ramp); } //------------------------------------------------------------------------------ bool Voodoo2Device::setVerticalSync( bool on ) { on; return( false ); } //------------------------------------------------------------------------------ // Stuff for Voodoo 2 detection: #ifndef GR_HARDWARE #define GR_HARDWARE 0xa1 #endif #define GLIDE_NUM_TMU 2 #define MAX_NUM_SST 4 typedef S32 GrSstType; #define GR_SSTTYPE_VOODOO 0 #define GR_SSTTYPE_SST96 1 #define GR_SSTTYPE_AT3D 2 typedef struct GrTMUConfig_St { S32 tmuRev; /* Rev of Texelfx chip */ S32 tmuRam; /* 1, 2, or 4 MB */ } GrTMUConfig_t; typedef struct GrVoodooConfig_St { S32 fbRam; /* 1, 2, or 4 MB */ S32 fbiRev; /* Rev of Pixelfx chip */ S32 nTexelfx; /* How many texelFX chips are there? */ FxBool sliDetect; /* Is it a scan-line interleaved board? */ GrTMUConfig_t tmuConfig[GLIDE_NUM_TMU]; /* Configuration of the Texelfx chips */ } GrVoodooConfig_t; typedef struct GrSst96Config_St { S32 fbRam; /* How much? */ S32 nTexelfx; GrTMUConfig_t tmuConfig; } GrSst96Config_t; typedef struct GrAT3DConfig_St { S32 rev; } GrAT3DConfig_t; typedef struct { S32 num_sst; /* # of HW units in the system */ struct { GrSstType type; /* Which hardware is it? */ union SstBoard_u { GrVoodooConfig_t VoodooConfig; GrSst96Config_t SST96Config; GrAT3DConfig_t AT3DConfig; } sstBoard; } SSTs[MAX_NUM_SST]; /* configuration for each board */ } GrHwConfiguration; typedef FxBool( FX_CALL *grSstQueryBoards_fpt )( GrHwConfiguration *hwconfig ); typedef FxBool( FX_CALL *grSstQueryHardware_fpt )( GrHwConfiguration *hwconfig ); typedef void ( FX_CALL *grGlideInit_fpt )( void ); typedef const char* ( FX_CALL *grGetString_fpt )( FxU32 pName ); typedef void( FX_CALL *grGlideShutdown_fpt )( void ); //------------------------------------------------------------------------------ DisplayDevice* Voodoo2Device::create() { // Detect the Voodoo2 OpenGL driver: GrHwConfiguration hwConfig; bool result = true; // Reset the available resolution specifier code: sResCode = 0; grGlideInit_fpt grGlideInit = NULL; grGlideShutdown_fpt grGlideShutdown = NULL; grSstQueryBoards_fpt grSstQueryBoards = NULL; grSstQueryHardware_fpt grSstQueryHardware = NULL; grGetString_fpt grGetString = NULL; // Let the Glide 2 stuff go first: HINSTANCE glide2DLL = LoadLibrary( dT("glide2x") ); if ( !glide2DLL ) return NULL; grSstQueryBoards = (grSstQueryBoards_fpt) GetProcAddress( glide2DLL, "_grSstQueryBoards@4" ); if ( grSstQueryBoards ) { grSstQueryBoards( &hwConfig ); if ( hwConfig.num_sst == 0 ) result = false; else { grGlideInit = (grGlideInit_fpt) GetProcAddress( glide2DLL, "_grGlideInit@0" ); grSstQueryHardware = (grSstQueryHardware_fpt) GetProcAddress( glide2DLL, "_grSstQueryHardware@4" ); grGlideShutdown = (grGlideShutdown_fpt) GetProcAddress( glide2DLL, "_grGlideShutdown@0" ); if ( grGlideInit && grSstQueryHardware && grGlideShutdown ) { grGlideInit(); grSstQueryHardware( &hwConfig ); // Find out what resolutions are available: if ( hwConfig.SSTs[0].sstBoard.VoodooConfig.sliDetect == 0 ) // SLI not detected. { if ( hwConfig.SSTs[0].sstBoard.VoodooConfig.fbRam >= 4 ) sResCode = 1; // low bit indicates 800x600 } else // SLI detected. { sResCode = 1; if ( hwConfig.SSTs[0].sstBoard.VoodooConfig.fbRam >= 4 ) sResCode += 2; } grGlideShutdown(); } grGlideInit = NULL; grGlideShutdown = NULL; } } else result = false; FreeLibrary( glide2DLL ); if ( result ) { // Glide 3's turn: HINSTANCE glide3DLL = LoadLibrary( dT("glide3x") ); if ( !glide3DLL ) return NULL; grGlideInit = (grGlideInit_fpt) GetProcAddress( glide3DLL, "_grGlideInit@0" ); grGetString = (grGetString_fpt) GetProcAddress( glide3DLL, "_grGetString@4" ); grGlideShutdown = (grGlideShutdown_fpt) GetProcAddress( glide3DLL, "_grGlideShutdown@0" ); if ( grGlideInit && grGetString && grGlideShutdown ) { grGlideInit(); const char* hardware = grGetString( GR_HARDWARE ); if ( dStrcmp( hardware, "Voodoo2" ) != 0 ) result = false; grGlideShutdown(); } else result = false; FreeLibrary( glide3DLL ); } if ( result ) return new Voodoo2Device(); else return NULL; }