W3DRadar.cpp 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606
  1. /*
  2. ** Command & Conquer Generals Zero Hour(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: W3DRadar.cpp /////////////////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, January 2002
  25. // Desc: W3D radar implementation, this has the necessary device dependent drawing
  26. // necessary for the radar
  27. ///////////////////////////////////////////////////////////////////////////////////////////////////
  28. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  29. #include "Common/AudioEventRTS.h"
  30. #include "Common/Debug.h"
  31. #include "Common/GlobalData.h"
  32. #include "Common/Player.h"
  33. #include "Common/PlayerList.h"
  34. #include "GameLogic/TerrainLogic.h"
  35. #include "GameLogic/GameLogic.h"
  36. #include "GameLogic/Object.h"
  37. #include "GameClient/Color.h"
  38. #include "GameClient/Display.h"
  39. #include "GameClient/GameClient.h"
  40. #include "GameClient/GameWindow.h"
  41. #include "GameClient/Image.h"
  42. #include "GameClient/Line2D.h"
  43. #include "GameClient/TerrainVisual.h"
  44. #include "GameClient/Water.h"
  45. #include "W3DDevice/Common/W3DRadar.h"
  46. #include "W3DDevice/GameClient/HeightMap.h"
  47. #include "W3DDevice/GameClient/W3DShroud.h"
  48. #include "WW3D2/Texture.h"
  49. #include "WW3D2/DX8Caps.h"
  50. #ifdef _INTERNAL
  51. // for occasional debugging...
  52. //#pragma optimize("", off)
  53. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  54. #endif
  55. // PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
  56. enum { OVERLAY_REFRESH_RATE = 6 }; ///< over updates once this many frames
  57. //-------------------------------------------------------------------------------------------------
  58. /** Is the point legal, that is, inside the resolution of the radar cells */
  59. //-------------------------------------------------------------------------------------------------
  60. inline Bool legalRadarPoint( Int px, Int py )
  61. {
  62. if( px < 0 || py < 0 || px >= RADAR_CELL_WIDTH || py >= RADAR_CELL_HEIGHT )
  63. return FALSE;
  64. return TRUE;
  65. }
  66. //-------------------------------------------------------------------------------------------------
  67. // ------------------------------------------------------------------------------------------------
  68. static WW3DFormat findFormat(const WW3DFormat formats[])
  69. {
  70. for( Int i = 0; formats[ i ] != WW3D_FORMAT_UNKNOWN; i++ )
  71. {
  72. if( DX8Wrapper::Get_Current_Caps()->Support_Texture_Format( formats[ i ] ) )
  73. {
  74. return formats[ i ];
  75. } // end if
  76. } // end for i
  77. DEBUG_CRASH(("WW3DRadar: No appropriate texture format\n") );
  78. return WW3D_FORMAT_UNKNOWN;
  79. }
  80. //-------------------------------------------------------------------------------------------------
  81. /** Find the texture format we're going to use for the radar. The texture format must
  82. * be supported by the hardware. The "more preferred" formats appear at the top of
  83. * the format tables in order from most preferred to least preferred */
  84. //-------------------------------------------------------------------------------------------------
  85. void W3DRadar::initializeTextureFormats( void )
  86. {
  87. const WW3DFormat terrainFormats[] =
  88. {
  89. WW3D_FORMAT_R8G8B8,
  90. WW3D_FORMAT_X8R8G8B8,
  91. WW3D_FORMAT_R5G6B5,
  92. WW3D_FORMAT_X1R5G5B5,
  93. WW3D_FORMAT_UNKNOWN // keep this one last
  94. };
  95. const WW3DFormat overlayFormats[] =
  96. {
  97. WW3D_FORMAT_A8R8G8B8,
  98. WW3D_FORMAT_A4R4G4B4,
  99. WW3D_FORMAT_UNKNOWN // keep this one last
  100. };
  101. const WW3DFormat shroudFormats[] =
  102. {
  103. WW3D_FORMAT_A8R8G8B8,
  104. WW3D_FORMAT_A4R4G4B4,
  105. WW3D_FORMAT_UNKNOWN // keep this one last
  106. };
  107. // find a format for the terrain texture
  108. m_terrainTextureFormat = findFormat(terrainFormats);
  109. // find a format for the overlay texture
  110. m_overlayTextureFormat = findFormat(overlayFormats);
  111. // find a format for the shroud texture
  112. m_shroudTextureFormat = findFormat(shroudFormats);
  113. } // end initializeTextureFormats
  114. //-------------------------------------------------------------------------------------------------
  115. /** Delete resources used specifically in this W3D radar implemetation */
  116. //-------------------------------------------------------------------------------------------------
  117. void W3DRadar::deleteResources( void )
  118. {
  119. //
  120. // delete terrain resources used
  121. //
  122. if( m_terrainTexture )
  123. m_terrainTexture->Release_Ref();
  124. m_terrainTexture = NULL;
  125. if( m_terrainImage )
  126. m_terrainImage->deleteInstance();
  127. m_terrainImage = NULL;
  128. //
  129. // delete overlay resources used
  130. //
  131. if( m_overlayTexture )
  132. m_overlayTexture->Release_Ref();
  133. m_overlayTexture = NULL;
  134. if( m_overlayImage )
  135. m_overlayImage->deleteInstance();
  136. m_overlayImage = NULL;
  137. //
  138. // delete shroud resources used
  139. //
  140. if( m_shroudTexture )
  141. m_shroudTexture->Release_Ref();
  142. m_shroudTexture = NULL;
  143. if( m_shroudImage )
  144. m_shroudImage->deleteInstance();
  145. m_shroudImage = NULL;
  146. } // end deleteResources
  147. //-------------------------------------------------------------------------------------------------
  148. /** Reconstruct the view box given the current camera settings */
  149. //-------------------------------------------------------------------------------------------------
  150. void W3DRadar::reconstructViewBox( void )
  151. {
  152. Coord3D world[ 4 ];
  153. ICoord2D radar[ 4 ];
  154. Int i;
  155. // get the 4 points of the view corners in the 3D world at the average Z height in the map
  156. TheTacticalView->getScreenCornerWorldPointsAtZ( &world[ 0 ],
  157. &world[ 1 ],
  158. &world[ 2 ],
  159. &world[ 3 ],
  160. getTerrainAverageZ() );
  161. // convert each of the 4 points in the world to radar cell positions
  162. for( i = 0; i < 4; i++ )
  163. {
  164. // first convert to radar cells
  165. radar[ i ].x = world[ i ].x / (m_mapExtent.width() / RADAR_CELL_WIDTH);
  166. radar[ i ].y = world[ i ].y / (m_mapExtent.height() / RADAR_CELL_HEIGHT);
  167. //
  168. // store these points in the view box array which contains a first position
  169. // of (0,0) and then offsets for each additional entry point
  170. //
  171. if( i == 0 )
  172. {
  173. m_viewBox[ i ].x = 0;
  174. m_viewBox[ i ].y = 0;
  175. } // end if
  176. else
  177. {
  178. m_viewBox[ i ].x = radar[ i ].x - radar[ i - 1 ].x;
  179. m_viewBox[ i ].y = radar[ i ].y - radar[ i - 1 ].y;
  180. } // end else
  181. } // end for i
  182. //
  183. // save the camera settings for this view box, we will need to make it again only
  184. // if some of these change
  185. //
  186. m_viewAngle = TheTacticalView->getAngle();
  187. Coord3D pos;
  188. TheTacticalView->getPosition( &pos );
  189. m_viewZoom = TheTacticalView->getZoom();
  190. m_reconstructViewBox = FALSE;
  191. } // end reconstructViewBox
  192. //-------------------------------------------------------------------------------------------------
  193. /** Convert radar position to actual pixel coord */
  194. //-------------------------------------------------------------------------------------------------
  195. void W3DRadar::radarToPixel( const ICoord2D *radar, ICoord2D *pixel,
  196. Int radarUpperLeftX, Int radarUpperLeftY,
  197. Int radarWidth, Int radarHeight )
  198. {
  199. // sanity
  200. if( radar == NULL || pixel == NULL )
  201. return;
  202. pixel->x = (radar->x * radarWidth / RADAR_CELL_WIDTH) + radarUpperLeftX;
  203. // note the "inverted" y here to orient the way our world looks with +x=right and -y=down
  204. pixel->y = ((RADAR_CELL_HEIGHT - 1 - radar->y) * radarHeight / RADAR_CELL_HEIGHT) + radarUpperLeftY;
  205. } // end radarToPixel
  206. //-------------------------------------------------------------------------------------------------
  207. /** Draw a hero icon at a position, given radar box upper left location and dimensions. */
  208. //-------------------------------------------------------------------------------------------------
  209. void W3DRadar::drawHeroIcon( Int pixelX, Int pixelY, Int width, Int height, const Coord3D *pos )
  210. {
  211. // get the hero icon image
  212. static const Image *image = (Image *)TheMappedImageCollection->findImageByName("HeroReticle");
  213. if (image != NULL)
  214. {
  215. // convert world to radar coords
  216. ICoord2D ulRadar;
  217. ulRadar.x = pos->x / (m_mapExtent.width() / RADAR_CELL_WIDTH);
  218. ulRadar.y = pos->y / (m_mapExtent.height() / RADAR_CELL_HEIGHT);
  219. // convert radar to screen coords
  220. ICoord2D offsetScreen;
  221. radarToPixel( &ulRadar, &offsetScreen, pixelX, pixelY, width, height );
  222. // shift from an upper left to a center focus for the icon
  223. int iconWidth = image->getImageWidth();
  224. int iconHeight = image->getImageHeight();
  225. offsetScreen.x -= (iconWidth / 2) - 1;
  226. offsetScreen.y -= iconHeight / 2;
  227. // draw the icon
  228. TheDisplay->drawImage( image, offsetScreen.x , offsetScreen.y, offsetScreen.x + iconWidth, offsetScreen.y + iconHeight );
  229. }
  230. }
  231. //-------------------------------------------------------------------------------------------------
  232. /** Draw a "box" into the texture passed in that represents the viewable area for
  233. * the tactical display into the game world */
  234. //-------------------------------------------------------------------------------------------------
  235. void W3DRadar::drawViewBox( Int pixelX, Int pixelY, Int width, Int height )
  236. {
  237. ICoord2D ulScreen;
  238. ICoord2D ulRadar;
  239. Coord3D ulWorld;
  240. ICoord2D ulStart = { 0, 0 };
  241. ICoord2D start, end;
  242. ICoord2D clipStart, clipEnd;
  243. Real lineWidth = 1.0f;
  244. Color topColor = GameMakeColor( 225, 225, 0, 255 );
  245. Color bottomColor = GameMakeColor( 158, 158, 0, 255 );
  246. //
  247. // setup the clipping region ... note that this clipping region is not over just the
  248. // radar image area ... it's in the WHOLE window available for the radar
  249. //
  250. IRegion2D clipRegion;
  251. ICoord2D radarWindowSize, radarWindowScreenPos;
  252. m_radarWindow->winGetSize( &radarWindowSize.x, &radarWindowSize.y );
  253. m_radarWindow->winGetScreenPosition( &radarWindowScreenPos.x, &radarWindowScreenPos.y );
  254. clipRegion.lo.x = radarWindowScreenPos.x;
  255. clipRegion.lo.y = radarWindowScreenPos.y;
  256. clipRegion.hi.x = radarWindowScreenPos.x + radarWindowSize.x;
  257. clipRegion.hi.y = radarWindowScreenPos.y + radarWindowSize.y;
  258. // convert top left of screen into world position
  259. TheTacticalView->getOrigin( &ulScreen.x, &ulScreen.y );
  260. TheTacticalView->screenToWorldAtZ( &ulScreen, &ulWorld, getTerrainAverageZ() );
  261. // convert world to radar coords
  262. ulRadar.x = ulWorld.x / (m_mapExtent.width() / RADAR_CELL_WIDTH);
  263. ulRadar.y = ulWorld.y / (m_mapExtent.height() / RADAR_CELL_HEIGHT);
  264. //
  265. // convert radar point to actual pixel coords on the screen, shifted
  266. // into position on the radar for where the radar is drawn and the size of the
  267. // area that the radar is drawn in
  268. //
  269. radarToPixel( &ulRadar, &ulStart, pixelX, pixelY, width, height );
  270. //
  271. // using our view box offset array, convert each of those radar cell offset points
  272. // into screen pixels and draw the box. The view box array is setup with the
  273. // first index containing (0,0) (the point we just converted in theory), with cell
  274. // offsets to each of the other corners in the following order
  275. // (upper left, upper right, lower right, lower left)
  276. //
  277. ICoord2D radar;
  278. // top line
  279. start = ulStart;
  280. radar.x = ulRadar.x + m_viewBox[ 1 ].x;
  281. radar.y = ulRadar.y + m_viewBox[ 1 ].y;
  282. radarToPixel( &radar, &end, pixelX, pixelY, width, height );
  283. if( ClipLine2D( &start, &end, &clipStart, &clipEnd, &clipRegion ) )
  284. TheDisplay->drawLine( clipStart.x, clipStart.y, clipEnd.x, clipEnd.y,
  285. lineWidth, topColor );
  286. // right line
  287. start = end;
  288. radar.x += m_viewBox[ 2 ].x;
  289. radar.y += m_viewBox[ 2 ].y;
  290. radarToPixel( &radar, &end, pixelX, pixelY, width, height );
  291. if( ClipLine2D( &start, &end, &clipStart, &clipEnd, &clipRegion ) )
  292. TheDisplay->drawLine( clipStart.x, clipStart.y, clipEnd.x, clipEnd.y,
  293. lineWidth, topColor, bottomColor );
  294. // bottom line
  295. start = end;
  296. radar.x += m_viewBox[ 3 ].x;
  297. radar.y += m_viewBox[ 3 ].y;
  298. radarToPixel( &radar, &end, pixelX, pixelY, width, height );
  299. if( ClipLine2D( &start, &end, &clipStart, &clipEnd, &clipRegion ) )
  300. TheDisplay->drawLine( clipStart.x, clipStart.y, clipEnd.x, clipEnd.y,
  301. lineWidth, bottomColor );
  302. // left line
  303. start = end;
  304. end = ulStart;
  305. if( ClipLine2D( &start, &end, &clipStart, &clipEnd, &clipRegion ) )
  306. TheDisplay->drawLine( clipStart.x, clipStart.y, clipEnd.x, clipEnd.y,
  307. lineWidth, bottomColor, topColor );
  308. } // end drawViewBox
  309. // ------------------------------------------------------------------------------------------------
  310. // ------------------------------------------------------------------------------------------------
  311. void W3DRadar::drawSingleBeaconEvent( Int pixelX, Int pixelY, Int width, Int height, Int index )
  312. {
  313. RadarEvent *event = &(m_event[index]);
  314. ICoord2D tri[ 3 ];
  315. ICoord2D start, end;
  316. Real angle, addAngle;
  317. Color startColor, endColor;
  318. Real lineWidth = 1.0f;
  319. UnsignedInt currentFrame = TheGameLogic->getFrame();
  320. UnsignedInt frameDiff; // frames the event has been alive for
  321. Real maxEventSize = width / 10.0f; // max size of the event marker
  322. Int minEventSize = 6; // min size of the event marker
  323. Int eventSize; // current size of a marker to draw
  324. const Real TIME_FROM_FULL_SIZE_TO_SMALL_SIZE = LOGICFRAMES_PER_SECOND * 1.5;
  325. Real totalAnglesToSpin = 2.0f * PI; ///< spin around this many angles going from big to small
  326. UnsignedByte r, g, b, a;
  327. // setup screen clipping region
  328. IRegion2D clipRegion;
  329. clipRegion.lo.x = pixelX;
  330. clipRegion.lo.y = pixelY;
  331. clipRegion.hi.x = pixelX + width;
  332. clipRegion.hi.y = pixelY + height;
  333. // get the difference in frame from the current frame to the frame we were created on
  334. frameDiff = currentFrame - event->createFrame;
  335. // compute the size of the event marker, it is largest when it starts and smallest at the end
  336. eventSize = REAL_TO_INT( maxEventSize * ( 1.0f - frameDiff / TIME_FROM_FULL_SIZE_TO_SMALL_SIZE) );;
  337. // we never let the event size get too small
  338. if( eventSize < minEventSize )
  339. eventSize = minEventSize;
  340. // compute how much "angle" we will add to each point to make it rotate as it's getting small
  341. addAngle = -totalAnglesToSpin * (frameDiff / TIME_FROM_FULL_SIZE_TO_SMALL_SIZE);
  342. // create a triangle around the event
  343. angle = 0.0f - addAngle;
  344. tri[ 0 ].x = REAL_TO_INT( (DOUBLE_TO_REAL( Cos( angle ) ) * eventSize) + event->radarLoc.x );
  345. tri[ 0 ].y = REAL_TO_INT( (DOUBLE_TO_REAL( Sin( angle ) ) * eventSize) + event->radarLoc.y );
  346. angle = 2.0f * PI / 3.0f - addAngle;
  347. tri[ 1 ].x = REAL_TO_INT( (DOUBLE_TO_REAL( Cos( angle ) ) * eventSize) + event->radarLoc.x );
  348. tri[ 1 ].y = REAL_TO_INT( (DOUBLE_TO_REAL( Sin( angle ) ) * eventSize) + event->radarLoc.y );
  349. angle = -2.0f * PI / 3.0f - addAngle;
  350. tri[ 2 ].x = REAL_TO_INT( (DOUBLE_TO_REAL( Cos( angle ) ) * eventSize) + event->radarLoc.x );
  351. tri[ 2 ].y = REAL_TO_INT( (DOUBLE_TO_REAL( Sin( angle ) ) * eventSize) + event->radarLoc.y );
  352. // translate radar coords to screen coords
  353. radarToPixel( &tri[ 0 ], &tri[ 0 ], pixelX, pixelY, width, height );
  354. radarToPixel( &tri[ 1 ], &tri[ 1 ], pixelX, pixelY, width, height );
  355. radarToPixel( &tri[ 2 ], &tri[ 2 ], pixelX, pixelY, width, height );
  356. //
  357. // make the colors we're going to use, when we're at our smallest size we will start to
  358. // fade the alpha away to transparent so that at our lifetime frame we are completely gone
  359. //
  360. // color 1 ------------------
  361. r = event->color1.red;
  362. g = event->color1.green;
  363. b = event->color1.blue;
  364. a = event->color1.alpha;
  365. if( currentFrame > event->fadeFrame )
  366. {
  367. a = REAL_TO_UNSIGNEDBYTE( (Real)a * (1.0f - (Real)(currentFrame - event->fadeFrame) /
  368. (Real)(event->dieFrame - event->fadeFrame) ) );
  369. } // end if
  370. startColor = GameMakeColor( r, g, b, a );
  371. // color 2 ------------------
  372. r = event->color2.red;
  373. g = event->color2.green;
  374. b = event->color2.blue;
  375. a = event->color2.alpha;
  376. if( currentFrame > event->fadeFrame )
  377. {
  378. a = REAL_TO_UNSIGNEDBYTE( (Real)a * (1.0f - (Real)(currentFrame - event->fadeFrame) /
  379. (Real)(event->dieFrame - event->fadeFrame) ) );
  380. } // end if
  381. endColor = GameMakeColor( r, g, b, a );
  382. // draw the lines
  383. if( ClipLine2D( &tri[ 0 ], &tri[ 1 ], &start, &end, &clipRegion ) )
  384. TheDisplay->drawLine( start.x, start.y, end.x, end.y, lineWidth, startColor, endColor );
  385. if( ClipLine2D( &tri[ 1 ], &tri[ 2 ], &start, &end, &clipRegion ) )
  386. TheDisplay->drawLine( start.x, start.y, end.x, end.y, lineWidth, startColor, endColor );
  387. if( ClipLine2D( &tri[ 2 ], &tri[ 0 ], &start, &end, &clipRegion ) )
  388. TheDisplay->drawLine( start.x, start.y, end.x, end.y, lineWidth, startColor, endColor );
  389. }
  390. // ------------------------------------------------------------------------------------------------
  391. // ------------------------------------------------------------------------------------------------
  392. void W3DRadar::drawSingleGenericEvent( Int pixelX, Int pixelY, Int width, Int height, Int index )
  393. {
  394. RadarEvent *event = &(m_event[index]);
  395. ICoord2D tri[ 3 ];
  396. ICoord2D start, end;
  397. Real angle, addAngle;
  398. Color startColor, endColor;
  399. Real lineWidth = 1.0f;
  400. UnsignedInt currentFrame = TheGameLogic->getFrame();
  401. UnsignedInt frameDiff; // frames the event has been alive for
  402. Real maxEventSize = width / 2.0f; // max size of the event marker
  403. Int minEventSize = 6; // min size of the event marker
  404. Int eventSize; // current size of a marker to draw
  405. const Real TIME_FROM_FULL_SIZE_TO_SMALL_SIZE = LOGICFRAMES_PER_SECOND * 1.5;
  406. Real totalAnglesToSpin = 2.0f * PI; ///< spin around this many angles going from big to small
  407. UnsignedByte r, g, b, a;
  408. // setup screen clipping region
  409. IRegion2D clipRegion;
  410. clipRegion.lo.x = pixelX;
  411. clipRegion.lo.y = pixelY;
  412. clipRegion.hi.x = pixelX + width;
  413. clipRegion.hi.y = pixelY + height;
  414. // get the difference in frame from the current frame to the frame we were created on
  415. frameDiff = currentFrame - event->createFrame;
  416. // compute the size of the event marker, it is largest when it starts and smallest at the end
  417. eventSize = REAL_TO_INT( maxEventSize * ( 1.0f - frameDiff / TIME_FROM_FULL_SIZE_TO_SMALL_SIZE) );;
  418. // we never let the event size get too small
  419. if( eventSize < minEventSize )
  420. eventSize = minEventSize;
  421. // compute how much "angle" we will add to each point to make it rotate as it's getting small
  422. addAngle = totalAnglesToSpin * (frameDiff / TIME_FROM_FULL_SIZE_TO_SMALL_SIZE);
  423. // create a triangle around the event
  424. angle = 0.0f - addAngle;
  425. tri[ 0 ].x = REAL_TO_INT( (DOUBLE_TO_REAL( Cos( angle ) ) * eventSize) + event->radarLoc.x );
  426. tri[ 0 ].y = REAL_TO_INT( (DOUBLE_TO_REAL( Sin( angle ) ) * eventSize) + event->radarLoc.y );
  427. angle = 2.0f * PI / 3.0f - addAngle;
  428. tri[ 1 ].x = REAL_TO_INT( (DOUBLE_TO_REAL( Cos( angle ) ) * eventSize) + event->radarLoc.x );
  429. tri[ 1 ].y = REAL_TO_INT( (DOUBLE_TO_REAL( Sin( angle ) ) * eventSize) + event->radarLoc.y );
  430. angle = -2.0f * PI / 3.0f - addAngle;
  431. tri[ 2 ].x = REAL_TO_INT( (DOUBLE_TO_REAL( Cos( angle ) ) * eventSize) + event->radarLoc.x );
  432. tri[ 2 ].y = REAL_TO_INT( (DOUBLE_TO_REAL( Sin( angle ) ) * eventSize) + event->radarLoc.y );
  433. // translate radar coords to screen coords
  434. radarToPixel( &tri[ 0 ], &tri[ 0 ], pixelX, pixelY, width, height );
  435. radarToPixel( &tri[ 1 ], &tri[ 1 ], pixelX, pixelY, width, height );
  436. radarToPixel( &tri[ 2 ], &tri[ 2 ], pixelX, pixelY, width, height );
  437. //
  438. // make the colors we're going to use, when we're at our smallest size we will start to
  439. // fade the alpha away to transparent so that at our lifetime frame we are completely gone
  440. //
  441. // color 1 ------------------
  442. r = event->color1.red;
  443. g = event->color1.green;
  444. b = event->color1.blue;
  445. a = event->color1.alpha;
  446. if( currentFrame > event->fadeFrame )
  447. {
  448. a = REAL_TO_UNSIGNEDBYTE( (Real)a * (1.0f - (Real)(currentFrame - event->fadeFrame) /
  449. (Real)(event->dieFrame - event->fadeFrame) ) );
  450. } // end if
  451. startColor = GameMakeColor( r, g, b, a );
  452. // color 2 ------------------
  453. r = event->color2.red;
  454. g = event->color2.green;
  455. b = event->color2.blue;
  456. a = event->color2.alpha;
  457. if( currentFrame > event->fadeFrame )
  458. {
  459. a = REAL_TO_UNSIGNEDBYTE( (Real)a * (1.0f - (Real)(currentFrame - event->fadeFrame) /
  460. (Real)(event->dieFrame - event->fadeFrame) ) );
  461. } // end if
  462. endColor = GameMakeColor( r, g, b, a );
  463. // draw the lines
  464. if( ClipLine2D( &tri[ 0 ], &tri[ 1 ], &start, &end, &clipRegion ) )
  465. TheDisplay->drawLine( start.x, start.y, end.x, end.y, lineWidth, startColor, endColor );
  466. if( ClipLine2D( &tri[ 1 ], &tri[ 2 ], &start, &end, &clipRegion ) )
  467. TheDisplay->drawLine( start.x, start.y, end.x, end.y, lineWidth, startColor, endColor );
  468. if( ClipLine2D( &tri[ 2 ], &tri[ 0 ], &start, &end, &clipRegion ) )
  469. TheDisplay->drawLine( start.x, start.y, end.x, end.y, lineWidth, startColor, endColor );
  470. }
  471. //-------------------------------------------------------------------------------------------------
  472. /** Draw all the radar events */
  473. //-------------------------------------------------------------------------------------------------
  474. void W3DRadar::drawEvents( Int pixelX, Int pixelY, Int width, Int height )
  475. {
  476. Int i;
  477. for( i = 0; i < MAX_RADAR_EVENTS; i++ )
  478. {
  479. // only 'active' events actually have something to draw
  480. if( m_event[ i ].active == TRUE && m_event[ i ].type != RADAR_EVENT_FAKE )
  481. {
  482. // if we haven't played the sound for this event, do it now that we can see it
  483. if( m_event[ i ].soundPlayed == FALSE && m_event[i].type != RADAR_EVENT_BEACON_PULSE )
  484. {
  485. static AudioEventRTS eventSound("RadarEvent");
  486. TheAudio->addAudioEvent( &eventSound );
  487. } // end if
  488. m_event[ i ].soundPlayed = TRUE;
  489. if ( m_event[ i ].type == RADAR_EVENT_BEACON_PULSE )
  490. drawSingleBeaconEvent( pixelX, pixelY, width, height, i );
  491. else
  492. drawSingleGenericEvent( pixelX, pixelY, width, height, i );
  493. } // end if
  494. } // end for i
  495. } // end drawEvents
  496. //-------------------------------------------------------------------------------------------------
  497. /** Draw all the radar icons */
  498. //-------------------------------------------------------------------------------------------------
  499. void W3DRadar::drawIcons( Int pixelX, Int pixelY, Int width, Int height )
  500. {
  501. // draw the hero icons
  502. std::list<const Coord3D *>::const_iterator iter = m_cachedHeroPosList.begin();
  503. while (iter != m_cachedHeroPosList.end())
  504. {
  505. drawHeroIcon( pixelX, pixelY, width, height, (*iter) );
  506. ++iter;
  507. }
  508. }
  509. //-------------------------------------------------------------------------------------------------
  510. /** Render an object list into the texture passed in */
  511. //-------------------------------------------------------------------------------------------------
  512. void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture, Bool calcHero )
  513. {
  514. // sanity
  515. if( listHead == NULL || texture == NULL )
  516. return;
  517. // get surface for texture to render into
  518. SurfaceClass *surface = texture->Get_Surface_Level();
  519. // loop through all objects and draw
  520. ICoord2D radarPoint;
  521. Player *player = ThePlayerList->getLocalPlayer();
  522. Int playerIndex=0;
  523. if (player)
  524. playerIndex=player->getPlayerIndex();
  525. if( calcHero )
  526. {
  527. // clear all entries from the cached hero object list
  528. m_cachedHeroPosList.clear();
  529. }
  530. for( const RadarObject *rObj = listHead; rObj; rObj = rObj->friend_getNext() )
  531. {
  532. if (rObj->isTemporarilyHidden())
  533. continue;
  534. // get object
  535. const Object *obj = rObj->friend_getObject();
  536. // cache hero object positions for drawing in icon layer
  537. if( calcHero && obj->isHero() )
  538. {
  539. m_cachedHeroPosList.push_back(obj->getPosition());
  540. }
  541. Bool skip = FALSE;
  542. // check for shrouded status
  543. if (obj->getShroudedStatus(playerIndex) > OBJECTSHROUD_PARTIAL_CLEAR)
  544. skip = TRUE; //object is fogged or shrouded, don't render it.
  545. //
  546. // objects with a local only unit priority will only appear on the radar if they
  547. // are controlled by the local player, or if the local player is an observer (cause
  548. // they are godlike and can see everything)
  549. //
  550. if( obj->getRadarPriority() == RADAR_PRIORITY_LOCAL_UNIT_ONLY &&
  551. obj->getControllingPlayer() != ThePlayerList->getLocalPlayer() &&
  552. ThePlayerList->getLocalPlayer()->isPlayerActive() )
  553. skip = TRUE;
  554. // get object position
  555. const Coord3D *pos = obj->getPosition();
  556. // compute object position as a radar blip
  557. radarPoint.x = pos->x / (m_mapExtent.width() / RADAR_CELL_WIDTH);
  558. radarPoint.y = pos->y / (m_mapExtent.height() / RADAR_CELL_HEIGHT);
  559. if ( skip )
  560. continue;
  561. // get the color we're going to draw in
  562. Color c = rObj->getColor();
  563. // adjust the alpha for stealth units so they "fade/blink" on the radar for the controller
  564. // if( obj->getRadarPriority() == RADAR_PRIORITY_LOCAL_UNIT_ONLY )
  565. // ML-- What the heck is this? local-only and neutral-observier-viewed units are stealthy?? Since when?
  566. // Now it twinkles for any stealthed object, whether locally controlled or neutral-observier-viewed
  567. if( obj->testStatus( OBJECT_STATUS_STEALTHED ) )
  568. {
  569. if ( ThePlayerList->getLocalPlayer()->getRelationship(obj->getTeam()) == ENEMIES )
  570. if( !obj->testStatus( OBJECT_STATUS_DETECTED ) && !obj->testStatus( OBJECT_STATUS_DISGUISED ) )
  571. skip = TRUE;
  572. UnsignedByte r, g, b, a;
  573. GameGetColorComponents( c, &r, &g, &b, &a );
  574. const UnsignedInt framesForTransition = LOGICFRAMES_PER_SECOND;
  575. const UnsignedByte minAlpha = 32;
  576. if (skip)
  577. continue;
  578. Real alphaScale = INT_TO_REAL(TheGameLogic->getFrame() % framesForTransition) / (framesForTransition / 2.0f);
  579. if( alphaScale > 0.0f )
  580. a = REAL_TO_UNSIGNEDBYTE( ((alphaScale - 1.0f) * (255.0f - minAlpha)) + minAlpha );
  581. else
  582. a = REAL_TO_UNSIGNEDBYTE( (alphaScale * (255.0f - minAlpha)) + minAlpha );
  583. c = GameMakeColor( r, g, b, a );
  584. } // end if
  585. // draw the blip, but make sure the points are legal
  586. if( legalRadarPoint( radarPoint.x, radarPoint.y ) )
  587. surface->DrawPixel( radarPoint.x, radarPoint.y, c );
  588. radarPoint.y++;
  589. if( legalRadarPoint( radarPoint.x, radarPoint.y ) )
  590. surface->DrawPixel( radarPoint.x, radarPoint.y, c );
  591. radarPoint.x++;
  592. if( legalRadarPoint( radarPoint.x, radarPoint.y ) )
  593. surface->DrawPixel( radarPoint.x, radarPoint.y, c );
  594. radarPoint.y--;
  595. if( legalRadarPoint( radarPoint.x, radarPoint.y ) )
  596. surface->DrawPixel( radarPoint.x, radarPoint.y, c );
  597. } // end for
  598. REF_PTR_RELEASE(surface);
  599. } // end renderObjectList
  600. //-------------------------------------------------------------------------------------------------
  601. /** Shade the color passed in using the height parameter to lighten and darken it. Colors
  602. * will be interpolated using the value "height" across the range from loZ to hiZ. The
  603. * midZ is the "middle" point, height values above it will be lightened, while
  604. * lower ones are darkened. */
  605. //-------------------------------------------------------------------------------------------------
  606. void W3DRadar::interpolateColorForHeight( RGBColor *color,
  607. Real height,
  608. Real hiZ,
  609. Real midZ,
  610. Real loZ )
  611. {
  612. const Real howBright = 0.95f; // bigger is brighter (0.0 to 1.0)
  613. const Real howDark = 0.60f; // bigger is darker (0.0 to 1.0)
  614. // sanity on map height (flat maps bomb)
  615. if (hiZ == midZ)
  616. hiZ = midZ+0.1f;
  617. if (midZ == loZ)
  618. loZ = midZ-0.1f;
  619. if (hiZ == loZ)
  620. hiZ = loZ+0.2f;
  621. Real t;
  622. RGBColor colorTarget;
  623. // if "over" the middle height, interpolate lighter
  624. if( height >= midZ )
  625. {
  626. // how far are we from the middleZ towards the hi Z
  627. t = (height - midZ) / (hiZ - midZ);
  628. // compute what our "lightest" color possible we want to use is
  629. colorTarget.red = color->red + (1.0f - color->red) * howBright;
  630. colorTarget.green = color->green + (1.0f - color->green) * howBright;
  631. colorTarget.blue = color->blue + (1.0f - color->blue) * howBright;
  632. } // end if
  633. else // interpolate darker
  634. {
  635. // how far are we from the middleZ towards the low Z
  636. t = (midZ - height) / (midZ - loZ);
  637. // compute what the "darkest" color possible we want to use is
  638. colorTarget.red = color->red + (0.0f - color->red) * howDark;
  639. colorTarget.green = color->green + (0.0f - color->green) * howDark;
  640. colorTarget.blue = color->blue + (0.0f - color->blue) * howDark;
  641. } // end else
  642. // interpolate toward the target color
  643. color->red = color->red + (colorTarget.red - color->red) * t;
  644. color->green = color->green + (colorTarget.green - color->green) * t;
  645. color->blue = color->blue + (colorTarget.blue - color->blue) * t;
  646. // keep the color real
  647. if( color->red < 0.0f )
  648. color->red = 0.0f;
  649. if( color->red > 1.0f )
  650. color->red = 1.0f;
  651. if( color->green < 0.0f )
  652. color->green = 0.0f;
  653. if( color->green > 1.0f )
  654. color->green = 1.0f;
  655. if( color->blue < 0.0f )
  656. color->blue = 0.0f;
  657. if( color->blue > 1.0f )
  658. color->blue = 1.0f;
  659. } // end interpolateColorForHeight
  660. ///////////////////////////////////////////////////////////////////////////////////////////////////
  661. // PUBLIC METHODS /////////////////////////////////////////////////////////////////////////////////
  662. ///////////////////////////////////////////////////////////////////////////////////////////////////
  663. //-------------------------------------------------------------------------------------------------
  664. //-------------------------------------------------------------------------------------------------
  665. W3DRadar::W3DRadar( void )
  666. {
  667. m_terrainTextureFormat = WW3D_FORMAT_UNKNOWN;
  668. m_terrainImage = NULL;
  669. m_terrainTexture = NULL;
  670. m_overlayTextureFormat = WW3D_FORMAT_UNKNOWN;
  671. m_overlayImage = NULL;
  672. m_overlayTexture = NULL;
  673. m_shroudTextureFormat = WW3D_FORMAT_UNKNOWN;
  674. m_shroudImage = NULL;
  675. m_shroudTexture = NULL;
  676. m_textureWidth = RADAR_CELL_WIDTH;
  677. m_textureHeight = RADAR_CELL_HEIGHT;
  678. m_reconstructViewBox = TRUE;
  679. m_viewAngle = 0.0f;
  680. m_viewZoom = 0.0f;
  681. for( Int i = 0; i < 4; i++ )
  682. {
  683. m_viewBox[ i ].x = 0;
  684. m_viewBox[ i ].y = 0;
  685. } // end for
  686. } // end W3DRadar
  687. //-------------------------------------------------------------------------------------------------
  688. //-------------------------------------------------------------------------------------------------
  689. W3DRadar::~W3DRadar( void )
  690. {
  691. // delete resources used for the W3D radar
  692. deleteResources();
  693. } // end ~W3DRadar
  694. //-------------------------------------------------------------------------------------------------
  695. /** Radar initialization */
  696. //-------------------------------------------------------------------------------------------------
  697. void W3DRadar::init( void )
  698. {
  699. ICoord2D size;
  700. Region2D uv;
  701. // extending functionality
  702. Radar::init();
  703. // gather specific texture format information
  704. initializeTextureFormats();
  705. // allocate our terrain texture
  706. // poolify
  707. m_terrainTexture = MSGNEW("TextureClass") TextureClass( m_textureWidth, m_textureHeight,
  708. m_terrainTextureFormat, MIP_LEVELS_1 );
  709. DEBUG_ASSERTCRASH( m_terrainTexture, ("W3DRadar: Unable to allocate terrain texture\n") );
  710. // allocate our overlay texture
  711. m_overlayTexture = MSGNEW("TextureClass") TextureClass( m_textureWidth, m_textureHeight,
  712. m_overlayTextureFormat, MIP_LEVELS_1 );
  713. DEBUG_ASSERTCRASH( m_overlayTexture, ("W3DRadar: Unable to allocate overlay texture\n") );
  714. // set filter type for the overlay texture, try it and see if you like it, I don't ;)
  715. // m_overlayTexture->Set_Min_Filter( TextureClass::FILTER_TYPE_NONE );
  716. // m_overlayTexture->Set_Mag_Filter( TextureClass::FILTER_TYPE_NONE );
  717. // allocate our shroud texture
  718. m_shroudTexture = MSGNEW("TextureClass") TextureClass( m_textureWidth, m_textureHeight,
  719. m_shroudTextureFormat, MIP_LEVELS_1 );
  720. DEBUG_ASSERTCRASH( m_shroudTexture, ("W3DRadar: Unable to allocate shroud texture\n") );
  721. m_shroudTexture->Get_Filter().Set_Min_Filter( TextureFilterClass::FILTER_TYPE_DEFAULT );
  722. m_shroudTexture->Get_Filter().Set_Mag_Filter( TextureFilterClass::FILTER_TYPE_DEFAULT );
  723. //
  724. // create images used for rendering and set them up with the textures
  725. //
  726. //
  727. // the terrain image, note the UV coords change it from (0,0) in the upper left
  728. // to (0,0) in the lower left cause that's how we are initially oriented in the
  729. // world (positive X to the right and positive Y up)
  730. //
  731. m_terrainImage = newInstance(Image);
  732. uv.lo.x = 0.0f;
  733. uv.lo.y = 1.0f;
  734. uv.hi.x = 1.0f;
  735. uv.hi.y = 0.0f;
  736. m_terrainImage->setStatus( IMAGE_STATUS_RAW_TEXTURE );
  737. m_terrainImage->setRawTextureData( m_terrainTexture );
  738. m_terrainImage->setUV( &uv );
  739. m_terrainImage->setTextureWidth( m_textureWidth );
  740. m_terrainImage->setTextureHeight( m_textureHeight );
  741. size.x = m_textureWidth;
  742. size.y = m_textureHeight;
  743. m_terrainImage->setImageSize( &size );
  744. // the overlay image
  745. m_overlayImage = newInstance(Image);
  746. uv.lo.x = 0.0f;
  747. uv.lo.y = 1.0f;
  748. uv.hi.x = 1.0f;
  749. uv.hi.y = 0.0f;
  750. m_overlayImage->setStatus( IMAGE_STATUS_RAW_TEXTURE );
  751. m_overlayImage->setRawTextureData( m_overlayTexture );
  752. m_overlayImage->setUV( &uv );
  753. m_overlayImage->setTextureWidth( m_textureWidth );
  754. m_overlayImage->setTextureHeight( m_textureHeight );
  755. size.x = m_textureWidth;
  756. size.y = m_textureHeight;
  757. m_overlayImage->setImageSize( &size );
  758. // the shroud image
  759. m_shroudImage = newInstance(Image);
  760. uv.lo.x = 0.0f;
  761. uv.lo.y = 1.0f;
  762. uv.hi.x = 1.0f;
  763. uv.hi.y = 0.0f;
  764. m_shroudImage->setStatus( IMAGE_STATUS_RAW_TEXTURE );
  765. m_shroudImage->setRawTextureData( m_shroudTexture );
  766. m_shroudImage->setUV( &uv );
  767. m_shroudImage->setTextureWidth( m_textureWidth );
  768. m_shroudImage->setTextureHeight( m_textureHeight );
  769. size.x = m_textureWidth;
  770. size.y = m_textureHeight;
  771. m_shroudImage->setImageSize( &size );
  772. } // end init
  773. //-------------------------------------------------------------------------------------------------
  774. /** Reset the radar to the initial empty state ready for new data */
  775. //-------------------------------------------------------------------------------------------------
  776. void W3DRadar::reset( void )
  777. {
  778. // extending functionality, call base class
  779. Radar::reset();
  780. // clear our texture data, but do not delete the resources
  781. SurfaceClass *surface;
  782. surface = m_terrainTexture->Get_Surface_Level();
  783. if( surface )
  784. {
  785. surface->Clear();
  786. REF_PTR_RELEASE(surface);
  787. }
  788. surface = m_overlayTexture->Get_Surface_Level();
  789. if( surface )
  790. {
  791. surface->Clear();
  792. REF_PTR_RELEASE(surface);
  793. }
  794. // don't call Clear(); that wips to transparent. do this instead.
  795. //gs Dude, it's called CLEARshroud. It needs to clear the shroud.
  796. clearShroud();
  797. } // end reset
  798. //-------------------------------------------------------------------------------------------------
  799. /** Update */
  800. //-------------------------------------------------------------------------------------------------
  801. void W3DRadar::update( void )
  802. {
  803. // extend base class
  804. Radar::update();
  805. } // end update
  806. //-------------------------------------------------------------------------------------------------
  807. /** Reset the radar for the new map data being given to it */
  808. //-------------------------------------------------------------------------------------------------
  809. void W3DRadar::newMap( TerrainLogic *terrain )
  810. {
  811. //
  812. // extending functionality, call the base class ... this will cause a reset of the
  813. // system which will clear out our textures but not free them
  814. //
  815. Radar::newMap( terrain );
  816. // sanity
  817. if( terrain == NULL )
  818. return;
  819. // build terrain texture
  820. buildTerrainTexture( terrain );
  821. } // end newMap
  822. // ------------------------------------------------------------------------------------------------
  823. // ------------------------------------------------------------------------------------------------
  824. void W3DRadar::buildTerrainTexture( TerrainLogic *terrain )
  825. {
  826. SurfaceClass *surface;
  827. RGBColor waterColor;
  828. // we will want to reconstruct our new view box now
  829. m_reconstructViewBox = TRUE;
  830. // setup our water color
  831. waterColor.red = TheWaterTransparency->m_radarColor.red;
  832. waterColor.green = TheWaterTransparency->m_radarColor.green;
  833. waterColor.blue = TheWaterTransparency->m_radarColor.blue;
  834. // get the terrain surface to draw in
  835. surface = m_terrainTexture->Get_Surface_Level();
  836. DEBUG_ASSERTCRASH( surface, ("W3DRadar: Can't get surface for terrain texture\n") );
  837. // build the terrain
  838. RGBColor sampleColor;
  839. RGBColor color;
  840. Int i, j, samples;
  841. Int x, y;
  842. ICoord2D radarPoint;
  843. Coord3D worldPoint;
  844. Bridge *bridge;
  845. for( y = 0; y < m_textureHeight; y++ )
  846. {
  847. for( x = 0; x < m_textureWidth; x++ )
  848. {
  849. // what point are we inspecting
  850. radarPoint.x = x;
  851. radarPoint.y = y;
  852. radarToWorld2D( &radarPoint, &worldPoint );
  853. // check to see if this point is part of a working bridge
  854. Bool workingBridge = FALSE;
  855. bridge = TheTerrainLogic->findBridgeAt( &worldPoint );
  856. if( bridge != NULL )
  857. {
  858. Object *obj = TheGameLogic->findObjectByID( bridge->peekBridgeInfo()->bridgeObjectID );
  859. if( obj )
  860. {
  861. BodyModuleInterface *body = obj->getBodyModule();
  862. if( body->getDamageState() != BODY_RUBBLE )
  863. workingBridge = TRUE;
  864. } // end if
  865. } // end if
  866. // create a color based on the Z height of the map
  867. Real waterZ;
  868. if( workingBridge == FALSE && terrain->isUnderwater( worldPoint.x, worldPoint.y, &waterZ ) )
  869. {
  870. const Int waterSamplesAway = 1; // how many "tiles" from the center tile we will sample away
  871. // to average a color for the tile color
  872. sampleColor.red = sampleColor.green = sampleColor.blue = 0.0f;
  873. samples = 0;
  874. for( j = y - waterSamplesAway; j <= y + waterSamplesAway; j++ )
  875. {
  876. if( j >= 0 && j < m_textureHeight )
  877. {
  878. for( i = x - waterSamplesAway; i <= x + waterSamplesAway; i++ )
  879. {
  880. if( i >= 0 && i < m_textureWidth )
  881. {
  882. // the the world point we are concerned with
  883. radarPoint.x = i;
  884. radarPoint.y = j;
  885. radarToWorld2D( &radarPoint, &worldPoint );
  886. // get color for this Z and add to our sample color
  887. Real underwaterZ;
  888. if( terrain->isUnderwater( worldPoint.x, worldPoint.y, NULL, &underwaterZ ) )
  889. {
  890. // this is our "color" for water
  891. color = waterColor;
  892. // interpolate the water color for height in the water table
  893. interpolateColorForHeight( &color, underwaterZ, waterZ,
  894. waterZ,
  895. m_mapExtent.lo.z );
  896. // add color to our samples
  897. sampleColor.red += color.red;
  898. sampleColor.green += color.green;
  899. sampleColor.blue += color.blue;
  900. samples++;
  901. } // end if
  902. } // end if
  903. } // end for i
  904. } // end if
  905. } // end for j
  906. // prevent divide by zeros
  907. if( samples == 0 )
  908. samples = 1;
  909. // set the color to an average of the colors read
  910. color.red = sampleColor.red / (Real)samples;
  911. color.green = sampleColor.green / (Real)samples;
  912. color.blue = sampleColor.blue / (Real)samples;
  913. } // end if
  914. else // regular terrain ...
  915. {
  916. const Int samplesAway = 1; // how many "tiles" from the center tile we will sample away
  917. // to average a color for the tile color
  918. sampleColor.red = sampleColor.green = sampleColor.blue = 0.0f;
  919. samples = 0;
  920. for( j = y - samplesAway; j <= y + samplesAway; j++ )
  921. {
  922. if( j >= 0 && j < m_textureHeight )
  923. {
  924. for( i = x - samplesAway; i <= x + samplesAway; i++ )
  925. {
  926. if( i >= 0 && i < m_textureWidth )
  927. {
  928. // the the world point we are concerned with
  929. radarPoint.x = i;
  930. radarPoint.y = j;
  931. radarToWorld( &radarPoint, &worldPoint );
  932. // get the color we're going to use here
  933. if( workingBridge )
  934. {
  935. AsciiString bridgeTName = bridge->getBridgeTemplateName();
  936. TerrainRoadType *bridgeTemplate = TheTerrainRoads->findBridge( bridgeTName );
  937. // sanity
  938. DEBUG_ASSERTCRASH( bridgeTemplate, ("W3DRadar::buildTerrainTexture - Can't find bridge template for '%s'\n", bridgeTName.str()) );
  939. // use bridge color
  940. if ( bridgeTemplate )
  941. color = bridgeTemplate->getRadarColor();
  942. else
  943. color.setFromInt(0xffffffff);
  944. //
  945. // we won't use the height of the terrain at this sample point, we will
  946. // instead use the height for the entire bridge
  947. //
  948. Real bridgeHeight = (bridge->peekBridgeInfo()->fromLeft.z +
  949. bridge->peekBridgeInfo()->fromRight.z +
  950. bridge->peekBridgeInfo()->toLeft.z +
  951. bridge->peekBridgeInfo()->toRight.z) / 4.0f;
  952. // interpolate the color, but use the bridge height, not the terrain height
  953. interpolateColorForHeight( &color, bridgeHeight,
  954. getTerrainAverageZ(),
  955. m_mapExtent.hi.z, m_mapExtent.lo.z );
  956. } // end if
  957. else
  958. {
  959. // get the color at this point
  960. TheTerrainVisual->getTerrainColorAt( worldPoint.x, worldPoint.y, &color );
  961. // interpolate the color for height
  962. interpolateColorForHeight( &color, worldPoint.z, getTerrainAverageZ(),
  963. m_mapExtent.hi.z, m_mapExtent.lo.z );
  964. } // end else
  965. // add color to our samples
  966. sampleColor.red += color.red;
  967. sampleColor.green += color.green;
  968. sampleColor.blue += color.blue;
  969. samples++;
  970. } // end if
  971. } // end for i
  972. } // end if
  973. } // end for j
  974. // prevent divide by zeros
  975. if( samples == 0 )
  976. samples = 1;
  977. // set the color to an average of the colors read
  978. color.red = sampleColor.red / (Real)samples;
  979. color.green = sampleColor.green / (Real)samples;
  980. color.blue = sampleColor.blue / (Real)samples;
  981. } // end else
  982. //
  983. // draw the pixel for the terrain at this point, note that because of the orientation
  984. // of our world we draw it with positive y in the "up" direction
  985. //
  986. // FYI: I tried making this faster by pulling out all the code inside DrawPixel
  987. // and locking only once ... but it made absolutely *no* performance difference,
  988. // the sampling and interpolation algorithm for generating pretty looking terrain
  989. // and water for the radar is just, well, expensive.
  990. //
  991. surface->DrawPixel( x, y, GameMakeColor( color.red * 255,
  992. color.green * 255,
  993. color.blue * 255,
  994. 255 ) );
  995. } // end for x
  996. } // end for y
  997. // all done with the surface
  998. REF_PTR_RELEASE(surface);
  999. } // end buildTerrainTexture
  1000. // ------------------------------------------------------------------------------------------------
  1001. //-------------------------------------------------------------------------------------------------
  1002. void W3DRadar::clearShroud()
  1003. {
  1004. #if defined(_DEBUG) || defined(_INTERNAL)
  1005. if (!TheGlobalData->m_shroudOn)
  1006. return;
  1007. #endif
  1008. SurfaceClass *surface = m_shroudTexture->Get_Surface_Level();
  1009. // fill to clear, shroud will make black. Don't want to make something black that logic can't clear
  1010. unsigned int color = GameMakeColor( 0, 0, 0, 0 );
  1011. for( Int y = 0; y < m_textureHeight; y++ )
  1012. {
  1013. surface->DrawHLine(y, 0, m_textureWidth-1, color);
  1014. }
  1015. REF_PTR_RELEASE(surface);
  1016. }
  1017. // ------------------------------------------------------------------------------------------------
  1018. //-------------------------------------------------------------------------------------------------
  1019. void W3DRadar::setShroudLevel(Int shroudX, Int shroudY, CellShroudStatus setting)
  1020. {
  1021. #if defined(_DEBUG) || defined(_INTERNAL)
  1022. if (!TheGlobalData->m_shroudOn)
  1023. return;
  1024. #endif
  1025. W3DShroud* shroud = TheTerrainRenderObject ? TheTerrainRenderObject->getShroud() : NULL;
  1026. if (!shroud)
  1027. return;
  1028. SurfaceClass* surface = m_shroudTexture->Get_Surface_Level();
  1029. DEBUG_ASSERTCRASH( surface, ("W3DRadar: Can't get surface for Shroud texture\n") );
  1030. Int mapMinX = shroudX * shroud->getCellWidth();
  1031. Int mapMinY = shroudY * shroud->getCellHeight();
  1032. Int mapMaxX = (shroudX+1) * shroud->getCellWidth();
  1033. Int mapMaxY = (shroudY+1) * shroud->getCellHeight();
  1034. ICoord2D radarPoint;
  1035. Coord3D worldPoint;
  1036. worldPoint.x = mapMinX;
  1037. worldPoint.y = mapMinY;
  1038. worldToRadar( &worldPoint, &radarPoint );
  1039. Int radarMinX = radarPoint.x;
  1040. Int radarMinY = radarPoint.y;
  1041. worldPoint.x = mapMaxX;
  1042. worldPoint.y = mapMaxY;
  1043. worldToRadar( &worldPoint, &radarPoint );
  1044. Int radarMaxX = radarPoint.x;
  1045. Int radarMaxY = radarPoint.y;
  1046. /*
  1047. Int radarMinX = REAL_TO_INT_FLOOR(mapMinX / getXSample());
  1048. Int radarMinY = REAL_TO_INT_FLOOR(mapMinY / getYSample());
  1049. Int radarMaxX = REAL_TO_INT_CEIL(mapMaxX / getXSample());
  1050. Int radarMaxY = REAL_TO_INT_CEIL(mapMaxY / getYSample());
  1051. */
  1052. /// @todo srj -- this really needs to smooth the display!
  1053. //Logic is saying shroud. We can add alpha levels here in client if needed.
  1054. // W3DShroud is a 0-255 alpha byte. Logic shroud is a double reference count.
  1055. Int alpha;
  1056. if( setting == CELLSHROUD_SHROUDED )
  1057. alpha = 255;
  1058. else if( setting == CELLSHROUD_FOGGED )
  1059. alpha = 127;///< @todo placeholder to get feedback on logic work while graphic side being decided
  1060. else
  1061. alpha = 0;
  1062. for( Int y = radarMinY; y <= radarMaxY; y++ )
  1063. {
  1064. for( Int x = radarMinX; x <= radarMaxX; x++ )
  1065. {
  1066. if( legalRadarPoint( x, y ) )
  1067. surface->DrawPixel( x, y, GameMakeColor( 0, 0, 0, alpha ) );
  1068. }
  1069. }
  1070. REF_PTR_RELEASE(surface);
  1071. }
  1072. //-------------------------------------------------------------------------------------------------
  1073. /** Actually draw the radar at the screen coordinates provided
  1074. * NOTE about how drawing works: The radar images are computed at samples across the
  1075. * map and are built into a "square" texture area. At the time of drawing and computing
  1076. * radar<->world coords we consider the "ratio" of width to height of the map dimensions
  1077. * so that when we draw we preserve the aspect ratio of the map and don't squish it in
  1078. * any direction that would cause the map to be distorted. Extra blank space is drawn
  1079. * around the radar images to keep the whole radar area covered when the map displayed
  1080. * is "long" or "tall" */
  1081. //-------------------------------------------------------------------------------------------------
  1082. void W3DRadar::draw( Int pixelX, Int pixelY, Int width, Int height )
  1083. {
  1084. // if the local player does not have a radar then we can't draw anything
  1085. Player *player = ThePlayerList->getLocalPlayer();
  1086. if( !player->hasRadar() && !TheRadar->isRadarForced() )
  1087. return;
  1088. //
  1089. // given a upper left corner at pixelX|Y and a width and height to draw into, figure out
  1090. // where we should start and end the image so that the final drawn image has the
  1091. // same ratio as the map and isn't stretched or distorted
  1092. //
  1093. ICoord2D ul, lr;
  1094. findDrawPositions( pixelX, pixelY, width, height, &ul, &lr );
  1095. Int scaledWidth = lr.x - ul.x;
  1096. Int scaledHeight = lr.y - ul.y;
  1097. // draw black border areas where we need map
  1098. Color fillColor = GameMakeColor( 0, 0, 0, 255 );
  1099. Color lineColor = GameMakeColor( 50, 50, 50, 255 );
  1100. if( m_mapExtent.width()/width >= m_mapExtent.height()/height )
  1101. {
  1102. // draw horizontal bars at top and bottom
  1103. TheDisplay->drawFillRect( pixelX, pixelY, width, ul.y - pixelY - 1, fillColor );
  1104. TheDisplay->drawFillRect( pixelX, lr.y + 1, width, pixelY + height - lr.y - 1, fillColor);
  1105. TheDisplay->drawLine(pixelX, ul.y, pixelX + width, ul.y, 1, lineColor);
  1106. TheDisplay->drawLine(pixelX, lr.y + 1, pixelX + width, lr.y + 1, 1, lineColor);
  1107. } // end if
  1108. else
  1109. {
  1110. // draw vertical bars to the left and right
  1111. TheDisplay->drawFillRect( pixelX, pixelY, ul.x - pixelX - 1, height, fillColor );
  1112. TheDisplay->drawFillRect( lr.x + 1, pixelY, width - (lr.x - pixelX) - 1, height, fillColor );
  1113. TheDisplay->drawLine(ul.x, pixelY, ul.x, pixelY + height, 1, lineColor);
  1114. TheDisplay->drawLine(lr.x + 1, pixelY, lr.x + 1, pixelY + height, 1, lineColor);
  1115. } // end else
  1116. // draw the terrain texture
  1117. TheDisplay->drawImage( m_terrainImage, ul.x, ul.y, lr.x, lr.y );
  1118. // refresh the overlay texture once every so many frames
  1119. if( TheGameClient->getFrame() % OVERLAY_REFRESH_RATE == 0 )
  1120. {
  1121. // reset the overlay texture
  1122. SurfaceClass *surface = m_overlayTexture->Get_Surface_Level();
  1123. surface->Clear();
  1124. REF_PTR_RELEASE(surface);
  1125. // rebuild the object overlay
  1126. renderObjectList( getObjectList(), m_overlayTexture );
  1127. renderObjectList( getLocalObjectList(), m_overlayTexture, TRUE );
  1128. } // end if
  1129. // draw the overlay image
  1130. TheDisplay->drawImage( m_overlayImage, ul.x, ul.y, lr.x, lr.y );
  1131. // draw the shroud image
  1132. #if defined(_DEBUG) || defined(_INTERNAL)
  1133. if( TheGlobalData->m_shroudOn )
  1134. #else
  1135. if (true)
  1136. #endif
  1137. {
  1138. TheDisplay->drawImage( m_shroudImage, ul.x, ul.y, lr.x, lr.y );
  1139. }
  1140. // draw any icons
  1141. drawIcons( ul.x, ul.y, scaledWidth, scaledHeight );
  1142. // draw any radar events
  1143. drawEvents( ul.x, ul.y, scaledWidth, scaledHeight );
  1144. // see if we need to reconstruct the view box
  1145. if( TheTacticalView->getZoom() != m_viewZoom )
  1146. m_reconstructViewBox = TRUE;
  1147. if( TheTacticalView->getAngle() != m_viewAngle )
  1148. m_reconstructViewBox = TRUE;
  1149. if( m_reconstructViewBox == TRUE )
  1150. reconstructViewBox();
  1151. // draw the view region on top of the radar reconstructing if necessary
  1152. drawViewBox( ul.x, ul.y, scaledWidth, scaledHeight );
  1153. } // end draw
  1154. // ------------------------------------------------------------------------------------------------
  1155. // ------------------------------------------------------------------------------------------------
  1156. void W3DRadar::refreshTerrain( TerrainLogic *terrain )
  1157. {
  1158. // extend base class
  1159. Radar::refreshTerrain( terrain );
  1160. // rebuild the entire terrain texture
  1161. buildTerrainTexture( terrain );
  1162. } // end refreshTerrain
  1163. ///The following is an "archive" of an attempt to foil the mapshroud hack... saved for later, since it is too close to release to try it
  1164. /*
  1165. *
  1166. void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture, Bool calcHero )
  1167. {
  1168. // sanity
  1169. if( listHead == NULL || texture == NULL )
  1170. return;
  1171. // get surface for texture to render into
  1172. SurfaceClass *surface = texture->Get_Surface_Level();
  1173. // loop through all objects and draw
  1174. ICoord2D radarPoint;
  1175. Player *player = ThePlayerList->getLocalPlayer();
  1176. Int playerIndex=0;
  1177. if (player)
  1178. playerIndex=player->getPlayerIndex();
  1179. UnsignedByte minAlpha = 8;
  1180. if( calcHero )
  1181. {
  1182. // clear all entries from the cached hero object list
  1183. m_cachedHeroPosList.clear();
  1184. }
  1185. for( const RadarObject *rObj = listHead; rObj; rObj = rObj->friend_getNext() )
  1186. {
  1187. UnsignedByte h = (UnsignedByte)(rObj->isTemporarilyHidden());
  1188. if ( h )
  1189. continue;
  1190. UnsignedByte a = 0;
  1191. // get object
  1192. const Object *obj = rObj->friend_getObject();
  1193. UnsignedByte r = 1; // all decoys
  1194. // cache hero object positions for drawing in icon layer
  1195. if( calcHero && obj->isHero() )
  1196. {
  1197. m_cachedHeroPosList.push_back(obj->getPosition());
  1198. }
  1199. // get the color we're going to draw in
  1200. UnsignedInt c = 0xfe000000;// this is a decoy
  1201. c |= (UnsignedInt)( obj->testStatus( OBJECT_STATUS_STEALTHED ) );//so is this
  1202. // check for shrouded status
  1203. UnsignedByte k = (UnsignedByte)(obj->getShroudedStatus(playerIndex) > OBJECTSHROUD_PARTIAL_CLEAR);
  1204. if ( k || a)
  1205. continue; //object is fogged or shrouded, don't render it.
  1206. //
  1207. // objects with a local only unit priority will only appear on the radar if they
  1208. // are controlled by the local player, or if the local player is an observer (cause
  1209. // they are godlike and can see everything)
  1210. //
  1211. if( obj->getRadarPriority() == RADAR_PRIORITY_LOCAL_UNIT_ONLY &&
  1212. obj->getControllingPlayer() != ThePlayerList->getLocalPlayer() &&
  1213. ThePlayerList->getLocalPlayer()->isPlayerActive() )
  1214. continue;
  1215. UnsignedByte g = c|a;
  1216. UnsignedByte b = h|a;
  1217. // get object position
  1218. const Coord3D *pos = obj->getPosition();
  1219. // compute object position as a radar blip
  1220. radarPoint.x = pos->x / (m_mapExtent.width() / RADAR_CELL_WIDTH);
  1221. radarPoint.y = pos->y / (m_mapExtent.height() / RADAR_CELL_HEIGHT);
  1222. const UnsignedInt framesForTransition = LOGICFRAMES_PER_SECOND;
  1223. // adjust the alpha for stealth units so they "fade/blink" on the radar for the controller
  1224. // if( obj->getRadarPriority() == RADAR_PRIORITY_LOCAL_UNIT_ONLY )
  1225. // ML-- What the heck is this? local-only and neutral-observier-viewed units are stealthy?? Since when?
  1226. // Now it twinkles for any stealthed object, whether locally controlled or neutral-observier-viewed
  1227. c = rObj->getColor();
  1228. if( g & r )
  1229. {
  1230. Real alphaScale = INT_TO_REAL(TheGameLogic->getFrame() % framesForTransition) / (framesForTransition * 0.5f);
  1231. minAlpha <<= 2; // decoy
  1232. if ( ( obj->isLocallyControlled() == (Bool)a ) // another decoy, comparing the return of this non-inline with a local
  1233. && !obj->testStatus( OBJECT_STATUS_DISGUISED )
  1234. && !obj->testStatus( OBJECT_STATUS_DETECTED )
  1235. && ++a != 0 // The trick is that this increment does not occur unless all three above conditions are true
  1236. && minAlpha == 32 // tricksy hobbit decoy
  1237. && c != 0 ) // ditto
  1238. {
  1239. g = (UnsignedByte)(rObj->getColor());
  1240. continue;
  1241. }
  1242. a |= k | b;
  1243. GameGetColorComponentsWithCheatSpy( c, &r, &g, &b, &a );//this function does not touch the low order bit in 'a'
  1244. if( alphaScale > 0.0f )
  1245. a = REAL_TO_UNSIGNEDBYTE( ((alphaScale - 1.0f) * (255.0f - minAlpha)) + minAlpha );
  1246. else
  1247. a = REAL_TO_UNSIGNEDBYTE( (alphaScale * (255.0f - minAlpha)) + minAlpha );
  1248. c = GameMakeColor( r, g, b, a );
  1249. } // end if
  1250. // draw the blip, but make sure the points are legal
  1251. if( legalRadarPoint( radarPoint.x, radarPoint.y ) )
  1252. surface->DrawPixel( radarPoint.x, radarPoint.y, c );
  1253. radarPoint.x++;
  1254. if( legalRadarPoint( radarPoint.x, radarPoint.y ) )
  1255. surface->DrawPixel( radarPoint.x, radarPoint.y, c );
  1256. radarPoint.y++;
  1257. if( legalRadarPoint( radarPoint.x, radarPoint.y ) )
  1258. surface->DrawPixel( radarPoint.x, radarPoint.y, c );
  1259. radarPoint.x--;
  1260. if( legalRadarPoint( radarPoint.x, radarPoint.y ) )
  1261. surface->DrawPixel( radarPoint.x, radarPoint.y, c );
  1262. } // end for
  1263. REF_PTR_RELEASE(surface);
  1264. } // end renderObjectList
  1265. *
  1266. */