W3DDisplay.cpp 102 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: W3DDisplay.cpp ///////////////////////////////////////////////////////
  24. //
  25. // W3D Implementation for the Game Display which is responsible for creating
  26. // and maintaning the entire visual display
  27. //
  28. // Author: Colin Day, April 2001
  29. //
  30. ///////////////////////////////////////////////////////////////////////////////
  31. static void drawFramerateBar(void);
  32. // SYSTEM INCLUDES ////////////////////////////////////////////////////////////
  33. #include <stdlib.h>
  34. #include <windows.h>
  35. #include <io.h>
  36. #include <time.h>
  37. // USER INCLUDES //////////////////////////////////////////////////////////////
  38. #include "Common/ThingFactory.h"
  39. #include "Common/GameEngine.h"
  40. #include "Common/GlobalData.h"
  41. #include "Common/PerfTimer.h"
  42. #include "Common/FileSystem.h"
  43. #include "Common/LocalFileSystem.h"
  44. #include "Common/Player.h"
  45. #include "Common/PlayerList.h"
  46. #include "Common/ThingTemplate.h"
  47. #include "Common/GameLOD.h"
  48. #include "Common/DrawModule.h"
  49. #include "GameLogic/AIPathfind.h"
  50. #include "GameClient/Drawable.h"
  51. #include "GameClient/GameText.h"
  52. #include "GameClient/GraphDraw.h"
  53. #include "GameClient/Line2D.h"
  54. #include "GameClient/Mouse.h"
  55. #include "GameClient/GlobalLanguage.h"
  56. #include "GameClient/Water.h"
  57. #include "GameNetwork/NetworkInterface.h"
  58. #include "Common/ModelState.h"
  59. #include "Lib/BaseType.h"
  60. #include "W3DDevice/Common/W3DConvert.h"
  61. #include "W3DDevice/GameClient/W3DAssetManager.h"
  62. #include "W3DDevice/GameClient/W3DGameClient.h"
  63. #include "W3DDevice/GameClient/W3DFileSystem.h"
  64. #include "W3DDevice/GameClient/W3DDynamicLight.h"
  65. #include "W3DDevice/GameClient/HeightMap.h"
  66. #include "W3DDevice/GameClient/WorldHeightMap.h"
  67. #include "W3DDevice/GameClient/W3DScene.h"
  68. #include "W3DDevice/GameClient/W3DTerrainTracks.h"
  69. #include "W3DDevice/GameClient/W3DWater.h"
  70. #include "W3DDevice/GameClient/W3DVideoBuffer.h"
  71. #include "W3DDevice/GameClient/W3DShaderManager.h"
  72. #include "W3DDevice/GameClient/W3DDebugDisplay.h"
  73. #include "W3DDevice/GameClient/W3DProjectedShadow.h"
  74. #include "W3DDevice/GameClient/W3DShroud.h"
  75. #include "WWMath/WWMath.h"
  76. #include "WWLib/Registry.h"
  77. #include "WW3D2/WW3D.h"
  78. #include "WW3D2/PredLod.h"
  79. #include "WW3D2/Part_Emt.h"
  80. #include "WW3D2/Part_Ldr.h"
  81. #include "WW3D2/DX8Caps.h"
  82. #include "WW3D2/WW3DFormat.h"
  83. #include "WW3D2/agg_def.h"
  84. #include "WW3D2/Render2DSentence.h"
  85. #include "WW3D2/SortingRenderer.h"
  86. #include "WW3D2/Textureloader.h"
  87. #include "WW3D2/DX8WebBrowser.h"
  88. #include "WW3D2/Mesh.h"
  89. #include "WW3D2/HLOD.h"
  90. #include "WW3D2/Meshmatdesc.h"
  91. #include "WW3D2/Meshmdl.h"
  92. #include "WW3D2/rddesc.h"
  93. #include "targa.h"
  94. #include "Lib/BaseType.h"
  95. #include "GameLogic/ScriptEngine.h" // For TheScriptEngine - jkmcd
  96. #include "GameLogic/GameLogic.h"
  97. #ifdef DUMP_PERF_STATS
  98. #include "GameLogic/PartitionManager.h"
  99. #endif
  100. #include "WinMain.h"
  101. #ifdef _INTERNAL
  102. // for occasional debugging...
  103. //#pragma optimize("", off)
  104. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  105. #endif
  106. // DEFINE AND ENUMS ///////////////////////////////////////////////////////////
  107. #define W3D_DISPLAY_DEFAULT_BIT_DEPTH 32
  108. #define no_SAMPLE_DYNAMIC_LIGHT 1
  109. #ifdef SAMPLE_DYNAMIC_LIGHT
  110. static W3DDynamicLight * theDynamicLight = NULL;
  111. static Real theLightXOffset = 0.1f;
  112. static Real theLightYOffset = 0.07f;
  113. static Int theFlashCount = 0;
  114. #endif
  115. //*****************************************************************************************
  116. //*****************************************************************************************
  117. //**** Start Statistical Dump *************************************************************
  118. //*****************************************************************************************
  119. #ifdef DUMP_PERF_STATS
  120. #include <cstdarg>
  121. class StatDumpClass
  122. {
  123. public:
  124. StatDumpClass( const char *fname );
  125. ~StatDumpClass();
  126. void dumpStats();
  127. protected:
  128. FILE *m_fp;
  129. };
  130. //=============================================================================
  131. //Open the file once at the beginning of the game -- everything appends to it.
  132. //=============================================================================
  133. StatDumpClass::StatDumpClass( const char *fname )
  134. {
  135. char buffer[ _MAX_PATH ];
  136. GetModuleFileName( NULL, buffer, sizeof( buffer ) );
  137. char *pEnd = buffer + strlen( buffer );
  138. while( pEnd != buffer )
  139. {
  140. if( *pEnd == '\\' )
  141. {
  142. *pEnd = 0;
  143. break;
  144. }
  145. pEnd--;
  146. }
  147. AsciiString fullPath;
  148. fullPath.format( "%s\\%s", buffer, fname );
  149. m_fp = fopen( fullPath.str(), "wt" );
  150. }
  151. //=============================================================================
  152. //Close the file at the end of the application
  153. //=============================================================================
  154. StatDumpClass::~StatDumpClass()
  155. {
  156. if( m_fp )
  157. {
  158. fclose( m_fp );
  159. }
  160. }
  161. static const char *getCurrentTimeString(void)
  162. {
  163. time_t aclock;
  164. time(&aclock);
  165. struct tm *newtime = localtime(&aclock);
  166. return asctime(newtime);
  167. }
  168. //=============================================================================
  169. //Dump the stats
  170. //=============================================================================
  171. void StatDumpClass::dumpStats()
  172. {
  173. if( !m_fp )
  174. {
  175. return;
  176. }
  177. //static char buf[1024];
  178. fprintf( m_fp, "----------------------------------------------------------------\n" );
  179. fprintf( m_fp, "Performance Statistical Dump -- Frame %d\n", TheGameLogic->getFrame() );
  180. fprintf( m_fp, "Time:\t%s", getCurrentTimeString() );
  181. fprintf( m_fp, "Map:\t%s\n", TheGlobalData->m_mapName.str());
  182. fprintf( m_fp, "Side:\t%s\n", ThePlayerList->getLocalPlayer()->getSide().str());
  183. fprintf( m_fp, "----------------------------------------------------------------\n" );
  184. //FPS
  185. Real fps = TheDisplay->getAverageFPS();
  186. fprintf( m_fp, "Average FPS: %.1f (%.5f msec)\n", fps, 1000.0f / fps );
  187. //Rendering stats
  188. fprintf( m_fp, "Draws: %d Skins: %d SortedPolys: %d SkinPolys: %d\n",(Int)Debug_Statistics::Get_Draw_Calls(),
  189. (Int)Debug_Statistics::Get_DX8_Skin_Renders(),
  190. (Int)Debug_Statistics::Get_Sorting_Polygons(), (Int)Debug_Statistics::Get_DX8_Skin_Polygons());
  191. //Object stats
  192. UnsignedInt objCount = TheGameLogic->getObjectCount();
  193. UnsignedInt objScreenCount = TheGameClient->getRenderedObjectCount();
  194. fprintf( m_fp, "Objects: %d in world (%d onscreen)\n", objCount, objScreenCount );
  195. //AI stats
  196. UnsignedInt numAI, numMoving, numAttacking, numWaitingForPath, overallFailedPathfinds;
  197. TheGameLogic->getAIMetricsStatistics( &numAI, &numMoving, &numAttacking, &numWaitingForPath, &overallFailedPathfinds );
  198. fprintf( m_fp, "\n" );
  199. fprintf( m_fp, "AI Statistics:\n" );
  200. fprintf( m_fp, " Total AI Objects: %d\n", numAI );
  201. fprintf( m_fp, " -moving: %d\n", numMoving );
  202. fprintf( m_fp, " -attacking: %d\n", numAttacking );
  203. fprintf( m_fp, " -waiting for path: %d\n", numWaitingForPath );
  204. fprintf( m_fp, " Total failed pathfinds: %d\n", overallFailedPathfinds );
  205. fprintf( m_fp, "\n" );
  206. // Script stats
  207. Real timeLastFrame, slowScript1, slowScript2;
  208. AsciiString slowScripts = TheScriptEngine->getStats(&timeLastFrame, &slowScript1, &slowScript2);
  209. fprintf( m_fp, "\n" );
  210. fprintf( m_fp, "Script Engine Statistics:\n" );
  211. fprintf( m_fp, " Total time last frame: %.5f msec\n", timeLastFrame*1000 );
  212. fprintf( m_fp, " -Slowest 2 scripts %s\n", slowScripts.str() );
  213. fprintf( m_fp, " -Slowest 2 script times %.5f msec, %.5f msec \n", slowScript1*1000, slowScript2*1000 );
  214. fprintf( m_fp, "\n" );
  215. //PartitionMgr stats
  216. double gcoTimeThisFrameTotal, gcoTimeThisFrameAvg;
  217. ThePartitionManager->getPMStats(gcoTimeThisFrameTotal, gcoTimeThisFrameAvg);
  218. fprintf(m_fp, "Partition Manager Statistics:\n");
  219. fprintf(m_fp, " Total time for object scans this frame is %.5f msec\n", gcoTimeThisFrameTotal);
  220. fprintf(m_fp, " Avg time per object scan this frame is %.5f msec\n", gcoTimeThisFrameAvg);
  221. fprintf( m_fp, "\n" );
  222. // setup texture stats
  223. Debug_Statistics::Record_Texture_Mode(Debug_Statistics::RECORD_TEXTURE_SIMPLE/*RECORD_TEXTURE_NONE*/);
  224. fprintf( m_fp, "Video Statistics:\n" );
  225. //Particle system stats
  226. fprintf( m_fp, " Particle Systems: %d\n", TheParticleSystemManager->getParticleSystemCount() );
  227. Int totalParticles = TheParticleSystemManager->getParticleCount();
  228. Int onScreenParticleCount = TheParticleSystemManager->getOnScreenParticleCount();
  229. fprintf( m_fp, " Particles: %d in world (%d onscreen)\n", totalParticles, onScreenParticleCount );
  230. // polygons this frame
  231. Int polyPerFrame = Debug_Statistics::Get_DX8_Polygons();
  232. Int polyPerSecond = (Int)(polyPerFrame * fps);
  233. fprintf( m_fp, " Polygons: %d per frame (%d per second)\n", polyPerFrame, polyPerSecond );
  234. // vertices this frame
  235. fprintf( m_fp, " Vertices: %d\n", Debug_Statistics::Get_DX8_Vertices() );
  236. //
  237. // I'm adjusting the texture memory usage counter by subtracting
  238. // out the terrain alpha texture (since it's really == terrain texture).
  239. //
  240. fprintf( m_fp, " Video RAM: %d\n", Debug_Statistics::Get_Record_Texture_Size() - 1376256 );
  241. // terrain stats
  242. fprintf( m_fp, " 3-Way Blends: %d, Shoreline Blends: %d\n", TheTerrainRenderObject->getNumExtraBlendTiles(), TheTerrainRenderObject->getNumShoreLineTiles() );
  243. fprintf( m_fp, "\n" );
  244. #if defined(_DEBUG) || defined(_INTERNAL)
  245. TheAudio->audioDebugDisplay( NULL, NULL, m_fp );
  246. fprintf( m_fp, "\n" );
  247. #endif
  248. #ifdef MEMORYPOOL_DEBUG
  249. //Report memory usage.
  250. TheMemoryPoolFactory->debugMemoryReport( REPORT_FACTORYINFO | REPORT_POOLINFO, 0, 0, m_fp );
  251. #else
  252. fprintf( m_fp, "Memory Report -- unavailable (build doesn't have MEMORYPOOL_DEBUG defined)\n" );
  253. #endif
  254. fprintf( m_fp, "\n" );
  255. fprintf( m_fp, "%s", TheSubsystemList->dumpTimesForAll().str());
  256. fprintf( m_fp, "----------------------------------------------------------------\n" );
  257. fprintf( m_fp, "END -- Frame %d\n", TheGameLogic->getFrame() );
  258. fprintf( m_fp, "----------------------------------------------------------------\n\n\n" );
  259. fflush(m_fp);
  260. }
  261. StatDumpClass TheStatDump("StatisticsDump.txt");
  262. #endif //DUMP_PERF_STATS
  263. //*****************************************************************************************
  264. //**** End Statistical Dump ***************************************************************
  265. //*****************************************************************************************
  266. //*****************************************************************************************
  267. ///////////////////////////////////////////////////////////////////////////////
  268. // DEFINITIONS ////////////////////////////////////////////////////////////////
  269. ///////////////////////////////////////////////////////////////////////////////
  270. //=============================================================================
  271. RTS3DScene *W3DDisplay::m_3DScene = NULL;
  272. RTS2DScene *W3DDisplay::m_2DScene = NULL;
  273. RTS3DInterfaceScene *W3DDisplay::m_3DInterfaceScene = NULL;
  274. W3DAssetManager *W3DDisplay::m_assetManager = NULL;
  275. //=============================================================================
  276. // note, can't use the ones from PerfTimer.h 'cuz they are currently
  277. // only valid when "-vtune" is used... (srj)
  278. inline Int64 getPerformanceCounter()
  279. {
  280. Int64 tmp;
  281. QueryPerformanceCounter((LARGE_INTEGER*)&tmp);
  282. return tmp;
  283. }
  284. inline Int64 getPerformanceCounterFrequency()
  285. {
  286. Int64 tmp;
  287. QueryPerformanceFrequency((LARGE_INTEGER*)&tmp);
  288. return tmp;
  289. }
  290. // W3DDisplay::W3DDisplay =====================================================
  291. /** */
  292. //=============================================================================
  293. W3DDisplay::W3DDisplay()
  294. {
  295. Int i;
  296. m_initialized = false;
  297. m_assetManager = NULL;
  298. m_3DScene = NULL;
  299. m_2DScene = NULL;
  300. m_3DInterfaceScene = NULL;
  301. m_averageFPS = TheGlobalData->m_framesPerSecondLimit;
  302. #if defined(_DEBUG) || defined(_INTERNAL)
  303. m_timerAtCumuFPSStart = 0;
  304. #endif
  305. for (i=0; i<LightEnvironmentClass::MAX_LIGHTS; i++)
  306. m_myLight[i] = NULL;
  307. m_2DRender = NULL;
  308. m_isClippedEnabled = FALSE;
  309. m_clipRegion.lo.x = 0;
  310. m_clipRegion.lo.y = 0;
  311. m_clipRegion.hi.x = 0;
  312. m_clipRegion.hi.y = 0;
  313. for (i = 0; i < DisplayStringCount; i++)
  314. m_displayStrings[i] = NULL;
  315. } // end W3DDisplay
  316. // W3DDisplay::~W3DDisplay ====================================================
  317. /** */
  318. //=============================================================================
  319. W3DDisplay::~W3DDisplay()
  320. {
  321. // get rid of the debug display
  322. delete m_debugDisplay;
  323. // delete the display strings
  324. for (int i = 0; i < DisplayStringCount; i++)
  325. TheDisplayStringManager->freeDisplayString(m_displayStrings[i]);
  326. // delete 2D renderer
  327. if( m_2DRender )
  328. {
  329. m_2DRender->Reset();
  330. delete m_2DRender;
  331. m_2DRender = NULL;
  332. } // end if
  333. //
  334. // delete all our views now since they are W3D views and we need to
  335. // free them BEFORE we shutdown W3D
  336. //
  337. Display::deleteViews();
  338. REF_PTR_RELEASE( m_3DScene );
  339. REF_PTR_RELEASE( m_2DScene );
  340. REF_PTR_RELEASE( m_3DInterfaceScene );
  341. for (Int j=0; j<LightEnvironmentClass::MAX_LIGHTS; j++)
  342. REF_PTR_RELEASE( m_myLight[j] );
  343. PredictiveLODOptimizerClass::Free();
  344. // shutdown
  345. Debug_Statistics::Shutdown_Statistics();
  346. TextureLoadTaskClass::shutdown();
  347. W3DShaderManager::shutdown();
  348. m_assetManager->Free_Assets();
  349. delete m_assetManager;
  350. WW3D::Shutdown();
  351. WWMath::Shutdown();
  352. DX8WebBrowser::Shutdown();
  353. delete TheW3DFileSystem;
  354. TheW3DFileSystem = NULL;
  355. } // end ~W3DDisplay
  356. /*Return number of screen modes supported by the current device*/
  357. Int W3DDisplay::getDisplayModeCount(void)
  358. {
  359. const RenderDeviceDescClass &devDesc=WW3D::Get_Render_Device_Desc(0);
  360. const DynamicVectorClass <ResolutionDescClass> &resolutions=devDesc.Enumerate_Resolutions();
  361. Int numResolutions=0;
  362. /* Bool needStencil=false;
  363. Bool needDestinationAlpha=false;
  364. Int minBitDepth=16;
  365. //Walk through all resolutions and determine which ones are compatible with other settings
  366. //chosen by user. For example, 32-bit may be required for shadows, occlusion, soft water edge, etc.
  367. if (TheGlobalData->m_useShadowVolumes || (TheGlobalData->m_enableBehindBuildingMarkers && TheGameLogic->getShowBehindBuildingMarkers()))
  368. needStencil=true;
  369. if (TheGlobalData->m_showSoftWaterEdge)
  370. { minBitDepth=32;
  371. }
  372. */
  373. for (int res = 0; res < resolutions.Count (); res ++)
  374. {
  375. // Is this the resolution we are looking for?
  376. 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.
  377. {
  378. numResolutions++;
  379. }
  380. }
  381. return numResolutions;
  382. }
  383. void W3DDisplay::getDisplayModeDescription(Int modeIndex, Int *xres, Int *yres, Int *bitDepth)
  384. {
  385. Int numResolutions=0;
  386. const RenderDeviceDescClass &devDesc=WW3D::Get_Render_Device_Desc(0);
  387. const DynamicVectorClass <ResolutionDescClass> &resolutions=devDesc.Enumerate_Resolutions();
  388. for (int res = 0; res < resolutions.Count (); res ++)
  389. {
  390. // Is this the resolution we are looking for?
  391. 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.
  392. {
  393. if (numResolutions == modeIndex)
  394. { //found the mode
  395. *xres=resolutions[res].Width;
  396. *yres=resolutions[res].Height;
  397. *bitDepth=resolutions[res].BitDepth;
  398. return;
  399. }
  400. numResolutions++;
  401. }
  402. }
  403. }
  404. void W3DDisplay::setGamma(Real gamma, Real bright, Real contrast, Bool calibrate)
  405. {
  406. if (m_windowed)
  407. return; //we don't allow gamma to change in window because it would affect desktop.
  408. DX8Wrapper::Set_Gamma(gamma,bright,contrast,calibrate, false);
  409. }
  410. /*Giant hack in order to keep the game from getting stuck when alt-tabbing*/
  411. void Reset_D3D_Device(bool active)
  412. {
  413. if (TheDisplay && WW3D::Is_Initted() && !TheDisplay->getWindowed())
  414. {
  415. if (active)
  416. {
  417. //switch back to desired mode when user alt-tabs back into game
  418. WW3D::Set_Render_Device( WW3D::Get_Render_Device(),TheDisplay->getWidth(),TheDisplay->getHeight(),TheDisplay->getBitDepth(),TheDisplay->getWindowed(),true, true);
  419. OSVERSIONINFO osvi;
  420. osvi.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
  421. if (GetVersionEx(&osvi))
  422. { //check if we're running Win9x variant since they have buggy alt-tab that requires
  423. //reloading all textures.
  424. if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
  425. { //only do this on Win9x boxes because it makes alt-tab very slow.
  426. WW3D::_Invalidate_Textures();
  427. }
  428. }
  429. }
  430. else
  431. {
  432. //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.
  433. WW3D::Set_Render_Device( WW3D::Get_Render_Device(),TheDisplay->getWidth(),TheDisplay->getHeight(),TheDisplay->getBitDepth(),TheDisplay->getWindowed(),true, true, false);
  434. }
  435. }
  436. }
  437. /** Set resolution of display */
  438. //=============================================================================
  439. Bool W3DDisplay::setDisplayMode( UnsignedInt xres, UnsignedInt yres, UnsignedInt bitdepth, Bool windowed )
  440. {
  441. if (WW3D_ERROR_OK == WW3D::Set_Device_Resolution(xres,yres,bitdepth,windowed,true))
  442. {
  443. Render2DClass::Set_Screen_Resolution(RectClass(0, 0, xres, yres));
  444. Display::setDisplayMode(xres, yres, bitdepth, windowed);
  445. return TRUE;
  446. }
  447. //set back to the original mode.
  448. WW3D::Set_Device_Resolution(getWidth(),getHeight(),getBitDepth(),getWindowed(), true);
  449. Render2DClass::Set_Screen_Resolution(RectClass(0, 0, getWidth(),getHeight()));
  450. Display::setDisplayMode(getWidth(),getHeight(),getBitDepth(), getWindowed());
  451. return FALSE; //did not change to a new mode.
  452. }
  453. /** Set width of display */
  454. //=============================================================================
  455. void W3DDisplay::setWidth( UnsignedInt width )
  456. {
  457. // extending functionality
  458. Display::setWidth( width );
  459. // our 2D renderer will use mapping coords to make (0,0) the upper left
  460. // of the screen with (width,height) at the lower right
  461. m_2DRender->Set_Coordinate_Range( RectClass( 0, 0, getWidth(), getHeight() ) );
  462. } // end set width
  463. // W3DDisplay::setHeight ======================================================
  464. /** Set height of display */
  465. //=============================================================================
  466. void W3DDisplay::setHeight( UnsignedInt height )
  467. {
  468. // extending functionality
  469. Display::setHeight( height );
  470. // our 2D renderer will use mapping coords to make (0,0) the upper left
  471. // of the screen with (width,height) at the lower right
  472. m_2DRender->Set_Coordinate_Range( RectClass( 0, 0, getWidth(), getHeight() ) );
  473. } // end set height
  474. // W3DDisplay::initAssets =====================================================
  475. /** */
  476. //=============================================================================
  477. void W3DDisplay::initAssets( void )
  478. {
  479. } // end initAssets
  480. // W3DDisplay::init3DScene ====================================================
  481. /** */
  482. //=============================================================================
  483. void W3DDisplay::init3DScene( void )
  484. {
  485. } // end init3DScene
  486. // W3DDisplay::init2DScene ====================================================
  487. /** This is the 2D scene, you can use it to draw on a 2D plane over the
  488. * 3D background */
  489. //=============================================================================
  490. void W3DDisplay::init2DScene( void )
  491. {
  492. } // end init2DScene
  493. // W3DDisplay::init ===========================================================
  494. /** Initialize or re-initialize the W3D display system. Here we need to
  495. * create our window, and get our 3D hardware setup and online */
  496. //=============================================================================
  497. void W3DDisplay::init( void )
  498. {
  499. //
  500. // call our base class init, this method should be able to handle re-entry
  501. // with its own logic
  502. //
  503. Display::init();
  504. // handle re-entry for ourselves
  505. if( m_initialized )
  506. {
  507. /// @todo W3DDisplay needs RE-init logic!
  508. return;
  509. } // end if
  510. // Override the W3D File system
  511. TheW3DFileSystem = NEW W3DFileSystem;
  512. // init the Westwood math library
  513. WWMath::Init();
  514. // create our 3D interface scene
  515. m_3DInterfaceScene = NEW_REF( RTS3DInterfaceScene, () );
  516. m_3DInterfaceScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) );
  517. // create our 2D scene
  518. m_2DScene = NEW_REF( RTS2DScene, () );
  519. m_2DScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) );
  520. // create our 3D scene
  521. m_3DScene =NEW_REF( RTS3DScene, () );
  522. #if defined(_DEBUG) || defined(_INTERNAL)
  523. if( TheGlobalData->m_wireframe )
  524. m_3DScene->Set_Polygon_Mode( SceneClass::LINE );
  525. #endif
  526. //============================================================================
  527. // m_myLight = NEW_REF
  528. //============================================================================
  529. Int lindex;
  530. for (lindex=0; lindex<TheGlobalData->m_numGlobalLights; lindex++)
  531. { m_myLight[lindex] = NEW_REF( LightClass, (LightClass::DIRECTIONAL) );
  532. }
  533. setTimeOfDay( TheGlobalData->m_timeOfDay ); //set each light to correct values for given time
  534. for (lindex=0; lindex<TheGlobalData->m_numGlobalLights; lindex++)
  535. { m_3DScene->setGlobalLight( m_myLight[lindex], lindex );
  536. }
  537. #ifdef SAMPLE_DYNAMIC_LIGHT
  538. theDynamicLight = NEW_REF(W3DDynamicLight, ());
  539. Real red = 1;
  540. Real green = 1;
  541. Real blue = 0;
  542. if(red==0 && blue==0 && green==0) {
  543. red = green = blue = 1;
  544. }
  545. theDynamicLight->Set_Ambient( Vector3( red, green, blue ) );
  546. theDynamicLight->Set_Diffuse( Vector3( red, green, blue) );
  547. theDynamicLight->Set_Position(Vector3(0, 0, 4));
  548. theDynamicLight->Set_Far_Attenuation_Range(1, 8);
  549. // Note: Don't Add_Render_Object dynamic lights.
  550. m_3DScene->addDynamicLight( theDynamicLight );
  551. #endif
  552. // create a new asset manager
  553. m_assetManager = NEW W3DAssetManager;
  554. m_assetManager->Register_Prototype_Loader(&_ParticleEmitterLoader );
  555. m_assetManager->Register_Prototype_Loader(&_AggregateLoader);
  556. m_assetManager->Set_WW3D_Load_On_Demand( true );
  557. if (TheGlobalData->m_incrementalAGPBuf)
  558. {
  559. SortingRendererClass::SetMinVertexBufferSize(1);
  560. }
  561. if (WW3D::Init( ApplicationHWnd ) != WW3D_ERROR_OK)
  562. throw ERROR_INVALID_D3D; //failed to initialize. User probably doesn't have DX 8.1
  563. WW3D::Set_Prelit_Mode( WW3D::PRELIT_MODE_LIGHTMAP_MULTI_PASS );
  564. WW3D::Set_Collision_Box_Display_Mask(0x00); ///<set to 0xff to make collision boxes visible
  565. WW3D::Enable_Static_Sort_Lists(true);
  566. WW3D::Set_Texture_Compression_Mode(WW3D::TEXTURE_COMPRESSION_ENABLE);
  567. WW3D::Set_Texture_Thumbnail_Mode(WW3D::TEXTURE_THUMBNAIL_MODE_OFF);
  568. WW3D::Set_Screen_UV_Bias( TRUE ); ///< this makes text look good :)
  569. setWindowed( TheGlobalData->m_windowed );
  570. // create a 2D renderer helper
  571. m_2DRender = NEW Render2DClass;
  572. DEBUG_ASSERTCRASH( m_2DRender, ("Cannot create Render2DClass") );
  573. // set our default width and height and bit depth
  574. /// @todo we should set this according to options read from a file
  575. setWidth( TheGlobalData->m_xResolution );
  576. setHeight( TheGlobalData->m_yResolution );
  577. setBitDepth( W3D_DISPLAY_DEFAULT_BIT_DEPTH );
  578. if( WW3D::Set_Render_Device( 0,
  579. getWidth(),
  580. getHeight(),
  581. getBitDepth(),
  582. getWindowed(),
  583. true ) != WW3D_ERROR_OK )
  584. {
  585. // Getting the device at the default bit depth (32) didn't work, so try
  586. // getting a 16 bit display. (Voodoo 1-3 only supported 16 bit.) jba.
  587. setBitDepth( 16 );
  588. if( WW3D::Set_Render_Device( 0,
  589. getWidth(),
  590. getHeight(),
  591. getBitDepth(),
  592. getWindowed(),
  593. true ) != WW3D_ERROR_OK )
  594. {
  595. WW3D::Shutdown();
  596. WWMath::Shutdown();
  597. throw ERROR_INVALID_D3D; //failed to initialize. User probably doesn't have DX 8.1
  598. DEBUG_ASSERTCRASH( 0, ("Unable to set render device\n") );
  599. return;
  600. }
  601. } // end if
  602. //Check if level was never set and default to setting most suitable for system.
  603. if (TheGameLODManager->getStaticLODLevel() == STATIC_GAME_LOD_UNKNOWN)
  604. TheGameLODManager->setStaticLODLevel(TheGameLODManager->findStaticLODLevel());
  605. else
  606. { //Static LOD level was applied during GameLOD manager init except for texture reduction
  607. //which needs to be applied here.
  608. Int txtReduction=TheWritableGlobalData->m_textureReductionFactor;
  609. if (txtReduction > 0)
  610. { WW3D::Set_Texture_Reduction(txtReduction,6);
  611. //Tell LOD manager that texture reduction was applied.
  612. TheGameLODManager->setCurrentTextureReduction(txtReduction);
  613. }
  614. }
  615. if (TheGlobalData->m_displayGamma != 1.0f)
  616. setGamma(TheGlobalData->m_displayGamma,0.0f,1.0f,FALSE);
  617. initAssets();
  618. init2DScene();
  619. init3DScene();
  620. W3DShaderManager::init();
  621. // Create and initialize the debug display
  622. m_nativeDebugDisplay = NEW W3DDebugDisplay();
  623. m_debugDisplay = m_nativeDebugDisplay;
  624. if ( m_nativeDebugDisplay )
  625. {
  626. m_nativeDebugDisplay->init();
  627. GameFont *font;
  628. if (TheGlobalLanguageData && TheGlobalLanguageData->m_nativeDebugDisplay.name.isNotEmpty())
  629. {
  630. font=TheFontLibrary->getFont(
  631. TheGlobalLanguageData->m_nativeDebugDisplay.name,
  632. TheGlobalLanguageData->m_nativeDebugDisplay.size,
  633. TheGlobalLanguageData->m_nativeDebugDisplay.bold);
  634. }
  635. else
  636. font=TheFontLibrary->getFont( AsciiString("FixedSys"), 8, FALSE );
  637. m_nativeDebugDisplay->setFont( font );
  638. m_nativeDebugDisplay->setFontHeight( 13 );
  639. m_nativeDebugDisplay->setFontWidth( 9 );
  640. }
  641. DX8WebBrowser::Initialize();
  642. // we're now online
  643. m_initialized = true;
  644. if( TheGlobalData->m_displayDebug )
  645. {
  646. m_debugDisplayCallback = StatDebugDisplay;
  647. }
  648. } // end init
  649. // W3DDisplay::reset ===========================================================
  650. /** Reset the W3D display system. Here we need to
  651. * remove the objects from the previous map. */
  652. //=============================================================================
  653. void W3DDisplay::reset( void )
  654. {
  655. Display::reset();
  656. // Remove all render objects.
  657. SceneIterator *sceneIter = m_3DScene->Create_Iterator();
  658. sceneIter->First();
  659. while(!sceneIter->Is_Done()) {
  660. RenderObjClass * robj = sceneIter->Current_Item();
  661. robj->Add_Ref();
  662. m_3DScene->Remove_Render_Object(robj);
  663. robj->Release_Ref();
  664. sceneIter->Next();
  665. }
  666. m_3DScene->Destroy_Iterator(sceneIter);
  667. m_isClippedEnabled = FALSE;
  668. // release any unused assets from W3D
  669. /// @todo really need that "scene abstraction", having this stuff in the display is icky
  670. m_assetManager->Release_Unused_Assets();
  671. if (TheWritableGlobalData)
  672. TheWritableGlobalData->m_drawSkyBox =0;
  673. }
  674. const UnsignedInt START_CUMU_FRAME = LOGICFRAMES_PER_SECOND / 2; // skip first half-sec
  675. /** Update a moving average of the last 30 fps measurements. Also try to filter out temporary spikes.
  676. This code is designed to be used by the GameLOD sytems to determine the correct dynamic LOD setting.
  677. */
  678. void W3DDisplay::updateAverageFPS(void)
  679. {
  680. const Real MaximumFrameTimeCutoff = 0.5f; //largest frame interval (seconds) we accept before ignoring it as a momentary "spike"
  681. const Int FPS_HISTORY_SIZE = 30; //keep track of the last 30 frames
  682. static Int64 lastUpdateTime64 = 0;
  683. static Int historyOffset = 0;
  684. static Int numSamples = 0;
  685. static double fpsHistory[FPS_HISTORY_SIZE];
  686. Int64 freq64 = getPerformanceCounterFrequency();
  687. Int64 time64 = getPerformanceCounter();
  688. #if defined(_DEBUG) || defined(_INTERNAL)
  689. if (TheGameLogic->getFrame() == START_CUMU_FRAME)
  690. {
  691. m_timerAtCumuFPSStart = time64;
  692. }
  693. #endif
  694. Int64 timeDiff = time64 - lastUpdateTime64;
  695. // convert elapsed time to seconds
  696. double elapsedSeconds = (double)timeDiff/(double)(freq64);
  697. if (elapsedSeconds <= MaximumFrameTimeCutoff) //make sure it's not a spike
  698. {
  699. // append new sameple to fps history.
  700. if (historyOffset >= FPS_HISTORY_SIZE)
  701. historyOffset = 0;
  702. double currentFPS = 1.0/elapsedSeconds;
  703. fpsHistory[historyOffset++] = currentFPS;
  704. numSamples++;
  705. if (numSamples > FPS_HISTORY_SIZE)
  706. numSamples = FPS_HISTORY_SIZE;
  707. }
  708. if (numSamples)
  709. {
  710. // determine average frame rate over our past history.
  711. Real average=0;
  712. for (Int i=0,j=historyOffset-1; i<numSamples; i++,j--)
  713. {
  714. if (j < 0)
  715. j=FPS_HISTORY_SIZE-1; // wrap around to front of buffer
  716. average += fpsHistory[j];
  717. }
  718. m_averageFPS = average / (Real)numSamples;
  719. }
  720. lastUpdateTime64 = time64;
  721. }
  722. #if defined(_DEBUG) || defined(_INTERNAL) //debug hack to view object under mouse stats
  723. ICoord2D TheMousePos;
  724. #endif
  725. // W3DDisplay::gatherDebugStats ===================================================
  726. /** Compute and display debug stats on screen */
  727. //=============================================================================
  728. void W3DDisplay::gatherDebugStats( void )
  729. {
  730. static UnsignedInt s_framesRenderedSinceLastUpdate = 0;
  731. static Int64 s_lastUpdateTime64 = 0;
  732. static double s_timeSinceLastUpdateInSecs = 0.0;
  733. static Int s_drawCallsSinceLastUpdate = 0;
  734. static Int s_sortedPolysSinceLastUpdate = 0;
  735. // allocate the display strings if needed
  736. if( m_displayStrings[0] == NULL )
  737. {
  738. GameFont *font;
  739. if (TheGlobalLanguageData && TheGlobalLanguageData->m_nativeDebugDisplay.name.isNotEmpty())
  740. {
  741. font=TheFontLibrary->getFont(
  742. TheGlobalLanguageData->m_nativeDebugDisplay.name,
  743. TheGlobalLanguageData->m_nativeDebugDisplay.size,
  744. TheGlobalLanguageData->m_nativeDebugDisplay.bold);
  745. }
  746. else
  747. font = TheFontLibrary->getFont( AsciiString("FixedSys"), 8, FALSE );
  748. for (int i = 0; i < DisplayStringCount; i++)
  749. {
  750. if (m_displayStrings[i] == NULL)
  751. {
  752. m_displayStrings[i] = TheDisplayStringManager->newDisplayString();
  753. DEBUG_ASSERTCRASH( m_displayStrings[i], ("Failed to create DisplayString") );
  754. m_displayStrings[i]->setFont( font );
  755. }
  756. }
  757. } // end if
  758. if (m_benchmarkDisplayString == NULL)
  759. {
  760. GameFont *thisFont = TheFontLibrary->getFont( AsciiString("FixedSys"), 8, FALSE );
  761. m_benchmarkDisplayString = TheDisplayStringManager->newDisplayString();
  762. DEBUG_ASSERTCRASH( m_benchmarkDisplayString, ("Failed to create DisplayString") );
  763. m_benchmarkDisplayString->setFont( thisFont );
  764. }
  765. ++s_framesRenderedSinceLastUpdate;
  766. s_drawCallsSinceLastUpdate += Debug_Statistics::Get_Draw_Calls();
  767. s_sortedPolysSinceLastUpdate += Debug_Statistics::Get_Sorting_Polygons();
  768. Int64 freq64 = getPerformanceCounterFrequency();
  769. Int64 time64 = getPerformanceCounter();
  770. s_timeSinceLastUpdateInSecs = ((double)(time64 - s_lastUpdateTime64) / (double)(freq64));
  771. #ifdef EXTENDED_STATS
  772. static FILE *pListFile = NULL;
  773. static Int64 lastFrameTime=0;
  774. static samples = 0;
  775. if (pListFile == NULL) {
  776. pListFile = fopen("FrameRateLog.txt", "w");
  777. }
  778. samples++;
  779. if (pListFile && lastFrameTime && samples<100) {
  780. float timeSinceLastFrame = (float)((double)(time64-lastFrameTime) / (double)(freq64));
  781. fprintf(pListFile, "%d ", (int)(1/timeSinceLastFrame));
  782. }
  783. lastFrameTime = time64;
  784. #endif
  785. // we update stats on a delay
  786. const Real UPDATE_RATE_SECS = 2.0;
  787. if( s_timeSinceLastUpdateInSecs >= UPDATE_RATE_SECS || TheGlobalData->m_constantDebugUpdate )
  788. {
  789. UnicodeString unibuffer, unibuffer2;
  790. UnicodeString fpsString;
  791. // setup texture stats
  792. Debug_Statistics::Record_Texture_Mode(Debug_Statistics::RECORD_TEXTURE_SIMPLE/*RECORD_TEXTURE_NONE*/);
  793. // frames per second
  794. double fps = (Real)s_framesRenderedSinceLastUpdate / s_timeSinceLastUpdateInSecs;
  795. double drawsPerFrame = Debug_Statistics::Get_Draw_Calls(); //(Real)s_drawCallsSinceLastUpdate / (Real)s_framesRenderedSinceLastUpdate;
  796. double sortPolysPerFrame = Debug_Statistics::Get_Sorting_Polygons(); //(Real)s_sortedPolysSinceLastUpdate / (Real)s_framesRenderedSinceLastUpdate;
  797. double skinDrawsPerFrame = Debug_Statistics::Get_DX8_Skin_Renders();
  798. if (fps<0.1) fps = 0.1;
  799. double ms = 1000.0f/fps;
  800. #if defined(_DEBUG) || defined(_INTERNAL)
  801. double cumuTime = ((double)(time64 - m_timerAtCumuFPSStart) / (double)(freq64));
  802. if (cumuTime < 0.0) cumuTime = 0.0;
  803. Int numFrames = (Int)TheGameLogic->getFrame() - (Int)START_CUMU_FRAME;
  804. double cumuFPS = (numFrames > 0 && cumuTime > 0.0) ? (numFrames / cumuTime) : 0.0;
  805. double skinPolysPerFrame = Debug_Statistics::Get_DX8_Skin_Polygons();
  806. //Int LOD = TheGlobalData->m_terrainLOD;
  807. //unibuffer.format( L"FPS: %.2f, %.2fms mapLOD=%d [cumu FPS=%.2f] draws: %.2f sort: %.2f", fps, ms, LOD, cumuFPS, drawsPerFrame,sortPolysPerFrame);
  808. if (TheGlobalData->m_useFpsLimit)
  809. unibuffer.format( L"%.2f/%d FPS, ", fps, TheGameEngine->getFramesPerSecondLimit());
  810. else
  811. unibuffer.format( L"%.2f FPS, ", fps);
  812. unibuffer2.format( L"%.2fms [cumuFPS=%.2f] draws: %d skins: %d sortP: %d skinP: %d", ms, cumuFPS, (Int)drawsPerFrame,(Int)skinDrawsPerFrame,(Int)sortPolysPerFrame, (Int)skinPolysPerFrame);
  813. unibuffer.concat(unibuffer2);
  814. #else
  815. //Int LOD = TheGlobalData->m_terrainLOD;
  816. //unibuffer.format( L"FPS: %.2f, %.2fms mapLOD=%d draws: %.2f sort %.2f", fps, ms, LOD, drawsPerFrame,sortPolysPerFrame);
  817. unibuffer.format( L"FPS: %.2f, %.2fms draws: %.2f skins: %.2f sort %.2f", fps, ms, drawsPerFrame,skinDrawsPerFrame,sortPolysPerFrame);
  818. if (TheGlobalData->m_useFpsLimit)
  819. {
  820. unibuffer2.format(L", FPSLock %d",TheGlobalData->m_framesPerSecondLimit);
  821. unibuffer.concat(unibuffer2);
  822. }
  823. #endif
  824. fpsString.format( L"FPS: %.2f", fps);
  825. m_benchmarkDisplayString->setText( fpsString );
  826. Int polyPerFrame = Debug_Statistics::Get_DX8_Polygons();
  827. #ifdef EXTENDED_STATS
  828. static float gameOverheadMS = 0.0f;
  829. static float consoleMS = 0.0f;
  830. static float threeDOverheadMS = 0.0f;
  831. static float terrainMS = 0.0f;
  832. static float objectMS = 0.0f;
  833. static float overlapMS = 0.0f;
  834. static int extendedStats = 0;
  835. const int SHOW_STATS_TIME=12; // show extended stats for 5 cycles == 10 seconds.
  836. static enum {disabled, sync, gameOverhead, console, threeDOverhead, terrain, objects, overlap, normal} statMode = disabled;
  837. if (statMode == sync) {
  838. extendedStats = SHOW_STATS_TIME;
  839. statMode = gameOverhead;
  840. } else if (statMode == gameOverhead) {
  841. gameOverheadMS = ms;
  842. statMode = console;
  843. DX8Wrapper::stats.m_disableTerrain = true;
  844. DX8Wrapper::stats.m_disableOverhead = true;
  845. DX8Wrapper::stats.m_disableWater = true;
  846. DX8Wrapper::stats.m_disableObjects = true;
  847. DX8Wrapper::stats.m_disableConsole = false;
  848. DX8Wrapper::stats.m_debugLinesToShow = 1;
  849. } else if (statMode == console) {
  850. consoleMS = ms;
  851. statMode = threeDOverhead;
  852. DX8Wrapper::stats.m_disableTerrain = true;
  853. DX8Wrapper::stats.m_disableOverhead = true;
  854. DX8Wrapper::stats.m_disableWater = true;
  855. DX8Wrapper::stats.m_disableObjects = true;
  856. DX8Wrapper::stats.m_disableConsole = true;
  857. DX8Wrapper::stats.m_debugLinesToShow = 1;
  858. } else if (statMode == threeDOverhead) {
  859. threeDOverheadMS = ms;
  860. statMode = terrain;
  861. DX8Wrapper::stats.m_disableTerrain = false;
  862. DX8Wrapper::stats.m_disableOverhead = true;
  863. DX8Wrapper::stats.m_disableWater = true;
  864. DX8Wrapper::stats.m_disableObjects = true;
  865. DX8Wrapper::stats.m_disableConsole = true;
  866. DX8Wrapper::stats.m_debugLinesToShow = 1;
  867. } else if (statMode == terrain) {
  868. terrainMS = ms;
  869. statMode = objects;
  870. DX8Wrapper::stats.m_disableOverhead = true;
  871. DX8Wrapper::stats.m_disableTerrain = true;
  872. DX8Wrapper::stats.m_disableWater = true;
  873. DX8Wrapper::stats.m_disableObjects = false;
  874. DX8Wrapper::stats.m_disableConsole = true;
  875. DX8Wrapper::stats.m_debugLinesToShow = 1;
  876. } else if (statMode == objects) {
  877. objectMS = ms;
  878. statMode = overlap;
  879. DX8Wrapper::stats.m_disableOverhead = false;
  880. DX8Wrapper::stats.m_disableTerrain = false;
  881. DX8Wrapper::stats.m_disableWater = false;
  882. DX8Wrapper::stats.m_disableObjects = false;
  883. DX8Wrapper::stats.m_disableConsole = true;
  884. DX8Wrapper::stats.m_sleepTime = (int)(terrainMS);
  885. DX8Wrapper::stats.m_debugLinesToShow = 1;
  886. } else if (statMode == overlap) {
  887. overlapMS = ms;
  888. statMode = normal;
  889. DX8Wrapper::stats.m_disableOverhead = false;
  890. DX8Wrapper::stats.m_disableTerrain = false;
  891. DX8Wrapper::stats.m_disableWater = false;
  892. DX8Wrapper::stats.m_disableObjects = false;
  893. DX8Wrapper::stats.m_disableConsole = true;
  894. DX8Wrapper::stats.m_sleepTime = 0;
  895. DX8Wrapper::stats.m_debugLinesToShow = 1;
  896. } else if (statMode == normal) {
  897. overlapMS = (ms + ((int)terrainMS) - overlapMS );
  898. statMode = disabled;
  899. extendedStats = SHOW_STATS_TIME;
  900. // Done collecting stats. Re-enable stuff
  901. DX8Wrapper::stats.m_disableConsole = false;
  902. DX8Wrapper::stats.m_debugLinesToShow = -1;
  903. } else if (!DX8Wrapper::stats.m_showingStats) {
  904. // start collecting extended info.
  905. DX8Wrapper::stats.m_showingStats = true;
  906. DX8Wrapper::stats.m_disableOverhead = false;
  907. DX8Wrapper::stats.m_disableTerrain = true;
  908. DX8Wrapper::stats.m_disableWater = true;
  909. DX8Wrapper::stats.m_disableObjects = true;
  910. DX8Wrapper::stats.m_disableConsole = true;
  911. DX8Wrapper::stats.m_debugLinesToShow = 1;
  912. statMode = sync;
  913. gameOverheadMS = 0.0f;
  914. threeDOverheadMS = 0.0f;
  915. terrainMS = 0.0f;
  916. objectMS = 0.0f;
  917. }
  918. if (statMode != disabled) {
  919. unibuffer.format(L"FPS: %.2f, %.2fms - Collecting extended stats.", fps, ms);
  920. } else if (extendedStats>0) {
  921. extendedStats--;
  922. unibuffer.format( L"FPS: %.2f, %.2fms - OH %.2fms, Console %.2fms, 3D OH %.2fms, Terrain %.2fms, Obs %.2fms, CPU %.2fms",
  923. fps, ms, gameOverheadMS, consoleMS, threeDOverheadMS, terrainMS, objectMS, overlapMS);
  924. if (extendedStats==SHOW_STATS_TIME-2) {
  925. char bufferA[ 256 ];
  926. sprintf( bufferA, "FPS: %.2f, %.2fms - OH %.2fms, Console %.2fms, 3D OH %.2fms, Terrain %.2fms, Obs %.2fms, CPU %.2fms\n",
  927. fps, ms, gameOverheadMS, consoleMS, threeDOverheadMS, terrainMS, objectMS, overlapMS);
  928. ::OutputDebugString(bufferA);
  929. if (pListFile) {
  930. fprintf(pListFile, "\n%s", bufferA);
  931. }
  932. sprintf( bufferA, "Polygons: per frame %d, per second %d\n", polyPerFrame,
  933. (Int)(polyPerFrame*fps));
  934. ::OutputDebugString(bufferA);
  935. if (pListFile) {
  936. fprintf(pListFile, "%s", bufferA);
  937. fflush(pListFile);
  938. }
  939. }
  940. }
  941. if (pListFile) {
  942. fprintf(pListFile, "\nFPS: %.2f, %.2fms\n", fps, ms);
  943. fflush(pListFile);
  944. }
  945. if (pListFile) {
  946. samples = 0;
  947. if (statMode != disabled) {
  948. fprintf(pListFile, "Stat%d-", statMode);
  949. }
  950. }
  951. #endif
  952. // check for debug D3D
  953. Bool debugD3D=false;
  954. RegistryClass registry ("Software\\Microsoft\\Direct3d");
  955. if (registry.Is_Valid ()) {
  956. if (registry.Get_Int ("LoadDebugRuntime", 0) == 1) {
  957. debugD3D = true;
  958. }
  959. }
  960. if (debugD3D) {
  961. unibuffer.concat(L", DEBUG D3D");
  962. }
  963. #ifdef _DEBUG
  964. unibuffer.concat(L", DEBUG app");
  965. #endif
  966. m_displayStrings[FPS]->setText( unibuffer );
  967. // Actual GameLogic frame number
  968. unibuffer.format(L"Frame: %d", TheGameLogic->getFrame());
  969. m_displayStrings[Frame]->setText( unibuffer );
  970. // polygons this frame
  971. unibuffer.format( L"Polygons: per frame %d, per second %d", polyPerFrame,
  972. (Int)(polyPerFrame*fps));
  973. m_displayStrings[Polygons]->setText( unibuffer );
  974. // vertices this frame
  975. unibuffer.format( L"Vertices: %d", Debug_Statistics::Get_DX8_Vertices() );
  976. m_displayStrings[Vertices]->setText( unibuffer );
  977. //
  978. // I'm adjusting the texture memory usage counter by subtracting
  979. // out the terrain alpha texture (since it's really == terrain texture).
  980. //
  981. unibuffer.format( L"Video RAM: %d", Debug_Statistics::Get_Record_Texture_Size() - 1376256 );
  982. m_displayStrings[VideoRam]->setText( unibuffer );
  983. s_lastUpdateTime64 = time64;
  984. s_timeSinceLastUpdateInSecs = 0.0f;
  985. s_framesRenderedSinceLastUpdate = 0;
  986. s_drawCallsSinceLastUpdate = 0;
  987. s_sortedPolysSinceLastUpdate = 0;
  988. // terrain stats
  989. unibuffer.format( L"3-Way Blends: %d, Shoreline Blends: %d", TheTerrainRenderObject->getNumExtraBlendTiles(),
  990. TheTerrainRenderObject->getNumShoreLineTiles());
  991. m_displayStrings[TerrainStats]->setText( unibuffer );
  992. // misc debug info
  993. Coord3D camPos;
  994. TheTacticalView->getPosition(&camPos);
  995. Real zoom = TheTacticalView->getZoom();
  996. Real pitch = TheTacticalView->getPitch();
  997. Real FXPitch = TheTacticalView->getFXPitch();
  998. Real angle = TheTacticalView->getAngle();
  999. Real FOV = TheTacticalView->getFieldOfView();
  1000. //Real desiredHeight = TheTacticalView->getHeightAboveGround();
  1001. Real terrainHeight = TheTacticalView->getTerrainHeightUnderCamera();
  1002. Real actualHeightAboveGround = TheTacticalView->getCurrentHeightAboveGround();
  1003. 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",
  1004. zoom,
  1005. pitch,
  1006. FXPitch,
  1007. angle,
  1008. camPos.x, camPos.y, camPos.z,
  1009. FOV,
  1010. /*
  1011. zoom,
  1012. pitch * 180.0f / PI,
  1013. FXPitch * 180.0f / PI,
  1014. angle * 180.0f / PI,
  1015. camPos.x, camPos.y, camPos.z,
  1016. FOV * 180.0f / PI,
  1017. */
  1018. actualHeightAboveGround, terrainHeight );
  1019. m_displayStrings[DebugInfo]->setText( unibuffer );
  1020. // display the keyboard modifier and mouse states.
  1021. unibuffer.format( L"States: " );
  1022. if( TheKeyboard->isShift() )
  1023. {
  1024. unibuffer.concat( L"Shift(" );
  1025. if( TheKeyboard->getModifierFlags() & KEY_STATE_LSHIFT )
  1026. {
  1027. unibuffer.concat( L"L" );
  1028. }
  1029. if( TheKeyboard->getModifierFlags() & KEY_STATE_RSHIFT )
  1030. {
  1031. unibuffer.concat( L"R" );
  1032. }
  1033. unibuffer.concat( L") " );
  1034. }
  1035. if( TheKeyboard->isCtrl() )
  1036. {
  1037. unibuffer.concat( L"Ctrl(" );
  1038. if( TheKeyboard->getModifierFlags() & KEY_STATE_LCONTROL )
  1039. {
  1040. unibuffer.concat( L"L" );
  1041. }
  1042. if( TheKeyboard->getModifierFlags() & KEY_STATE_RCONTROL )
  1043. {
  1044. unibuffer.concat( L"R" );
  1045. }
  1046. unibuffer.concat( L") " );
  1047. }
  1048. if( TheKeyboard->isAlt() )
  1049. {
  1050. unibuffer.concat( L"Alt(" );
  1051. if( TheKeyboard->getModifierFlags() & KEY_STATE_LALT )
  1052. {
  1053. unibuffer.concat( L"L" );
  1054. }
  1055. if( TheKeyboard->getModifierFlags() & KEY_STATE_RALT )
  1056. {
  1057. unibuffer.concat( L"R" );
  1058. }
  1059. unibuffer.concat( L") " );
  1060. }
  1061. const MouseIO *mouseStatus = TheMouse->getMouseStatus();
  1062. if( mouseStatus->leftState )
  1063. {
  1064. unibuffer.concat( L"LMB " );
  1065. }
  1066. if( mouseStatus->middleState )
  1067. {
  1068. unibuffer.concat( L"MMB " );
  1069. }
  1070. if( mouseStatus->rightState )
  1071. {
  1072. unibuffer.concat( L"RMB " );
  1073. }
  1074. Object *object = NULL;
  1075. #if defined(_DEBUG) || defined(_INTERNAL) //debug hack to view object under mouse stats
  1076. Drawable *draw = TheTacticalView->pickDrawable(&TheMousePos, FALSE, (PickType)0xffffffff );
  1077. #else
  1078. Drawable *draw = TheGameClient->findDrawableByID( TheInGameUI->getMousedOverDrawableID() );
  1079. #endif
  1080. if( draw )
  1081. object = draw->getObject();
  1082. if( object )
  1083. {
  1084. unibuffer2.format( L"Moused over object: %S (%d) ", object->getTemplate()->getName().str(), object->getID() );
  1085. unibuffer.concat( unibuffer2 );
  1086. }
  1087. else
  1088. {
  1089. unibuffer.concat( L"Moused over object: TERRAIN " );
  1090. }
  1091. m_displayStrings[ KEY_MOUSE_STATES ]->setText( unibuffer );
  1092. //display the x and y mouse coordinates
  1093. const MouseIO *mouseIO = TheMouse->getMouseStatus();
  1094. Coord3D worldPos;
  1095. TheTacticalView->screenToTerrain(&mouseIO->pos, &worldPos);
  1096. unibuffer.format( L"Mouse position: screen: (%d, %d), world: (%g, %g, %g)", mouseIO->pos.x, mouseIO->pos.y,
  1097. worldPos.x, worldPos.y, worldPos.z);
  1098. m_displayStrings[MousePosition]->setText( unibuffer );
  1099. //display the number of particles in the world and being displayed on screen
  1100. Int totalParticles = TheParticleSystemManager->getParticleCount();
  1101. Int onScreenParticleCount = TheParticleSystemManager->getOnScreenParticleCount();
  1102. unibuffer.format( L"Particles: %d in world, %d being displayed", totalParticles, onScreenParticleCount );
  1103. m_displayStrings[Particles]->setText( unibuffer );
  1104. //display the number of objects in the world
  1105. UnsignedInt objCount = TheGameLogic->getObjectCount();
  1106. UnsignedInt objScreenCount = TheGameClient->getRenderedObjectCount();
  1107. unibuffer.format(L"Objects: %d in world, %d being displayed", objCount, objScreenCount );
  1108. m_displayStrings[Objects]->setText( unibuffer );
  1109. // Network incoming bandwidth stats
  1110. if (TheNetwork != NULL) {
  1111. unibuffer.format(L"IN: %.2f bytes/sec, %.2f packets/sec",
  1112. TheNetwork->getIncomingBytesPerSecond(), TheNetwork->getIncomingPacketsPerSecond());
  1113. m_displayStrings[NetIncoming]->setText( unibuffer );
  1114. // Network outgoing bandwidth stats
  1115. unibuffer.format(L"OUT: %.2f bytes/sec, %.2f packets/sec",
  1116. TheNetwork->getOutgoingBytesPerSecond(), TheNetwork->getOutgoingPacketsPerSecond());
  1117. m_displayStrings[NetOutgoing]->setText( unibuffer );
  1118. // Network performance stats
  1119. unibuffer.format(L"Run Ahead: %d, Net FPS: %d, Packet arrival cushion: %d",
  1120. TheNetwork->getRunAhead(), TheNetwork->getFrameRate(), TheNetwork->getPacketArrivalCushion());
  1121. m_displayStrings[NetStats]->setText( unibuffer );
  1122. // Client frame rate averages for all players in the game. This only works right for the packet router.
  1123. unibuffer.clear();
  1124. Int numPlayers = TheNetwork->getNumPlayers();
  1125. for (Int i = 0; i < numPlayers; ++i) {
  1126. UnicodeString tempstr;
  1127. tempstr.format(L"%s: %d ", TheNetwork->getPlayerName(i).str(), TheNetwork->getSlotAverageFPS(i));
  1128. unibuffer.concat(tempstr);
  1129. }
  1130. m_displayStrings[NetFPSAverages]->setText( unibuffer );
  1131. } else {
  1132. // unibuffer.format(L"IN: 0.0 bytes/sec, 0.0 packets/sec");
  1133. // m_displayStrings[NetIncoming]->setText( unibuffer );
  1134. // Network outgoing bandwidth stats
  1135. // unibuffer.format(L"OUT: 0.0 bytes/sec, 0.0 packets/sec");
  1136. // m_displayStrings[NetOutgoing]->setText( unibuffer );
  1137. unibuffer.format(L"");
  1138. // unibuffer.format(L"Network not present");
  1139. m_displayStrings[NetOutgoing]->setText(unibuffer);
  1140. m_displayStrings[NetIncoming]->setText(unibuffer);
  1141. m_displayStrings[NetStats]->setText(unibuffer);
  1142. m_displayStrings[NetFPSAverages]->setText( unibuffer );
  1143. }
  1144. // selected object info stats
  1145. unibuffer.format( L"Select Info: '%d' drawables selected", TheInGameUI->getSelectCount() );
  1146. //Sorry, guys. I need a special kluge here to get constantdebug results for angry mob.
  1147. //Do no be cross with me.
  1148. //if there is not exactly one drawable selected it will report on the moused-over drawable
  1149. if (TheInGameUI->getSelectCount() == 1)
  1150. draw = TheInGameUI->getFirstSelectedDrawable();
  1151. if( draw )
  1152. {
  1153. Object *obj = draw->getObject();
  1154. AsciiString objectName;
  1155. objectName.set( "No-Name" );
  1156. if( obj && obj->getName().isEmpty() == FALSE )
  1157. objectName = obj->getName();
  1158. unibuffer.format( L"Select Info: '%S'(%S) at (%.3f,%.3f,%.3f)",
  1159. draw->getTemplate()->getName().str(),
  1160. objectName.str(),
  1161. draw->getPosition()->x,
  1162. draw->getPosition()->y,
  1163. draw->getPosition()->z );
  1164. // (gth) compute some stats about the rendering cost of this drawable
  1165. #if defined(_DEBUG) || defined(_INTERNAL)
  1166. RenderCost rcost;
  1167. for (DrawModule** dm = draw->getDrawModules(); *dm; ++dm)
  1168. {
  1169. (*dm)->getRenderCost(rcost);
  1170. }
  1171. if (rcost.getDrawCallCount() > 0)
  1172. {
  1173. unibuffer2.format( L"\ndraw calls: %d(+%d) sort meshes: %d skins: %d bones: %d",rcost.getDrawCallCount(),rcost.getShadowDrawCount(),rcost.getSortedMeshCount(),rcost.getSkinMeshCount(),rcost.getBoneCount());
  1174. unibuffer.concat( unibuffer2 );
  1175. }
  1176. #endif
  1177. unibuffer.concat( L"\nModelStates: " );
  1178. ModelConditionFlags mcFlags = draw->getModelConditionFlags();
  1179. const numEntriesPerLine = 4;
  1180. int lineCount = 0;
  1181. for( int i = 0; i < MODELCONDITION_COUNT; i++ )
  1182. {
  1183. if( mcFlags.test( i ) )
  1184. {
  1185. unibuffer2.format( L"%S ", ModelConditionFlags::getBitNames()[ i ] );
  1186. unibuffer.concat( unibuffer2 );
  1187. lineCount++;
  1188. if( lineCount == numEntriesPerLine )
  1189. {
  1190. lineCount = 0;
  1191. unibuffer.concat( L"\n" );
  1192. }
  1193. }
  1194. }
  1195. //Render ALL modelcondition statii
  1196. } // end if
  1197. m_displayStrings[ SelectedInfo ]->setText( unibuffer );
  1198. }
  1199. }
  1200. // W3DDisplay::drawDebugStats =================================================
  1201. /** Draw debug statistics */
  1202. //=============================================================================
  1203. void W3DDisplay::drawDebugStats( void )
  1204. {
  1205. Int x = 3;
  1206. Int y = 3;
  1207. Color textColor = GameMakeColor( 255, 255, 255, 255 );
  1208. Color dropColor = GameMakeColor( 0, 0, 0, 255 );
  1209. int linesOfStrings = DisplayStringCount;
  1210. #ifdef EXTENDED_STATS
  1211. if (DX8Wrapper::stats.m_debugLinesToShow > -1)
  1212. {
  1213. linesOfStrings = DX8Wrapper::stats.m_debugLinesToShow;
  1214. }
  1215. #endif
  1216. Int w, h;
  1217. for (int i = 0; i < linesOfStrings; i++)
  1218. {
  1219. m_displayStrings[i]->draw( x, y, textColor, dropColor );
  1220. m_displayStrings[i]->getSize(&w, &h);
  1221. y += h;
  1222. }
  1223. } // end drawDebugStats
  1224. // W3DDisplay::drawFPSStats =================================================
  1225. /** Draw the FPS on the screen */
  1226. //=============================================================================
  1227. void W3DDisplay::drawFPSStats( void )
  1228. {
  1229. Int x = 3;
  1230. Int y = 20;
  1231. Color textColor = GameMakeColor( 255, 255, 255, 255 );
  1232. Color dropColor = GameMakeColor( 0, 0, 0, 255 );
  1233. int linesOfStrings = 1;
  1234. for (int i = 0; i < linesOfStrings; i++)
  1235. {
  1236. m_benchmarkDisplayString->draw( x, y, textColor, dropColor );
  1237. }
  1238. }
  1239. //=============================================================================
  1240. void StatDebugDisplay( DebugDisplayInterface *, void *, FILE *fp )
  1241. {
  1242. DEBUG_CRASH(("This should never be called directly, but is just a placeholder for drawDebugStats()"));
  1243. }
  1244. // W3DDisplay::drawCurrentDebugDisplay =================================================
  1245. /** Draw current debug display */
  1246. //=============================================================================
  1247. void W3DDisplay::drawCurrentDebugDisplay( void )
  1248. {
  1249. if (m_debugDisplayCallback == StatDebugDisplay)
  1250. {
  1251. drawDebugStats();
  1252. }
  1253. else
  1254. {
  1255. if ( m_debugDisplay && m_debugDisplayCallback )
  1256. {
  1257. m_debugDisplay->reset();
  1258. m_debugDisplayCallback( m_debugDisplay, m_debugDisplayUserData );
  1259. }
  1260. }
  1261. } // end drawCurrentDebugDisplay
  1262. // W3DDisplay::calculateTerrainLOD =================================================
  1263. /** Calculates an adequately speedy terrain Level Of Detail. */
  1264. //=============================================================================
  1265. void W3DDisplay::calculateTerrainLOD( void )
  1266. {
  1267. const Int NUM_SAMPLES=20;
  1268. const Int NUM_TO_DISCARD=5;
  1269. Int64 freq64 = getPerformanceCounterFrequency();
  1270. char buf[_MAX_PATH];
  1271. float frameTime = 0;
  1272. float maxTimeLimit = TheGlobalData->m_terrainLODTargetTimeMS/1000.0f;
  1273. TerrainLOD goodLOD = TERRAIN_LOD_MIN;
  1274. TerrainLOD curLOD = TERRAIN_LOD_AUTOMATIC;
  1275. Int count = 0;
  1276. #ifdef _DEBUG
  1277. // just go to TERRAIN_LOD_NO_WATER, mirror off.
  1278. TheWritableGlobalData->m_terrainLOD = TERRAIN_LOD_NO_WATER;
  1279. m_3DScene->drawTerrainOnly(false);
  1280. TheTerrainRenderObject->adjustTerrainLOD(0);
  1281. return;
  1282. #endif
  1283. do {
  1284. Int i;
  1285. float timeForFrame=0;
  1286. frameTime = 0;
  1287. switch(curLOD) {
  1288. default: curLOD = TERRAIN_LOD_DISABLE; break;
  1289. case TERRAIN_LOD_AUTOMATIC: curLOD = TERRAIN_LOD_MAX; break;
  1290. case TERRAIN_LOD_MAX: curLOD = TERRAIN_LOD_NO_WATER; break;
  1291. case TERRAIN_LOD_HALF_CLOUDS: curLOD = TERRAIN_LOD_DISABLE; break;
  1292. case TERRAIN_LOD_NO_WATER: curLOD = TERRAIN_LOD_HALF_CLOUDS; break;
  1293. }
  1294. if (curLOD == TERRAIN_LOD_DISABLE) {
  1295. break;
  1296. }
  1297. TheWritableGlobalData->m_terrainLOD = curLOD;
  1298. m_3DScene->drawTerrainOnly(true);
  1299. TheTerrainRenderObject->adjustTerrainLOD(0);
  1300. for (i=0; i<NUM_SAMPLES; i++) {
  1301. Int64 startTime64 = getPerformanceCounter();
  1302. // start render block
  1303. updateViews();
  1304. if (WW3D::Begin_Render( true, true, Vector3( 0.0f, 0.0f, 0.0f ) ) == WW3D_ERROR_OK)
  1305. { // draw all views of the world
  1306. drawViews();
  1307. // render is all done!
  1308. WW3D::End_Render();
  1309. }
  1310. Int64 time64 = getPerformanceCounter();
  1311. timeForFrame = (float)((double)(time64-startTime64) / (double)(freq64));
  1312. sprintf(buf, "%.2fms ", timeForFrame*1000.0f);
  1313. ::OutputDebugString(buf);
  1314. if (i>=NUM_TO_DISCARD) {
  1315. frameTime += timeForFrame;
  1316. if (i>NUM_TO_DISCARD+1 &&
  1317. (timeForFrame / ((i+1)-NUM_TO_DISCARD)) > 2*maxTimeLimit) {
  1318. i++;
  1319. break;
  1320. }
  1321. }
  1322. }
  1323. frameTime /= ((i)-NUM_TO_DISCARD);
  1324. count++;
  1325. sprintf(buf, "\n LOD %d, time %.2fms\n", curLOD, frameTime*1000.0f);
  1326. ::OutputDebugString(buf);
  1327. if (frameTime<maxTimeLimit && goodLOD<curLOD) {
  1328. goodLOD = curLOD;
  1329. }
  1330. if (frameTime < maxTimeLimit) break;
  1331. } while (count<10);
  1332. TheWritableGlobalData->m_terrainLOD = goodLOD;
  1333. m_3DScene->drawTerrainOnly(false);
  1334. TheTerrainRenderObject->adjustTerrainLOD(0);
  1335. #ifdef _DEBUG
  1336. DEBUG_ASSERTCRASH(count<10, ("calculateTerrainLOD") );
  1337. #endif
  1338. }
  1339. Real W3DDisplay::getAverageFPS()
  1340. {
  1341. return m_averageFPS;
  1342. }
  1343. Int W3DDisplay::getLastFrameDrawCalls()
  1344. {
  1345. return Debug_Statistics::Get_Draw_Calls();
  1346. }
  1347. //DECLARE_PERF_TIMER(BigAssRenderLoop)
  1348. // W3DDisplay::draw ===========================================================
  1349. /** Draw the entire W3D Display */
  1350. //=============================================================================
  1351. //DECLARE_PERF_TIMER(W3DDisplay_draw)
  1352. void W3DDisplay::draw( void )
  1353. {
  1354. //USE_PERF_TIMER(W3DDisplay_draw)
  1355. static UnsignedInt syncTime = 0;
  1356. extern HWND ApplicationHWnd;
  1357. if (ApplicationHWnd && ::IsIconic(ApplicationHWnd)) {
  1358. return;
  1359. }
  1360. updateAverageFPS();
  1361. if (TheGlobalData->m_enableDynamicLOD && TheGameLogic->getShowDynamicLOD())
  1362. {
  1363. DynamicGameLODLevel lod=TheGameLODManager->findDynamicLODLevel(m_averageFPS);
  1364. TheGameLODManager->setDynamicLODLevel(lod);
  1365. }
  1366. else
  1367. { //if dynamic LOD is turned off, force highest LOD
  1368. TheGameLODManager->setDynamicLODLevel(DYNAMIC_GAME_LOD_VERY_HIGH);
  1369. }
  1370. if (TheGlobalData->m_terrainLOD == TERRAIN_LOD_AUTOMATIC && TheTerrainRenderObject)
  1371. {
  1372. calculateTerrainLOD();
  1373. }
  1374. #ifdef EXTENDED_STATS
  1375. AGAIN:
  1376. #endif
  1377. #ifdef DUMP_PERF_STATS
  1378. if( TheGlobalData->m_dumpPerformanceStatistics )
  1379. {
  1380. TheStatDump.dumpStats();
  1381. TheWritableGlobalData->m_dumpPerformanceStatistics = FALSE;
  1382. }
  1383. #endif
  1384. // compute debug statistics for display later
  1385. if ( m_debugDisplayCallback == StatDebugDisplay
  1386. #if defined(_DEBUG) || defined(_INTERNAL)
  1387. || TheGlobalData->m_benchmarkTimer > 0
  1388. #endif
  1389. )
  1390. {
  1391. gatherDebugStats();
  1392. }
  1393. #ifdef EXTENDED_STATS
  1394. else
  1395. {
  1396. DX8Wrapper::stats.m_showingStats = false;
  1397. }
  1398. #endif
  1399. #ifdef SAMPLE_DYNAMIC_LIGHT
  1400. Vector3 loc;
  1401. loc = theDynamicLight->Get_Position();
  1402. loc.X += theLightXOffset;
  1403. if(loc.X>128) theLightXOffset = -theLightXOffset;
  1404. if(loc.X<0) theLightXOffset = -theLightXOffset;
  1405. loc.Y += theLightYOffset;
  1406. if(loc.Y>128) theLightYOffset = -theLightYOffset;
  1407. if(loc.Y<0) theLightYOffset = -theLightYOffset;
  1408. theDynamicLight->Set_Position(loc);
  1409. #endif
  1410. /// @todo Make more explicit drawing layers(ground, ground UI, objects, object UI, overlay UI)
  1411. ///@todo: Ask Vegas why the LOD optimizer hangs particle system.
  1412. //
  1413. // Predictive LOD optimizer optimizes the mesh LOD levels to match
  1414. // the given polygon budget
  1415. //
  1416. //PredictiveLODOptimizerClass::Optimize_LODs( 5000 );
  1417. Bool freezeTime = TheTacticalView->isTimeFrozen() && !TheTacticalView->isCameraMovementFinished();
  1418. freezeTime = freezeTime || TheScriptEngine->isTimeFrozenDebug() || TheScriptEngine->isTimeFrozenScript();
  1419. freezeTime = freezeTime || TheGameLogic->isGamePaused();
  1420. // hack to let client spin fast in network games but still do effects at the same pace. -MDC
  1421. static UnsignedInt lastFrame = ~0;
  1422. freezeTime = freezeTime || (lastFrame == TheGameClient->getFrame());
  1423. lastFrame = TheGameClient->getFrame();
  1424. /// @todo: I'm assuming the first view is our main 3D view.
  1425. W3DView *primaryW3DView=(W3DView *)getFirstView();
  1426. if (!freezeTime && TheScriptEngine->isTimeFast())
  1427. {
  1428. primaryW3DView->updateCameraMovements(); // Update camera motion effects.
  1429. syncTime += TheW3DFrameLengthInMsec;
  1430. return;
  1431. }
  1432. Debug_Statistics::Begin_Statistics(); //reset all counters (polygons, vertices, etc) before drawing
  1433. //update state of all the terrain tracks (fade, remove, etc.)
  1434. /// @todo: Is there a better place to put per-frame updates like this?
  1435. if(TheGlobalData->m_loadScreenRender != TRUE)
  1436. {
  1437. if (TheTerrainTracksRenderObjClassSystem)
  1438. TheTerrainTracksRenderObjClassSystem->update();
  1439. //Shroud data is needed to render all other views, so handle this first.
  1440. if (TheTerrainRenderObject)
  1441. {
  1442. //update the shroud surface here since it may be needed by reflections
  1443. if (TheTerrainRenderObject->getMap()) //make sure a valid map is loaded into terrain.
  1444. {
  1445. if (TheTerrainRenderObject->getShroud())
  1446. {
  1447. TheTerrainRenderObject->getShroud()->render(primaryW3DView->get3DCamera());
  1448. }
  1449. }
  1450. }
  1451. }
  1452. if (!freezeTime)
  1453. {
  1454. /// @todo Decouple framerate from timestep
  1455. // for now, use constant time steps to avoid animations running independent of framerate
  1456. syncTime += TheW3DFrameLengthInMsec;
  1457. // allow W3D to update its internals
  1458. // WW3D::Sync( GetTickCount() );
  1459. }
  1460. WW3D::Sync( syncTime );
  1461. // Fast & Frozen time limits the time to 33 fps.
  1462. Int minTime = 30;
  1463. static Int prevTime = timeGetTime(), now;
  1464. now=timeGetTime();
  1465. if (TheTacticalView->getTimeMultiplier()>1)
  1466. {
  1467. static Int timeMultiplierCounter = 1;
  1468. timeMultiplierCounter--;
  1469. if (timeMultiplierCounter>1)
  1470. return;
  1471. timeMultiplierCounter = TheTacticalView->getTimeMultiplier();
  1472. // limit the framerate, because while fast time is on, the game logic is running as fast as it can.
  1473. }
  1474. else
  1475. {
  1476. now = timeGetTime();
  1477. prevTime = now - minTime; // do the first frame immediately.
  1478. }
  1479. do {
  1480. {
  1481. if(TheGlobalData->m_loadScreenRender != TRUE)
  1482. {
  1483. // limit the framerate
  1484. while(TheGlobalData->m_useFpsLimit && (now - prevTime) < minTime-1)
  1485. {
  1486. now = timeGetTime();
  1487. }
  1488. prevTime = now;
  1489. }
  1490. }
  1491. // update all views of the world - recomputes data which will affect drawing
  1492. if (DX8Wrapper::_Get_D3D_Device8() && (DX8Wrapper::_Get_D3D_Device8()->TestCooperativeLevel()) == D3D_OK)
  1493. { //Checking if we have the device before updating views because the heightmap crashes otherwise while
  1494. //trying to refresh the visible terrain geometry.
  1495. // if(TheGlobalData->m_loadScreenRender != TRUE)
  1496. updateViews();
  1497. if (TheWaterRenderObj && TheGlobalData->m_waterType == 2)
  1498. TheWaterRenderObj->updateRenderTargetTextures(primaryW3DView->get3DCamera()); //do a render into each texture
  1499. //Can't render into textures while rendering to screen so these textures need to be updated
  1500. //before we enter main rendering loop.
  1501. if (TheW3DProjectedShadowManager)
  1502. TheW3DProjectedShadowManager->updateRenderTargetTextures();
  1503. }
  1504. Debug_Statistics::End_Statistics(); //record number of polygons rendered in RenderTargetTextures.
  1505. //Store number of polygons rendered in renderTargetTextures.
  1506. Int numRenderTargetPolygons=Debug_Statistics::Get_DX8_Polygons();
  1507. Int numRenderTargetVertices=Debug_Statistics::Get_DX8_Vertices();
  1508. // start render block
  1509. {
  1510. //USE_PERF_TIMER(BigAssRenderLoop)
  1511. static Bool couldRender = true;
  1512. 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)
  1513. {
  1514. if(TheGlobalData->m_loadScreenRender == TRUE)
  1515. {
  1516. TheInGameUI->draw();
  1517. if( TheMouse )
  1518. TheMouse->draw(); //keep applying the current cursor style so it remains hidden if needed.
  1519. WW3D::End_Render();
  1520. continue;
  1521. }
  1522. couldRender = true;
  1523. // add the number of verts/polygons drawn before the main scene
  1524. if (numRenderTargetPolygons || numRenderTargetVertices)
  1525. Debug_Statistics::Record_DX8_Polys_And_Vertices(numRenderTargetPolygons,numRenderTargetVertices,ShaderClass::_PresetOpaqueShader);
  1526. // draw all views of the world
  1527. drawViews();
  1528. // draw the user interface
  1529. TheInGameUI->DRAW();
  1530. // end of video example code
  1531. // draw the mouse
  1532. if( TheMouse )
  1533. TheMouse->DRAW();
  1534. if ( m_videoStream && m_videoBuffer )
  1535. {
  1536. drawVideoBuffer( m_videoBuffer, 0, 0, getWidth(), getHeight() );
  1537. }
  1538. if( m_copyrightDisplayString )
  1539. {
  1540. Int x, y, dX, dY;
  1541. m_copyrightDisplayString->getSize(&dX, &dY);
  1542. x = (getWidth() / 2) - (dX /2);
  1543. y = getHeight() - dY - 20 ;
  1544. m_copyrightDisplayString->draw(x, y, GameMakeColor(0,0,0,255), GameMakeColor(0,0,0,0),0,0);
  1545. }
  1546. // render letter box before debug display so debug info isn't hidden
  1547. renderLetterBox(now);
  1548. // display cinematicText over the black
  1549. if( m_cinematicText != AsciiString::TheEmptyString && m_cinematicTextFrames != 0)
  1550. {
  1551. DisplayString *displayString = TheDisplayStringManager->newDisplayString();
  1552. // set word wrap if neccessary
  1553. Int wordWrapWidth = TheDisplay->getWidth() - 20;
  1554. displayString->setWordWrap( wordWrapWidth );
  1555. displayString->setWordWrapCentered( TRUE );
  1556. UnicodeString text;
  1557. text.translate( m_cinematicText );
  1558. displayString->setText( text );
  1559. Color color = GameMakeColor( 255, 255, 255, 255 ); // white
  1560. Color backColor = GameMakeColor( 0, 0, 0, 0 ); // black
  1561. displayString->setFont( m_cinematicFont );
  1562. Int height = TheDisplay->getHeight() * .9;
  1563. Int width;
  1564. if( displayString->getWidth() > TheDisplay->getWidth() )
  1565. width = 20;
  1566. else
  1567. width = ( TheDisplay->getWidth() - displayString->getWidth() ) / 2;
  1568. displayString->draw( width, height, color, backColor );
  1569. m_cinematicTextFrames--;
  1570. }
  1571. if ( m_debugDisplayCallback )
  1572. {
  1573. // draw the current debug display
  1574. drawCurrentDebugDisplay();
  1575. }
  1576. #if defined(_DEBUG) || defined(_INTERNAL)
  1577. if (TheGlobalData->m_benchmarkTimer > 0)
  1578. {
  1579. drawFPSStats();
  1580. }
  1581. #endif
  1582. #if defined(_DEBUG) || defined(_INTERNAL)
  1583. if (TheGlobalData->m_debugShowGraphicalFramerate)
  1584. {
  1585. drawFramerateBar();
  1586. }
  1587. #endif
  1588. #ifdef PERF_TIMERS
  1589. TheGraphDraw->render();
  1590. TheGraphDraw->clear();
  1591. #endif
  1592. // render is all done!
  1593. WW3D::End_Render();
  1594. }
  1595. else
  1596. {
  1597. if (couldRender)
  1598. {
  1599. couldRender = false;
  1600. DEBUG_LOG(("Could not do WW3D::Begin_Render()! Are we ALT-Tabbed out?\n"));
  1601. }
  1602. }
  1603. }
  1604. if (TheScriptEngine->isTimeFrozenDebug() || TheScriptEngine->isTimeFrozenScript() || TheGameLogic->isGamePaused())
  1605. {
  1606. freezeTime = false; // We're frozen for debug or for pause, and need to continue out of the loop.
  1607. }
  1608. } while (freezeTime && !TheTacticalView->isCameraMovementFinished());
  1609. #ifdef EXTENDED_STATS
  1610. if (DX8Wrapper::stats.m_disableOverhead) {
  1611. goto AGAIN;
  1612. }
  1613. #endif
  1614. } // end draw
  1615. #define LETTER_BOX_FADE_TIME 1000.0f ///1000 ms.
  1616. /** Render letter-box border at top/bottom of display
  1617. */
  1618. void W3DDisplay::renderLetterBox(UnsignedInt currentTime)
  1619. {
  1620. if (m_letterBoxEnabled)
  1621. { if (m_letterBoxFadeLevel != 1.0f)
  1622. {
  1623. m_letterBoxFadeLevel = (currentTime - m_letterBoxFadeStartTime)/LETTER_BOX_FADE_TIME;
  1624. if (m_letterBoxFadeLevel > 1.0f)
  1625. m_letterBoxFadeLevel = 1.0f;
  1626. }
  1627. UnsignedInt lbcolor = (Int)(m_letterBoxFadeLevel * 255.0f) << 24;
  1628. #ifdef SLIDE_LETTERBOX
  1629. Int height = (Int)(getHeight() * 0.12f * m_letterBoxFadeLevel);
  1630. TheTacticalView->setOrigin(0, height);
  1631. #else
  1632. drawFillRect( 0, 0, m_width, (m_height-(9.0f/16.0f * m_width))*0.5f, lbcolor );
  1633. drawFillRect( 0, m_height-(m_height-(9.0f/16.0f * m_width))*0.5f, m_width, m_height, lbcolor );
  1634. #endif
  1635. }
  1636. else
  1637. { //letter box is disabled, but may still be fading out
  1638. if (m_letterBoxFadeLevel != 0.0f)
  1639. {
  1640. m_letterBoxFadeLevel = 1.0f - (currentTime - m_letterBoxFadeStartTime)/LETTER_BOX_FADE_TIME;
  1641. if (m_letterBoxFadeLevel < 0.0f)
  1642. m_letterBoxFadeLevel = 0.0f;
  1643. UnsignedInt lbcolor = (Int)(m_letterBoxFadeLevel * 255.0f) << 24;
  1644. #ifdef SLIDE_LETTERBOX
  1645. Int height = (Int)(getHeight() * 0.12f * m_letterBoxFadeLevel);
  1646. TheTacticalView->setOrigin(0, height);
  1647. #else
  1648. drawFillRect( 0, 0, m_width, (m_height-(9.0f/16.0f * m_width))*0.5f, lbcolor );
  1649. //drawFillRect( 0, m_height-(m_height-(9.0f/16.0f * m_width))*0.5f, m_width, m_height, lbcolor );
  1650. #endif
  1651. }
  1652. else
  1653. { //box has finished fading out
  1654. #ifdef SLIDE_LETTERBOX
  1655. TheTacticalView->setOrigin(0, 0);
  1656. #else
  1657. m_letterBoxEnabled = FALSE;
  1658. #endif
  1659. }
  1660. }
  1661. }
  1662. Bool W3DDisplay::isLetterBoxFading(void)
  1663. {
  1664. if (m_letterBoxEnabled && m_letterBoxFadeLevel != 1.0f)
  1665. return TRUE;
  1666. if (!m_letterBoxEnabled && m_letterBoxFadeLevel != 0.0f)
  1667. return TRUE;
  1668. return FALSE;
  1669. }
  1670. // W3DDisplay::createLightPulse ===============================================
  1671. /** Create a "light pulse" which is a dynamic light that grows, decays
  1672. * and vanishes over several frames */
  1673. //=============================================================================
  1674. void W3DDisplay::createLightPulse( const Coord3D *pos, const RGBColor *color,
  1675. Real innerRadius, Real attenuationWidth,
  1676. UnsignedInt increaseFrameTime,
  1677. UnsignedInt decayFrameTime//, Bool donut
  1678. )
  1679. {
  1680. if (innerRadius+attenuationWidth<2.0*PATHFIND_CELL_SIZE_F + 1.0f) {
  1681. return; // it basically won't make any visual difference. jba.
  1682. }
  1683. W3DDynamicLight * theDynamicLight = m_3DScene->getADynamicLight();
  1684. // turn it on.
  1685. theDynamicLight->setEnabled(true);
  1686. theDynamicLight->Set_Ambient( Vector3( color->red, color->green, color->blue ) );
  1687. theDynamicLight->Set_Diffuse( Vector3( color->red, color->green, color->blue) );
  1688. theDynamicLight->Set_Position(Vector3(pos->x, pos->y, pos->z));
  1689. theDynamicLight->Set_Far_Attenuation_Range(innerRadius, innerRadius + attenuationWidth);
  1690. theDynamicLight->setFrameFade(increaseFrameTime, decayFrameTime);
  1691. theDynamicLight->setDecayRange();
  1692. theDynamicLight->setDecayColor();
  1693. //theDynamicLight->setDonut(donut);
  1694. }
  1695. void W3DDisplay::toggleLetterBox(void)
  1696. {
  1697. m_letterBoxEnabled = !m_letterBoxEnabled;
  1698. m_letterBoxFadeStartTime = timeGetTime();
  1699. }
  1700. void W3DDisplay::enableLetterBox(Bool enable)
  1701. {
  1702. if (enable)
  1703. {
  1704. if (!m_letterBoxEnabled)
  1705. { //letterbox mode not previously enabled
  1706. m_letterBoxEnabled = TRUE;
  1707. m_letterBoxFadeStartTime = timeGetTime();
  1708. }
  1709. }
  1710. else
  1711. {
  1712. if (m_letterBoxEnabled)
  1713. { //letterbox mode no previously disabled
  1714. m_letterBoxEnabled = FALSE;
  1715. m_letterBoxFadeStartTime = timeGetTime();
  1716. }
  1717. }
  1718. }
  1719. // W3DDisplay::setTimeOfDay ===================================================
  1720. /** */
  1721. //=============================================================================
  1722. void W3DDisplay::setTimeOfDay( TimeOfDay tod )
  1723. {
  1724. const GlobalData::TerrainLighting *ol=&TheGlobalData->m_terrainObjectsLighting[tod][0];
  1725. if( m_3DScene )
  1726. {
  1727. m_3DScene->Set_Ambient_Light( Vector3(ol->ambient.red, ol->ambient.green, ol->ambient.blue) );
  1728. }
  1729. for (Int i=0; i<LightEnvironmentClass::MAX_LIGHTS; i++)
  1730. {
  1731. if( m_myLight[i] )
  1732. {
  1733. ol=&TheGlobalData->m_terrainObjectsLighting[tod][i];
  1734. m_myLight[i]->Set_Ambient( Vector3( 0.0f, 0.0f, 0.0f ) );
  1735. m_myLight[i]->Set_Diffuse( Vector3(ol->diffuse.red, ol->diffuse.green, ol->diffuse.blue ) );
  1736. m_myLight[i]->Set_Specular( Vector3(0,0,0) );
  1737. Matrix3D mtx;
  1738. mtx.Set(Vector3(1,0,0), Vector3(0,1,0), Vector3(ol->lightPos.x, ol->lightPos.y, ol->lightPos.z), Vector3(0,0,0));
  1739. m_myLight[i]->Set_Transform(mtx);
  1740. }
  1741. }
  1742. if(TheTerrainRenderObject) {
  1743. TheTerrainRenderObject->setTimeOfDay(tod);
  1744. TheTacticalView->forceRedraw();
  1745. }
  1746. }
  1747. // W3DDisplay::drawLine =======================================================
  1748. /** draw a line on the display in pixel coordinates with the specified color */
  1749. //=============================================================================
  1750. void W3DDisplay::drawLine( Int startX, Int startY,
  1751. Int endX, Int endY,
  1752. Real lineWidth,
  1753. UnsignedInt lineColor )
  1754. {
  1755. /// @todo we need to consider the efficiency of the 2D renderer
  1756. m_2DRender->Reset();
  1757. m_2DRender->Enable_Texturing( FALSE );
  1758. m_2DRender->Add_Line( Vector2( startX, startY ), Vector2( endX, endY ),
  1759. lineWidth, lineColor );
  1760. m_2DRender->Render();
  1761. } // end drawLine
  1762. // W3DDisplay::drawLine =======================================================
  1763. /** draw a line on the display in pixel coordinates with the specified color */
  1764. //=============================================================================
  1765. void W3DDisplay::drawLine( Int startX, Int startY,
  1766. Int endX, Int endY,
  1767. Real lineWidth,
  1768. UnsignedInt lineColor1,UnsignedInt lineColor2 )
  1769. {
  1770. /// @todo we need to consider the efficiency of the 2D renderer
  1771. m_2DRender->Reset();
  1772. m_2DRender->Enable_Texturing( FALSE );
  1773. m_2DRender->Add_Line( Vector2( startX, startY ), Vector2( endX, endY ),
  1774. lineWidth, lineColor1, lineColor2 );
  1775. m_2DRender->Render();
  1776. } // end drawLine
  1777. // W3DDisplay::drawOpenRect ===================================================
  1778. //=============================================================================
  1779. void W3DDisplay::drawOpenRect( Int startX, Int startY, Int width, Int height,
  1780. Real lineWidth, UnsignedInt lineColor )
  1781. {
  1782. if (m_isClippedEnabled)
  1783. {
  1784. ICoord2D start, end, returnStart, returnEnd;
  1785. start.x = startX;
  1786. start.y = startY;
  1787. end.x = start.x;
  1788. end.y = start.y + height;
  1789. if(ClipLine2D(&start, &end, &returnStart, &returnEnd, &m_clipRegion ))
  1790. drawLine( returnStart.x, returnStart.y, returnEnd.x, returnEnd.y, lineWidth, lineColor);
  1791. end.x = start.x + width;
  1792. end.y = start.y;
  1793. if(ClipLine2D(&start, &end, &returnStart, &returnEnd, &m_clipRegion ))
  1794. drawLine( returnStart.x, returnStart.y, returnEnd.x, returnEnd.y, lineWidth, lineColor);
  1795. start.x = startX + width;
  1796. start.y = startY;
  1797. end.x = start.x;
  1798. end.y = start.y + height;
  1799. if(ClipLine2D(&start, &end, &returnStart, &returnEnd, &m_clipRegion ))
  1800. drawLine( returnStart.x, returnStart.y, returnEnd.x, returnEnd.y, lineWidth, lineColor);
  1801. start.x = startX;
  1802. start.y = startY + height;
  1803. end.x = start.x + width;
  1804. end.y = start.y;
  1805. if(ClipLine2D(&start, &end, &returnStart, &returnEnd, &m_clipRegion ))
  1806. drawLine( returnStart.x, returnStart.y, returnEnd.x, returnEnd.y, lineWidth, lineColor);
  1807. }
  1808. else
  1809. {
  1810. /// @todo we need to consider the efficiency of the 2D renderer
  1811. m_2DRender->Reset();
  1812. m_2DRender->Enable_Texturing( FALSE );
  1813. m_2DRender->Add_Outline( RectClass( startX, startY,
  1814. startX + width, startY + height ),
  1815. lineWidth, lineColor );
  1816. // render it now!
  1817. m_2DRender->Render();
  1818. }
  1819. } // end drawOpenRect
  1820. // W3DDisplay::drawFillRect ===================================================
  1821. //=============================================================================
  1822. void W3DDisplay::drawFillRect( Int startX, Int startY, Int width, Int height,
  1823. UnsignedInt color )
  1824. {
  1825. /// @todo we need to consider the efficiency of the 2D renderer
  1826. m_2DRender->Reset();
  1827. m_2DRender->Enable_Texturing( FALSE );
  1828. m_2DRender->Add_Rect( RectClass( startX, startY,
  1829. startX + width, startY + height ),
  1830. 0, 0, color );
  1831. // render it now!
  1832. m_2DRender->Render();
  1833. } // end drawFillRect
  1834. void W3DDisplay::drawRectClock(Int startX, Int startY, Int width, Int height, Int percent, UnsignedInt color)
  1835. {
  1836. // sanity
  1837. if(percent < 1 || percent > 100)
  1838. return;
  1839. m_2DRender->Reset();
  1840. m_2DRender->Enable_Texturing( FALSE );
  1841. // The rectanges are numberd as follows
  1842. //(x,y) |---------|
  1843. // | 4 | 1 |
  1844. // |----+----|
  1845. // | 3 | 2 |
  1846. // |---------| (x + width, y + width)
  1847. //
  1848. // we're done, lets just draw one rectangle for it all.
  1849. if(percent == 100)
  1850. {
  1851. m_2DRender->Add_Rect(RectClass( startX, startY,
  1852. startX + width, startY + height), 0,0, color);
  1853. }
  1854. else if( percent> 75)
  1855. {
  1856. //rectangle #1 & 2
  1857. m_2DRender->Add_Rect(RectClass( startX + width/2, startY,
  1858. startX + width, startY + height), 0,0, color);
  1859. // rectangle #3
  1860. m_2DRender->Add_Rect(RectClass( startX, startY + height/2,
  1861. startX + width/2, startY + height), 0,0, color);
  1862. // draw the part of rectangle 4
  1863. Real remain = percent - 75;
  1864. if(remain > 12)
  1865. {
  1866. //draw the full triangle
  1867. m_2DRender->Add_Tri(Vector2(startX, startY),
  1868. Vector2(startX, startY + height/2),
  1869. Vector2(startX + width/2, startY + height/2),
  1870. Vector2(0,0),Vector2(0,0),Vector2(0,0),color);
  1871. // draw the part of triangle
  1872. Real percentDraw = (Real)(remain - 12)/ 13;
  1873. m_2DRender->Add_Tri(Vector2(startX, startY),
  1874. Vector2(startX + width/2, startY + height/2),
  1875. Vector2(startX + (width/2 * percentDraw), startY),
  1876. Vector2(0,0),Vector2(0,0),Vector2(0,0),color);
  1877. }
  1878. else
  1879. {
  1880. // draw the part of triangle
  1881. Real percentDraw = (Real)(remain)/ 12;
  1882. m_2DRender->Add_Tri(Vector2(startX, startY + height/2 - (height/2 * percentDraw)),
  1883. Vector2(startX, startY + height/2),
  1884. Vector2(startX + width/2, startY + height/2),
  1885. Vector2(0,0),Vector2(0,0),Vector2(0,0),color);
  1886. }
  1887. }
  1888. else if( percent > 50)
  1889. {
  1890. //rectangle #1 & 2
  1891. m_2DRender->Add_Rect(RectClass( startX + width/2, startY,
  1892. startX + width, startY + height), 0,0, color);
  1893. // draw the part of rectangle 3
  1894. Real remain = percent - 50;
  1895. if(remain > 12)
  1896. {
  1897. //draw the full triangle
  1898. m_2DRender->Add_Tri(Vector2(startX + width/2, startY + height/2),
  1899. Vector2(startX, startY + height),
  1900. Vector2(startX + width/2, startY + height),
  1901. Vector2(0,0),Vector2(0,0),Vector2(0,0),color);
  1902. // draw the part of triangle
  1903. Real percentDraw = (Real)(remain - 12)/ 13;
  1904. m_2DRender->Add_Tri(Vector2(startX, startY + height - (height/2 * percentDraw)),
  1905. Vector2(startX, startY + height),
  1906. Vector2(startX + width/2, startY + height/2),
  1907. Vector2(0,0),Vector2(0,0),Vector2(0,0),color);
  1908. }
  1909. else
  1910. {
  1911. // draw the part of triangle
  1912. Real percentDraw = (Real)(remain)/ 12;
  1913. m_2DRender->Add_Tri(Vector2(startX + width/2, startY + height),
  1914. Vector2(startX + width/2, startY + height/2),
  1915. Vector2(startX + width/2 - ( width/2 * percentDraw), startY + height),
  1916. Vector2(0,0),Vector2(0,0),Vector2(0,0),color);
  1917. }
  1918. }
  1919. else if(percent > 25)
  1920. {
  1921. // rectangel #1
  1922. m_2DRender->Add_Rect(RectClass( startX + width/2, startY,
  1923. startX + width, startY + height/2), 0,0, color);
  1924. // draw the part of rectangle 2
  1925. Real remain = percent - 25;
  1926. if(remain > 12)
  1927. {
  1928. //draw the full triangle
  1929. m_2DRender->Add_Tri(Vector2(startX + width/2, startY + height/2),
  1930. Vector2(startX + width, startY + height),
  1931. Vector2(startX + width, startY + height/2),
  1932. Vector2(0,0),Vector2(0,0),Vector2(0,0),color);
  1933. // draw the part of triangle
  1934. Real percentDraw = (Real)(remain - 12)/ 13;
  1935. m_2DRender->Add_Tri(Vector2(startX + width/2, startY + height/2),
  1936. Vector2(startX + width - (width/2 * percentDraw), startY + height),
  1937. Vector2(startX + width, startY + height),
  1938. Vector2(0,0),Vector2(0,0),Vector2(0,0),color);
  1939. }
  1940. else
  1941. {
  1942. // draw the part of triangle
  1943. Real percentDraw = (Real)(remain)/ 12;
  1944. m_2DRender->Add_Tri(Vector2(startX + width, startY + height/2),
  1945. Vector2(startX + width/2, startY + height/2),
  1946. Vector2(startX + width, startY + height/2 + ( height/2 * percentDraw)),
  1947. Vector2(0,0),Vector2(0,0),Vector2(0,0),color);
  1948. }
  1949. }
  1950. else
  1951. {
  1952. // draw the part of rectangle 1
  1953. if(percent > 12)
  1954. {
  1955. //draw the full triangle
  1956. m_2DRender->Add_Tri(Vector2(startX + width/2, startY),
  1957. Vector2(startX + width/2, startY + height/2),
  1958. Vector2(startX + width, startY),
  1959. Vector2(0,0),Vector2(0,0),Vector2(0,0),color);
  1960. // draw the part of triangle
  1961. Real percentDraw = (Real)(percent - 12)/ 13;
  1962. m_2DRender->Add_Tri(Vector2(startX + width, startY),
  1963. Vector2(startX + width/2, startY + height/2),
  1964. Vector2(startX + width, startY + (height/2 * percentDraw)),
  1965. Vector2(0,0),Vector2(0,0),Vector2(0,0),color);
  1966. }
  1967. else
  1968. {
  1969. // draw the part of triangle
  1970. Real percentDraw = (Real)(percent)/ 12;
  1971. m_2DRender->Add_Tri(Vector2(startX + width/2, startY),
  1972. Vector2(startX + width/2, startY + height/2),
  1973. Vector2(startX + width/2 + (width/2 * percentDraw), startY ),
  1974. Vector2(0,0),Vector2(0,0),Vector2(0,0),color);
  1975. }
  1976. }
  1977. // render it now!
  1978. m_2DRender->Render();
  1979. }
  1980. //--------------------------------------------------------------------------------------------------------------------
  1981. // W3DDisplay::drawRemainingRectClock
  1982. // Variation added by Kris -- October 2002
  1983. // This version will overlay a clock progress from the specified percentage to 100%. Essentially, this function will
  1984. // "reveal" an icon as it progresses towards completion.
  1985. //--------------------------------------------------------------------------------------------------------------------
  1986. void W3DDisplay::drawRemainingRectClock(Int startX, Int startY, Int width, Int height, Int percent, UnsignedInt color)
  1987. {
  1988. // sanity
  1989. if( percent < 0 || percent > 99 )
  1990. return;
  1991. m_2DRender->Reset();
  1992. m_2DRender->Enable_Texturing( FALSE );
  1993. // The rectanges are numbered as follows
  1994. //(x,y) |---------|
  1995. // | 4 | 1 |
  1996. // |----+----|
  1997. // | 3 | 2 |
  1998. // |---------| (x + width, y + width)
  1999. //
  2000. Int midX = startX + width/2;
  2001. Int midY = startY + height/2;
  2002. Int endX = startX + width;
  2003. Int endY = startY + height;
  2004. Int halfWidth = width/2;
  2005. Int halfHeight = height/2;
  2006. if( percent == 0 )
  2007. {
  2008. // We just started, so draw the entire remaining rectangle.
  2009. // #1, #2, #3, and #4
  2010. m_2DRender->Add_Rect( RectClass( startX, startY, endX, endY ), 0, 0, color );
  2011. }
  2012. else if( percent < 25 )
  2013. {
  2014. //1-25%
  2015. //-----
  2016. //Rectangle #3 & 4
  2017. m_2DRender->Add_Rect( RectClass( startX, startY, midX, endY ), 0, 0, color );
  2018. //Rectangle #2
  2019. m_2DRender->Add_Rect( RectClass( midX, midY, endX, endY ), 0, 0, color );
  2020. //Handle rectangle #1 than needs partial rendering.
  2021. if( percent < 13 )
  2022. {
  2023. //1-12%
  2024. //-----
  2025. //Draw the 2nd half of rectangle #1
  2026. m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( endX, midY ), Vector2( endX, startY ),
  2027. Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color );
  2028. //Draw the last part of the 1st portion of rectangle #1
  2029. Real percentDraw = (Real)( 13 - percent ) / 13;
  2030. m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( endX, startY ), Vector2( endX - halfWidth * percentDraw, startY ),
  2031. Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color );
  2032. }
  2033. else
  2034. {
  2035. //13-24%
  2036. //------
  2037. //Draw the last part of the 2nd half of rectangle #1
  2038. Real percentDraw = (Real)( percent - 13 ) / 12;
  2039. m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( endX, midY ), Vector2( endX, startY + halfHeight * percentDraw ),
  2040. Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color );
  2041. }
  2042. }
  2043. else if( percent < 50 )
  2044. {
  2045. //25-49%
  2046. //------
  2047. //rectangle #3 & 4
  2048. m_2DRender->Add_Rect( RectClass( startX, startY, midX, endY ), 0, 0, color );
  2049. //Handle rectangle #2 that needs partial rendering.
  2050. if( percent < 38 )
  2051. {
  2052. //25-37%
  2053. //-----
  2054. //Draw the 2nd half of rectangle #2
  2055. m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( midX, endY ), Vector2( endX, endY ),
  2056. Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color );
  2057. //Draw the last part of the 1st portion of rectangle #2
  2058. Real percentDraw = (Real)( percent - 25 ) / 13;
  2059. m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( endX, endY ), Vector2( endX, midY + halfHeight * percentDraw ),
  2060. Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color );
  2061. }
  2062. else
  2063. {
  2064. //38-49%
  2065. //------
  2066. //Draw the last part of the 2nd half of rectangle #1
  2067. Real percentDraw = (Real)( percent - 38 ) / 12;
  2068. m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( midX, endY ), Vector2( endX - halfWidth * percentDraw, endY ),
  2069. Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color );
  2070. }
  2071. }
  2072. else if( percent < 75 )
  2073. {
  2074. //50-74%
  2075. //------
  2076. //Rectangle #4
  2077. m_2DRender->Add_Rect( RectClass( startX, startY, midX, midY ), 0, 0, color );
  2078. //Handle rectangle #3 that needs partial rendering.
  2079. if( percent < 63 )
  2080. {
  2081. //50-62%
  2082. //-----
  2083. //Draw the 2nd half of rectangle #3
  2084. m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( startX, midY ), Vector2( startX, endY ),
  2085. Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color );
  2086. //Draw the last part of the 1st portion of rectangle #3
  2087. Real percentDraw = (Real)( percent - 50 ) / 13;
  2088. m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( startX, endY ), Vector2( midX - halfWidth * percentDraw, endY ),
  2089. Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color );
  2090. }
  2091. else
  2092. {
  2093. //62-74%
  2094. //------
  2095. //Draw the last part of the 2nd half of rectangle #3
  2096. Real percentDraw = (Real)( percent - 62 ) / 12;
  2097. m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( startX, midY ), Vector2( startX, endY - halfHeight * percentDraw ),
  2098. Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color );
  2099. }
  2100. }
  2101. else
  2102. {
  2103. //75-99%
  2104. //------
  2105. //Handle rectangle #4 that needs partial rendering.
  2106. if( percent < 87 )
  2107. {
  2108. //75-87%
  2109. //-----
  2110. //Draw the 2nd half of rectangle #4
  2111. m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( midX, startY ), Vector2( startX, startY ),
  2112. Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color );
  2113. //Draw the last part of the 1st portion of rectangle #4
  2114. Real percentDraw = (Real)( percent - 75 ) / 13;
  2115. m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( startX, startY ), Vector2( startX, midY - halfHeight * percentDraw ),
  2116. Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color );
  2117. }
  2118. else
  2119. {
  2120. //88-99%
  2121. //------
  2122. //Draw the last part of the 2nd half of rectangle #4
  2123. Real percentDraw = (Real)( percent - 88 ) / 12;
  2124. m_2DRender->Add_Tri( Vector2( midX, midY ), Vector2( midX, startY ), Vector2( startX + halfWidth * percentDraw, startY ),
  2125. Vector2( 0, 0 ), Vector2( 0, 0 ), Vector2( 0, 0 ), color );
  2126. }
  2127. }
  2128. // render it now!
  2129. m_2DRender->Render();
  2130. }
  2131. // W3DDisplay::drawImage ======================================================
  2132. /** Draws an images at the screen coordinates and keeps it within the end
  2133. * screen coords specified */
  2134. //=============================================================================
  2135. void W3DDisplay::drawImage( const Image *image, Int startX, Int startY,
  2136. Int endX, Int endY, Color color, DrawImageMode mode)
  2137. {
  2138. // sanity
  2139. if( image == NULL )
  2140. return;
  2141. // !!
  2142. // Remember to update the GUIEditDisplay::drawImage when you make
  2143. // changes to this, it technically uses W3D code to render itself,
  2144. // but it not derived on the W3DDisplay
  2145. // !!
  2146. const Region2D *uv = image->getUV();
  2147. m_2DRender->Reset();
  2148. m_2DRender->Enable_Texturing( TRUE );
  2149. Bool doAlphaReset=FALSE;
  2150. ///@todo: Why are we alpha blending all images? Reduces our fillrate. -MW
  2151. switch (mode)
  2152. {
  2153. case DRAW_IMAGE_ALPHA: //nothing to do since alpha is the default state
  2154. break;
  2155. case DRAW_IMAGE_GRAYSCALE:
  2156. m_2DRender->Enable_Grayscale(true);
  2157. break;
  2158. case DRAW_IMAGE_ADDITIVE:
  2159. m_2DRender->Enable_Additive(true);
  2160. doAlphaReset = TRUE;
  2161. break;
  2162. case DRAW_IMAGE_SOLID:
  2163. m_2DRender->Enable_Additive(false);
  2164. m_2DRender->Enable_Alpha(false);
  2165. doAlphaReset = TRUE;
  2166. default:
  2167. break;
  2168. }
  2169. // if we have raw texture data we will use it, otherwise we are referencing filenames
  2170. if( BitTest( image->getStatus(), IMAGE_STATUS_RAW_TEXTURE ) )
  2171. m_2DRender->Set_Texture( (TextureClass *)(image->getRawTextureData()) );
  2172. else
  2173. m_2DRender->Set_Texture( image->getFilename().str() );
  2174. RectClass screen_rect(startX,startY,endX,endY);
  2175. RectClass uv_rect(uv->lo.x,uv->lo.y,uv->hi.x,uv->hi.y);
  2176. if (m_isClippedEnabled)
  2177. { //need to clip this quad to clip rectangle
  2178. //
  2179. // Check for completely clipped
  2180. //
  2181. if ( endX <= m_clipRegion.lo.x ||
  2182. endY <= m_clipRegion.lo.y)
  2183. {
  2184. return; //nothing to render
  2185. } else {
  2186. RectClass clipped_rect;
  2187. RectClass clipped_uv_rect;
  2188. if( BitTest( image->getStatus(), IMAGE_STATUS_ROTATED_90_CLOCKWISE ) )
  2189. {
  2190. //
  2191. // Clip the polygons to the specified area
  2192. //
  2193. clipped_rect.Left = __max (screen_rect.Left, m_clipRegion.lo.x);
  2194. clipped_rect.Right = __min (screen_rect.Right, m_clipRegion.hi.x);
  2195. clipped_rect.Top = __max (screen_rect.Top, m_clipRegion.lo.y);
  2196. clipped_rect.Bottom = __min (screen_rect.Bottom, m_clipRegion.hi.y);
  2197. //
  2198. // Clip the texture to the specified area
  2199. //
  2200. float percent = ((clipped_rect.Left - screen_rect.Left) / screen_rect.Width ());
  2201. clipped_uv_rect.Top = uv_rect.Top + (uv_rect.Height () * percent);
  2202. percent = ((clipped_rect.Right - screen_rect.Left) / screen_rect.Width ());
  2203. clipped_uv_rect.Bottom = uv_rect.Top + (uv_rect.Height () * percent);
  2204. percent = ((clipped_rect.Top - screen_rect.Top) / screen_rect.Height ());
  2205. clipped_uv_rect.Right = uv_rect.Right - (uv_rect.Width () * percent);
  2206. percent = ((clipped_rect.Bottom - screen_rect.Top) / screen_rect.Height ());
  2207. clipped_uv_rect.Left = uv_rect.Right - (uv_rect.Width () * percent);
  2208. }
  2209. else
  2210. {
  2211. //
  2212. // Clip the polygons to the specified area
  2213. //
  2214. clipped_rect.Left = __max (screen_rect.Left, m_clipRegion.lo.x);
  2215. clipped_rect.Right = __min (screen_rect.Right, m_clipRegion.hi.x);
  2216. clipped_rect.Top = __max (screen_rect.Top, m_clipRegion.lo.y);
  2217. clipped_rect.Bottom = __min (screen_rect.Bottom, m_clipRegion.hi.y);
  2218. //
  2219. // Clip the texture to the specified area
  2220. //
  2221. float percent = ((clipped_rect.Left - screen_rect.Left) / screen_rect.Width ());
  2222. clipped_uv_rect.Left = uv_rect.Left + (uv_rect.Width () * percent);
  2223. percent = ((clipped_rect.Right - screen_rect.Left) / screen_rect.Width ());
  2224. clipped_uv_rect.Right = uv_rect.Left + (uv_rect.Width () * percent);
  2225. percent = ((clipped_rect.Top - screen_rect.Top) / screen_rect.Height ());
  2226. clipped_uv_rect.Top = uv_rect.Top + (uv_rect.Height () * percent);
  2227. percent = ((clipped_rect.Bottom - screen_rect.Top) / screen_rect.Height ());
  2228. clipped_uv_rect.Bottom = uv_rect.Top + (uv_rect.Height () * percent);
  2229. }
  2230. //
  2231. // Use the clipped rectangles to render
  2232. //
  2233. screen_rect = clipped_rect;
  2234. uv_rect = clipped_uv_rect;
  2235. }
  2236. }
  2237. // if rotated 90 degrees clockwise we have to adjust the uv coords
  2238. if( BitTest( image->getStatus(), IMAGE_STATUS_ROTATED_90_CLOCKWISE ) )
  2239. {
  2240. m_2DRender->Add_Tri( Vector2( screen_rect.Left, screen_rect.Top ),
  2241. Vector2( screen_rect.Left, screen_rect.Bottom ),
  2242. Vector2( screen_rect.Right, screen_rect.Top ),
  2243. Vector2( uv_rect.Right, uv_rect.Top),
  2244. Vector2( uv_rect.Left, uv_rect.Top),
  2245. Vector2( uv_rect.Right, uv_rect.Bottom ),
  2246. color );
  2247. m_2DRender->Add_Tri( Vector2( screen_rect.Right, screen_rect.Bottom ),
  2248. Vector2( screen_rect.Right, screen_rect.Top ),
  2249. Vector2( screen_rect.Left, screen_rect.Bottom ),
  2250. Vector2( uv_rect.Left, uv_rect.Bottom ),
  2251. Vector2( uv_rect.Right, uv_rect.Bottom ),
  2252. Vector2( uv_rect.Left, uv_rect.Top ),
  2253. color );
  2254. } // end if
  2255. else
  2256. {
  2257. // just draw as normal
  2258. m_2DRender->Add_Quad( screen_rect, uv_rect, color );
  2259. } // end else
  2260. m_2DRender->Render();
  2261. //reset to default states for next time this method is called.
  2262. m_2DRender->Enable_Grayscale(false); //never leave it in this mode
  2263. if (doAlphaReset)
  2264. m_2DRender->Enable_Alpha(true);
  2265. } // end drawImage
  2266. //============================================================================
  2267. // W3DDisplay::createVideoBuffer
  2268. //============================================================================
  2269. VideoBuffer* W3DDisplay::createVideoBuffer( void )
  2270. {
  2271. VideoBuffer::Type format = VideoBuffer::TYPE_UNKNOWN;
  2272. /// @todo query video player for supported formats - we assume bink formats here
  2273. // first try to use the native format
  2274. WW3DFormat displayFormat = DX8Wrapper::getBackBufferFormat();
  2275. if ( DX8Caps::Support_Texture_Format( displayFormat ))
  2276. {
  2277. format = W3DVideoBuffer::W3DFormatToType( displayFormat );
  2278. }
  2279. if ( format == VideoBuffer::TYPE_UNKNOWN )
  2280. {
  2281. if ( DX8Caps::Support_Texture_Format( WW3D_FORMAT_X8R8G8B8 ))
  2282. {
  2283. format = VideoBuffer::TYPE_X8R8G8B8;
  2284. }
  2285. else if ( DX8Caps::Support_Texture_Format( WW3D_FORMAT_R8G8B8 ))
  2286. {
  2287. format = VideoBuffer::TYPE_R8G8B8;
  2288. }
  2289. else if ( DX8Caps::Support_Texture_Format( WW3D_FORMAT_R5G6B5 ))
  2290. {
  2291. format = VideoBuffer::TYPE_R5G6B5;
  2292. }
  2293. else if ( DX8Caps::Support_Texture_Format( WW3D_FORMAT_X1R5G5B5 ))
  2294. {
  2295. format = VideoBuffer::TYPE_X1R5G5B5;
  2296. }
  2297. else
  2298. {
  2299. // card does not support any of the formats we need
  2300. return NULL;
  2301. }
  2302. }
  2303. // on low mem machines, render every video in 16bit except for the EA Logo movie
  2304. if(!TheGlobalData->m_playIntro )//&& TheGameLODManager && (!TheGameLODManager->didMemPass() || W3DShaderManager::getChipset() == DC_GEFORCE2))
  2305. format = VideoBuffer::TYPE_R5G6B5;
  2306. W3DVideoBuffer *buffer = NEW W3DVideoBuffer( format );
  2307. return buffer;
  2308. }
  2309. //============================================================================
  2310. // W3DDisplay::drawVideoBuffer
  2311. //============================================================================
  2312. void W3DDisplay::drawVideoBuffer( VideoBuffer *buffer, Int startX, Int startY, Int endX, Int endY )
  2313. {
  2314. W3DVideoBuffer *vbuffer = (W3DVideoBuffer*) buffer;
  2315. m_2DRender->Reset();
  2316. m_2DRender->Enable_Texturing( TRUE );
  2317. m_2DRender->Set_Texture( vbuffer->texture() );
  2318. m_2DRender->Add_Quad( RectClass( startX, startY, endX, endY ),
  2319. vbuffer->Rect( 0, 0, 1, 1) );
  2320. m_2DRender->Render();
  2321. }
  2322. // W3DDisplay::setClipRegion ============================================
  2323. /** Set the clipping region for images.
  2324. @todo: Make this work for all primitives, not just drawImage. */
  2325. //=============================================================================
  2326. void W3DDisplay::setClipRegion( IRegion2D *region )
  2327. {
  2328. // assign new region
  2329. m_clipRegion = *region;
  2330. m_isClippedEnabled = TRUE;
  2331. } // end setClipRegion
  2332. //=============================================================================
  2333. /* we don't really need to override this call, since we will soon be called to
  2334. update every shroud cell explicitly...
  2335. */
  2336. void W3DDisplay::clearShroud()
  2337. {
  2338. // nothing
  2339. }
  2340. //=============================================================================
  2341. void W3DDisplay::setBorderShroudLevel(UnsignedByte level)
  2342. {
  2343. if (TheTerrainRenderObject && TheTerrainRenderObject->getShroud())
  2344. {
  2345. TheTerrainRenderObject->getShroud()->setBorderShroudLevel((W3DShroudLevel)level);
  2346. }
  2347. }
  2348. //=============================================================================
  2349. void W3DDisplay::setShroudLevel( Int x, Int y, CellShroudStatus setting )
  2350. {
  2351. if (TheTerrainRenderObject && TheTerrainRenderObject->getShroud())
  2352. {
  2353. #ifdef INTENSE_DEBUG
  2354. TheTerrainRenderObject->getShroud()->setShroudFilter(false);
  2355. #endif
  2356. if( setting == CELLSHROUD_SHROUDED )
  2357. TheTerrainRenderObject->getShroud()->setShroudLevel(x, y, (W3DShroudLevel)TheGlobalData->m_shroudAlpha );
  2358. else if( setting == CELLSHROUD_FOGGED )
  2359. TheTerrainRenderObject->getShroud()->setShroudLevel(x, y, (W3DShroudLevel)TheGlobalData->m_fogAlpha );///< @todo placeholder to get feedback on logic work while graphic side being decided
  2360. else
  2361. TheTerrainRenderObject->getShroud()->setShroudLevel(x, y, (W3DShroudLevel)TheGlobalData->m_clearAlpha );
  2362. //Logic is saying shroud. We can add alpha levels here in client if needed.
  2363. // W3DShroud is a 0-255 alpha byte. Logic shroud is a double reference count.
  2364. }
  2365. }
  2366. //=============================================================================
  2367. ///Utility function to dump data into a .BMP file
  2368. static void CreateBMPFile(LPTSTR pszFile, char *image, Int width, Int height)
  2369. {
  2370. HANDLE hf; // file handle
  2371. BITMAPFILEHEADER hdr; // bitmap file-header
  2372. PBITMAPINFOHEADER pbih; // bitmap info-header
  2373. LPBYTE lpBits; // memory pointer
  2374. DWORD dwTotal; // total count of bytes
  2375. DWORD cb; // incremental count of bytes
  2376. BYTE *hp; // byte pointer
  2377. DWORD dwTmp;
  2378. PBITMAPINFO pbmi;
  2379. pbmi = (PBITMAPINFO) LocalAlloc(LPTR,sizeof(BITMAPINFOHEADER));
  2380. pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  2381. pbmi->bmiHeader.biWidth = width;
  2382. pbmi->bmiHeader.biHeight = height;
  2383. pbmi->bmiHeader.biPlanes = 1;
  2384. pbmi->bmiHeader.biBitCount = 24;
  2385. pbmi->bmiHeader.biCompression = BI_RGB;
  2386. pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7) /8 * pbmi->bmiHeader.biHeight * 24;
  2387. pbmi->bmiHeader.biClrImportant = 0;
  2388. pbih = (PBITMAPINFOHEADER) pbmi;
  2389. lpBits = (LPBYTE) image;
  2390. // Create the .BMP file.
  2391. hf = CreateFile(pszFile,
  2392. GENERIC_READ | GENERIC_WRITE,
  2393. (DWORD) 0,
  2394. NULL,
  2395. CREATE_ALWAYS,
  2396. FILE_ATTRIBUTE_NORMAL,
  2397. (HANDLE) NULL);
  2398. if (hf == INVALID_HANDLE_VALUE)
  2399. return;
  2400. hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
  2401. // Compute the size of the entire file.
  2402. hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
  2403. pbih->biSize + pbih->biClrUsed
  2404. * sizeof(RGBQUAD) + pbih->biSizeImage);
  2405. hdr.bfReserved1 = 0;
  2406. hdr.bfReserved2 = 0;
  2407. // Compute the offset to the array of color indices.
  2408. hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
  2409. pbih->biSize + pbih->biClrUsed
  2410. * sizeof (RGBQUAD);
  2411. // Copy the BITMAPFILEHEADER into the .BMP file.
  2412. if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),
  2413. (LPDWORD) &dwTmp, NULL))
  2414. return;
  2415. // Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
  2416. if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof (RGBQUAD),(LPDWORD) &dwTmp, NULL))
  2417. return;
  2418. // Copy the array of color indices into the .BMP file.
  2419. dwTotal = cb = pbih->biSizeImage;
  2420. hp = lpBits;
  2421. if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL))
  2422. return;
  2423. // Close the .BMP file.
  2424. if (!CloseHandle(hf))
  2425. return;
  2426. // Free memory.
  2427. LocalFree( (HLOCAL) pbmi);
  2428. }
  2429. ///Save Screen Capture to a file
  2430. void W3DDisplay::takeScreenShot(void)
  2431. {
  2432. char leafname[256];
  2433. char pathname[1024];
  2434. static int frame_number = 1;
  2435. Bool done = false;
  2436. while (!done) {
  2437. #ifdef CAPTURE_TO_TARGA
  2438. sprintf( leafname, "%s%.3d.tga", "sshot", frame_number++);
  2439. #else
  2440. sprintf( leafname, "%s%.3d.bmp", "sshot", frame_number++);
  2441. #endif
  2442. strcpy(pathname, TheGlobalData->getPath_UserData().str());
  2443. strcat(pathname, leafname);
  2444. if (_access( pathname, 0 ) == -1)
  2445. done = true;
  2446. }
  2447. // Lock front buffer and copy
  2448. IDirect3DSurface8 *fb;
  2449. fb=DX8Wrapper::_Get_DX8_Front_Buffer();
  2450. D3DSURFACE_DESC desc;
  2451. fb->GetDesc(&desc);
  2452. RECT bounds;
  2453. POINT point;
  2454. GetClientRect(ApplicationHWnd,&bounds);
  2455. point.x=bounds.left; point.y=bounds.top;
  2456. ClientToScreen(ApplicationHWnd, &point);
  2457. bounds.left=point.x; bounds.top=point.y;
  2458. point.x=bounds.right; point.y=bounds.bottom;
  2459. ClientToScreen(ApplicationHWnd, &point);
  2460. bounds.right=point.x; bounds.bottom=point.y;
  2461. D3DLOCKED_RECT lrect;
  2462. DX8_ErrorCode(fb->LockRect(&lrect,&bounds,D3DLOCK_READONLY));
  2463. unsigned int x,y,index,index2,width,height;
  2464. width=bounds.right-bounds.left;
  2465. height=bounds.bottom-bounds.top;
  2466. char *image=NEW char[3*width*height];
  2467. #ifdef CAPTURE_TO_TARGA
  2468. //bytes are mixed in targa files, not rgb order.
  2469. for (y=0; y<height; y++)
  2470. {
  2471. for (x=0; x<width; x++)
  2472. {
  2473. // index for image
  2474. index=3*(x+y*width);
  2475. // index for fb
  2476. index2=y*lrect.Pitch+4*x;
  2477. image[index]=*((char *) lrect.pBits + index2+2);
  2478. image[index+1]=*((char *) lrect.pBits + index2+1);
  2479. image[index+2]=*((char *) lrect.pBits + index2+0);
  2480. }
  2481. }
  2482. fb->Release();
  2483. Targa targ;
  2484. memset(&targ.Header,0,sizeof(targ.Header));
  2485. targ.Header.Width=width;
  2486. targ.Header.Height=height;
  2487. targ.Header.PixelDepth=24;
  2488. targ.Header.ImageType=TGA_TRUECOLOR;
  2489. targ.SetImage(image);
  2490. targ.YFlip();
  2491. targ.Save(pathname,TGAF_IMAGE,false);
  2492. #else //capturing to bmp file
  2493. //bmp is same byte order
  2494. for (y=0; y<height; y++)
  2495. {
  2496. for (x=0; x<width; x++)
  2497. {
  2498. // index for image
  2499. index=3*(x+y*width);
  2500. // index for fb
  2501. index2=y*lrect.Pitch+4*x;
  2502. image[index]=*((char *) lrect.pBits + index2+0);
  2503. image[index+1]=*((char *) lrect.pBits + index2+1);
  2504. image[index+2]=*((char *) lrect.pBits + index2+2);
  2505. }
  2506. }
  2507. fb->Release();
  2508. //Flip the image
  2509. char *ptr,*ptr1;
  2510. char v,v1;
  2511. for (y = 0; y < (height >> 1); y++)
  2512. {
  2513. /* Compute address of lines to exchange. */
  2514. ptr = (image + ((width * y) * 3));
  2515. ptr1 = (image + ((width * (height - 1)) * 3));
  2516. ptr1 -= ((width * y) * 3);
  2517. /* Exchange all the pixels on this scan line. */
  2518. for (x = 0; x < (width * 3); x++)
  2519. {
  2520. v = *ptr;
  2521. v1 = *ptr1;
  2522. *ptr = v1;
  2523. *ptr1 = v;
  2524. ptr++;
  2525. ptr1++;
  2526. }
  2527. }
  2528. CreateBMPFile(pathname, image, width, height);
  2529. #endif
  2530. delete [] image;
  2531. UnicodeString ufileName;
  2532. ufileName.translate(leafname);
  2533. TheInGameUI->message(TheGameText->fetch("GUI:ScreenCapture"), ufileName.str());
  2534. }
  2535. /** Start/Stop campturing an AVI movie*/
  2536. void W3DDisplay::toggleMovieCapture(void)
  2537. {
  2538. WW3D::Toggle_Movie_Capture("Movie",30);
  2539. }
  2540. #if defined(_DEBUG) || defined(_INTERNAL)
  2541. static FILE *AssetDumpFile=NULL;
  2542. void dumpMeshAssets(MeshClass *mesh)
  2543. {
  2544. if (mesh)
  2545. {
  2546. TextureClass *texture;
  2547. //MaterialInfoClass *material = mesh->Get_Material_Info();
  2548. MeshModelClass *model=mesh->Get_Model();
  2549. for (int stage=0;stage<MeshMatDescClass::MAX_TEX_STAGES;++stage)
  2550. {
  2551. for (int pass=0;pass<model->Get_Pass_Count();++pass)
  2552. {
  2553. if (model->Has_Texture_Array(pass,stage))
  2554. {
  2555. for (int i=0;i<model->Get_Polygon_Count();++i)
  2556. {
  2557. if ((texture=model->Peek_Texture(i,pass,stage)) != NULL)
  2558. {
  2559. fprintf(AssetDumpFile,"\t%s\n",texture->Get_Texture_Name());
  2560. }
  2561. }
  2562. }
  2563. else
  2564. {
  2565. if ((texture=model->Peek_Single_Texture(pass,stage)) != NULL)
  2566. {
  2567. fprintf(AssetDumpFile,"\t%s\n",texture->Get_Texture_Name());
  2568. }
  2569. }
  2570. }
  2571. }
  2572. }
  2573. }
  2574. void dumpHLODAssets(HLodClass *hlod)
  2575. {
  2576. if (hlod)
  2577. {
  2578. //model composed of multiple meshes.
  2579. for (Int i=0; i<hlod->Get_Num_Sub_Objects(); i++)
  2580. {
  2581. RenderObjClass *subObj=hlod->Get_Sub_Object(i);
  2582. if (subObj->Class_ID() == RenderObjClass::CLASSID_HLOD)
  2583. dumpHLODAssets((HLodClass *)subObj);
  2584. else
  2585. if (subObj->Class_ID() == RenderObjClass::CLASSID_MESH)
  2586. dumpMeshAssets((MeshClass *)subObj);
  2587. }
  2588. }
  2589. }
  2590. //-------------------------------------------------------------------------------------------------
  2591. /** dump all used models/textures to a file.*/
  2592. //-------------------------------------------------------------------------------------------------
  2593. void W3DDisplay::dumpModelAssets(const char *path)
  2594. {
  2595. if (m_3DScene)
  2596. {
  2597. AssetDumpFile=fopen(path,"w");
  2598. if (AssetDumpFile)
  2599. {
  2600. fprintf(AssetDumpFile,"Models and Textures used on %s:\n\n",TheGlobalData->m_mapName.str());
  2601. SceneIterator *sceneIter = m_3DScene->Create_Iterator();
  2602. sceneIter->First();
  2603. while(!sceneIter->Is_Done())
  2604. {
  2605. RenderObjClass * robj = sceneIter->Current_Item();
  2606. if (robj->Class_ID() == RenderObjClass::CLASSID_HLOD)
  2607. { fprintf(AssetDumpFile,"%s.W3D:\n",robj->Get_Name());
  2608. dumpHLODAssets((HLodClass *)robj);
  2609. }
  2610. else
  2611. if (robj->Class_ID() == RenderObjClass::CLASSID_MESH)
  2612. { fprintf(AssetDumpFile,"%s.W3D:\n",robj->Get_Name());
  2613. dumpMeshAssets((MeshClass *)robj);
  2614. }
  2615. sceneIter->Next();
  2616. }
  2617. m_3DScene->Destroy_Iterator(sceneIter);
  2618. fclose(AssetDumpFile);
  2619. }
  2620. }
  2621. }
  2622. #endif //only include above code in debug and internal
  2623. //-------------------------------------------------------------------------------------------------
  2624. /** Preload using the W3D asset manager the model referenced by the string parameter */
  2625. //-------------------------------------------------------------------------------------------------
  2626. void W3DDisplay::preloadModelAssets( AsciiString model )
  2627. {
  2628. if( m_assetManager )
  2629. {
  2630. AsciiString nameWithExtension;
  2631. nameWithExtension.format( "%s.w3d", model.str() );
  2632. m_assetManager->Load_3D_Assets( nameWithExtension.str() );
  2633. } // end if
  2634. } // end preloadModelAssets
  2635. //-------------------------------------------------------------------------------------------------
  2636. /** Preload using the W3D asset manager the texture referenced by the string parameter */
  2637. //-------------------------------------------------------------------------------------------------
  2638. void W3DDisplay::preloadTextureAssets( AsciiString texture )
  2639. {
  2640. if( m_assetManager )
  2641. {
  2642. TextureClass *theTexture = m_assetManager->Get_Texture( texture.str() );
  2643. theTexture->Release_Ref();//release reference
  2644. } // end if
  2645. } // end preloadModelAssets
  2646. //-------------------------------------------------------------------------------------------------
  2647. //-------------------------------------------------------------------------------------------------
  2648. void W3DDisplay::doSmartAssetPurgeAndPreload(const char* usageFileName)
  2649. {
  2650. if (!m_assetManager || !usageFileName || !*usageFileName)
  2651. return;
  2652. DynamicVectorClass<StringClass> names(8000);
  2653. // use TheFileSystem here so we can bigify these files
  2654. File* f = TheFileSystem->openFile(usageFileName, File::READ | File::TEXT);
  2655. if (f)
  2656. {
  2657. for (;;)
  2658. {
  2659. AsciiString tmp;
  2660. if (f->scanString(tmp) == FALSE)
  2661. break;
  2662. // allow for comments in the file. Note that this doesn't allow for comments
  2663. // with spaces! doh. oh well. better than nothing.
  2664. if (tmp.str()[0] == ';')
  2665. continue;
  2666. names.Add(StringClass(tmp.str()));
  2667. }
  2668. f->close();
  2669. }
  2670. // just free everything if there's no exclusion list file (send in an empty list)
  2671. m_assetManager->Free_Assets_With_Exclusion_List(names);
  2672. }
  2673. //-------------------------------------------------------------------------------------------------
  2674. //-------------------------------------------------------------------------------------------------
  2675. #if defined(_DEBUG) || defined(_INTERNAL)
  2676. void W3DDisplay::dumpAssetUsage(const char* mapname)
  2677. {
  2678. if (!m_assetManager || !mapname || !*mapname)
  2679. return;
  2680. DynamicVectorClass<StringClass> names(8000);
  2681. m_assetManager->Create_Asset_List(names);
  2682. const char* leafname = strrchr(mapname, '\\');
  2683. if (leafname)
  2684. ++leafname; // point to first character after the last backslash
  2685. else
  2686. leafname = mapname; // point to the start of the filename
  2687. char buf[256];
  2688. int idx = 1;
  2689. while (true)
  2690. {
  2691. sprintf(buf, "AssetUsage_%s_%04d.txt",leafname,idx);
  2692. if (_access(buf, 0) != 0)
  2693. break; // it exists, we're good
  2694. ++idx;
  2695. }
  2696. FILE *fp = fopen(buf, "w");
  2697. if (fp)
  2698. {
  2699. for (int i=0; i<names.Count(); i++)
  2700. {
  2701. const char* n = names[i];
  2702. fprintf(fp, "%s\n", n);
  2703. }
  2704. fclose(fp);
  2705. }
  2706. }
  2707. #endif
  2708. //-------------------------------------------------------------------------------------------------
  2709. static void drawFramerateBar(void)
  2710. {
  2711. static DWORD prevTime = timeGetTime();
  2712. DWORD now = timeGetTime();
  2713. Real percTime = (1000.0f / (now - prevTime) ) / (1000.0f / TheGlobalData->m_framesPerSecondLimit);
  2714. if (percTime > 1.0f)
  2715. percTime = 1.0f;
  2716. else if (percTime < 0.0f)
  2717. percTime = 0.0f;
  2718. Int width = REAL_TO_INT(percTime * TheDisplay->getWidth());
  2719. UnsignedInt colorToUse = GameMakeColor( REAL_TO_UNSIGNEDBYTE((1.0f - percTime) * 255),
  2720. REAL_TO_UNSIGNEDBYTE(percTime * 255),
  2721. 0,
  2722. 0x7F);
  2723. TheDisplay->drawFillRect(1, 1, width, 15, colorToUse);
  2724. prevTime = now;
  2725. }