/* ** Command & Conquer Generals(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: W3DDisplay.cpp /////////////////////////////////////////////////////// // // W3D Implementation for the Game Display which is responsible for creating // and maintaning the entire visual display // // Author: Colin Day, April 2001 // /////////////////////////////////////////////////////////////////////////////// static void drawFramerateBar(void); // SYSTEM INCLUDES //////////////////////////////////////////////////////////// #include #include #include #include // USER INCLUDES ////////////////////////////////////////////////////////////// #include "Common/ThingFactory.h" #include "Common/GameEngine.h" #include "Common/GlobalData.h" #include "Common/PerfTimer.h" #include "Common/FileSystem.h" #include "Common/LocalFileSystem.h" #include "Common/Player.h" #include "Common/PlayerList.h" #include "Common/ThingTemplate.h" #include "Common/GameLOD.h" #include "Common/DrawModule.h" #include "GameLogic/AIPathfind.h" #include "GameClient/Drawable.h" #include "GameClient/GameText.h" #include "GameClient/GraphDraw.h" #include "GameClient/Line2D.h" #include "GameClient/Mouse.h" #include "GameClient/GlobalLanguage.h" #include "GameClient/Water.h" #include "GameNetwork/NetworkInterface.h" #include "Common/ModelState.h" #include "Lib/BaseType.h" #include "W3DDevice/Common/W3DConvert.h" #include "W3DDevice/GameClient/W3DAssetManager.h" #include "W3DDevice/GameClient/W3DGameClient.h" #include "W3DDevice/GameClient/W3DFileSystem.h" #include "W3DDevice/GameClient/W3DDynamicLight.h" #include "W3DDevice/GameClient/HeightMap.h" #include "W3DDevice/GameClient/WorldHeightMap.h" #include "W3DDevice/GameClient/W3DScene.h" #include "W3DDevice/GameClient/W3DTerrainTracks.h" #include "W3DDevice/GameClient/W3DWater.h" #include "W3DDevice/GameClient/W3DVideoBuffer.h" #include "W3DDevice/GameClient/W3DShaderManager.h" #include "W3DDevice/GameClient/W3DDebugDisplay.h" #include "W3DDevice/GameClient/W3DProjectedShadow.h" #include "W3DDevice/GameClient/W3DShroud.h" #include "WWMath/WWMath.h" #include "WWLib/Registry.h" #include "WW3D2/WW3D.h" #include "WW3D2/PredLod.h" #include "WW3D2/Part_Emt.h" #include "WW3D2/Part_Ldr.h" #include "WW3D2/DX8Caps.h" #include "WW3D2/WW3DFormat.h" #include "WW3D2/agg_def.h" #include "WW3D2/Render2DSentence.h" #include "WW3D2/SortingRenderer.h" #include "WW3D2/Textureloader.h" #include "WW3D2/DX8WebBrowser.h" #include "WW3D2/Mesh.h" #include "WW3D2/HLOD.h" #include "WW3D2/Meshmatdesc.h" #include "WW3D2/Meshmdl.h" #include "WW3D2/rddesc.h" #include "targa.h" #include "Lib/BaseType.h" #include "GameLogic/ScriptEngine.h" // For TheScriptEngine - jkmcd #include "GameLogic/GameLogic.h" #ifdef DUMP_PERF_STATS #include "GameLogic/PartitionManager.h" #endif #include "WinMain.h" #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif // DEFINE AND ENUMS /////////////////////////////////////////////////////////// #define W3D_DISPLAY_DEFAULT_BIT_DEPTH 32 #define no_SAMPLE_DYNAMIC_LIGHT 1 #ifdef SAMPLE_DYNAMIC_LIGHT static W3DDynamicLight * theDynamicLight = NULL; static Real theLightXOffset = 0.1f; static Real theLightYOffset = 0.07f; static Int theFlashCount = 0; #endif //***************************************************************************************** //***************************************************************************************** //**** Start Statistical Dump ************************************************************* //***************************************************************************************** #ifdef DUMP_PERF_STATS #include class StatDumpClass { public: StatDumpClass( const char *fname ); ~StatDumpClass(); void dumpStats(); protected: FILE *m_fp; }; //============================================================================= //Open the file once at the beginning of the game -- everything appends to it. //============================================================================= StatDumpClass::StatDumpClass( const char *fname ) { char buffer[ _MAX_PATH ]; GetModuleFileName( NULL, buffer, sizeof( buffer ) ); char *pEnd = buffer + strlen( buffer ); while( pEnd != buffer ) { if( *pEnd == '\\' ) { *pEnd = 0; break; } pEnd--; } AsciiString fullPath; fullPath.format( "%s\\%s", buffer, fname ); m_fp = fopen( fullPath.str(), "wt" ); } //============================================================================= //Close the file at the end of the application //============================================================================= StatDumpClass::~StatDumpClass() { if( m_fp ) { fclose( m_fp ); } } static const char *getCurrentTimeString(void) { time_t aclock; time(&aclock); struct tm *newtime = localtime(&aclock); return asctime(newtime); } //============================================================================= //Dump the stats //============================================================================= void StatDumpClass::dumpStats() { if( !m_fp ) { return; } //static char buf[1024]; fprintf( m_fp, "----------------------------------------------------------------\n" ); fprintf( m_fp, "Performance Statistical Dump -- Frame %d\n", TheGameLogic->getFrame() ); fprintf( m_fp, "Time:\t%s", getCurrentTimeString() ); fprintf( m_fp, "Map:\t%s\n", TheGlobalData->m_mapName.str()); fprintf( m_fp, "Side:\t%s\n", ThePlayerList->getLocalPlayer()->getSide().str()); fprintf( m_fp, "----------------------------------------------------------------\n" ); //FPS Real fps = TheDisplay->getAverageFPS(); fprintf( m_fp, "Average FPS: %.1f (%.5f msec)\n", fps, 1000.0f / fps ); //Rendering stats fprintf( m_fp, "Draws: %d Skins: %d SortedPolys: %d SkinPolys: %d\n",(Int)Debug_Statistics::Get_Draw_Calls(), (Int)Debug_Statistics::Get_DX8_Skin_Renders(), (Int)Debug_Statistics::Get_Sorting_Polygons(), (Int)Debug_Statistics::Get_DX8_Skin_Polygons()); //Object stats UnsignedInt objCount = TheGameLogic->getObjectCount(); UnsignedInt objScreenCount = TheGameClient->getRenderedObjectCount(); fprintf( m_fp, "Objects: %d in world (%d onscreen)\n", objCount, objScreenCount ); //AI stats UnsignedInt numAI, numMoving, numAttacking, numWaitingForPath, overallFailedPathfinds; TheGameLogic->getAIMetricsStatistics( &numAI, &numMoving, &numAttacking, &numWaitingForPath, &overallFailedPathfinds ); fprintf( m_fp, "\n" ); fprintf( m_fp, "AI Statistics:\n" ); fprintf( m_fp, " Total AI Objects: %d\n", numAI ); fprintf( m_fp, " -moving: %d\n", numMoving ); fprintf( m_fp, " -attacking: %d\n", numAttacking ); fprintf( m_fp, " -waiting for path: %d\n", numWaitingForPath ); fprintf( m_fp, " Total failed pathfinds: %d\n", overallFailedPathfinds ); fprintf( m_fp, "\n" ); // Script stats Real timeLastFrame, slowScript1, slowScript2; AsciiString slowScripts = TheScriptEngine->getStats(&timeLastFrame, &slowScript1, &slowScript2); fprintf( m_fp, "\n" ); fprintf( m_fp, "Script Engine Statistics:\n" ); fprintf( m_fp, " Total time last frame: %.5f msec\n", timeLastFrame*1000 ); fprintf( m_fp, " -Slowest 2 scripts %s\n", slowScripts.str() ); fprintf( m_fp, " -Slowest 2 script times %.5f msec, %.5f msec \n", slowScript1*1000, slowScript2*1000 ); fprintf( m_fp, "\n" ); //PartitionMgr stats double gcoTimeThisFrameTotal, gcoTimeThisFrameAvg; ThePartitionManager->getPMStats(gcoTimeThisFrameTotal, gcoTimeThisFrameAvg); fprintf(m_fp, "Partition Manager Statistics:\n"); fprintf(m_fp, " Total time for object scans this frame is %.5f msec\n", gcoTimeThisFrameTotal); fprintf(m_fp, " Avg time per object scan this frame is %.5f msec\n", gcoTimeThisFrameAvg); fprintf( m_fp, "\n" ); // setup texture stats Debug_Statistics::Record_Texture_Mode(Debug_Statistics::RECORD_TEXTURE_SIMPLE/*RECORD_TEXTURE_NONE*/); fprintf( m_fp, "Video Statistics:\n" ); //Particle system stats fprintf( m_fp, " Particle Systems: %d\n", TheParticleSystemManager->getParticleSystemCount() ); Int totalParticles = TheParticleSystemManager->getParticleCount(); Int onScreenParticleCount = TheParticleSystemManager->getOnScreenParticleCount(); fprintf( m_fp, " Particles: %d in world (%d onscreen)\n", totalParticles, onScreenParticleCount ); // polygons this frame Int polyPerFrame = Debug_Statistics::Get_DX8_Polygons(); Int polyPerSecond = (Int)(polyPerFrame * fps); fprintf( m_fp, " Polygons: %d per frame (%d per second)\n", polyPerFrame, polyPerSecond ); // vertices this frame fprintf( m_fp, " Vertices: %d\n", Debug_Statistics::Get_DX8_Vertices() ); // // I'm adjusting the texture memory usage counter by subtracting // out the terrain alpha texture (since it's really == terrain texture). // fprintf( m_fp, " Video RAM: %d\n", Debug_Statistics::Get_Record_Texture_Size() - 1376256 ); // terrain stats fprintf( m_fp, " 3-Way Blends: %d, Shoreline Blends: %d\n", TheTerrainRenderObject->getNumExtraBlendTiles(), TheTerrainRenderObject->getNumShoreLineTiles() ); fprintf( m_fp, "\n" ); #if defined(_DEBUG) || defined(_INTERNAL) TheAudio->audioDebugDisplay( NULL, NULL, m_fp ); fprintf( m_fp, "\n" ); #endif #ifdef MEMORYPOOL_DEBUG //Report memory usage. TheMemoryPoolFactory->debugMemoryReport( REPORT_FACTORYINFO | REPORT_POOLINFO, 0, 0, m_fp ); #else fprintf( m_fp, "Memory Report -- unavailable (build doesn't have MEMORYPOOL_DEBUG defined)\n" ); #endif fprintf( m_fp, "\n" ); fprintf( m_fp, "%s", TheSubsystemList->dumpTimesForAll().str()); fprintf( m_fp, "----------------------------------------------------------------\n" ); fprintf( m_fp, "END -- Frame %d\n", TheGameLogic->getFrame() ); fprintf( m_fp, "----------------------------------------------------------------\n\n\n" ); fflush(m_fp); } StatDumpClass TheStatDump("StatisticsDump.txt"); #endif //DUMP_PERF_STATS //***************************************************************************************** //**** End Statistical Dump *************************************************************** //***************************************************************************************** //***************************************************************************************** /////////////////////////////////////////////////////////////////////////////// // DEFINITIONS //////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// //============================================================================= RTS3DScene *W3DDisplay::m_3DScene = NULL; RTS2DScene *W3DDisplay::m_2DScene = NULL; RTS3DInterfaceScene *W3DDisplay::m_3DInterfaceScene = NULL; W3DAssetManager *W3DDisplay::m_assetManager = NULL; //============================================================================= // note, can't use the ones from PerfTimer.h 'cuz they are currently // only valid when "-vtune" is used... (srj) inline Int64 getPerformanceCounter() { Int64 tmp; QueryPerformanceCounter((LARGE_INTEGER*)&tmp); return tmp; } inline Int64 getPerformanceCounterFrequency() { Int64 tmp; QueryPerformanceFrequency((LARGE_INTEGER*)&tmp); return tmp; } // W3DDisplay::W3DDisplay ===================================================== /** */ //============================================================================= W3DDisplay::W3DDisplay() { Int i; m_initialized = false; m_assetManager = NULL; m_3DScene = NULL; m_2DScene = NULL; m_3DInterfaceScene = NULL; m_averageFPS = TheGlobalData->m_framesPerSecondLimit; #if defined(_DEBUG) || defined(_INTERNAL) m_timerAtCumuFPSStart = 0; #endif for (i=0; ifreeDisplayString(m_displayStrings[i]); // delete 2D renderer if( m_2DRender ) { m_2DRender->Reset(); delete m_2DRender; m_2DRender = NULL; } // end if // // delete all our views now since they are W3D views and we need to // free them BEFORE we shutdown W3D // Display::deleteViews(); REF_PTR_RELEASE( m_3DScene ); REF_PTR_RELEASE( m_2DScene ); REF_PTR_RELEASE( m_3DInterfaceScene ); for (Int j=0; jFree_Assets(); delete m_assetManager; WW3D::Shutdown(); WWMath::Shutdown(); DX8WebBrowser::Shutdown(); delete TheW3DFileSystem; TheW3DFileSystem = NULL; } // end ~W3DDisplay /*Return number of screen modes supported by the current device*/ Int W3DDisplay::getDisplayModeCount(void) { const RenderDeviceDescClass &devDesc=WW3D::Get_Render_Device_Desc(0); const DynamicVectorClass &resolutions=devDesc.Enumerate_Resolutions(); Int numResolutions=0; /* Bool needStencil=false; Bool needDestinationAlpha=false; Int minBitDepth=16; //Walk through all resolutions and determine which ones are compatible with other settings //chosen by user. For example, 32-bit may be required for shadows, occlusion, soft water edge, etc. if (TheGlobalData->m_useShadowVolumes || (TheGlobalData->m_enableBehindBuildingMarkers && TheGameLogic->getShowBehindBuildingMarkers())) needStencil=true; if (TheGlobalData->m_showSoftWaterEdge) { minBitDepth=32; } */ for (int res = 0; res < resolutions.Count (); res ++) { // Is this the resolution we are looking for? if (resolutions[res].BitDepth >= 24 && resolutions[res].Width >= 800 && (fabs((Real)resolutions[res].Width/(Real)resolutions[res].Height - 1.3333f)) < 0.01f) //only accept 4:3 aspect ratio modes. { numResolutions++; } } return numResolutions; } void W3DDisplay::getDisplayModeDescription(Int modeIndex, Int *xres, Int *yres, Int *bitDepth) { Int numResolutions=0; const RenderDeviceDescClass &devDesc=WW3D::Get_Render_Device_Desc(0); const DynamicVectorClass &resolutions=devDesc.Enumerate_Resolutions(); for (int res = 0; res < resolutions.Count (); res ++) { // Is this the resolution we are looking for? if (resolutions[res].BitDepth >= 24 && resolutions[res].Width >= 800 && (fabs((Real)resolutions[res].Width/(Real)resolutions[res].Height - 1.3333f)) < 0.01f) //only accept 4:3 aspect ratio modes. { if (numResolutions == modeIndex) { //found the mode *xres=resolutions[res].Width; *yres=resolutions[res].Height; *bitDepth=resolutions[res].BitDepth; return; } numResolutions++; } } } void W3DDisplay::setGamma(Real gamma, Real bright, Real contrast, Bool calibrate) { if (m_windowed) return; //we don't allow gamma to change in window because it would affect desktop. DX8Wrapper::Set_Gamma(gamma,bright,contrast,calibrate, false); } /*Giant hack in order to keep the game from getting stuck when alt-tabbing*/ void Reset_D3D_Device(bool active) { if (TheDisplay && WW3D::Is_Initted() && !TheDisplay->getWindowed()) { if (active) { //switch back to desired mode when user alt-tabs back into game WW3D::Set_Render_Device( WW3D::Get_Render_Device(),TheDisplay->getWidth(),TheDisplay->getHeight(),TheDisplay->getBitDepth(),TheDisplay->getWindowed(),true, true); OSVERSIONINFO osvi; osvi.dwOSVersionInfoSize=sizeof(OSVERSIONINFO); if (GetVersionEx(&osvi)) { //check if we're running Win9x variant since they have buggy alt-tab that requires //reloading all textures. if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { //only do this on Win9x boxes because it makes alt-tab very slow. WW3D::_Invalidate_Textures(); } } } else { //switch to windowed mode whenever the user alt-tabs out of game. Don't restore assets after reset since we'll do it when returning. WW3D::Set_Render_Device( WW3D::Get_Render_Device(),TheDisplay->getWidth(),TheDisplay->getHeight(),TheDisplay->getBitDepth(),TheDisplay->getWindowed(),true, true, false); } } } /** Set resolution of display */ //============================================================================= Bool W3DDisplay::setDisplayMode( UnsignedInt xres, UnsignedInt yres, UnsignedInt bitdepth, Bool windowed ) { if (WW3D_ERROR_OK == WW3D::Set_Device_Resolution(xres,yres,bitdepth,windowed,true)) { Render2DClass::Set_Screen_Resolution(RectClass(0, 0, xres, yres)); Display::setDisplayMode(xres, yres, bitdepth, windowed); return TRUE; } //set back to the original mode. WW3D::Set_Device_Resolution(getWidth(),getHeight(),getBitDepth(),getWindowed(), true); Render2DClass::Set_Screen_Resolution(RectClass(0, 0, getWidth(),getHeight())); Display::setDisplayMode(getWidth(),getHeight(),getBitDepth(), getWindowed()); return FALSE; //did not change to a new mode. } /** Set width of display */ //============================================================================= void W3DDisplay::setWidth( UnsignedInt width ) { // extending functionality Display::setWidth( width ); // our 2D renderer will use mapping coords to make (0,0) the upper left // of the screen with (width,height) at the lower right m_2DRender->Set_Coordinate_Range( RectClass( 0, 0, getWidth(), getHeight() ) ); } // end set width // W3DDisplay::setHeight ====================================================== /** Set height of display */ //============================================================================= void W3DDisplay::setHeight( UnsignedInt height ) { // extending functionality Display::setHeight( height ); // our 2D renderer will use mapping coords to make (0,0) the upper left // of the screen with (width,height) at the lower right m_2DRender->Set_Coordinate_Range( RectClass( 0, 0, getWidth(), getHeight() ) ); } // end set height // W3DDisplay::initAssets ===================================================== /** */ //============================================================================= void W3DDisplay::initAssets( void ) { } // end initAssets // W3DDisplay::init3DScene ==================================================== /** */ //============================================================================= void W3DDisplay::init3DScene( void ) { } // end init3DScene // W3DDisplay::init2DScene ==================================================== /** This is the 2D scene, you can use it to draw on a 2D plane over the * 3D background */ //============================================================================= void W3DDisplay::init2DScene( void ) { } // end init2DScene // W3DDisplay::init =========================================================== /** Initialize or re-initialize the W3D display system. Here we need to * create our window, and get our 3D hardware setup and online */ //============================================================================= void W3DDisplay::init( void ) { // // call our base class init, this method should be able to handle re-entry // with its own logic // Display::init(); // handle re-entry for ourselves if( m_initialized ) { /// @todo W3DDisplay needs RE-init logic! return; } // end if // Override the W3D File system TheW3DFileSystem = NEW W3DFileSystem; // init the Westwood math library WWMath::Init(); // create our 3D interface scene m_3DInterfaceScene = NEW_REF( RTS3DInterfaceScene, () ); m_3DInterfaceScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) ); // create our 2D scene m_2DScene = NEW_REF( RTS2DScene, () ); m_2DScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) ); // create our 3D scene m_3DScene =NEW_REF( RTS3DScene, () ); #if defined(_DEBUG) || defined(_INTERNAL) if( TheGlobalData->m_wireframe ) m_3DScene->Set_Polygon_Mode( SceneClass::LINE ); #endif //============================================================================ // m_myLight = NEW_REF //============================================================================ Int lindex; for (lindex=0; lindexm_numGlobalLights; lindex++) { m_myLight[lindex] = NEW_REF( LightClass, (LightClass::DIRECTIONAL) ); } setTimeOfDay( TheGlobalData->m_timeOfDay ); //set each light to correct values for given time for (lindex=0; lindexm_numGlobalLights; lindex++) { m_3DScene->setGlobalLight( m_myLight[lindex], lindex ); } #ifdef SAMPLE_DYNAMIC_LIGHT theDynamicLight = NEW_REF(W3DDynamicLight, ()); Real red = 1; Real green = 1; Real blue = 0; if(red==0 && blue==0 && green==0) { red = green = blue = 1; } theDynamicLight->Set_Ambient( Vector3( red, green, blue ) ); theDynamicLight->Set_Diffuse( Vector3( red, green, blue) ); theDynamicLight->Set_Position(Vector3(0, 0, 4)); theDynamicLight->Set_Far_Attenuation_Range(1, 8); // Note: Don't Add_Render_Object dynamic lights. m_3DScene->addDynamicLight( theDynamicLight ); #endif // create a new asset manager m_assetManager = NEW W3DAssetManager; m_assetManager->Register_Prototype_Loader(&_ParticleEmitterLoader ); m_assetManager->Register_Prototype_Loader(&_AggregateLoader); m_assetManager->Set_WW3D_Load_On_Demand( true ); if (TheGlobalData->m_incrementalAGPBuf) { SortingRendererClass::SetMinVertexBufferSize(1); } if (WW3D::Init( ApplicationHWnd ) != WW3D_ERROR_OK) throw ERROR_INVALID_D3D; //failed to initialize. User probably doesn't have DX 8.1 WW3D::Set_Prelit_Mode( WW3D::PRELIT_MODE_LIGHTMAP_MULTI_PASS ); WW3D::Set_Collision_Box_Display_Mask(0x00); ///m_windowed ); // create a 2D renderer helper m_2DRender = NEW Render2DClass; DEBUG_ASSERTCRASH( m_2DRender, ("Cannot create Render2DClass") ); // set our default width and height and bit depth /// @todo we should set this according to options read from a file setWidth( TheGlobalData->m_xResolution ); setHeight( TheGlobalData->m_yResolution ); setBitDepth( W3D_DISPLAY_DEFAULT_BIT_DEPTH ); if( WW3D::Set_Render_Device( 0, getWidth(), getHeight(), getBitDepth(), getWindowed(), true ) != WW3D_ERROR_OK ) { // Getting the device at the default bit depth (32) didn't work, so try // getting a 16 bit display. (Voodoo 1-3 only supported 16 bit.) jba. setBitDepth( 16 ); if( WW3D::Set_Render_Device( 0, getWidth(), getHeight(), getBitDepth(), getWindowed(), true ) != WW3D_ERROR_OK ) { WW3D::Shutdown(); WWMath::Shutdown(); throw ERROR_INVALID_D3D; //failed to initialize. User probably doesn't have DX 8.1 DEBUG_ASSERTCRASH( 0, ("Unable to set render device\n") ); return; } } // end if //Check if level was never set and default to setting most suitable for system. if (TheGameLODManager->getStaticLODLevel() == STATIC_GAME_LOD_UNKNOWN) TheGameLODManager->setStaticLODLevel(TheGameLODManager->findStaticLODLevel()); else { //Static LOD level was applied during GameLOD manager init except for texture reduction //which needs to be applied here. Int txtReduction=TheWritableGlobalData->m_textureReductionFactor; if (txtReduction > 0) { WW3D::Set_Texture_Reduction(txtReduction,6); //Tell LOD manager that texture reduction was applied. TheGameLODManager->setCurrentTextureReduction(txtReduction); } } if (TheGlobalData->m_displayGamma != 1.0f) setGamma(TheGlobalData->m_displayGamma,0.0f,1.0f,FALSE); initAssets(); init2DScene(); init3DScene(); W3DShaderManager::init(); // Create and initialize the debug display m_nativeDebugDisplay = NEW W3DDebugDisplay(); m_debugDisplay = m_nativeDebugDisplay; if ( m_nativeDebugDisplay ) { m_nativeDebugDisplay->init(); GameFont *font; if (TheGlobalLanguageData && TheGlobalLanguageData->m_nativeDebugDisplay.name.isNotEmpty()) { font=TheFontLibrary->getFont( TheGlobalLanguageData->m_nativeDebugDisplay.name, TheGlobalLanguageData->m_nativeDebugDisplay.size, TheGlobalLanguageData->m_nativeDebugDisplay.bold); } else font=TheFontLibrary->getFont( AsciiString("FixedSys"), 8, FALSE ); m_nativeDebugDisplay->setFont( font ); m_nativeDebugDisplay->setFontHeight( 13 ); m_nativeDebugDisplay->setFontWidth( 9 ); } DX8WebBrowser::Initialize(); // we're now online m_initialized = true; if( TheGlobalData->m_displayDebug ) { m_debugDisplayCallback = StatDebugDisplay; } } // end init // W3DDisplay::reset =========================================================== /** Reset the W3D display system. Here we need to * remove the objects from the previous map. */ //============================================================================= void W3DDisplay::reset( void ) { Display::reset(); // Remove all render objects. SceneIterator *sceneIter = m_3DScene->Create_Iterator(); sceneIter->First(); while(!sceneIter->Is_Done()) { RenderObjClass * robj = sceneIter->Current_Item(); robj->Add_Ref(); m_3DScene->Remove_Render_Object(robj); robj->Release_Ref(); sceneIter->Next(); } m_3DScene->Destroy_Iterator(sceneIter); m_isClippedEnabled = FALSE; // release any unused assets from W3D /// @todo really need that "scene abstraction", having this stuff in the display is icky m_assetManager->Release_Unused_Assets(); if (TheWritableGlobalData) TheWritableGlobalData->m_drawSkyBox =0; } const UnsignedInt START_CUMU_FRAME = LOGICFRAMES_PER_SECOND / 2; // skip first half-sec /** Update a moving average of the last 30 fps measurements. Also try to filter out temporary spikes. This code is designed to be used by the GameLOD sytems to determine the correct dynamic LOD setting. */ void W3DDisplay::updateAverageFPS(void) { const Real MaximumFrameTimeCutoff = 0.5f; //largest frame interval (seconds) we accept before ignoring it as a momentary "spike" const Int FPS_HISTORY_SIZE = 30; //keep track of the last 30 frames static Int64 lastUpdateTime64 = 0; static Int historyOffset = 0; static Int numSamples = 0; static double fpsHistory[FPS_HISTORY_SIZE]; Int64 freq64 = getPerformanceCounterFrequency(); Int64 time64 = getPerformanceCounter(); #if defined(_DEBUG) || defined(_INTERNAL) if (TheGameLogic->getFrame() == START_CUMU_FRAME) { m_timerAtCumuFPSStart = time64; } #endif Int64 timeDiff = time64 - lastUpdateTime64; // convert elapsed time to seconds double elapsedSeconds = (double)timeDiff/(double)(freq64); if (elapsedSeconds <= MaximumFrameTimeCutoff) //make sure it's not a spike { // append new sameple to fps history. if (historyOffset >= FPS_HISTORY_SIZE) historyOffset = 0; double currentFPS = 1.0/elapsedSeconds; fpsHistory[historyOffset++] = currentFPS; numSamples++; if (numSamples > FPS_HISTORY_SIZE) numSamples = FPS_HISTORY_SIZE; } if (numSamples) { // determine average frame rate over our past history. Real average=0; for (Int i=0,j=historyOffset-1; im_nativeDebugDisplay.name.isNotEmpty()) { font=TheFontLibrary->getFont( TheGlobalLanguageData->m_nativeDebugDisplay.name, TheGlobalLanguageData->m_nativeDebugDisplay.size, TheGlobalLanguageData->m_nativeDebugDisplay.bold); } else font = TheFontLibrary->getFont( AsciiString("FixedSys"), 8, FALSE ); for (int i = 0; i < DisplayStringCount; i++) { if (m_displayStrings[i] == NULL) { m_displayStrings[i] = TheDisplayStringManager->newDisplayString(); DEBUG_ASSERTCRASH( m_displayStrings[i], ("Failed to create DisplayString") ); m_displayStrings[i]->setFont( font ); } } } // end if if (m_benchmarkDisplayString == NULL) { GameFont *thisFont = TheFontLibrary->getFont( AsciiString("FixedSys"), 8, FALSE ); m_benchmarkDisplayString = TheDisplayStringManager->newDisplayString(); DEBUG_ASSERTCRASH( m_benchmarkDisplayString, ("Failed to create DisplayString") ); m_benchmarkDisplayString->setFont( thisFont ); } ++s_framesRenderedSinceLastUpdate; s_drawCallsSinceLastUpdate += Debug_Statistics::Get_Draw_Calls(); s_sortedPolysSinceLastUpdate += Debug_Statistics::Get_Sorting_Polygons(); Int64 freq64 = getPerformanceCounterFrequency(); Int64 time64 = getPerformanceCounter(); s_timeSinceLastUpdateInSecs = ((double)(time64 - s_lastUpdateTime64) / (double)(freq64)); #ifdef EXTENDED_STATS static FILE *pListFile = NULL; static Int64 lastFrameTime=0; static samples = 0; if (pListFile == NULL) { pListFile = fopen("FrameRateLog.txt", "w"); } samples++; if (pListFile && lastFrameTime && samples<100) { float timeSinceLastFrame = (float)((double)(time64-lastFrameTime) / (double)(freq64)); fprintf(pListFile, "%d ", (int)(1/timeSinceLastFrame)); } lastFrameTime = time64; #endif // we update stats on a delay const Real UPDATE_RATE_SECS = 2.0; if( s_timeSinceLastUpdateInSecs >= UPDATE_RATE_SECS || TheGlobalData->m_constantDebugUpdate ) { UnicodeString unibuffer, unibuffer2; UnicodeString fpsString; // setup texture stats Debug_Statistics::Record_Texture_Mode(Debug_Statistics::RECORD_TEXTURE_SIMPLE/*RECORD_TEXTURE_NONE*/); // frames per second double fps = (Real)s_framesRenderedSinceLastUpdate / s_timeSinceLastUpdateInSecs; double drawsPerFrame = Debug_Statistics::Get_Draw_Calls(); //(Real)s_drawCallsSinceLastUpdate / (Real)s_framesRenderedSinceLastUpdate; double sortPolysPerFrame = Debug_Statistics::Get_Sorting_Polygons(); //(Real)s_sortedPolysSinceLastUpdate / (Real)s_framesRenderedSinceLastUpdate; double skinDrawsPerFrame = Debug_Statistics::Get_DX8_Skin_Renders(); if (fps<0.1) fps = 0.1; double ms = 1000.0f/fps; #if defined(_DEBUG) || defined(_INTERNAL) double cumuTime = ((double)(time64 - m_timerAtCumuFPSStart) / (double)(freq64)); if (cumuTime < 0.0) cumuTime = 0.0; Int numFrames = (Int)TheGameLogic->getFrame() - (Int)START_CUMU_FRAME; double cumuFPS = (numFrames > 0 && cumuTime > 0.0) ? (numFrames / cumuTime) : 0.0; double skinPolysPerFrame = Debug_Statistics::Get_DX8_Skin_Polygons(); //Int LOD = TheGlobalData->m_terrainLOD; //unibuffer.format( L"FPS: %.2f, %.2fms mapLOD=%d [cumu FPS=%.2f] draws: %.2f sort: %.2f", fps, ms, LOD, cumuFPS, drawsPerFrame,sortPolysPerFrame); if (TheGlobalData->m_useFpsLimit) unibuffer.format( L"%.2f/%d FPS, ", fps, TheGameEngine->getFramesPerSecondLimit()); else unibuffer.format( L"%.2f FPS, ", fps); unibuffer2.format( L"%.2fms [cumuFPS=%.2f] draws: %d skins: %d sortP: %d skinP: %d", ms, cumuFPS, (Int)drawsPerFrame,(Int)skinDrawsPerFrame,(Int)sortPolysPerFrame, (Int)skinPolysPerFrame); unibuffer.concat(unibuffer2); #else //Int LOD = TheGlobalData->m_terrainLOD; //unibuffer.format( L"FPS: %.2f, %.2fms mapLOD=%d draws: %.2f sort %.2f", fps, ms, LOD, drawsPerFrame,sortPolysPerFrame); unibuffer.format( L"FPS: %.2f, %.2fms draws: %.2f skins: %.2f sort %.2f", fps, ms, drawsPerFrame,skinDrawsPerFrame,sortPolysPerFrame); if (TheGlobalData->m_useFpsLimit) { unibuffer2.format(L", FPSLock %d",TheGlobalData->m_framesPerSecondLimit); unibuffer.concat(unibuffer2); } #endif fpsString.format( L"FPS: %.2f", fps); m_benchmarkDisplayString->setText( fpsString ); Int polyPerFrame = Debug_Statistics::Get_DX8_Polygons(); #ifdef EXTENDED_STATS static float gameOverheadMS = 0.0f; static float consoleMS = 0.0f; static float threeDOverheadMS = 0.0f; static float terrainMS = 0.0f; static float objectMS = 0.0f; static float overlapMS = 0.0f; static int extendedStats = 0; const int SHOW_STATS_TIME=12; // show extended stats for 5 cycles == 10 seconds. static enum {disabled, sync, gameOverhead, console, threeDOverhead, terrain, objects, overlap, normal} statMode = disabled; if (statMode == sync) { extendedStats = SHOW_STATS_TIME; statMode = gameOverhead; } else if (statMode == gameOverhead) { gameOverheadMS = ms; statMode = console; DX8Wrapper::stats.m_disableTerrain = true; DX8Wrapper::stats.m_disableOverhead = true; DX8Wrapper::stats.m_disableWater = true; DX8Wrapper::stats.m_disableObjects = true; DX8Wrapper::stats.m_disableConsole = false; DX8Wrapper::stats.m_debugLinesToShow = 1; } else if (statMode == console) { consoleMS = ms; statMode = threeDOverhead; DX8Wrapper::stats.m_disableTerrain = true; DX8Wrapper::stats.m_disableOverhead = true; DX8Wrapper::stats.m_disableWater = true; DX8Wrapper::stats.m_disableObjects = true; DX8Wrapper::stats.m_disableConsole = true; DX8Wrapper::stats.m_debugLinesToShow = 1; } else if (statMode == threeDOverhead) { threeDOverheadMS = ms; statMode = terrain; DX8Wrapper::stats.m_disableTerrain = false; DX8Wrapper::stats.m_disableOverhead = true; DX8Wrapper::stats.m_disableWater = true; DX8Wrapper::stats.m_disableObjects = true; DX8Wrapper::stats.m_disableConsole = true; DX8Wrapper::stats.m_debugLinesToShow = 1; } else if (statMode == terrain) { terrainMS = ms; statMode = objects; DX8Wrapper::stats.m_disableOverhead = true; DX8Wrapper::stats.m_disableTerrain = true; DX8Wrapper::stats.m_disableWater = true; DX8Wrapper::stats.m_disableObjects = false; DX8Wrapper::stats.m_disableConsole = true; DX8Wrapper::stats.m_debugLinesToShow = 1; } else if (statMode == objects) { objectMS = ms; statMode = overlap; DX8Wrapper::stats.m_disableOverhead = false; DX8Wrapper::stats.m_disableTerrain = false; DX8Wrapper::stats.m_disableWater = false; DX8Wrapper::stats.m_disableObjects = false; DX8Wrapper::stats.m_disableConsole = true; DX8Wrapper::stats.m_sleepTime = (int)(terrainMS); DX8Wrapper::stats.m_debugLinesToShow = 1; } else if (statMode == overlap) { overlapMS = ms; statMode = normal; DX8Wrapper::stats.m_disableOverhead = false; DX8Wrapper::stats.m_disableTerrain = false; DX8Wrapper::stats.m_disableWater = false; DX8Wrapper::stats.m_disableObjects = false; DX8Wrapper::stats.m_disableConsole = true; DX8Wrapper::stats.m_sleepTime = 0; DX8Wrapper::stats.m_debugLinesToShow = 1; } else if (statMode == normal) { overlapMS = (ms + ((int)terrainMS) - overlapMS ); statMode = disabled; extendedStats = SHOW_STATS_TIME; // Done collecting stats. Re-enable stuff DX8Wrapper::stats.m_disableConsole = false; DX8Wrapper::stats.m_debugLinesToShow = -1; } else if (!DX8Wrapper::stats.m_showingStats) { // start collecting extended info. DX8Wrapper::stats.m_showingStats = true; DX8Wrapper::stats.m_disableOverhead = false; DX8Wrapper::stats.m_disableTerrain = true; DX8Wrapper::stats.m_disableWater = true; DX8Wrapper::stats.m_disableObjects = true; DX8Wrapper::stats.m_disableConsole = true; DX8Wrapper::stats.m_debugLinesToShow = 1; statMode = sync; gameOverheadMS = 0.0f; threeDOverheadMS = 0.0f; terrainMS = 0.0f; objectMS = 0.0f; } if (statMode != disabled) { unibuffer.format(L"FPS: %.2f, %.2fms - Collecting extended stats.", fps, ms); } else if (extendedStats>0) { extendedStats--; unibuffer.format( L"FPS: %.2f, %.2fms - OH %.2fms, Console %.2fms, 3D OH %.2fms, Terrain %.2fms, Obs %.2fms, CPU %.2fms", fps, ms, gameOverheadMS, consoleMS, threeDOverheadMS, terrainMS, objectMS, overlapMS); if (extendedStats==SHOW_STATS_TIME-2) { char bufferA[ 256 ]; sprintf( bufferA, "FPS: %.2f, %.2fms - OH %.2fms, Console %.2fms, 3D OH %.2fms, Terrain %.2fms, Obs %.2fms, CPU %.2fms\n", fps, ms, gameOverheadMS, consoleMS, threeDOverheadMS, terrainMS, objectMS, overlapMS); ::OutputDebugString(bufferA); if (pListFile) { fprintf(pListFile, "\n%s", bufferA); } sprintf( bufferA, "Polygons: per frame %d, per second %d\n", polyPerFrame, (Int)(polyPerFrame*fps)); ::OutputDebugString(bufferA); if (pListFile) { fprintf(pListFile, "%s", bufferA); fflush(pListFile); } } } if (pListFile) { fprintf(pListFile, "\nFPS: %.2f, %.2fms\n", fps, ms); fflush(pListFile); } if (pListFile) { samples = 0; if (statMode != disabled) { fprintf(pListFile, "Stat%d-", statMode); } } #endif // check for debug D3D Bool debugD3D=false; RegistryClass registry ("Software\\Microsoft\\Direct3d"); if (registry.Is_Valid ()) { if (registry.Get_Int ("LoadDebugRuntime", 0) == 1) { debugD3D = true; } } if (debugD3D) { unibuffer.concat(L", DEBUG D3D"); } #ifdef _DEBUG unibuffer.concat(L", DEBUG app"); #endif m_displayStrings[FPS]->setText( unibuffer ); // Actual GameLogic frame number unibuffer.format(L"Frame: %d", TheGameLogic->getFrame()); m_displayStrings[Frame]->setText( unibuffer ); // polygons this frame unibuffer.format( L"Polygons: per frame %d, per second %d", polyPerFrame, (Int)(polyPerFrame*fps)); m_displayStrings[Polygons]->setText( unibuffer ); // vertices this frame unibuffer.format( L"Vertices: %d", Debug_Statistics::Get_DX8_Vertices() ); m_displayStrings[Vertices]->setText( unibuffer ); // // I'm adjusting the texture memory usage counter by subtracting // out the terrain alpha texture (since it's really == terrain texture). // unibuffer.format( L"Video RAM: %d", Debug_Statistics::Get_Record_Texture_Size() - 1376256 ); m_displayStrings[VideoRam]->setText( unibuffer ); s_lastUpdateTime64 = time64; s_timeSinceLastUpdateInSecs = 0.0f; s_framesRenderedSinceLastUpdate = 0; s_drawCallsSinceLastUpdate = 0; s_sortedPolysSinceLastUpdate = 0; // terrain stats unibuffer.format( L"3-Way Blends: %d, Shoreline Blends: %d", TheTerrainRenderObject->getNumExtraBlendTiles(), TheTerrainRenderObject->getNumShoreLineTiles()); m_displayStrings[TerrainStats]->setText( unibuffer ); // misc debug info Coord3D camPos; TheTacticalView->getPosition(&camPos); Real zoom = TheTacticalView->getZoom(); Real pitch = TheTacticalView->getPitch(); Real FXPitch = TheTacticalView->getFXPitch(); Real angle = TheTacticalView->getAngle(); Real FOV = TheTacticalView->getFieldOfView(); //Real desiredHeight = TheTacticalView->getHeightAboveGround(); Real terrainHeight = TheTacticalView->getTerrainHeightUnderCamera(); Real actualHeightAboveGround = TheTacticalView->getCurrentHeightAboveGround(); unibuffer.format( L"Camera zoom: %g, pitch: %g/%g, yaw: %g, pos: %g, %g, %g, FOV: %g\n Height above ground: %g Terrain height: %g", zoom, pitch, FXPitch, angle, camPos.x, camPos.y, camPos.z, FOV, /* zoom, pitch * 180.0f / PI, FXPitch * 180.0f / PI, angle * 180.0f / PI, camPos.x, camPos.y, camPos.z, FOV * 180.0f / PI, */ actualHeightAboveGround, terrainHeight ); m_displayStrings[DebugInfo]->setText( unibuffer ); // display the keyboard modifier and mouse states. unibuffer.format( L"States: " ); if( TheKeyboard->isShift() ) { unibuffer.concat( L"Shift(" ); if( TheKeyboard->getModifierFlags() & KEY_STATE_LSHIFT ) { unibuffer.concat( L"L" ); } if( TheKeyboard->getModifierFlags() & KEY_STATE_RSHIFT ) { unibuffer.concat( L"R" ); } unibuffer.concat( L") " ); } if( TheKeyboard->isCtrl() ) { unibuffer.concat( L"Ctrl(" ); if( TheKeyboard->getModifierFlags() & KEY_STATE_LCONTROL ) { unibuffer.concat( L"L" ); } if( TheKeyboard->getModifierFlags() & KEY_STATE_RCONTROL ) { unibuffer.concat( L"R" ); } unibuffer.concat( L") " ); } if( TheKeyboard->isAlt() ) { unibuffer.concat( L"Alt(" ); if( TheKeyboard->getModifierFlags() & KEY_STATE_LALT ) { unibuffer.concat( L"L" ); } if( TheKeyboard->getModifierFlags() & KEY_STATE_RALT ) { unibuffer.concat( L"R" ); } unibuffer.concat( L") " ); } const MouseIO *mouseStatus = TheMouse->getMouseStatus(); if( mouseStatus->leftState ) { unibuffer.concat( L"LMB " ); } if( mouseStatus->middleState ) { unibuffer.concat( L"MMB " ); } if( mouseStatus->rightState ) { unibuffer.concat( L"RMB " ); } Object *object = NULL; #if defined(_DEBUG) || defined(_INTERNAL) //debug hack to view object under mouse stats Drawable *draw = TheTacticalView->pickDrawable(&TheMousePos, FALSE, (PickType)0xffffffff ); #else Drawable *draw = TheGameClient->findDrawableByID( TheInGameUI->getMousedOverDrawableID() ); #endif if( draw ) object = draw->getObject(); if( object ) { unibuffer2.format( L"Moused over object: %S (%d) ", object->getTemplate()->getName().str(), object->getID() ); unibuffer.concat( unibuffer2 ); } else { unibuffer.concat( L"Moused over object: TERRAIN " ); } m_displayStrings[ KEY_MOUSE_STATES ]->setText( unibuffer ); //display the x and y mouse coordinates const MouseIO *mouseIO = TheMouse->getMouseStatus(); Coord3D worldPos; TheTacticalView->screenToTerrain(&mouseIO->pos, &worldPos); unibuffer.format( L"Mouse position: screen: (%d, %d), world: (%g, %g, %g)", mouseIO->pos.x, mouseIO->pos.y, worldPos.x, worldPos.y, worldPos.z); m_displayStrings[MousePosition]->setText( unibuffer ); //display the number of particles in the world and being displayed on screen Int totalParticles = TheParticleSystemManager->getParticleCount(); Int onScreenParticleCount = TheParticleSystemManager->getOnScreenParticleCount(); unibuffer.format( L"Particles: %d in world, %d being displayed", totalParticles, onScreenParticleCount ); m_displayStrings[Particles]->setText( unibuffer ); //display the number of objects in the world UnsignedInt objCount = TheGameLogic->getObjectCount(); UnsignedInt objScreenCount = TheGameClient->getRenderedObjectCount(); unibuffer.format(L"Objects: %d in world, %d being displayed", objCount, objScreenCount ); m_displayStrings[Objects]->setText( unibuffer ); // Network incoming bandwidth stats if (TheNetwork != NULL) { unibuffer.format(L"IN: %.2f bytes/sec, %.2f packets/sec", TheNetwork->getIncomingBytesPerSecond(), TheNetwork->getIncomingPacketsPerSecond()); m_displayStrings[NetIncoming]->setText( unibuffer ); // Network outgoing bandwidth stats unibuffer.format(L"OUT: %.2f bytes/sec, %.2f packets/sec", TheNetwork->getOutgoingBytesPerSecond(), TheNetwork->getOutgoingPacketsPerSecond()); m_displayStrings[NetOutgoing]->setText( unibuffer ); // Network performance stats unibuffer.format(L"Run Ahead: %d, Net FPS: %d, Packet arrival cushion: %d", TheNetwork->getRunAhead(), TheNetwork->getFrameRate(), TheNetwork->getPacketArrivalCushion()); m_displayStrings[NetStats]->setText( unibuffer ); // Client frame rate averages for all players in the game. This only works right for the packet router. unibuffer.clear(); Int numPlayers = TheNetwork->getNumPlayers(); for (Int i = 0; i < numPlayers; ++i) { UnicodeString tempstr; tempstr.format(L"%s: %d ", TheNetwork->getPlayerName(i).str(), TheNetwork->getSlotAverageFPS(i)); unibuffer.concat(tempstr); } m_displayStrings[NetFPSAverages]->setText( unibuffer ); } else { // unibuffer.format(L"IN: 0.0 bytes/sec, 0.0 packets/sec"); // m_displayStrings[NetIncoming]->setText( unibuffer ); // Network outgoing bandwidth stats // unibuffer.format(L"OUT: 0.0 bytes/sec, 0.0 packets/sec"); // m_displayStrings[NetOutgoing]->setText( unibuffer ); unibuffer.format(L""); // unibuffer.format(L"Network not present"); m_displayStrings[NetOutgoing]->setText(unibuffer); m_displayStrings[NetIncoming]->setText(unibuffer); m_displayStrings[NetStats]->setText(unibuffer); m_displayStrings[NetFPSAverages]->setText( unibuffer ); } // selected object info stats unibuffer.format( L"Select Info: '%d' drawables selected", TheInGameUI->getSelectCount() ); //Sorry, guys. I need a special kluge here to get constantdebug results for angry mob. //Do no be cross with me. //if there is not exactly one drawable selected it will report on the moused-over drawable if (TheInGameUI->getSelectCount() == 1) draw = TheInGameUI->getFirstSelectedDrawable(); if( draw ) { Object *obj = draw->getObject(); AsciiString objectName; objectName.set( "No-Name" ); if( obj && obj->getName().isEmpty() == FALSE ) objectName = obj->getName(); unibuffer.format( L"Select Info: '%S'(%S) at (%.3f,%.3f,%.3f)", draw->getTemplate()->getName().str(), objectName.str(), draw->getPosition()->x, draw->getPosition()->y, draw->getPosition()->z ); // (gth) compute some stats about the rendering cost of this drawable #if defined(_DEBUG) || defined(_INTERNAL) RenderCost rcost; for (DrawModule** dm = draw->getDrawModules(); *dm; ++dm) { (*dm)->getRenderCost(rcost); } if (rcost.getDrawCallCount() > 0) { unibuffer2.format( L"\ndraw calls: %d(+%d) sort meshes: %d skins: %d bones: %d",rcost.getDrawCallCount(),rcost.getShadowDrawCount(),rcost.getSortedMeshCount(),rcost.getSkinMeshCount(),rcost.getBoneCount()); unibuffer.concat( unibuffer2 ); } #endif unibuffer.concat( L"\nModelStates: " ); ModelConditionFlags mcFlags = draw->getModelConditionFlags(); const numEntriesPerLine = 4; int lineCount = 0; for( int i = 0; i < MODELCONDITION_COUNT; i++ ) { if( mcFlags.test( i ) ) { unibuffer2.format( L"%S ", ModelConditionFlags::getBitNames()[ i ] ); unibuffer.concat( unibuffer2 ); lineCount++; if( lineCount == numEntriesPerLine ) { lineCount = 0; unibuffer.concat( L"\n" ); } } } //Render ALL modelcondition statii } // end if m_displayStrings[ SelectedInfo ]->setText( unibuffer ); } } // W3DDisplay::drawDebugStats ================================================= /** Draw debug statistics */ //============================================================================= void W3DDisplay::drawDebugStats( void ) { Int x = 3; Int y = 3; Color textColor = GameMakeColor( 255, 255, 255, 255 ); Color dropColor = GameMakeColor( 0, 0, 0, 255 ); int linesOfStrings = DisplayStringCount; #ifdef EXTENDED_STATS if (DX8Wrapper::stats.m_debugLinesToShow > -1) { linesOfStrings = DX8Wrapper::stats.m_debugLinesToShow; } #endif Int w, h; for (int i = 0; i < linesOfStrings; i++) { m_displayStrings[i]->draw( x, y, textColor, dropColor ); m_displayStrings[i]->getSize(&w, &h); y += h; } } // end drawDebugStats // W3DDisplay::drawFPSStats ================================================= /** Draw the FPS on the screen */ //============================================================================= void W3DDisplay::drawFPSStats( void ) { Int x = 3; Int y = 20; Color textColor = GameMakeColor( 255, 255, 255, 255 ); Color dropColor = GameMakeColor( 0, 0, 0, 255 ); int linesOfStrings = 1; for (int i = 0; i < linesOfStrings; i++) { m_benchmarkDisplayString->draw( x, y, textColor, dropColor ); } } //============================================================================= void StatDebugDisplay( DebugDisplayInterface *, void *, FILE *fp ) { DEBUG_CRASH(("This should never be called directly, but is just a placeholder for drawDebugStats()")); } // W3DDisplay::drawCurrentDebugDisplay ================================================= /** Draw current debug display */ //============================================================================= void W3DDisplay::drawCurrentDebugDisplay( void ) { if (m_debugDisplayCallback == StatDebugDisplay) { drawDebugStats(); } else { if ( m_debugDisplay && m_debugDisplayCallback ) { m_debugDisplay->reset(); m_debugDisplayCallback( m_debugDisplay, m_debugDisplayUserData ); } } } // end drawCurrentDebugDisplay // W3DDisplay::calculateTerrainLOD ================================================= /** Calculates an adequately speedy terrain Level Of Detail. */ //============================================================================= void W3DDisplay::calculateTerrainLOD( void ) { const Int NUM_SAMPLES=20; const Int NUM_TO_DISCARD=5; Int64 freq64 = getPerformanceCounterFrequency(); char buf[_MAX_PATH]; float frameTime = 0; float maxTimeLimit = TheGlobalData->m_terrainLODTargetTimeMS/1000.0f; TerrainLOD goodLOD = TERRAIN_LOD_MIN; TerrainLOD curLOD = TERRAIN_LOD_AUTOMATIC; Int count = 0; #ifdef _DEBUG // just go to TERRAIN_LOD_NO_WATER, mirror off. TheWritableGlobalData->m_terrainLOD = TERRAIN_LOD_NO_WATER; m_3DScene->drawTerrainOnly(false); TheTerrainRenderObject->adjustTerrainLOD(0); return; #endif do { Int i; float timeForFrame=0; frameTime = 0; switch(curLOD) { default: curLOD = TERRAIN_LOD_DISABLE; break; case TERRAIN_LOD_AUTOMATIC: curLOD = TERRAIN_LOD_MAX; break; case TERRAIN_LOD_MAX: curLOD = TERRAIN_LOD_NO_WATER; break; case TERRAIN_LOD_HALF_CLOUDS: curLOD = TERRAIN_LOD_DISABLE; break; case TERRAIN_LOD_NO_WATER: curLOD = TERRAIN_LOD_HALF_CLOUDS; break; } if (curLOD == TERRAIN_LOD_DISABLE) { break; } TheWritableGlobalData->m_terrainLOD = curLOD; m_3DScene->drawTerrainOnly(true); TheTerrainRenderObject->adjustTerrainLOD(0); for (i=0; i=NUM_TO_DISCARD) { frameTime += timeForFrame; if (i>NUM_TO_DISCARD+1 && (timeForFrame / ((i+1)-NUM_TO_DISCARD)) > 2*maxTimeLimit) { i++; break; } } } frameTime /= ((i)-NUM_TO_DISCARD); count++; sprintf(buf, "\n LOD %d, time %.2fms\n", curLOD, frameTime*1000.0f); ::OutputDebugString(buf); if (frameTimem_terrainLOD = goodLOD; m_3DScene->drawTerrainOnly(false); TheTerrainRenderObject->adjustTerrainLOD(0); #ifdef _DEBUG DEBUG_ASSERTCRASH(count<10, ("calculateTerrainLOD") ); #endif } Real W3DDisplay::getAverageFPS() { return m_averageFPS; } Int W3DDisplay::getLastFrameDrawCalls() { return Debug_Statistics::Get_Draw_Calls(); } //DECLARE_PERF_TIMER(BigAssRenderLoop) // W3DDisplay::draw =========================================================== /** Draw the entire W3D Display */ //============================================================================= //DECLARE_PERF_TIMER(W3DDisplay_draw) void W3DDisplay::draw( void ) { //USE_PERF_TIMER(W3DDisplay_draw) static UnsignedInt syncTime = 0; extern HWND ApplicationHWnd; if (ApplicationHWnd && ::IsIconic(ApplicationHWnd)) { return; } updateAverageFPS(); if (TheGlobalData->m_enableDynamicLOD && TheGameLogic->getShowDynamicLOD()) { DynamicGameLODLevel lod=TheGameLODManager->findDynamicLODLevel(m_averageFPS); TheGameLODManager->setDynamicLODLevel(lod); } else { //if dynamic LOD is turned off, force highest LOD TheGameLODManager->setDynamicLODLevel(DYNAMIC_GAME_LOD_VERY_HIGH); } if (TheGlobalData->m_terrainLOD == TERRAIN_LOD_AUTOMATIC && TheTerrainRenderObject) { calculateTerrainLOD(); } #ifdef EXTENDED_STATS AGAIN: #endif #ifdef DUMP_PERF_STATS if( TheGlobalData->m_dumpPerformanceStatistics ) { TheStatDump.dumpStats(); TheWritableGlobalData->m_dumpPerformanceStatistics = FALSE; } #endif // compute debug statistics for display later if ( m_debugDisplayCallback == StatDebugDisplay #if defined(_DEBUG) || defined(_INTERNAL) || TheGlobalData->m_benchmarkTimer > 0 #endif ) { gatherDebugStats(); } #ifdef EXTENDED_STATS else { DX8Wrapper::stats.m_showingStats = false; } #endif #ifdef SAMPLE_DYNAMIC_LIGHT Vector3 loc; loc = theDynamicLight->Get_Position(); loc.X += theLightXOffset; if(loc.X>128) theLightXOffset = -theLightXOffset; if(loc.X<0) theLightXOffset = -theLightXOffset; loc.Y += theLightYOffset; if(loc.Y>128) theLightYOffset = -theLightYOffset; if(loc.Y<0) theLightYOffset = -theLightYOffset; theDynamicLight->Set_Position(loc); #endif /// @todo Make more explicit drawing layers(ground, ground UI, objects, object UI, overlay UI) ///@todo: Ask Vegas why the LOD optimizer hangs particle system. // // Predictive LOD optimizer optimizes the mesh LOD levels to match // the given polygon budget // //PredictiveLODOptimizerClass::Optimize_LODs( 5000 ); Bool freezeTime = TheTacticalView->isTimeFrozen() && !TheTacticalView->isCameraMovementFinished(); freezeTime = freezeTime || TheScriptEngine->isTimeFrozenDebug() || TheScriptEngine->isTimeFrozenScript(); freezeTime = freezeTime || TheGameLogic->isGamePaused(); // hack to let client spin fast in network games but still do effects at the same pace. -MDC static UnsignedInt lastFrame = ~0; freezeTime = freezeTime || (lastFrame == TheGameClient->getFrame()); lastFrame = TheGameClient->getFrame(); /// @todo: I'm assuming the first view is our main 3D view. W3DView *primaryW3DView=(W3DView *)getFirstView(); if (!freezeTime && TheScriptEngine->isTimeFast()) { primaryW3DView->updateCameraMovements(); // Update camera motion effects. syncTime += TheW3DFrameLengthInMsec; return; } Debug_Statistics::Begin_Statistics(); //reset all counters (polygons, vertices, etc) before drawing //update state of all the terrain tracks (fade, remove, etc.) /// @todo: Is there a better place to put per-frame updates like this? if(TheGlobalData->m_loadScreenRender != TRUE) { if (TheTerrainTracksRenderObjClassSystem) TheTerrainTracksRenderObjClassSystem->update(); //Shroud data is needed to render all other views, so handle this first. if (TheTerrainRenderObject) { //update the shroud surface here since it may be needed by reflections if (TheTerrainRenderObject->getMap()) //make sure a valid map is loaded into terrain. { if (TheTerrainRenderObject->getShroud()) { TheTerrainRenderObject->getShroud()->render(primaryW3DView->get3DCamera()); } } } } if (!freezeTime) { /// @todo Decouple framerate from timestep // for now, use constant time steps to avoid animations running independent of framerate syncTime += TheW3DFrameLengthInMsec; // allow W3D to update its internals // WW3D::Sync( GetTickCount() ); } WW3D::Sync( syncTime ); // Fast & Frozen time limits the time to 33 fps. Int minTime = 30; static Int prevTime = timeGetTime(), now; now=timeGetTime(); if (TheTacticalView->getTimeMultiplier()>1) { static Int timeMultiplierCounter = 1; timeMultiplierCounter--; if (timeMultiplierCounter>1) return; timeMultiplierCounter = TheTacticalView->getTimeMultiplier(); // limit the framerate, because while fast time is on, the game logic is running as fast as it can. } else { now = timeGetTime(); prevTime = now - minTime; // do the first frame immediately. } do { { if(TheGlobalData->m_loadScreenRender != TRUE) { // limit the framerate while(TheGlobalData->m_useFpsLimit && (now - prevTime) < minTime-1) { now = timeGetTime(); } prevTime = now; } } // update all views of the world - recomputes data which will affect drawing if (DX8Wrapper::_Get_D3D_Device8() && (DX8Wrapper::_Get_D3D_Device8()->TestCooperativeLevel()) == D3D_OK) { //Checking if we have the device before updating views because the heightmap crashes otherwise while //trying to refresh the visible terrain geometry. // if(TheGlobalData->m_loadScreenRender != TRUE) updateViews(); if (TheWaterRenderObj && TheGlobalData->m_waterType == 2) TheWaterRenderObj->updateRenderTargetTextures(primaryW3DView->get3DCamera()); //do a render into each texture //Can't render into textures while rendering to screen so these textures need to be updated //before we enter main rendering loop. if (TheW3DProjectedShadowManager) TheW3DProjectedShadowManager->updateRenderTargetTextures(); } Debug_Statistics::End_Statistics(); //record number of polygons rendered in RenderTargetTextures. //Store number of polygons rendered in renderTargetTextures. Int numRenderTargetPolygons=Debug_Statistics::Get_DX8_Polygons(); Int numRenderTargetVertices=Debug_Statistics::Get_DX8_Vertices(); // start render block { //USE_PERF_TIMER(BigAssRenderLoop) static Bool couldRender = true; if ((TheGlobalData->m_breakTheMovie == FALSE) && (TheGlobalData->m_disableRender == false) && WW3D::Begin_Render( true, true, Vector3( 0.0f, 0.0f, 0.0f ), TheWaterTransparency->m_minWaterOpacity ) == WW3D_ERROR_OK) { if(TheGlobalData->m_loadScreenRender == TRUE) { TheInGameUI->draw(); if( TheMouse ) TheMouse->draw(); //keep applying the current cursor style so it remains hidden if needed. WW3D::End_Render(); continue; } couldRender = true; // add the number of verts/polygons drawn before the main scene if (numRenderTargetPolygons || numRenderTargetVertices) Debug_Statistics::Record_DX8_Polys_And_Vertices(numRenderTargetPolygons,numRenderTargetVertices,ShaderClass::_PresetOpaqueShader); // draw all views of the world drawViews(); // draw the user interface TheInGameUI->DRAW(); // end of video example code // draw the mouse if( TheMouse ) TheMouse->DRAW(); if ( m_videoStream && m_videoBuffer ) { drawVideoBuffer( m_videoBuffer, 0, 0, getWidth(), getHeight() ); } if( m_copyrightDisplayString ) { Int x, y, dX, dY; m_copyrightDisplayString->getSize(&dX, &dY); x = (getWidth() / 2) - (dX /2); y = getHeight() - dY - 20 ; m_copyrightDisplayString->draw(x, y, GameMakeColor(0,0,0,255), GameMakeColor(0,0,0,0),0,0); } // render letter box before debug display so debug info isn't hidden renderLetterBox(now); // display cinematicText over the black if( m_cinematicText != AsciiString::TheEmptyString && m_cinematicTextFrames != 0) { DisplayString *displayString = TheDisplayStringManager->newDisplayString(); // set word wrap if neccessary Int wordWrapWidth = TheDisplay->getWidth() - 20; displayString->setWordWrap( wordWrapWidth ); displayString->setWordWrapCentered( TRUE ); UnicodeString text; text.translate( m_cinematicText ); displayString->setText( text ); Color color = GameMakeColor( 255, 255, 255, 255 ); // white Color backColor = GameMakeColor( 0, 0, 0, 0 ); // black displayString->setFont( m_cinematicFont ); Int height = TheDisplay->getHeight() * .9; Int width; if( displayString->getWidth() > TheDisplay->getWidth() ) width = 20; else width = ( TheDisplay->getWidth() - displayString->getWidth() ) / 2; displayString->draw( width, height, color, backColor ); m_cinematicTextFrames--; } if ( m_debugDisplayCallback ) { // draw the current debug display drawCurrentDebugDisplay(); } #if defined(_DEBUG) || defined(_INTERNAL) if (TheGlobalData->m_benchmarkTimer > 0) { drawFPSStats(); } #endif #if defined(_DEBUG) || defined(_INTERNAL) if (TheGlobalData->m_debugShowGraphicalFramerate) { drawFramerateBar(); } #endif #ifdef PERF_TIMERS TheGraphDraw->render(); TheGraphDraw->clear(); #endif // render is all done! WW3D::End_Render(); } else { if (couldRender) { couldRender = false; DEBUG_LOG(("Could not do WW3D::Begin_Render()! Are we ALT-Tabbed out?\n")); } } } if (TheScriptEngine->isTimeFrozenDebug() || TheScriptEngine->isTimeFrozenScript() || TheGameLogic->isGamePaused()) { freezeTime = false; // We're frozen for debug or for pause, and need to continue out of the loop. } } while (freezeTime && !TheTacticalView->isCameraMovementFinished()); #ifdef EXTENDED_STATS if (DX8Wrapper::stats.m_disableOverhead) { goto AGAIN; } #endif } // end draw #define LETTER_BOX_FADE_TIME 1000.0f ///1000 ms. /** Render letter-box border at top/bottom of display */ void W3DDisplay::renderLetterBox(UnsignedInt currentTime) { if (m_letterBoxEnabled) { if (m_letterBoxFadeLevel != 1.0f) { m_letterBoxFadeLevel = (currentTime - m_letterBoxFadeStartTime)/LETTER_BOX_FADE_TIME; if (m_letterBoxFadeLevel > 1.0f) m_letterBoxFadeLevel = 1.0f; } UnsignedInt lbcolor = (Int)(m_letterBoxFadeLevel * 255.0f) << 24; #ifdef SLIDE_LETTERBOX Int height = (Int)(getHeight() * 0.12f * m_letterBoxFadeLevel); TheTacticalView->setOrigin(0, height); #else drawFillRect( 0, 0, m_width, (m_height-(9.0f/16.0f * m_width))*0.5f, lbcolor ); drawFillRect( 0, m_height-(m_height-(9.0f/16.0f * m_width))*0.5f, m_width, m_height, lbcolor ); #endif } else { //letter box is disabled, but may still be fading out if (m_letterBoxFadeLevel != 0.0f) { m_letterBoxFadeLevel = 1.0f - (currentTime - m_letterBoxFadeStartTime)/LETTER_BOX_FADE_TIME; if (m_letterBoxFadeLevel < 0.0f) m_letterBoxFadeLevel = 0.0f; UnsignedInt lbcolor = (Int)(m_letterBoxFadeLevel * 255.0f) << 24; #ifdef SLIDE_LETTERBOX Int height = (Int)(getHeight() * 0.12f * m_letterBoxFadeLevel); TheTacticalView->setOrigin(0, height); #else drawFillRect( 0, 0, m_width, (m_height-(9.0f/16.0f * m_width))*0.5f, lbcolor ); //drawFillRect( 0, m_height-(m_height-(9.0f/16.0f * m_width))*0.5f, m_width, m_height, lbcolor ); #endif } else { //box has finished fading out #ifdef SLIDE_LETTERBOX TheTacticalView->setOrigin(0, 0); #else m_letterBoxEnabled = FALSE; #endif } } } Bool W3DDisplay::isLetterBoxFading(void) { if (m_letterBoxEnabled && m_letterBoxFadeLevel != 1.0f) return TRUE; if (!m_letterBoxEnabled && m_letterBoxFadeLevel != 0.0f) return TRUE; return FALSE; } // W3DDisplay::createLightPulse =============================================== /** Create a "light pulse" which is a dynamic light that grows, decays * and vanishes over several frames */ //============================================================================= void W3DDisplay::createLightPulse( const Coord3D *pos, const RGBColor *color, Real innerRadius, Real attenuationWidth, UnsignedInt increaseFrameTime, UnsignedInt decayFrameTime//, Bool donut ) { if (innerRadius+attenuationWidth<2.0*PATHFIND_CELL_SIZE_F + 1.0f) { return; // it basically won't make any visual difference. jba. } W3DDynamicLight * theDynamicLight = m_3DScene->getADynamicLight(); // turn it on. theDynamicLight->setEnabled(true); theDynamicLight->Set_Ambient( Vector3( color->red, color->green, color->blue ) ); theDynamicLight->Set_Diffuse( Vector3( color->red, color->green, color->blue) ); theDynamicLight->Set_Position(Vector3(pos->x, pos->y, pos->z)); theDynamicLight->Set_Far_Attenuation_Range(innerRadius, innerRadius + attenuationWidth); theDynamicLight->setFrameFade(increaseFrameTime, decayFrameTime); theDynamicLight->setDecayRange(); theDynamicLight->setDecayColor(); //theDynamicLight->setDonut(donut); } void W3DDisplay::toggleLetterBox(void) { m_letterBoxEnabled = !m_letterBoxEnabled; m_letterBoxFadeStartTime = timeGetTime(); } void W3DDisplay::enableLetterBox(Bool enable) { if (enable) { if (!m_letterBoxEnabled) { //letterbox mode not previously enabled m_letterBoxEnabled = TRUE; m_letterBoxFadeStartTime = timeGetTime(); } } else { if (m_letterBoxEnabled) { //letterbox mode no previously disabled m_letterBoxEnabled = FALSE; m_letterBoxFadeStartTime = timeGetTime(); } } } // W3DDisplay::setTimeOfDay =================================================== /** */ //============================================================================= void W3DDisplay::setTimeOfDay( TimeOfDay tod ) { const GlobalData::TerrainLighting *ol=&TheGlobalData->m_terrainObjectsLighting[tod][0]; if( m_3DScene ) { m_3DScene->Set_Ambient_Light( Vector3(ol->ambient.red, ol->ambient.green, ol->ambient.blue) ); } for (Int i=0; im_terrainObjectsLighting[tod][i]; m_myLight[i]->Set_Ambient( Vector3( 0.0f, 0.0f, 0.0f ) ); m_myLight[i]->Set_Diffuse( Vector3(ol->diffuse.red, ol->diffuse.green, ol->diffuse.blue ) ); m_myLight[i]->Set_Specular( Vector3(0,0,0) ); Matrix3D mtx; mtx.Set(Vector3(1,0,0), Vector3(0,1,0), Vector3(ol->lightPos.x, ol->lightPos.y, ol->lightPos.z), Vector3(0,0,0)); m_myLight[i]->Set_Transform(mtx); } } if(TheTerrainRenderObject) { TheTerrainRenderObject->setTimeOfDay(tod); TheTacticalView->forceRedraw(); } } // W3DDisplay::drawLine ======================================================= /** draw a line on the display in pixel coordinates with the specified color */ //============================================================================= void W3DDisplay::drawLine( Int startX, Int startY, Int endX, Int endY, Real lineWidth, UnsignedInt lineColor ) { /// @todo we need to consider the efficiency of the 2D renderer m_2DRender->Reset(); m_2DRender->Enable_Texturing( FALSE ); m_2DRender->Add_Line( Vector2( startX, startY ), Vector2( endX, endY ), lineWidth, lineColor ); m_2DRender->Render(); } // end drawLine // W3DDisplay::drawLine ======================================================= /** draw a line on the display in pixel coordinates with the specified color */ //============================================================================= void W3DDisplay::drawLine( Int startX, Int startY, Int endX, Int endY, Real lineWidth, UnsignedInt lineColor1,UnsignedInt lineColor2 ) { /// @todo we need to consider the efficiency of the 2D renderer m_2DRender->Reset(); m_2DRender->Enable_Texturing( FALSE ); m_2DRender->Add_Line( Vector2( startX, startY ), Vector2( endX, endY ), lineWidth, lineColor1, lineColor2 ); m_2DRender->Render(); } // end drawLine // W3DDisplay::drawOpenRect =================================================== //============================================================================= void W3DDisplay::drawOpenRect( Int startX, Int startY, Int width, Int height, Real lineWidth, UnsignedInt lineColor ) { if (m_isClippedEnabled) { ICoord2D start, end, returnStart, returnEnd; start.x = startX; start.y = startY; end.x = start.x; end.y = start.y + height; if(ClipLine2D(&start, &end, &returnStart, &returnEnd, &m_clipRegion )) drawLine( returnStart.x, returnStart.y, returnEnd.x, returnEnd.y, lineWidth, lineColor); end.x = start.x + width; end.y = start.y; if(ClipLine2D(&start, &end, &returnStart, &returnEnd, &m_clipRegion )) drawLine( returnStart.x, returnStart.y, returnEnd.x, returnEnd.y, lineWidth, lineColor); start.x = startX + width; start.y = startY; end.x = start.x; end.y = start.y + height; if(ClipLine2D(&start, &end, &returnStart, &returnEnd, &m_clipRegion )) drawLine( returnStart.x, returnStart.y, returnEnd.x, returnEnd.y, lineWidth, lineColor); start.x = startX; start.y = startY + height; end.x = start.x + width; end.y = start.y; if(ClipLine2D(&start, &end, &returnStart, &returnEnd, &m_clipRegion )) drawLine( returnStart.x, returnStart.y, returnEnd.x, returnEnd.y, lineWidth, lineColor); } else { /// @todo we need to consider the efficiency of the 2D renderer m_2DRender->Reset(); m_2DRender->Enable_Texturing( FALSE ); m_2DRender->Add_Outline( RectClass( startX, startY, startX + width, startY + height ), lineWidth, lineColor ); // render it now! m_2DRender->Render(); } } // end drawOpenRect // W3DDisplay::drawFillRect =================================================== //============================================================================= void W3DDisplay::drawFillRect( Int startX, Int startY, Int width, Int height, UnsignedInt color ) { /// @todo we need to consider the efficiency of the 2D renderer m_2DRender->Reset(); m_2DRender->Enable_Texturing( FALSE ); m_2DRender->Add_Rect( RectClass( startX, startY, startX + width, startY + height ), 0, 0, color ); // render it now! m_2DRender->Render(); } // end drawFillRect void W3DDisplay::drawRectClock(Int startX, Int startY, Int width, Int height, Int percent, UnsignedInt color) { // sanity if(percent < 1 || percent > 100) return; m_2DRender->Reset(); m_2DRender->Enable_Texturing( FALSE ); // The rectanges are numberd as follows //(x,y) |---------| // | 4 | 1 | // |----+----| // | 3 | 2 | // |---------| (x + width, y + width) // // we're done, lets just draw one rectangle for it all. if(percent == 100) { m_2DRender->Add_Rect(RectClass( startX, startY, startX + width, startY + height), 0,0, color); } else if( percent> 75) { //rectangle #1 & 2 m_2DRender->Add_Rect(RectClass( startX + width/2, startY, startX + width, startY + height), 0,0, color); // rectangle #3 m_2DRender->Add_Rect(RectClass( startX, startY + height/2, startX + width/2, startY + height), 0,0, color); // draw the part of rectangle 4 Real remain = percent - 75; if(remain > 12) { //draw the full triangle m_2DRender->Add_Tri(Vector2(startX, startY), Vector2(startX, startY + height/2), Vector2(startX + width/2, startY + height/2), Vector2(0,0),Vector2(0,0),Vector2(0,0),color); // draw the part of triangle Real percentDraw = (Real)(remain - 12)/ 13; m_2DRender->Add_Tri(Vector2(startX, startY), Vector2(startX + width/2, startY + height/2), Vector2(startX + (width/2 * percentDraw), startY), Vector2(0,0),Vector2(0,0),Vector2(0,0),color); } else { // draw the part of triangle Real percentDraw = (Real)(remain)/ 12; m_2DRender->Add_Tri(Vector2(startX, startY + height/2 - (height/2 * percentDraw)), Vector2(startX, startY + height/2), Vector2(startX + width/2, startY + height/2), Vector2(0,0),Vector2(0,0),Vector2(0,0),color); } } else if( percent > 50) { //rectangle #1 & 2 m_2DRender->Add_Rect(RectClass( startX + width/2, startY, startX + width, startY + height), 0,0, color); // draw the part of rectangle 3 Real remain = percent - 50; if(remain > 12) { //draw the full triangle m_2DRender->Add_Tri(Vector2(startX + width/2, startY + height/2), Vector2(startX, startY + height), Vector2(startX + width/2, startY + height), Vector2(0,0),Vector2(0,0),Vector2(0,0),color); // draw the part of triangle Real percentDraw = (Real)(remain - 12)/ 13; m_2DRender->Add_Tri(Vector2(startX, startY + height - (height/2 * percentDraw)), Vector2(startX, startY + height), Vector2(startX + width/2, startY + height/2), Vector2(0,0),Vector2(0,0),Vector2(0,0),color); } else { // draw the part of triangle Real percentDraw = (Real)(remain)/ 12; m_2DRender->Add_Tri(Vector2(startX + width/2, startY + height), Vector2(startX + width/2, startY + height/2), Vector2(startX + width/2 - ( width/2 * percentDraw), startY + height), Vector2(0,0),Vector2(0,0),Vector2(0,0),color); } } else if(percent > 25) { // rectangel #1 m_2DRender->Add_Rect(RectClass( startX + width/2, startY, startX + width, startY + height/2), 0,0, color); // draw the part of rectangle 2 Real remain = percent - 25; if(remain > 12) { //draw the full triangle m_2DRender->Add_Tri(Vector2(startX + width/2, startY + height/2), Vector2(startX + width, startY + height), Vector2(startX + width, startY + height/2), Vector2(0,0),Vector2(0,0),Vector2(0,0),color); // draw the part of triangle Real percentDraw = (Real)(remain - 12)/ 13; m_2DRender->Add_Tri(Vector2(startX + width/2, startY + height/2), Vector2(startX + width - (width/2 * percentDraw), startY + height), Vector2(startX + width, startY + height), Vector2(0,0),Vector2(0,0),Vector2(0,0),color); } else { // draw the part of triangle Real percentDraw = (Real)(remain)/ 12; m_2DRender->Add_Tri(Vector2(startX + width, startY + height/2), Vector2(startX + width/2, startY + height/2), Vector2(startX + width, startY + height/2 + ( height/2 * percentDraw)), Vector2(0,0),Vector2(0,0),Vector2(0,0),color); } } else { // draw the part of rectangle 1 if(percent > 12) { //draw the full triangle m_2DRender->Add_Tri(Vector2(startX + width/2, startY), Vector2(startX + width/2, startY + height/2), Vector2(startX + width, startY), Vector2(0,0),Vector2(0,0),Vector2(0,0),color); // draw the part of triangle Real percentDraw = (Real)(percent - 12)/ 13; m_2DRender->Add_Tri(Vector2(startX + width, startY), Vector2(startX + width/2, startY + height/2), Vector2(startX + width, startY + (height/2 * percentDraw)), Vector2(0,0),Vector2(0,0),Vector2(0,0),color); } else { // draw the part of triangle Real percentDraw = (Real)(percent)/ 12; m_2DRender->Add_Tri(Vector2(startX + width/2, startY), Vector2(startX + width/2, startY + height/2), Vector2(startX + width/2 + (width/2 * percentDraw), startY ), Vector2(0,0),Vector2(0,0),Vector2(0,0),color); } } // render it now! m_2DRender->Render(); } //-------------------------------------------------------------------------------------------------------------------- // W3DDisplay::drawRemainingRectClock // Variation added by Kris -- October 2002 // This version will overlay a clock progress from the specified percentage to 100%. Essentially, this function will // "reveal" an icon as it progresses towards completion. //-------------------------------------------------------------------------------------------------------------------- void W3DDisplay::drawRemainingRectClock(Int startX, Int startY, Int width, Int height, Int percent, UnsignedInt color) { // sanity if( percent < 0 || percent > 99 ) return; m_2DRender->Reset(); m_2DRender->Enable_Texturing( FALSE ); // The rectanges are numbered as follows //(x,y) |---------| // | 4 | 1 | // |----+----| // | 3 | 2 | // |---------| (x + width, y + width) // Int midX = startX + width/2; Int midY = startY + height/2; Int endX = startX + width; Int endY = startY + height; Int halfWidth = width/2; Int halfHeight = height/2; if( percent == 0 ) { // We just started, so draw the entire remaining rectangle. // #1, #2, #3, and #4 m_2DRender->Add_Rect( RectClass( startX, startY, endX, endY ), 0, 0, color ); } else if( percent < 25 ) { //1-25% //----- //Rectangle #3 & 4 m_2DRender->Add_Rect( RectClass( startX, startY, midX, endY ), 0, 0, color ); //Rectangle #2 m_2DRender->Add_Rect( RectClass( midX, midY, endX, endY ), 0, 0, color ); //Handle rectangle #1 than needs partial rendering. if( percent < 13 ) { //1-12% //----- //Draw the 2nd half of rectangle #1 m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( endX, midY ), Vector2( endX, startY ), Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color ); //Draw the last part of the 1st portion of rectangle #1 Real percentDraw = (Real)( 13 - percent ) / 13; m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( endX, startY ), Vector2( endX - halfWidth * percentDraw, startY ), Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color ); } else { //13-24% //------ //Draw the last part of the 2nd half of rectangle #1 Real percentDraw = (Real)( percent - 13 ) / 12; m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( endX, midY ), Vector2( endX, startY + halfHeight * percentDraw ), Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color ); } } else if( percent < 50 ) { //25-49% //------ //rectangle #3 & 4 m_2DRender->Add_Rect( RectClass( startX, startY, midX, endY ), 0, 0, color ); //Handle rectangle #2 that needs partial rendering. if( percent < 38 ) { //25-37% //----- //Draw the 2nd half of rectangle #2 m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( midX, endY ), Vector2( endX, endY ), Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color ); //Draw the last part of the 1st portion of rectangle #2 Real percentDraw = (Real)( percent - 25 ) / 13; m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( endX, endY ), Vector2( endX, midY + halfHeight * percentDraw ), Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color ); } else { //38-49% //------ //Draw the last part of the 2nd half of rectangle #1 Real percentDraw = (Real)( percent - 38 ) / 12; m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( midX, endY ), Vector2( endX - halfWidth * percentDraw, endY ), Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color ); } } else if( percent < 75 ) { //50-74% //------ //Rectangle #4 m_2DRender->Add_Rect( RectClass( startX, startY, midX, midY ), 0, 0, color ); //Handle rectangle #3 that needs partial rendering. if( percent < 63 ) { //50-62% //----- //Draw the 2nd half of rectangle #3 m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( startX, midY ), Vector2( startX, endY ), Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color ); //Draw the last part of the 1st portion of rectangle #3 Real percentDraw = (Real)( percent - 50 ) / 13; m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( startX, endY ), Vector2( midX - halfWidth * percentDraw, endY ), Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color ); } else { //62-74% //------ //Draw the last part of the 2nd half of rectangle #3 Real percentDraw = (Real)( percent - 62 ) / 12; m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( startX, midY ), Vector2( startX, endY - halfHeight * percentDraw ), Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color ); } } else { //75-99% //------ //Handle rectangle #4 that needs partial rendering. if( percent < 87 ) { //75-87% //----- //Draw the 2nd half of rectangle #4 m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( midX, startY ), Vector2( startX, startY ), Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color ); //Draw the last part of the 1st portion of rectangle #4 Real percentDraw = (Real)( percent - 75 ) / 13; m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( startX, startY ), Vector2( startX, midY - halfHeight * percentDraw ), Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color ); } else { //88-99% //------ //Draw the last part of the 2nd half of rectangle #4 Real percentDraw = (Real)( percent - 88 ) / 12; m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( midX, startY ), Vector2( startX + halfWidth * percentDraw, startY ), Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color ); } } // render it now! m_2DRender->Render(); } // W3DDisplay::drawImage ====================================================== /** Draws an images at the screen coordinates and keeps it within the end * screen coords specified */ //============================================================================= void W3DDisplay::drawImage( const Image *image, Int startX, Int startY, Int endX, Int endY, Color color, DrawImageMode mode) { // sanity if( image == NULL ) return; // !! // Remember to update the GUIEditDisplay::drawImage when you make // changes to this, it technically uses W3D code to render itself, // but it not derived on the W3DDisplay // !! const Region2D *uv = image->getUV(); m_2DRender->Reset(); m_2DRender->Enable_Texturing( TRUE ); Bool doAlphaReset=FALSE; ///@todo: Why are we alpha blending all images? Reduces our fillrate. -MW switch (mode) { case DRAW_IMAGE_ALPHA: //nothing to do since alpha is the default state break; case DRAW_IMAGE_GRAYSCALE: m_2DRender->Enable_Grayscale(true); break; case DRAW_IMAGE_ADDITIVE: m_2DRender->Enable_Additive(true); doAlphaReset = TRUE; break; case DRAW_IMAGE_SOLID: m_2DRender->Enable_Additive(false); m_2DRender->Enable_Alpha(false); doAlphaReset = TRUE; default: break; } // if we have raw texture data we will use it, otherwise we are referencing filenames if( BitTest( image->getStatus(), IMAGE_STATUS_RAW_TEXTURE ) ) m_2DRender->Set_Texture( (TextureClass *)(image->getRawTextureData()) ); else m_2DRender->Set_Texture( image->getFilename().str() ); RectClass screen_rect(startX,startY,endX,endY); RectClass uv_rect(uv->lo.x,uv->lo.y,uv->hi.x,uv->hi.y); if (m_isClippedEnabled) { //need to clip this quad to clip rectangle // // Check for completely clipped // if ( endX <= m_clipRegion.lo.x || endY <= m_clipRegion.lo.y) { return; //nothing to render } else { RectClass clipped_rect; RectClass clipped_uv_rect; if( BitTest( image->getStatus(), IMAGE_STATUS_ROTATED_90_CLOCKWISE ) ) { // // Clip the polygons to the specified area // clipped_rect.Left = __max (screen_rect.Left, m_clipRegion.lo.x); clipped_rect.Right = __min (screen_rect.Right, m_clipRegion.hi.x); clipped_rect.Top = __max (screen_rect.Top, m_clipRegion.lo.y); clipped_rect.Bottom = __min (screen_rect.Bottom, m_clipRegion.hi.y); // // Clip the texture to the specified area // float percent = ((clipped_rect.Left - screen_rect.Left) / screen_rect.Width ()); clipped_uv_rect.Top = uv_rect.Top + (uv_rect.Height () * percent); percent = ((clipped_rect.Right - screen_rect.Left) / screen_rect.Width ()); clipped_uv_rect.Bottom = uv_rect.Top + (uv_rect.Height () * percent); percent = ((clipped_rect.Top - screen_rect.Top) / screen_rect.Height ()); clipped_uv_rect.Right = uv_rect.Right - (uv_rect.Width () * percent); percent = ((clipped_rect.Bottom - screen_rect.Top) / screen_rect.Height ()); clipped_uv_rect.Left = uv_rect.Right - (uv_rect.Width () * percent); } else { // // Clip the polygons to the specified area // clipped_rect.Left = __max (screen_rect.Left, m_clipRegion.lo.x); clipped_rect.Right = __min (screen_rect.Right, m_clipRegion.hi.x); clipped_rect.Top = __max (screen_rect.Top, m_clipRegion.lo.y); clipped_rect.Bottom = __min (screen_rect.Bottom, m_clipRegion.hi.y); // // Clip the texture to the specified area // float percent = ((clipped_rect.Left - screen_rect.Left) / screen_rect.Width ()); clipped_uv_rect.Left = uv_rect.Left + (uv_rect.Width () * percent); percent = ((clipped_rect.Right - screen_rect.Left) / screen_rect.Width ()); clipped_uv_rect.Right = uv_rect.Left + (uv_rect.Width () * percent); percent = ((clipped_rect.Top - screen_rect.Top) / screen_rect.Height ()); clipped_uv_rect.Top = uv_rect.Top + (uv_rect.Height () * percent); percent = ((clipped_rect.Bottom - screen_rect.Top) / screen_rect.Height ()); clipped_uv_rect.Bottom = uv_rect.Top + (uv_rect.Height () * percent); } // // Use the clipped rectangles to render // screen_rect = clipped_rect; uv_rect = clipped_uv_rect; } } // if rotated 90 degrees clockwise we have to adjust the uv coords if( BitTest( image->getStatus(), IMAGE_STATUS_ROTATED_90_CLOCKWISE ) ) { m_2DRender->Add_Tri( Vector2( screen_rect.Left, screen_rect.Top ), Vector2( screen_rect.Left, screen_rect.Bottom ), Vector2( screen_rect.Right, screen_rect.Top ), Vector2( uv_rect.Right, uv_rect.Top), Vector2( uv_rect.Left, uv_rect.Top), Vector2( uv_rect.Right, uv_rect.Bottom ), color ); m_2DRender->Add_Tri( Vector2( screen_rect.Right, screen_rect.Bottom ), Vector2( screen_rect.Right, screen_rect.Top ), Vector2( screen_rect.Left, screen_rect.Bottom ), Vector2( uv_rect.Left, uv_rect.Bottom ), Vector2( uv_rect.Right, uv_rect.Bottom ), Vector2( uv_rect.Left, uv_rect.Top ), color ); } // end if else { // just draw as normal m_2DRender->Add_Quad( screen_rect, uv_rect, color ); } // end else m_2DRender->Render(); //reset to default states for next time this method is called. m_2DRender->Enable_Grayscale(false); //never leave it in this mode if (doAlphaReset) m_2DRender->Enable_Alpha(true); } // end drawImage //============================================================================ // W3DDisplay::createVideoBuffer //============================================================================ VideoBuffer* W3DDisplay::createVideoBuffer( void ) { VideoBuffer::Type format = VideoBuffer::TYPE_UNKNOWN; /// @todo query video player for supported formats - we assume bink formats here // first try to use the native format WW3DFormat displayFormat = DX8Wrapper::getBackBufferFormat(); if ( DX8Caps::Support_Texture_Format( displayFormat )) { format = W3DVideoBuffer::W3DFormatToType( displayFormat ); } if ( format == VideoBuffer::TYPE_UNKNOWN ) { if ( DX8Caps::Support_Texture_Format( WW3D_FORMAT_X8R8G8B8 )) { format = VideoBuffer::TYPE_X8R8G8B8; } else if ( DX8Caps::Support_Texture_Format( WW3D_FORMAT_R8G8B8 )) { format = VideoBuffer::TYPE_R8G8B8; } else if ( DX8Caps::Support_Texture_Format( WW3D_FORMAT_R5G6B5 )) { format = VideoBuffer::TYPE_R5G6B5; } else if ( DX8Caps::Support_Texture_Format( WW3D_FORMAT_X1R5G5B5 )) { format = VideoBuffer::TYPE_X1R5G5B5; } else { // card does not support any of the formats we need return NULL; } } // on low mem machines, render every video in 16bit except for the EA Logo movie if(!TheGlobalData->m_playIntro )//&& TheGameLODManager && (!TheGameLODManager->didMemPass() || W3DShaderManager::getChipset() == DC_GEFORCE2)) format = VideoBuffer::TYPE_R5G6B5; W3DVideoBuffer *buffer = NEW W3DVideoBuffer( format ); return buffer; } //============================================================================ // W3DDisplay::drawVideoBuffer //============================================================================ void W3DDisplay::drawVideoBuffer( VideoBuffer *buffer, Int startX, Int startY, Int endX, Int endY ) { W3DVideoBuffer *vbuffer = (W3DVideoBuffer*) buffer; m_2DRender->Reset(); m_2DRender->Enable_Texturing( TRUE ); m_2DRender->Set_Texture( vbuffer->texture() ); m_2DRender->Add_Quad( RectClass( startX, startY, endX, endY ), vbuffer->Rect( 0, 0, 1, 1) ); m_2DRender->Render(); } // W3DDisplay::setClipRegion ============================================ /** Set the clipping region for images. @todo: Make this work for all primitives, not just drawImage. */ //============================================================================= void W3DDisplay::setClipRegion( IRegion2D *region ) { // assign new region m_clipRegion = *region; m_isClippedEnabled = TRUE; } // end setClipRegion //============================================================================= /* we don't really need to override this call, since we will soon be called to update every shroud cell explicitly... */ void W3DDisplay::clearShroud() { // nothing } //============================================================================= void W3DDisplay::setBorderShroudLevel(UnsignedByte level) { if (TheTerrainRenderObject && TheTerrainRenderObject->getShroud()) { TheTerrainRenderObject->getShroud()->setBorderShroudLevel((W3DShroudLevel)level); } } //============================================================================= void W3DDisplay::setShroudLevel( Int x, Int y, CellShroudStatus setting ) { if (TheTerrainRenderObject && TheTerrainRenderObject->getShroud()) { #ifdef INTENSE_DEBUG TheTerrainRenderObject->getShroud()->setShroudFilter(false); #endif if( setting == CELLSHROUD_SHROUDED ) TheTerrainRenderObject->getShroud()->setShroudLevel(x, y, (W3DShroudLevel)TheGlobalData->m_shroudAlpha ); else if( setting == CELLSHROUD_FOGGED ) TheTerrainRenderObject->getShroud()->setShroudLevel(x, y, (W3DShroudLevel)TheGlobalData->m_fogAlpha );///< @todo placeholder to get feedback on logic work while graphic side being decided else TheTerrainRenderObject->getShroud()->setShroudLevel(x, y, (W3DShroudLevel)TheGlobalData->m_clearAlpha ); //Logic is saying shroud. We can add alpha levels here in client if needed. // W3DShroud is a 0-255 alpha byte. Logic shroud is a double reference count. } } //============================================================================= ///Utility function to dump data into a .BMP file static void CreateBMPFile(LPTSTR pszFile, char *image, Int width, Int height) { HANDLE hf; // file handle BITMAPFILEHEADER hdr; // bitmap file-header PBITMAPINFOHEADER pbih; // bitmap info-header LPBYTE lpBits; // memory pointer DWORD dwTotal; // total count of bytes DWORD cb; // incremental count of bytes BYTE *hp; // byte pointer DWORD dwTmp; PBITMAPINFO pbmi; pbmi = (PBITMAPINFO) LocalAlloc(LPTR,sizeof(BITMAPINFOHEADER)); pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pbmi->bmiHeader.biWidth = width; pbmi->bmiHeader.biHeight = height; pbmi->bmiHeader.biPlanes = 1; pbmi->bmiHeader.biBitCount = 24; pbmi->bmiHeader.biCompression = BI_RGB; pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7) /8 * pbmi->bmiHeader.biHeight * 24; pbmi->bmiHeader.biClrImportant = 0; pbih = (PBITMAPINFOHEADER) pbmi; lpBits = (LPBYTE) image; // Create the .BMP file. hf = CreateFile(pszFile, GENERIC_READ | GENERIC_WRITE, (DWORD) 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL); if (hf == INVALID_HANDLE_VALUE) return; hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M" // Compute the size of the entire file. hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage); hdr.bfReserved1 = 0; hdr.bfReserved2 = 0; // Compute the offset to the array of color indices. hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof (RGBQUAD); // Copy the BITMAPFILEHEADER into the .BMP file. if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), (LPDWORD) &dwTmp, NULL)) return; // Copy the BITMAPINFOHEADER and RGBQUAD array into the file. if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof (RGBQUAD),(LPDWORD) &dwTmp, NULL)) return; // Copy the array of color indices into the .BMP file. dwTotal = cb = pbih->biSizeImage; hp = lpBits; if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL)) return; // Close the .BMP file. if (!CloseHandle(hf)) return; // Free memory. LocalFree( (HLOCAL) pbmi); } ///Save Screen Capture to a file void W3DDisplay::takeScreenShot(void) { char leafname[256]; char pathname[1024]; static int frame_number = 1; Bool done = false; while (!done) { #ifdef CAPTURE_TO_TARGA sprintf( leafname, "%s%.3d.tga", "sshot", frame_number++); #else sprintf( leafname, "%s%.3d.bmp", "sshot", frame_number++); #endif strcpy(pathname, TheGlobalData->getPath_UserData().str()); strcat(pathname, leafname); if (_access( pathname, 0 ) == -1) done = true; } // Lock front buffer and copy IDirect3DSurface8 *fb; fb=DX8Wrapper::_Get_DX8_Front_Buffer(); D3DSURFACE_DESC desc; fb->GetDesc(&desc); RECT bounds; POINT point; GetClientRect(ApplicationHWnd,&bounds); point.x=bounds.left; point.y=bounds.top; ClientToScreen(ApplicationHWnd, &point); bounds.left=point.x; bounds.top=point.y; point.x=bounds.right; point.y=bounds.bottom; ClientToScreen(ApplicationHWnd, &point); bounds.right=point.x; bounds.bottom=point.y; D3DLOCKED_RECT lrect; DX8_ErrorCode(fb->LockRect(&lrect,&bounds,D3DLOCK_READONLY)); unsigned int x,y,index,index2,width,height; width=bounds.right-bounds.left; height=bounds.bottom-bounds.top; char *image=NEW char[3*width*height]; #ifdef CAPTURE_TO_TARGA //bytes are mixed in targa files, not rgb order. for (y=0; yRelease(); Targa targ; memset(&targ.Header,0,sizeof(targ.Header)); targ.Header.Width=width; targ.Header.Height=height; targ.Header.PixelDepth=24; targ.Header.ImageType=TGA_TRUECOLOR; targ.SetImage(image); targ.YFlip(); targ.Save(pathname,TGAF_IMAGE,false); #else //capturing to bmp file //bmp is same byte order for (y=0; yRelease(); //Flip the image char *ptr,*ptr1; char v,v1; for (y = 0; y < (height >> 1); y++) { /* Compute address of lines to exchange. */ ptr = (image + ((width * y) * 3)); ptr1 = (image + ((width * (height - 1)) * 3)); ptr1 -= ((width * y) * 3); /* Exchange all the pixels on this scan line. */ for (x = 0; x < (width * 3); x++) { v = *ptr; v1 = *ptr1; *ptr = v1; *ptr1 = v; ptr++; ptr1++; } } CreateBMPFile(pathname, image, width, height); #endif delete [] image; UnicodeString ufileName; ufileName.translate(leafname); TheInGameUI->message(TheGameText->fetch("GUI:ScreenCapture"), ufileName.str()); } /** Start/Stop campturing an AVI movie*/ void W3DDisplay::toggleMovieCapture(void) { WW3D::Toggle_Movie_Capture("Movie",30); } #if defined(_DEBUG) || defined(_INTERNAL) static FILE *AssetDumpFile=NULL; void dumpMeshAssets(MeshClass *mesh) { if (mesh) { TextureClass *texture; //MaterialInfoClass *material = mesh->Get_Material_Info(); MeshModelClass *model=mesh->Get_Model(); for (int stage=0;stageGet_Pass_Count();++pass) { if (model->Has_Texture_Array(pass,stage)) { for (int i=0;iGet_Polygon_Count();++i) { if ((texture=model->Peek_Texture(i,pass,stage)) != NULL) { fprintf(AssetDumpFile,"\t%s\n",texture->Get_Texture_Name()); } } } else { if ((texture=model->Peek_Single_Texture(pass,stage)) != NULL) { fprintf(AssetDumpFile,"\t%s\n",texture->Get_Texture_Name()); } } } } } } void dumpHLODAssets(HLodClass *hlod) { if (hlod) { //model composed of multiple meshes. for (Int i=0; iGet_Num_Sub_Objects(); i++) { RenderObjClass *subObj=hlod->Get_Sub_Object(i); if (subObj->Class_ID() == RenderObjClass::CLASSID_HLOD) dumpHLODAssets((HLodClass *)subObj); else if (subObj->Class_ID() == RenderObjClass::CLASSID_MESH) dumpMeshAssets((MeshClass *)subObj); } } } //------------------------------------------------------------------------------------------------- /** dump all used models/textures to a file.*/ //------------------------------------------------------------------------------------------------- void W3DDisplay::dumpModelAssets(const char *path) { if (m_3DScene) { AssetDumpFile=fopen(path,"w"); if (AssetDumpFile) { fprintf(AssetDumpFile,"Models and Textures used on %s:\n\n",TheGlobalData->m_mapName.str()); SceneIterator *sceneIter = m_3DScene->Create_Iterator(); sceneIter->First(); while(!sceneIter->Is_Done()) { RenderObjClass * robj = sceneIter->Current_Item(); if (robj->Class_ID() == RenderObjClass::CLASSID_HLOD) { fprintf(AssetDumpFile,"%s.W3D:\n",robj->Get_Name()); dumpHLODAssets((HLodClass *)robj); } else if (robj->Class_ID() == RenderObjClass::CLASSID_MESH) { fprintf(AssetDumpFile,"%s.W3D:\n",robj->Get_Name()); dumpMeshAssets((MeshClass *)robj); } sceneIter->Next(); } m_3DScene->Destroy_Iterator(sceneIter); fclose(AssetDumpFile); } } } #endif //only include above code in debug and internal //------------------------------------------------------------------------------------------------- /** Preload using the W3D asset manager the model referenced by the string parameter */ //------------------------------------------------------------------------------------------------- void W3DDisplay::preloadModelAssets( AsciiString model ) { if( m_assetManager ) { AsciiString nameWithExtension; nameWithExtension.format( "%s.w3d", model.str() ); m_assetManager->Load_3D_Assets( nameWithExtension.str() ); } // end if } // end preloadModelAssets //------------------------------------------------------------------------------------------------- /** Preload using the W3D asset manager the texture referenced by the string parameter */ //------------------------------------------------------------------------------------------------- void W3DDisplay::preloadTextureAssets( AsciiString texture ) { if( m_assetManager ) { TextureClass *theTexture = m_assetManager->Get_Texture( texture.str() ); theTexture->Release_Ref();//release reference } // end if } // end preloadModelAssets //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void W3DDisplay::doSmartAssetPurgeAndPreload(const char* usageFileName) { if (!m_assetManager || !usageFileName || !*usageFileName) return; DynamicVectorClass names(8000); // use TheFileSystem here so we can bigify these files File* f = TheFileSystem->openFile(usageFileName, File::READ | File::TEXT); if (f) { for (;;) { AsciiString tmp; if (f->scanString(tmp) == FALSE) break; // allow for comments in the file. Note that this doesn't allow for comments // with spaces! doh. oh well. better than nothing. if (tmp.str()[0] == ';') continue; names.Add(StringClass(tmp.str())); } f->close(); } // just free everything if there's no exclusion list file (send in an empty list) m_assetManager->Free_Assets_With_Exclusion_List(names); } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- #if defined(_DEBUG) || defined(_INTERNAL) void W3DDisplay::dumpAssetUsage(const char* mapname) { if (!m_assetManager || !mapname || !*mapname) return; DynamicVectorClass names(8000); m_assetManager->Create_Asset_List(names); const char* leafname = strrchr(mapname, '\\'); if (leafname) ++leafname; // point to first character after the last backslash else leafname = mapname; // point to the start of the filename char buf[256]; int idx = 1; while (true) { sprintf(buf, "AssetUsage_%s_%04d.txt",leafname,idx); if (_access(buf, 0) != 0) break; // it exists, we're good ++idx; } FILE *fp = fopen(buf, "w"); if (fp) { for (int i=0; im_framesPerSecondLimit); if (percTime > 1.0f) percTime = 1.0f; else if (percTime < 0.0f) percTime = 0.0f; Int width = REAL_TO_INT(percTime * TheDisplay->getWidth()); UnsignedInt colorToUse = GameMakeColor( REAL_TO_UNSIGNEDBYTE((1.0f - percTime) * 255), REAL_TO_UNSIGNEDBYTE(percTime * 255), 0, 0x7F); TheDisplay->drawFillRect(1, 1, width, 15, colorToUse); prevTime = now; }