//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. // // Portions taken from OpenGL Full Screen.c sample from Apple Computer, Inc. // (that's where many of the lead helper functions originated from, but code // has been significantly changed & revised.) //----------------------------------------------------------------------------- #define NO_REDEFINE_GL_FUNCS #include "platform/types.h" #if defined(TORQUE_OS_MAC_OSX) // AAAAAAAAARGGGH. // OSX Frameworks version of AGL.h includes CoreServices, which hits the // OpenTransport-has-a-new-but-platform.h-#defs-new-to-something-nonstandard // issue, so we need to pre-include OT here... #include #endif #include "platformMacCarb/platformMacCarb.h" #include "platformMacCarb/platformGL.h" #include "platformMacCarb/maccarbOGLVideo.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" // !!!!!!!TBD // Note: Card Profiling code isn't doing anything. // Note: Screen-res list building is hardcoded, and should tap DSp. // Note: Gamma support. #define USE_AGL_FULLSCREEN 1 // whether to enable DSpSetDebugMode #define DSP_DEBUG_MODE (defined(TORQUE_DEBUG)) // whether to only capture single monitor under gl fullscreen #define ALLOW_SINGLEMON_CAPTURE (defined(TORQUE_DEBUG)) // flip this if we want smooth gamma-fades when switching screenres or in/out of DSp. // generally, it's less nice but easier to find probs if we just leave it off, even for release. #define FADE_ON_SWITCH 0 #define CALL_IN_SPOCKETS_BUT_NOT_IN_CARBON 1 #include #include AGLContext agl_ctx; //----------------------------------------------------------------------------------------- // prototypes and globals -- !!!!!!TBD - globals should mostly go away, into platState. //----------------------------------------------------------------------------------------- void ReportError (char * strError); OSStatus DSpDebugStr (OSStatus error); GLenum aglDebugStr (void); CGrafPtr SetupDSp (GDHandle *phGD, int width, int height, int bpp); void ShutdownDSp (CGrafPtr pDSpPort); AGLContext SetupAGL (AGLPixelFormat *inFmt, GDHandle hGD, AGLDrawable win, int bpp); AGLContext SetupAGLFullScreen (AGLPixelFormat *inFmt, GDHandle hGD, int width, int height, int bpp); const RGBColor rgbBlack = { 0x0000, 0x0000, 0x0000 }; // extern accessible global! bool gDSpActive = false; NumVersion gVersionDSp; DSpContextAttributes gContextAttributes; DSpContextReference gContext = 0; int gCurrentFrequency = 0; AGLDrawable gpDSpPort = NULL; // will be NULL for full screen under X Rect gRectPort = {0, 0, 0, 0}; bool gDSpOwnsPort = false; #define MAX_DEVICES 8 // Geez, this SHOULD be safe! GDHandle gDeviceList[MAX_DEVICES]; int gNumDevices = 0; DSpContextReference gBlockedContext[MAX_DEVICES]; int gNumBlockedContexts = 0; #if defined(TORQUE_DEBUG) bool gOutlineEnabled = false; void (* glDrawElementsProcPtr) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) = glDrawElements; void (* glDrawArraysProcPtr) (GLenum mode, GLint first, GLsizei count) = glDrawArrays; void (* glNormDrawElements) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) = glDrawElements; void (* glNormDrawArrays) (GLenum mode, GLint first, GLsizei count) = glDrawArrays; #endif // locals. static AGLDrawable drawable; static AGLPixelFormat mainfmt; //----------------------------------------------------------------------------------------- // display errors -- for the moment, dumps to the console. !!!!TBD //----------------------------------------------------------------------------------------- void ReportError (char * strError) { // !!!!!TBD - for now, dump all errors to console. Con::errorf(strError); // used to do a debug str. or should do an alert/assert. } //----------------------------------------------------------------------------------------- // translate DSp codes to strings. //----------------------------------------------------------------------------------------- OSStatus DSpDebugStr (OSStatus error) { switch (error) { case noErr: break; case kDSpNotInitializedErr: ReportError ("DSp Error: Not initialized"); break; case kDSpSystemSWTooOldErr: ReportError ("DSp Error: system Software too old"); break; case kDSpInvalidContextErr: ReportError ("DSp Error: Invalid context"); break; case kDSpInvalidAttributesErr: ReportError ("DSp Error: Invalid attributes"); break; case kDSpContextAlreadyReservedErr: ReportError ("DSp Error: Context already reserved"); break; case kDSpContextNotReservedErr: ReportError ("DSp Error: Context not reserved"); break; case kDSpContextNotFoundErr: ReportError ("DSp Error: Context not found"); break; case kDSpFrameRateNotReadyErr: ReportError ("DSp Error: Frame rate not ready"); break; case kDSpConfirmSwitchWarning: // ReportError ("DSp Warning: Must confirm switch"); // removed since it is just a warning, add back for debugging return 0; // don't want to fail on this warning break; case kDSpInternalErr: ReportError ("DSp Error: Internal error"); break; case kDSpStereoContextErr: ReportError ("DSp Error: Stereo context"); break; } return error; } //----------------------------------------------------------------------------------------- // if error dump agl errors to debugger string, return error //----------------------------------------------------------------------------------------- GLenum aglDebugStr (void) { GLenum err = aglGetError(); if (AGL_NO_ERROR != err) ReportError ((char *)aglErrorString(err)); return err; } //----------------------------------------------------------------------------------------- // if error dump agl errors to debugger string, return error //----------------------------------------------------------------------------------------- GLenum glDebugStr (void) { GLenum err = glGetError(); if (GL_NO_ERROR != err) ReportError ((char *)gluErrorString(err)); return err; } #pragma mark - //----------------------------------------------------------------------------------------- // release any blocks held //----------------------------------------------------------------------------------------- void UnblockDSpContexts(void) { int i; if (gNumBlockedContexts) // shouldn't happen, but... { for (i=0; i= 640 && attr.displayHeight >= 480 && (attr.displayBestDepth == 16 || attr.displayBestDepth == 32) ) { // Only add this resolution if it is not already in the list: bool alreadyInList = false; for (i=0; i>16; #if FADE_ON_SWITCH DSpDebugStr (DSpContext_FadeGammaOut (NULL, NULL)); // fade display, remove for debug #endif if (noErr != DSpDebugStr (DSpContext_SetState (gContext, kDSpContextState_Active))) // activate our context { ReportError ("DSpContext_SetState() had an error."); return NULL; } if (platState.osX && !((gVersionDSp.majorRev > 0x01) || ((gVersionDSp.majorRev == 0x01) && (gVersionDSp.minorAndBugRev >= 0x99))))// DSp should be supported in version after 1.98 { ReportError ("Mac OS X with DSp < 1.99 does not support DrawSprocket for OpenGL full screen"); return NULL; } else if (platState.osX) // DSp should be supported in versions 1.99 and later { #if originally /* // use DSp's front buffer on Mac OS X if (noErr != DSpDebugStr (DSpContext_GetFrontBuffer (gContext, &pPort))) { ReportError ("DSpContext_GetFrontBuffer() had an error."); return NULL; } // make sure to flag this! gDSpOwnsPort = true; // there is a problem in Mac OS X GM CoreGraphics that may not size the port pixmap correctly // this will check the vertical sizes and offset if required to fix the problem // this will not center ports that are smaller then a particular resolution { long deltaV, deltaH; Rect portBounds; PixMapHandle hPix = GetPortPixMap (pPort); Rect pixBounds = (**hPix).bounds; GetPortBounds (pPort, &portBounds); deltaV = (portBounds.bottom - portBounds.top) - (pixBounds.bottom - pixBounds.top) + (portBounds.bottom - portBounds.top - height) / 2; deltaH = -(portBounds.right - portBounds.left - width) / 2; if (deltaV || deltaH) { GrafPtr pPortSave; GetPort (&pPortSave); SetPort ((GrafPtr)pPort); // set origin to account for CG offset and if requested drawable smaller than screen rez SetOrigin (deltaH, deltaV); SetPort (pPortSave); } } platState.appWindow = GetWindowFromPort(pPort); #if FADE_ON_SWITCH DSpDebugStr (DSpContext_FadeGammaIn (NULL, NULL)); #endif return pPort; */ #endif } Con::printf( "Creating new Fullscreen window..." ); WindowPtr pWindow = CreateOpenGLWindow(*phGD, width, height, true); if (pWindow == NULL) return(NULL); // !!!!TBD err msg. platState.appWindow = pWindow; pPort = GetWindowPort(pWindow); #if FADE_ON_SWITCH DSpDebugStr (DSpContext_FadeGammaIn (NULL, NULL)); #endif gDSpActive = true; return pPort; } //----------------------------------------------------------------------------------------- // clean up DSp //----------------------------------------------------------------------------------------- void ShutdownDSp (CGrafPtr pDSpPort) { #if FADE_ON_SWITCH DSpContext_FadeGammaOut(NULL, NULL); #endif if ((NULL != pDSpPort)) { // retreive the actual window in question... WindowPtr w = GetWindowFromPort(pDSpPort); if (w == platState.appWindow) // then clear our storage variable. platState.appWindow = NULL; if (!gDSpOwnsPort) // then we created the window, so we have to dispose of it. { if (platState.osX) ReleaseWindow(w); else DisposeWindow(w); } } DSpContext_SetState(gContext, kDSpContextState_Inactive); #if FADE_ON_SWITCH DSpContext_FadeGammaIn(NULL, NULL); #endif // ShowCursor(); // !!!!!!TBD - what the heck is this doing here??? this could screw up mouse/cursor management!!! DSpContext_Release(gContext); if (gNumBlockedContexts) // shouldn't happen, but... { for (int i=0; i16?32:16; // !!!!!!!TBD this should be from a pref variable. // !!!!!TBD -- do we need to have a pixelsize in here? // attrib [i++] = AGL_PIXEL_SIZE; // attrib [i++] = bpp; // attrib [i++] = AGL_ALL_RENDERERS; // choose even non-compliant renderers // attrib [i++] = AGL_RENDERER_ID; // choose only renderer type specified in next parameter // attrib [i++] = AGL_RENDERER_ATI_ID; // ATI renderer // attrib [i++] = AGL_RENDERER_GENERIC_ID; // generic renderer // terminate the list. attrib [i++] = AGL_NONE; if (hGD && gNumDevices>1) fmt = aglChoosePixelFormat (&hGD, 1, attrib); // get an appropriate pixel format else fmt = aglChoosePixelFormat(NULL, 0, attrib); // get an appropriate pixel format aglDebugStr (); if (NULL == fmt) { ReportError("Could not find valid pixel format"); return NULL; } ctx = aglCreateContext (fmt, NULL); // Create an AGL context aglDebugStr (); if (NULL == ctx) { ReportError ("Could not create context"); return NULL; } if (!aglSetDrawable (ctx, drawable)) // attach the window to the context { ReportError ("SetDrawable failed"); aglDebugStr (); return NULL; } if (!aglSetCurrentContext (ctx)) // make the context the current context { aglDebugStr (); aglSetDrawable (ctx, NULL); return NULL; } #if !defined(TORQUE_DEBUG) agl_ctx = ctx; // maintain up aglMacro.h context #endif *inFmt = fmt; return ctx; } //----------------------------------------------------------------------------------------- // OpenGL Setup AGLContext SetupAGLFullScreen (AGLPixelFormat *inFmt, GDHandle hGD, int width, int height, int bpp) { GLint attrib[64]; AGLPixelFormat fmt; AGLContext ctx; if (*inFmt!=NULL) aglDestroyPixelFormat(*inFmt); // prev pixel format is no longer needed *inFmt = NULL; // different possible pixel format choices for different renderers // basics requirements are RGBA and double buffer // OpenGL will select acclerated context if available short i = 0; attrib [i++] = AGL_RGBA; // red green blue and alpha attrib [i++] = AGL_DOUBLEBUFFER; // double buffered attrib [i++] = AGL_ACCELERATED; // HWA pixel format only attrib [i++] = AGL_NO_RECOVERY; // HWA pixel format only attrib [i++] = AGL_DEPTH_SIZE; attrib [i++] = bpp>16?32:16; attrib [i++] = AGL_FULLSCREEN; attrib [i++] = AGL_PIXEL_SIZE; attrib [i++] = bpp; // attrib [i++] = AGL_ALL_RENDERERS; // choose even non-compliant renderers // attrib [i++] = AGL_RENDERER_ID; // choose only renderer type specified in next parameter // attrib [i++] = AGL_RENDERER_ATI_ID; // ATI renderer // attrib [i++] = AGL_RENDERER_GENERIC_ID; // generic renderer attrib [i++] = AGL_NONE; if (hGD && gNumDevices>1) fmt = aglChoosePixelFormat (&hGD, 1, attrib); // get an appropriate pixel format else fmt = aglChoosePixelFormat(NULL, 0, attrib); // get an appropriate pixel format if (NULL == fmt) { ReportError("Could not find valid pixel format"); aglDebugStr (); return NULL; } ctx = aglCreateContext (fmt, NULL); // Create an AGL context if (NULL == ctx) { ReportError ("Could not create context"); aglDebugStr (); return NULL; } #ifdef ALLOW_SINGLEMON_CAPTURE #ifndef AGL_FS_CAPTURE_SINGLE #define AGL_FS_CAPTURE_SINGLE 255 /* enable the capture of only a single display for aglFullscreen */ #endif aglEnable(ctx, AGL_FS_CAPTURE_SINGLE); #endif static int hzrate = 0; //gCurrentFrequency; // should be a fairly safe rate on most displays. we could use DSp to get rates, or DM maybe. if (!aglSetFullScreen (ctx, width, height, hzrate, 0)) // attach fulls screen device to the context { ReportError ("SetFullScreen(0Hz) failed"); aglDebugStr (); hzrate = 60; // safe mode. gCurrentFrequency = 60; // so we know what we set... if (!aglSetFullScreen (ctx, width, height, hzrate, 0)) // attach fulls screen device to the context { ReportError ("SetFullScreen(60Hz) failed"); aglDebugStr (); return NULL; } } if (!aglSetCurrentContext (ctx)) // make the context the current context { ReportError ("SetCurrentContext failed"); aglDebugStr (); aglSetDrawable (ctx, NULL); // turn off full screen return NULL; } #if !defined(TORQUE_DEBUG) agl_ctx = ctx; // maintain up aglMacro.h context #endif // aglDestroyPixelFormat(fmt); // pixel format is no longer needed *inFmt = fmt; return ctx; } #pragma mark - //=============================================================================== Boolean sCanDoFullscreen = TRUE; struct CardProfile { const char *vendor; // manufacturer const char *renderer; // driver name bool safeMode; // destroy rendering context for resolution change bool lockArray; // allow compiled vertex arrays bool subImage; // allow glTexSubImage* bool fogTexture; // require bound texture for combine extension bool noEnvColor; // no texture environment color bool clipHigh; // clip high resolutions bool deleteContext; // delete rendering context bool texCompress; // allow texture compression bool interiorLock; // lock arrays for Interior render bool skipFirstFog; // skip first two-pass fogging (dumb 3Dfx hack) bool only16; // inhibit 32-bit resolutions bool noArraysAlpha; // don't use glDrawArrays with a GL_ALPHA texture const char *proFile; // explicit profile of graphic settings }; struct OSCardProfile { const char *vendor; // manufacturer const char *renderer; // driver name }; static Vector sCardProfiles(__FILE__, __LINE__); static Vector sOSCardProfiles(__FILE__, __LINE__); struct ProcessorProfile { U16 clock; // clock range max U16 adjust; // CPU adjust }; static U8 sNumProcessors = 4; static ProcessorProfile sProcessorProfiles[] = { { 400, 0 }, { 600, 5 }, { 800, 10 }, { 1000, 15 }, }; struct SettingProfile { U16 performance; // metric range max const char *settings; // default file }; static U8 sNumSettings = 3; static SettingProfile sSettingProfiles[] = { { 33, "LowProfile.cs" }, { 66, "MediumProfile.cs" }, { 100, "HighProfile.cs" }, }; //------------------------------------------------------------------------------ ConsoleFunction( addCardProfile, void, 16, 16, "(string vendor, string renderer, " " bool safeMode, bool lockArray, bool subImage, " " bool fogTexture, bool noEnvColor, bool clipHigh, " " bool deleteContext, bool texCompress, bool interiorLock, " " bool skipFirstFog, bool only16, bool noArraysAlpha, " " string proFile)") { CardProfile profile; profile.vendor = dStrdup(argv[1]); profile.renderer = dStrdup(argv[2]); profile.safeMode = dAtob(argv[3]); profile.lockArray = dAtob(argv[4]); profile.subImage = dAtob(argv[5]); profile.fogTexture = dAtob(argv[6]); profile.noEnvColor = dAtob(argv[7]); profile.clipHigh = dAtob(argv[8]); profile.deleteContext = dAtob(argv[9]); profile.texCompress = dAtob(argv[10]); profile.interiorLock = dAtob(argv[11]); profile.skipFirstFog = dAtob(argv[12]); profile.only16 = dAtob(argv[13]); profile.noArraysAlpha = dAtob(argv[14]); if (dStrcmp(argv[15],"")) profile.proFile = dStrdup(argv[15]); else profile.proFile = NULL; sCardProfiles.push_back(profile); } ConsoleFunction( addOSCardPRofile, void, 6, 6, "(string vendor, string renderer, " " bool allowOpenGL, bool allowD3D, " " bool preferOpenGL) Mac builds ignore the last three flags.") { OSCardProfile profile; profile.vendor = dStrdup(argv[1]); profile.renderer = dStrdup(argv[2]); /* profile.allowOpenGL = dAtob(argv[3]); profile.allowD3D = dAtob(argv[4]); profile.preferOpenGL = dAtob(argv[5]); */ sOSCardProfiles.push_back(profile); } static void clearCardProfiles() { while (sCardProfiles.size()) { dFree((char *) sCardProfiles.last().vendor); dFree((char *) sCardProfiles.last().renderer); dFree((char *) sCardProfiles.last().proFile); sCardProfiles.decrement(); } } static void clearOSCardProfiles() { while (sOSCardProfiles.size()) { dFree((char *) sOSCardProfiles.last().vendor); dFree((char *) sOSCardProfiles.last().renderer); sOSCardProfiles.decrement(); } } 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; } static void profileSystem(const char *vendor, const char *renderer) { //Con::executef(2, "exec", "scripts/CardProfiles.cs"); execScript("CardProfiles.cs"); const char *os = NULL; char osProfiles[64]; #pragma message("todo: implement full profile checking") os = "MACCARB"; if ( os != NULL ) { dSprintf(osProfiles,64,"%s%sCardProfiles.cs","PPC",os); //Con::executef(2, "exec", osProfiles); execScript(osProfiles); } const char *proFile = NULL; U32 i; for (i = 0; i < sCardProfiles.size(); ++i) if (dStrstr(vendor, sCardProfiles[i].vendor) && (!dStrcmp(sCardProfiles[i].renderer, "*") || dStrstr(renderer, sCardProfiles[i].renderer))) { Con::setBoolVariable("$pref::Video::safeModeOn", sCardProfiles[i].safeMode); Con::setBoolVariable("$pref::OpenGL::disableEXTCompiledVertexArray", !sCardProfiles[i].lockArray); Con::setBoolVariable("$pref::OpenGL::disableSubImage", !sCardProfiles[i].subImage); Con::setBoolVariable("$pref::TS::fogTexture", sCardProfiles[i].fogTexture); Con::setBoolVariable("$pref::OpenGL::noEnvColor", sCardProfiles[i].noEnvColor); Con::setBoolVariable("$pref::Video::clipHigh", sCardProfiles[i].clipHigh); if (!sCardProfiles[i].deleteContext) { Con::setBoolVariable("$pref::Video::deleteContext", false); /* // HACK: The Voodoo3/5 on W2K crash when deleting a rendering context // So we're not deleting it. // Oh, and the Voodoo3 returns a Banshee renderer string under W2K dMemset( &OSVersionInfo, 0, sizeof( OSVERSIONINFO ) ); OSVersionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); if ( GetVersionEx( &OSVersionInfo ) && OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT && OSVersionInfo.dwMajorVersion == 5) Con::setBoolVariable("$pref::Video::deleteContext", false); else Con::setBoolVariable("$pref::Video::deleteContext", true); */ } else Con::setBoolVariable("$pref::Video::deleteContext", true); Con::setBoolVariable("$pref::OpenGL::disableARBTextureCompression", !sCardProfiles[i].texCompress); Con::setBoolVariable("$pref::Interior::lockArrays", sCardProfiles[i].interiorLock); Con::setBoolVariable("$pref::TS::skipFirstFog", sCardProfiles[i].skipFirstFog); Con::setBoolVariable("$pref::Video::only16", sCardProfiles[i].only16); Con::setBoolVariable("$pref::OpenGL::noDrawArraysAlpha", sCardProfiles[i].noArraysAlpha); proFile = sCardProfiles[i].proFile; break; } // defaults U16 glProfile; if (!proFile) { // no driver GL profile -- make one via weighting GL extensions glProfile = 25; glProfile += gGLState.suppARBMultitexture * 25; glProfile += gGLState.suppLockedArrays * 15; glProfile += gGLState.suppVertexArrayRange * 10; glProfile += gGLState.suppTextureEnvCombine * 5; glProfile += gGLState.suppPackedPixels * 5; glProfile += gGLState.suppTextureCompression * 5; glProfile += gGLState.suppS3TC * 5; glProfile += gGLState.suppFXT1 * 5; Con::setBoolVariable("$pref::Video::safeModeOn", true); Con::setBoolVariable("$pref::OpenGL::disableEXTCompiledVertexArray", false); Con::setBoolVariable("$pref::OpenGL::disableSubImage", false); Con::setBoolVariable("$pref::TS::fogTexture", false); Con::setBoolVariable("$pref::OpenGL::noEnvColor", false); Con::setBoolVariable("$pref::Video::clipHigh", false); Con::setBoolVariable("$pref::Video::deleteContext", true); Con::setBoolVariable("$pref::OpenGL::disableARBTextureCompression", false); Con::setBoolVariable("$pref::Interior::lockArrays", true); Con::setBoolVariable("$pref::TS::skipFirstFog", false); Con::setBoolVariable("$pref::Video::only16", false); Con::setBoolVariable("$pref::OpenGL::noDrawArraysAlpha", false); } Con::setVariable("$pref::Video::profiledVendor", vendor); Con::setVariable("$pref::Video::profiledRenderer", renderer); if (!Con::getBoolVariable("$DisableSystemProfiling") && ( dStrcmp(vendor, Con::getVariable("$pref::Video::defaultsVendor")) || dStrcmp(renderer, Con::getVariable("$pref::Video::defaultsRenderer")) )) { if (proFile) { char settings[64]; dSprintf(settings,64,"%s.cs",proFile); //Con::executef(2, "exec", settings); execScript(settings); } else { U16 adjust; // match clock with profile for (i = 0; i < sNumProcessors; ++i) { adjust = sProcessorProfiles[i].adjust; if (Platform::SystemInfo.processor.mhz < sProcessorProfiles[i].clock) break; } const char *settings; // match performance metric with profile for (i = 0; i < sNumSettings; ++i) { settings = sSettingProfiles[i].settings; if (glProfile+adjust <= sSettingProfiles[i].performance) break; } //Con::executef(2, "exec", settings); execScript(settings); } bool match = false; for (i = 0; i < sOSCardProfiles.size(); ++i) if (dStrstr(vendor, sOSCardProfiles[i].vendor) && (!dStrcmp(sOSCardProfiles[i].renderer, "*") || dStrstr(renderer, sOSCardProfiles[i].renderer))) { // Con::setBoolVariable("$pref::Video::allowOpenGL", sOSCardProfiles[i].allowOpenGL); // Con::setBoolVariable("$pref::Video::allowD3D", sOSCardProfiles[i].allowD3D); // Con::setBoolVariable("$pref::Video::preferOpenGL", sOSCardProfiles[i].preferOpenGL); match = true; break; } if (!match) { Con::setBoolVariable("$pref::Video::allowOpenGL", true); // Con::setBoolVariable("$pref::Video::allowD3D", true); Con::setBoolVariable("$pref::Video::preferOpenGL", true); } Con::setVariable("$pref::Video::defaultsVendor", vendor); Con::setVariable("$pref::Video::defaultsRenderer", renderer); } // write out prefs gEvalState.globalVars.exportVariables("$pref::*", "prefs/ClientPrefs.cs", false); clearCardProfiles(); clearOSCardProfiles(); } #pragma mark - //------------------------------------------------------------------------------ OpenGLDevice::OpenGLDevice() { initDevice(); } //------------------------------------------------------------------------------ void OpenGLDevice::initDevice() { // Set the device name: mDeviceName = "OpenGL"; // Never unload a code module aglConfigure(AGL_RETAIN_RENDERERS, GL_TRUE); #if defined(TORQUE_OS_MAC_OSX) && defined(AGL_TARGET_OS_MAC_OSX) {// carbon porting guide says we MUST do this in mach-o Carbon code... aglConfigure(AGL_TARGET_OS_MAC_OSX, GL_TRUE); } #endif // Set some initial conditions: mResolutionList.clear(); // sCanDoFullscreen = FALSE; bool gotEm = enumDisplayModes(platState.hDisplay); if (!gotEm) { mResolutionList.clear(); // fill it with some standard sizes for a machine, just to have something. Point sizes[4] = {{640,480},{800,600},{1024,768},{1280,1024}}; for(int i=0; i<4; i++) { Resolution newRes16( sizes[i].h, sizes[i].v, 16 ); Resolution newRes32( sizes[i].h, sizes[i].v, 32 ); mResolutionList.push_back( newRes16 ); mResolutionList.push_back( newRes32 ); } } } //------------------------------------------------------------------------------ bool OpenGLDevice::activate( U32 width, U32 height, U32 bpp, bool fullScreen ) { Con::printf( "Activating the OpenGL display device..." ); bool needResurrect = false; #pragma message("todo: need to error check on return from agl funcs.") // If the rendering context exists, delete it: /* if (platState.ctx) { Con::printf( "Killing the texture manager..." ); Game->textureKill(); needResurrect = true; Con::printf( "Making the rendering context not current..." ); aglSetCurrentContext(NULL); aglSetDrawable(platState.ctx, NULL); Con::printf( "Deleting the rendering context..." ); aglDestroyContext(platState.ctx); platState.ctx = NULL; } */ static bool onceAlready = false; bool profiled = false; // Set the resolution: if ( !setScreenMode( width, height, bpp, ( fullScreen || mFullScreenOnly ), true, false ) ) return false; // Get original gamma ramp // mRestoreGamma = GetDeviceGammaRamp(platState.appDC, mOriginalRamp); // 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 ); if ( needResurrect ) { // Reload the textures: Con::printf( "Resurrecting the texture manager..." ); Game->textureResurrect(); } //Now that we have video setup, and window established, we do any initialization/checking // of OpenGL interfaces necessary to setup the rendering system. GL_EXT_Init(); Con::setVariable( "$pref::Video::displayDevice", mDeviceName ); /* !!!!!!TBD LATER!!!!!!!! // only do this for the first session if (!profiled && !Con::getBoolVariable("$DisableSystemProfiling") && ( dStrcmp(vendorString, Con::getVariable("$pref::Video::profiledVendor")) || dStrcmp(rendererString, Con::getVariable("$pref::Video::profiledRenderer")) )) { profileSystem(vendorString, rendererString); profiled = true; } if (profiled) { U32 width, height, bpp; if (Con::getBoolVariable("$pref::Video::clipHigh", false)) for (S32 i = mResolutionList.size()-1; i >= 0; --i) if (mResolutionList[i].w > 1152 || mResolutionList[i].h > 864) mResolutionList.erase(i); if (Con::getBoolVariable("$pref::Video::only16", false)) for (S32 i = mResolutionList.size()-1; i >= 0; --i) if (mResolutionList[i].bpp == 32) mResolutionList.erase(i); dSscanf(Con::getVariable("$pref::Video::resolution"), "%d %d %d", &width, &height, &bpp); setScreenMode(width, height, bpp, Con::getBoolVariable("$pref::Video::fullScreen", true), false, false); } */ // Do this here because we now know about the extensions: // if ( gGLState.suppSwapInterval ) setVerticalSync( !Con::getBoolVariable( "$pref::Video::disableVerticalSync" ) ); Con::setBoolVariable("$pref::OpenGL::allowTexGen", true); return true; } //------------------------------------------------------------------------------ // returns TRUE if textures need resurrecting in future... //------------------------------------------------------------------------------ bool OpenGLDevice::GLCleanupHelper() { bool needResurrect = false; Con::printf( "Cleaning up the display device..." ); // !!!!!TBD // NOTE THAT A LARGE CHUNK OF THIS CODE IS SHARED WITH shutdown(), AND THUS SHOULD BE >REALLY< SHARED CODE. // Delete the rendering context: if (platState.ctx) { if (!Video::smNeedResurrect) // as that flag means we already ditched textures... { Con::printf( "Killing the texture manager..." ); Game->textureKill(); needResurrect = true; } Con::printf( "Making sure the rendering context is not current..." ); aglSetCurrentContext(NULL); #if !defined(TORQUE_DEBUG) agl_ctx = NULL; // maintain up aglMacro.h context #endif aglSetDrawable((AGLContext)platState.ctx, NULL); if (0) // !!!!TBD error check { AssertFatal( false, "OpenGLDevice::setScreenMode\naglSetDrawable( ctx, NULL ) failed!" ); return false; } Con::printf( "Deleting the rendering context..." ); // !!!! TBD WHY IS THIS CHECK HERE??? IS IT VALID ON MAC?? WHY NOT IN ACTIVATE??? // if ( Con::getBoolVariable("$pref::Video::deleteContext",true) ) aglDestroyContext((AGLContext)platState.ctx); if (0) // !!!!TBD error check { AssertFatal( false, "OpenGLDevice::setScreenMode\naglDestroyContext failed!" ); return false; } // make sure it is null... platState.ctx = NULL; } // !!!!!TBD HANDLE GAMMA // if (mRestoreGamma) // SetDeviceGammaRamp(platState.appDC, mOriginalRamp); if (gpDSpPort) // == smIsFullScreen... { Con::printf("Shutting down DrawSprocket and restoring Desktop (%dx%dx%d)...", platState.desktopWidth, platState.desktopHeight, platState.desktopBitsPixel ); ShutdownDSp(gpDSpPort); } // on the mac, if the window is still around for any reason, dispose of it now. // DestroyGL or ShutdownDSp will have nuked it already if they created it... if ( platState.appWindow ) { Con::printf( "Destroying the window..." ); if (platState.osX) ReleaseWindow(platState.appWindow); else DisposeWindow(platState.appWindow); platState.appWindow = NULL; } // clear some Res state, so we don't think we're set up already. smCurrentRes.w = 0; smCurrentRes.h = 0; return(needResurrect); } //------------------------------------------------------------------------------ void OpenGLDevice::shutdown() { OSStatus err = noErr; Con::printf( "Shutting down the OpenGL display device..." ); // call our new universal helper function... GLCleanupHelper(); // should be safe to always call. ShowMenuBar(); } //------------------------------------------------------------------------------ // This is the real workhorse function of the DisplayDevice... // static bool wasWindowed = true; bool OpenGLDevice::setScreenMode( U32 width, U32 height, U32 bpp, bool fullScreen, bool forceIt, bool repaint ) { // SANITY CHECK, AS THE FRICKING SCRIPTS PASS IN BAD VALUES. LIKE ZERO. if (!bpp) bpp = platState.desktopBitsPixel; WindowPtr curtain = NULL; char errorMessage[256]; bool newFullScreen = fullScreen; bool safeModeOn = Con::getBoolVariable( "$pref::Video::safeModeOn" ); bool needResurrect = false; Resolution newRes( width, height, bpp ); Resolution oldRes = smCurrentRes; OSStatus err; // !!!!!TBD // if no values changing, return immediately. // this is sort-of an optimization, but also useful to prevent thrashing (which the // current graphic startup would do otherwise...) if (smCurrentRes.w == width && smCurrentRes.h == height && smCurrentRes.bpp == bpp && smIsFullScreen == newFullScreen) return(true); if ( !newFullScreen && mFullScreenOnly ) { Con::warnf( ConsoleLogEntry::General, "OpenGLDevice::setScreenMode - device or desktop color depth does not allow windowed mode!" ); newFullScreen = true; } #pragma message("todo: how to handle picking available resolutions?") // !!!!!!!TBD NOT SURE THAT WOULD DO THE RIGHT THING FOR MAC IN ALL CASES! // THEREFORE, ALWAYS GO DO SHUTDOWN CODE!!! needResurrect = GLCleanupHelper(); // MAC DOESN'T NEED A CURTAIN -- prefers to use gamma fade out/in... // curtain = CreateCurtain( newRes.w, newRes.h ); Con::printf( ">> Attempting to change display settings to %s %dx%dx%d...", newFullScreen?"fullscreen":"windowed", newRes.w, newRes.h, newRes.bpp ); if ( newFullScreen ) smIsFullScreen = true; else if ( smIsFullScreen ) smIsFullScreen = false; // WRITE TO PREFS Con::setBoolVariable( "$pref::Video::fullScreen", smIsFullScreen ); // set our preferred device // default is the main device (which should be the one at [0,0]). GDHandle currDev = GetMainDevice(); int devicenum = Con::getIntVariable("$pref::Video::monitorNum", 0); if (devicenum) { // then find the right device. if (devicenum>gNumDevices || devicenum<1) { // well, then give them default device devicenum = 0; Con::setIntVariable("$pref::Video::monitorNum", devicenum); } else currDev = gDeviceList[devicenum-1]; } platState.hDisplay = currDev; // in case we fail, put back values for the moment, as we munged them in Cleanup. smCurrentRes = oldRes; bool aglReady = false; if (newFullScreen) { #if USE_AGL_FULLSCREEN if (platState.osX) // under osx we can try SetaglFullScreen { short display = devicenum; // !!!! IS THIS CORRECT? !!!!TBD -- need to define a GDHandle to display # mapping if (platState.ctx = SetupAGLFullScreen (&mainfmt, platState.hDisplay, newRes.w, newRes.h, newRes.bpp)) // Setup the OpenGL context { aglReady = true; SetRect (&gRectPort, 0, 0, newRes.w, newRes.h); // l, t, r, b Con::printf("Successfully set up AGLFullscreen support [%d x %d x %d : %dHz]", newRes.w, newRes.h, newRes.bpp, gCurrentFrequency); } else { Con::printf("Failed to set up AGLFullscreen support."); } } #endif if (!aglReady) // not ready yet. try normal SetupAGL. { gpDSpPort = SetupDSp (&(platState.hDisplay), newRes.w, newRes.h, newRes.bpp); // Setup DSp for OpenGL if (gpDSpPort==NULL) // oops { Con::warnf("Failed to establish DrawSprocket monitor control."); return(false); } if (platState.ctx = SetupAGL(&mainfmt, platState.hDisplay, gpDSpPort, newRes.bpp)) // Setup the OpenGL context { GetPortBounds (gpDSpPort, &gRectPort); if (platState.hDisplay) Con::printf("Successfully set up AGL support with DSp Fullscreen [%d x %d x %d : %dHz]", gRectPort.right - gRectPort.left, gRectPort.bottom - gRectPort.top, (**(**platState.hDisplay).gdPMap).pixelSize, gCurrentFrequency); else Con::printf("Successfully set up GL support [%d x %d]", gRectPort.right - gRectPort.left, gRectPort.bottom - gRectPort.top); } else { Con::printf("Failed to set up AGL context support."); return(false); } } } //if we haven't yet created/assigned a plat window, need to do so now. if (!platState.appWindow ) { platState.appWindow = CreateOpenGLWindow( platState.hDisplay, newRes.w, newRes.h, false ); if ( !platState.appWindow ) { AssertFatal( false, "OpenGLDevice::setScreenMode - Failed to create a new window!" ); return false; } else Con::printf( "Created new %swindow for GL [%d x %d].", newFullScreen ? "Fullscreen " : "", newRes.w, newRes.h ); } if (!newFullScreen) { CGrafPtr port = GetWindowPort(platState.appWindow); AGLDrawable drawto = port; // just to be clear. GetPortBounds (port, &gRectPort); if (NULL == (platState.ctx = SetupAGL(&mainfmt, platState.hDisplay, drawto, newRes.bpp))) // Setup the OpenGL context { Con::warnf("Failed to set up AGL windowed support."); return false; } Con::printf("Successfully set up AGL windowed support [%d x %d]", gRectPort.right - gRectPort.left, gRectPort.bottom - gRectPort.top); } //PRINT BACK THE FORMAT WE GOT!!!! !!!!TBD //qwglDescribePixelFormat( platState.appDC, chosenFormat, sizeof( pfd ), &pfd ); //Con::printf( "Pixel format set:" ); //Con::printf( " %d color bits, %d depth bits, %d stencil bits", platState.fmt.cColorBits, pfd.cDepthBits, pfd.cStencilBits ); // validate that this function is available in CFM on OSX... if ((Ptr) kUnresolvedCFragSymbolAddress != (Ptr) glPixelStorei) glPixelStorei(GL_UNPACK_ALIGNMENT, 1); if ( needResurrect ) { // Reload the textures: Con::printf( "Resurrecting the texture manager..." ); Game->textureResurrect(); } // if ( gGLState.suppSwapInterval ) // mac always supports this, basically. setVerticalSync( !Con::getBoolVariable( "$pref::Video::disableVerticalSync" ) ); smCurrentRes = newRes; // !!!TBD // this is kinda redundant in the mac schema... Platform::setWindowSize( newRes.w, newRes.h ); // WRITE TO PREFS char tempBuf[15]; if (smIsFullScreen) { dSprintf( tempBuf, sizeof( tempBuf ), "%d %d %d", smCurrentRes.w, smCurrentRes.h, smCurrentRes.bpp ); Con::setVariable( "$pref::Video::resolution", tempBuf ); if (wasWindowed) HideMenuBar(); wasWindowed = false; } else // windowed { dSprintf( tempBuf, sizeof( tempBuf ), "%d %d", smCurrentRes.w, smCurrentRes.h ); Con::setVariable( "$pref::Video::windowedRes", tempBuf ); wasWindowed = true; ShowMenuBar(); // should be safe to always call... } // if ( curtain ) // DisposeWindow( curtain ); if ( repaint ) Con::evaluate( "resetCanvas();" ); return true; } //------------------------------------------------------------------------------ void OpenGLDevice::swapBuffers() { // TBD!!!!! should this be setting to platState.ctx // in future, support multiple contexts? #if later //!!! //dhc - also adding in a sanity check here. AssertISV((AGLContext)platState.ctx, "OpenGLDevice::swapBuffers -- No GL context in place!"); #endif if (platState.ctx) aglSwapBuffers(aglGetCurrentContext()); #if defined(TORQUE_DEBUG) if(gOutlineEnabled) glClear(GL_COLOR_BUFFER_BIT); #endif } //------------------------------------------------------------------------------ const char* OpenGLDevice::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 ); 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 OpenGLDevice::getGammaCorrection(F32 &g) { U16 ramp[256*3]; #pragma message("todo: gamma") /* if (!GetDeviceGammaRamp(platState.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 OpenGLDevice::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)); bool t = false; #pragma message("todo: gamma") // t = SetDeviceGammaRamp(platState.appDC, ramp); return t; } //------------------------------------------------------------------------------ bool OpenGLDevice::setVerticalSync( bool on ) { // HACK on = false; // !!!!!TBD for some reason, the disable pref is set to one, the value is grabbed and flipped // and it STILL comes through as non-zero!!! // Apple has an internal vsync flag: if (platState.ctx) { GLint val = on ? 1 : 0; return(GL_FALSE != aglSetInteger((AGLContext)platState.ctx, AGL_SWAP_INTERVAL, &val)); } return(false); } //------------------------------------------------------------------------------ DisplayDevice* OpenGLDevice::create() { bool result = true; bool fullScreenOnly = false; // validate OpenGL existance/linkage here for now. if ((Ptr) kUnresolvedCFragSymbolAddress == (Ptr) glBegin || (Ptr) kUnresolvedCFragSymbolAddress == (Ptr) aglChoosePixelFormat) { ReportError ("OpenGL not installed"); return NULL; } if (!InitDSp()) return(NULL); #if !defined(TORQUE_DEBUG) // set up a dummy default agl context // this will be replaced later with the window's context, but // since we use aglMacro.h in release builds, we need agl_ctx to be valid at any time. InitDummyAgl(); #endif // go find all the screen devices, in device order. GDHandle hDevice; gNumDevices = 0; // always at least one screen device... hDevice = DMGetFirstScreenDevice (true); // check number of screens gDeviceList[gNumDevices++] = hDevice; while(hDevice = DMGetNextScreenDevice (hDevice, true)) gDeviceList[gNumDevices++] = hDevice; // set our preferred device // default is the main device (which should be the one at [0,0]). GDHandle currDev = GetMainDevice(); int devicenum = Con::getIntVariable("$pref::Video::monitorNum", 0); if (devicenum) { // then find the right device. if (devicenum>gNumDevices || devicenum<1) { // well, then give them default device devicenum = 0; Con::setIntVariable("$pref::Video::monitorNum", devicenum); } else currDev = gDeviceList[devicenum-1]; } platState.hDisplay = currDev; #pragma message("todo: ::create() full gl acceleration/availability test") if ( result ) { OpenGLDevice* newOGLDevice = new OpenGLDevice(); if ( newOGLDevice ) { newOGLDevice->mFullScreenOnly = fullScreenOnly; return (DisplayDevice*) newOGLDevice; } else return NULL; } else return NULL; } #if defined(TORQUE_DEBUG) static U32 getIndex(GLenum type, const void *indices, U32 i) { if(type == GL_UNSIGNED_BYTE) return ((U8 *) indices)[i]; else if(type == GL_UNSIGNED_SHORT) return ((U16 *) indices)[i]; else return ((U32 *) indices)[i]; } void glOutlineDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) { if(mode == GL_POLYGON) mode = GL_LINE_LOOP; if(mode == GL_POINTS || mode == GL_LINE_STRIP || mode == GL_LINE_LOOP || mode == GL_LINES) glDrawElements( mode, count, type, indices ); else { glBegin(GL_LINES); if(mode == GL_TRIANGLE_STRIP) { U32 i; for(i = 0; i < count - 1; i++) { glArrayElement(getIndex(type, indices, i)); glArrayElement(getIndex(type, indices, i + 1)); if(i + 2 != count) { glArrayElement(getIndex(type, indices, i)); glArrayElement(getIndex(type, indices, i + 2)); } } } else if(mode == GL_TRIANGLE_FAN) { for(U32 i = 1; i < count; i ++) { glArrayElement(getIndex(type, indices, 0)); glArrayElement(getIndex(type, indices, i)); if(i != count - 1) { glArrayElement(getIndex(type, indices, i)); glArrayElement(getIndex(type, indices, i + 1)); } } } else if(mode == GL_TRIANGLES) { for(U32 i = 3; i <= count; i += 3) { glArrayElement(getIndex(type, indices, i - 3)); glArrayElement(getIndex(type, indices, i - 2)); glArrayElement(getIndex(type, indices, i - 2)); glArrayElement(getIndex(type, indices, i - 1)); glArrayElement(getIndex(type, indices, i - 3)); glArrayElement(getIndex(type, indices, i - 1)); } } else if(mode == GL_QUADS) { for(U32 i = 4; i <= count; i += 4) { glArrayElement(getIndex(type, indices, i - 4)); glArrayElement(getIndex(type, indices, i - 3)); glArrayElement(getIndex(type, indices, i - 3)); glArrayElement(getIndex(type, indices, i - 2)); glArrayElement(getIndex(type, indices, i - 2)); glArrayElement(getIndex(type, indices, i - 1)); glArrayElement(getIndex(type, indices, i - 4)); glArrayElement(getIndex(type, indices, i - 1)); } } else if(mode == GL_QUAD_STRIP) { if(count < 4) return; glArrayElement(getIndex(type, indices, 0)); glArrayElement(getIndex(type, indices, 1)); for(U32 i = 4; i <= count; i += 2) { glArrayElement(getIndex(type, indices, i - 4)); glArrayElement(getIndex(type, indices, i - 2)); glArrayElement(getIndex(type, indices, i - 3)); glArrayElement(getIndex(type, indices, i - 1)); glArrayElement(getIndex(type, indices, i - 2)); glArrayElement(getIndex(type, indices, i - 1)); } } glEnd(); } } void glOutlineDrawArrays(GLenum mode, GLint first, GLsizei count) { if(mode == GL_POLYGON) mode = GL_LINE_LOOP; if(mode == GL_POINTS || mode == GL_LINE_STRIP || mode == GL_LINE_LOOP || mode == GL_LINES) glDrawArrays( mode, first, count ); else { glBegin(GL_LINES); if(mode == GL_TRIANGLE_STRIP) { U32 i; for(i = 0; i < count - 1; i++) { glArrayElement(first + i); glArrayElement(first + i + 1); if(i + 2 != count) { glArrayElement(first + i); glArrayElement(first + i + 2); } } } else if(mode == GL_TRIANGLE_FAN) { for(U32 i = 1; i < count; i ++) { glArrayElement(first); glArrayElement(first + i); if(i != count - 1) { glArrayElement(first + i); glArrayElement(first + i + 1); } } } else if(mode == GL_TRIANGLES) { for(U32 i = 3; i <= count; i += 3) { glArrayElement(first + i - 3); glArrayElement(first + i - 2); glArrayElement(first + i - 2); glArrayElement(first + i - 1); glArrayElement(first + i - 3); glArrayElement(first + i - 1); } } else if(mode == GL_QUADS) { for(U32 i = 4; i <= count; i += 4) { glArrayElement(first + i - 4); glArrayElement(first + i - 3); glArrayElement(first + i - 3); glArrayElement(first + i - 2); glArrayElement(first + i - 2); glArrayElement(first + i - 1); glArrayElement(first + i - 4); glArrayElement(first + i - 1); } } else if(mode == GL_QUAD_STRIP) { if(count < 4) return; glArrayElement(first + 0); glArrayElement(first + 1); for(U32 i = 4; i <= count; i += 2) { glArrayElement(first + i - 4); glArrayElement(first + i - 2); glArrayElement(first + i - 3); glArrayElement(first + i - 1); glArrayElement(first + i - 2); glArrayElement(first + i - 1); } } glEnd(); } } ConsoleFunction(GLEnableOutline,void,2,2,"GLEnableOutline( true | false ) - sets whether to render wireframe") { gOutlineEnabled = dAtob(argv[1]); if(gOutlineEnabled) { glDrawElementsProcPtr = glOutlineDrawElements; glDrawArraysProcPtr = glOutlineDrawArrays; Con::printf("swapped to outline mode funcs"); } else { glDrawElementsProcPtr = glDrawElements; glDrawArraysProcPtr = glDrawArrays; Con::printf("swapped to normal funcs"); } } #endif