W3DRadar.cpp 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469
  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: 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 "GameLogic/Module/StealthUpdate.h"
  38. #include "GameClient/Color.h"
  39. #include "GameClient/Display.h"
  40. #include "GameClient/GameClient.h"
  41. #include "GameClient/GameWindow.h"
  42. #include "GameClient/Image.h"
  43. #include "GameClient/Line2D.h"
  44. #include "GameClient/TerrainVisual.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( DX8Caps::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. static NameKeyType key_StealthUpdate = NAMEKEY( "StealthUpdate" );
  570. StealthUpdate* stealth = (StealthUpdate*)obj->findUpdateModule( key_StealthUpdate );
  571. if( !stealth )
  572. continue;
  573. if ( ThePlayerList->getLocalPlayer()->getRelationship(obj->getTeam()) == ENEMIES )
  574. if( !obj->testStatus( OBJECT_STATUS_DETECTED ) && !stealth->isDisguised() )
  575. skip = TRUE;
  576. UnsignedByte r, g, b, a;
  577. GameGetColorComponents( c, &r, &g, &b, &a );
  578. const UnsignedInt framesForTransition = LOGICFRAMES_PER_SECOND;
  579. const UnsignedByte minAlpha = 32;
  580. if (skip)
  581. continue;
  582. Real alphaScale = INT_TO_REAL(TheGameLogic->getFrame() % framesForTransition) / (framesForTransition / 2.0f);
  583. if( alphaScale > 0.0f )
  584. a = REAL_TO_UNSIGNEDBYTE( ((alphaScale - 1.0f) * (255.0f - minAlpha)) + minAlpha );
  585. else
  586. a = REAL_TO_UNSIGNEDBYTE( (alphaScale * (255.0f - minAlpha)) + minAlpha );
  587. c = GameMakeColor( r, g, b, a );
  588. } // end if
  589. // draw the blip, but make sure the points are legal
  590. if( legalRadarPoint( radarPoint.x, radarPoint.y ) )
  591. surface->DrawPixel( radarPoint.x, radarPoint.y, c );
  592. radarPoint.y++;
  593. if( legalRadarPoint( radarPoint.x, radarPoint.y ) )
  594. surface->DrawPixel( radarPoint.x, radarPoint.y, c );
  595. radarPoint.x++;
  596. if( legalRadarPoint( radarPoint.x, radarPoint.y ) )
  597. surface->DrawPixel( radarPoint.x, radarPoint.y, c );
  598. radarPoint.y--;
  599. if( legalRadarPoint( radarPoint.x, radarPoint.y ) )
  600. surface->DrawPixel( radarPoint.x, radarPoint.y, c );
  601. } // end for
  602. REF_PTR_RELEASE(surface);
  603. } // end renderObjectList
  604. //-------------------------------------------------------------------------------------------------
  605. /** Shade the color passed in using the height parameter to lighten and darken it. Colors
  606. * will be interpolated using the value "height" across the range from loZ to hiZ. The
  607. * midZ is the "middle" point, height values above it will be lightened, while
  608. * lower ones are darkened. */
  609. //-------------------------------------------------------------------------------------------------
  610. void W3DRadar::interpolateColorForHeight( RGBColor *color,
  611. Real height,
  612. Real hiZ,
  613. Real midZ,
  614. Real loZ )
  615. {
  616. const Real howBright = 0.95f; // bigger is brighter (0.0 to 1.0)
  617. const Real howDark = 0.60f; // bigger is darker (0.0 to 1.0)
  618. // sanity on map height (flat maps bomb)
  619. if (hiZ == midZ)
  620. hiZ = midZ+0.1f;
  621. if (midZ == loZ)
  622. loZ = midZ-0.1f;
  623. if (hiZ == loZ)
  624. hiZ = loZ+0.2f;
  625. Real t;
  626. RGBColor colorTarget;
  627. // if "over" the middle height, interpolate lighter
  628. if( height >= midZ )
  629. {
  630. // how far are we from the middleZ towards the hi Z
  631. t = (height - midZ) / (hiZ - midZ);
  632. // compute what our "lightest" color possible we want to use is
  633. colorTarget.red = color->red + (1.0f - color->red) * howBright;
  634. colorTarget.green = color->green + (1.0f - color->green) * howBright;
  635. colorTarget.blue = color->blue + (1.0f - color->blue) * howBright;
  636. } // end if
  637. else // interpolate darker
  638. {
  639. // how far are we from the middleZ towards the low Z
  640. t = (midZ - height) / (midZ - loZ);
  641. // compute what the "darkest" color possible we want to use is
  642. colorTarget.red = color->red + (0.0f - color->red) * howDark;
  643. colorTarget.green = color->green + (0.0f - color->green) * howDark;
  644. colorTarget.blue = color->blue + (0.0f - color->blue) * howDark;
  645. } // end else
  646. // interpolate toward the target color
  647. color->red = color->red + (colorTarget.red - color->red) * t;
  648. color->green = color->green + (colorTarget.green - color->green) * t;
  649. color->blue = color->blue + (colorTarget.blue - color->blue) * t;
  650. // keep the color real
  651. if( color->red < 0.0f )
  652. color->red = 0.0f;
  653. if( color->red > 1.0f )
  654. color->red = 1.0f;
  655. if( color->green < 0.0f )
  656. color->green = 0.0f;
  657. if( color->green > 1.0f )
  658. color->green = 1.0f;
  659. if( color->blue < 0.0f )
  660. color->blue = 0.0f;
  661. if( color->blue > 1.0f )
  662. color->blue = 1.0f;
  663. } // end interpolateColorForHeight
  664. ///////////////////////////////////////////////////////////////////////////////////////////////////
  665. // PUBLIC METHODS /////////////////////////////////////////////////////////////////////////////////
  666. ///////////////////////////////////////////////////////////////////////////////////////////////////
  667. //-------------------------------------------------------------------------------------------------
  668. //-------------------------------------------------------------------------------------------------
  669. W3DRadar::W3DRadar( void )
  670. {
  671. m_terrainTextureFormat = WW3D_FORMAT_UNKNOWN;
  672. m_terrainImage = NULL;
  673. m_terrainTexture = NULL;
  674. m_overlayTextureFormat = WW3D_FORMAT_UNKNOWN;
  675. m_overlayImage = NULL;
  676. m_overlayTexture = NULL;
  677. m_shroudTextureFormat = WW3D_FORMAT_UNKNOWN;
  678. m_shroudImage = NULL;
  679. m_shroudTexture = NULL;
  680. m_textureWidth = RADAR_CELL_WIDTH;
  681. m_textureHeight = RADAR_CELL_HEIGHT;
  682. m_reconstructViewBox = TRUE;
  683. m_viewAngle = 0.0f;
  684. m_viewZoom = 0.0f;
  685. for( Int i = 0; i < 4; i++ )
  686. {
  687. m_viewBox[ i ].x = 0;
  688. m_viewBox[ i ].y = 0;
  689. } // end for
  690. } // end W3DRadar
  691. //-------------------------------------------------------------------------------------------------
  692. //-------------------------------------------------------------------------------------------------
  693. W3DRadar::~W3DRadar( void )
  694. {
  695. // delete resources used for the W3D radar
  696. deleteResources();
  697. } // end ~W3DRadar
  698. //-------------------------------------------------------------------------------------------------
  699. /** Radar initialization */
  700. //-------------------------------------------------------------------------------------------------
  701. void W3DRadar::init( void )
  702. {
  703. ICoord2D size;
  704. Region2D uv;
  705. // extending functionality
  706. Radar::init();
  707. // gather specific texture format information
  708. initializeTextureFormats();
  709. // allocate our terrain texture
  710. // poolify
  711. m_terrainTexture = MSGNEW("TextureClass") TextureClass( m_textureWidth, m_textureHeight,
  712. m_terrainTextureFormat, TextureClass::MIP_LEVELS_1 );
  713. DEBUG_ASSERTCRASH( m_terrainTexture, ("W3DRadar: Unable to allocate terrain texture\n") );
  714. // allocate our overlay texture
  715. m_overlayTexture = MSGNEW("TextureClass") TextureClass( m_textureWidth, m_textureHeight,
  716. m_overlayTextureFormat, TextureClass::MIP_LEVELS_1 );
  717. DEBUG_ASSERTCRASH( m_overlayTexture, ("W3DRadar: Unable to allocate overlay texture\n") );
  718. // set filter type for the overlay texture, try it and see if you like it, I don't ;)
  719. // m_overlayTexture->Set_Min_Filter( TextureClass::FILTER_TYPE_NONE );
  720. // m_overlayTexture->Set_Mag_Filter( TextureClass::FILTER_TYPE_NONE );
  721. // allocate our shroud texture
  722. m_shroudTexture = MSGNEW("TextureClass") TextureClass( m_textureWidth, m_textureHeight,
  723. m_shroudTextureFormat, TextureClass::MIP_LEVELS_1 );
  724. DEBUG_ASSERTCRASH( m_shroudTexture, ("W3DRadar: Unable to allocate shroud texture\n") );
  725. m_shroudTexture->Set_Min_Filter( TextureClass::FILTER_TYPE_DEFAULT );
  726. m_shroudTexture->Set_Mag_Filter( TextureClass::FILTER_TYPE_DEFAULT );
  727. //
  728. // create images used for rendering and set them up with the textures
  729. //
  730. //
  731. // the terrain image, note the UV coords change it from (0,0) in the upper left
  732. // to (0,0) in the lower left cause that's how we are initially oriented in the
  733. // world (positive X to the right and positive Y up)
  734. //
  735. m_terrainImage = newInstance(Image);
  736. uv.lo.x = 0.0f;
  737. uv.lo.y = 1.0f;
  738. uv.hi.x = 1.0f;
  739. uv.hi.y = 0.0f;
  740. m_terrainImage->setStatus( IMAGE_STATUS_RAW_TEXTURE );
  741. m_terrainImage->setRawTextureData( m_terrainTexture );
  742. m_terrainImage->setUV( &uv );
  743. m_terrainImage->setTextureWidth( m_textureWidth );
  744. m_terrainImage->setTextureHeight( m_textureHeight );
  745. size.x = m_textureWidth;
  746. size.y = m_textureHeight;
  747. m_terrainImage->setImageSize( &size );
  748. // the overlay image
  749. m_overlayImage = newInstance(Image);
  750. uv.lo.x = 0.0f;
  751. uv.lo.y = 1.0f;
  752. uv.hi.x = 1.0f;
  753. uv.hi.y = 0.0f;
  754. m_overlayImage->setStatus( IMAGE_STATUS_RAW_TEXTURE );
  755. m_overlayImage->setRawTextureData( m_overlayTexture );
  756. m_overlayImage->setUV( &uv );
  757. m_overlayImage->setTextureWidth( m_textureWidth );
  758. m_overlayImage->setTextureHeight( m_textureHeight );
  759. size.x = m_textureWidth;
  760. size.y = m_textureHeight;
  761. m_overlayImage->setImageSize( &size );
  762. // the shroud image
  763. m_shroudImage = newInstance(Image);
  764. uv.lo.x = 0.0f;
  765. uv.lo.y = 1.0f;
  766. uv.hi.x = 1.0f;
  767. uv.hi.y = 0.0f;
  768. m_shroudImage->setStatus( IMAGE_STATUS_RAW_TEXTURE );
  769. m_shroudImage->setRawTextureData( m_shroudTexture );
  770. m_shroudImage->setUV( &uv );
  771. m_shroudImage->setTextureWidth( m_textureWidth );
  772. m_shroudImage->setTextureHeight( m_textureHeight );
  773. size.x = m_textureWidth;
  774. size.y = m_textureHeight;
  775. m_shroudImage->setImageSize( &size );
  776. } // end init
  777. //-------------------------------------------------------------------------------------------------
  778. /** Reset the radar to the initial empty state ready for new data */
  779. //-------------------------------------------------------------------------------------------------
  780. void W3DRadar::reset( void )
  781. {
  782. // extending functionality, call base class
  783. Radar::reset();
  784. // clear our texture data, but do not delete the resources
  785. SurfaceClass *surface;
  786. surface = m_terrainTexture->Get_Surface_Level();
  787. if( surface )
  788. {
  789. surface->Clear();
  790. REF_PTR_RELEASE(surface);
  791. }
  792. surface = m_overlayTexture->Get_Surface_Level();
  793. if( surface )
  794. {
  795. surface->Clear();
  796. REF_PTR_RELEASE(surface);
  797. }
  798. // don't call Clear(); that wips to transparent. do this instead.
  799. //gs Dude, it's called CLEARshroud. It needs to clear the shroud.
  800. clearShroud();
  801. } // end reset
  802. //-------------------------------------------------------------------------------------------------
  803. /** Update */
  804. //-------------------------------------------------------------------------------------------------
  805. void W3DRadar::update( void )
  806. {
  807. // extend base class
  808. Radar::update();
  809. } // end update
  810. //-------------------------------------------------------------------------------------------------
  811. /** Reset the radar for the new map data being given to it */
  812. //-------------------------------------------------------------------------------------------------
  813. void W3DRadar::newMap( TerrainLogic *terrain )
  814. {
  815. //
  816. // extending functionality, call the base class ... this will cause a reset of the
  817. // system which will clear out our textures but not free them
  818. //
  819. Radar::newMap( terrain );
  820. // sanity
  821. if( terrain == NULL )
  822. return;
  823. // build terrain texture
  824. buildTerrainTexture( terrain );
  825. } // end newMap
  826. // ------------------------------------------------------------------------------------------------
  827. // ------------------------------------------------------------------------------------------------
  828. void W3DRadar::buildTerrainTexture( TerrainLogic *terrain )
  829. {
  830. SurfaceClass *surface;
  831. RGBColor waterColor;
  832. // we will want to reconstruct our new view box now
  833. m_reconstructViewBox = TRUE;
  834. // setup our water color
  835. waterColor.red = 0.55f;
  836. waterColor.green = 0.55f;
  837. waterColor.blue = 1.0f;
  838. // get the terrain surface to draw in
  839. surface = m_terrainTexture->Get_Surface_Level();
  840. DEBUG_ASSERTCRASH( surface, ("W3DRadar: Can't get surface for terrain texture\n") );
  841. // build the terrain
  842. RGBColor sampleColor;
  843. RGBColor color;
  844. Int i, j, samples;
  845. Int x, y, z;
  846. ICoord2D radarPoint;
  847. Coord3D worldPoint;
  848. Bridge *bridge;
  849. for( y = 0; y < m_textureHeight; y++ )
  850. {
  851. for( x = 0; x < m_textureWidth; x++ )
  852. {
  853. // what point are we inspecting
  854. radarPoint.x = x;
  855. radarPoint.y = y;
  856. radarToWorld( &radarPoint, &worldPoint );
  857. // get height of the terrain at this sample point
  858. z = terrain->getGroundHeight( worldPoint.x, worldPoint.y );
  859. // check to see if this point is part of a working bridge
  860. Bool workingBridge = FALSE;
  861. bridge = TheTerrainLogic->findBridgeAt( &worldPoint );
  862. if( bridge != NULL )
  863. {
  864. Object *obj = TheGameLogic->findObjectByID( bridge->peekBridgeInfo()->bridgeObjectID );
  865. if( obj )
  866. {
  867. BodyModuleInterface *body = obj->getBodyModule();
  868. if( body->getDamageState() != BODY_RUBBLE )
  869. workingBridge = TRUE;
  870. } // end if
  871. } // end if
  872. // create a color based on the Z height of the map
  873. Real waterZ;
  874. if( workingBridge == FALSE && terrain->isUnderwater( worldPoint.x, worldPoint.y, &waterZ ) )
  875. {
  876. const Int waterSamplesAway = 1; // how many "tiles" from the center tile we will sample away
  877. // to average a color for the tile color
  878. sampleColor.red = sampleColor.green = sampleColor.blue = 0.0f;
  879. samples = 0;
  880. for( j = y - waterSamplesAway; j <= y + waterSamplesAway; j++ )
  881. {
  882. if( j >= 0 && j < m_textureHeight )
  883. {
  884. for( i = x - waterSamplesAway; i <= x + waterSamplesAway; i++ )
  885. {
  886. if( i >= 0 && i < m_textureWidth )
  887. {
  888. // the the world point we are concerned with
  889. radarPoint.x = i;
  890. radarPoint.y = j;
  891. radarToWorld( &radarPoint, &worldPoint );
  892. // get Z at this sample height
  893. Real underwaterZ = terrain->getGroundHeight( worldPoint.x, worldPoint.y );
  894. // get color for this Z and add to our sample color
  895. if( terrain->isUnderwater( worldPoint.x, worldPoint.y ) )
  896. {
  897. // this is our "color" for water
  898. color = waterColor;
  899. // interpolate the water color for height in the water table
  900. interpolateColorForHeight( &color, underwaterZ, waterZ,
  901. waterZ,
  902. m_mapExtent.lo.z );
  903. // add color to our samples
  904. sampleColor.red += color.red;
  905. sampleColor.green += color.green;
  906. sampleColor.blue += color.blue;
  907. samples++;
  908. } // end if
  909. } // end if
  910. } // end for i
  911. } // end if
  912. } // end for j
  913. // prevent divide by zeros
  914. if( samples == 0 )
  915. samples = 1;
  916. // set the color to an average of the colors read
  917. color.red = sampleColor.red / (Real)samples;
  918. color.green = sampleColor.green / (Real)samples;
  919. color.blue = sampleColor.blue / (Real)samples;
  920. } // end if
  921. else // regular terrain ...
  922. {
  923. const Int samplesAway = 1; // how many "tiles" from the center tile we will sample away
  924. // to average a color for the tile color
  925. sampleColor.red = sampleColor.green = sampleColor.blue = 0.0f;
  926. samples = 0;
  927. for( j = y - samplesAway; j <= y + samplesAway; j++ )
  928. {
  929. if( j >= 0 && j < m_textureHeight )
  930. {
  931. for( i = x - samplesAway; i <= x + samplesAway; i++ )
  932. {
  933. if( i >= 0 && i < m_textureWidth )
  934. {
  935. // the the world point we are concerned with
  936. radarPoint.x = i;
  937. radarPoint.y = j;
  938. radarToWorld( &radarPoint, &worldPoint );
  939. // get the color we're going to use here
  940. if( workingBridge )
  941. {
  942. AsciiString bridgeTName = bridge->getBridgeTemplateName();
  943. TerrainRoadType *bridgeTemplate = TheTerrainRoads->findBridge( bridgeTName );
  944. // sanity
  945. DEBUG_ASSERTCRASH( bridgeTemplate, ("W3DRadar::buildTerrainTexture - Can't find bridge template for '%s'\n", bridgeTName.str()) );
  946. // use bridge color
  947. if ( bridgeTemplate )
  948. color = bridgeTemplate->getRadarColor();
  949. else
  950. color.setFromInt(0xffffffff);
  951. //
  952. // we won't use the height of the terrain at this sample point, we will
  953. // instead use the height for the entire bridge
  954. //
  955. Real bridgeHeight = (bridge->peekBridgeInfo()->fromLeft.z +
  956. bridge->peekBridgeInfo()->fromRight.z +
  957. bridge->peekBridgeInfo()->toLeft.z +
  958. bridge->peekBridgeInfo()->toRight.z) / 4.0f;
  959. // interpolate the color, but use the bridge height, not the terrain height
  960. interpolateColorForHeight( &color, bridgeHeight,
  961. getTerrainAverageZ(),
  962. m_mapExtent.hi.z, m_mapExtent.lo.z );
  963. } // end if
  964. else
  965. {
  966. // get the color at this point
  967. TheTerrainVisual->getTerrainColorAt( worldPoint.x, worldPoint.y, &color );
  968. // interpolate the color for height
  969. interpolateColorForHeight( &color, z, getTerrainAverageZ(),
  970. m_mapExtent.hi.z, m_mapExtent.lo.z );
  971. } // end else
  972. // add color to our samples
  973. sampleColor.red += color.red;
  974. sampleColor.green += color.green;
  975. sampleColor.blue += color.blue;
  976. samples++;
  977. } // end if
  978. } // end for i
  979. } // end if
  980. } // end for j
  981. // prevent divide by zeros
  982. if( samples == 0 )
  983. samples = 1;
  984. // set the color to an average of the colors read
  985. color.red = sampleColor.red / (Real)samples;
  986. color.green = sampleColor.green / (Real)samples;
  987. color.blue = sampleColor.blue / (Real)samples;
  988. } // end else
  989. //
  990. // draw the pixel for the terrain at this point, note that because of the orientation
  991. // of our world we draw it with positive y in the "up" direction
  992. //
  993. // FYI: I tried making this faster by pulling out all the code inside DrawPixel
  994. // and locking only once ... but it made absolutely *no* performance difference,
  995. // the sampling and interpolation algorithm for generating pretty looking terrain
  996. // and water for the radar is just, well, expensive.
  997. //
  998. surface->DrawPixel( x, y, GameMakeColor( color.red * 255,
  999. color.green * 255,
  1000. color.blue * 255,
  1001. 255 ) );
  1002. } // end for x
  1003. } // end for y
  1004. // all done with the surface
  1005. REF_PTR_RELEASE(surface);
  1006. } // end buildTerrainTexture
  1007. // ------------------------------------------------------------------------------------------------
  1008. //-------------------------------------------------------------------------------------------------
  1009. void W3DRadar::clearShroud()
  1010. {
  1011. #if defined(_DEBUG) || defined(_INTERNAL)
  1012. if (!TheGlobalData->m_shroudOn)
  1013. return;
  1014. #endif
  1015. SurfaceClass *surface = m_shroudTexture->Get_Surface_Level();
  1016. // fill to clear, shroud will make black. Don't want to make something black that logic can't clear
  1017. unsigned int color = GameMakeColor( 0, 0, 0, 0 );
  1018. for( Int y = 0; y < m_textureHeight; y++ )
  1019. {
  1020. surface->DrawHLine(y, 0, m_textureWidth-1, color);
  1021. }
  1022. REF_PTR_RELEASE(surface);
  1023. }
  1024. // ------------------------------------------------------------------------------------------------
  1025. //-------------------------------------------------------------------------------------------------
  1026. void W3DRadar::setShroudLevel(Int shroudX, Int shroudY, CellShroudStatus setting)
  1027. {
  1028. #if defined(_DEBUG) || defined(_INTERNAL)
  1029. if (!TheGlobalData->m_shroudOn)
  1030. return;
  1031. #endif
  1032. W3DShroud* shroud = TheTerrainRenderObject ? TheTerrainRenderObject->getShroud() : NULL;
  1033. if (!shroud)
  1034. return;
  1035. SurfaceClass* surface = m_shroudTexture->Get_Surface_Level();
  1036. DEBUG_ASSERTCRASH( surface, ("W3DRadar: Can't get surface for Shroud texture\n") );
  1037. Int mapMinX = shroudX * shroud->getCellWidth();
  1038. Int mapMinY = shroudY * shroud->getCellHeight();
  1039. Int mapMaxX = (shroudX+1) * shroud->getCellWidth();
  1040. Int mapMaxY = (shroudY+1) * shroud->getCellHeight();
  1041. ICoord2D radarPoint;
  1042. Coord3D worldPoint;
  1043. worldPoint.x = mapMinX;
  1044. worldPoint.y = mapMinY;
  1045. worldToRadar( &worldPoint, &radarPoint );
  1046. Int radarMinX = radarPoint.x;
  1047. Int radarMinY = radarPoint.y;
  1048. worldPoint.x = mapMaxX;
  1049. worldPoint.y = mapMaxY;
  1050. worldToRadar( &worldPoint, &radarPoint );
  1051. Int radarMaxX = radarPoint.x;
  1052. Int radarMaxY = radarPoint.y;
  1053. /*
  1054. Int radarMinX = REAL_TO_INT_FLOOR(mapMinX / getXSample());
  1055. Int radarMinY = REAL_TO_INT_FLOOR(mapMinY / getYSample());
  1056. Int radarMaxX = REAL_TO_INT_CEIL(mapMaxX / getXSample());
  1057. Int radarMaxY = REAL_TO_INT_CEIL(mapMaxY / getYSample());
  1058. */
  1059. /// @todo srj -- this really needs to smooth the display!
  1060. //Logic is saying shroud. We can add alpha levels here in client if needed.
  1061. // W3DShroud is a 0-255 alpha byte. Logic shroud is a double reference count.
  1062. Int alpha;
  1063. if( setting == CELLSHROUD_SHROUDED )
  1064. alpha = 255;
  1065. else if( setting == CELLSHROUD_FOGGED )
  1066. alpha = 127;///< @todo placeholder to get feedback on logic work while graphic side being decided
  1067. else
  1068. alpha = 0;
  1069. for( Int y = radarMinY; y <= radarMaxY; y++ )
  1070. {
  1071. for( Int x = radarMinX; x <= radarMaxX; x++ )
  1072. {
  1073. if( legalRadarPoint( x, y ) )
  1074. surface->DrawPixel( x, y, GameMakeColor( 0, 0, 0, alpha ) );
  1075. }
  1076. }
  1077. REF_PTR_RELEASE(surface);
  1078. }
  1079. //-------------------------------------------------------------------------------------------------
  1080. /** Actually draw the radar at the screen coordinates provided
  1081. * NOTE about how drawing works: The radar images are computed at samples across the
  1082. * map and are built into a "square" texture area. At the time of drawing and computing
  1083. * radar<->world coords we consider the "ratio" of width to height of the map dimensions
  1084. * so that when we draw we preserve the aspect ratio of the map and don't squish it in
  1085. * any direction that would cause the map to be distorted. Extra blank space is drawn
  1086. * around the radar images to keep the whole radar area covered when the map displayed
  1087. * is "long" or "tall" */
  1088. //-------------------------------------------------------------------------------------------------
  1089. void W3DRadar::draw( Int pixelX, Int pixelY, Int width, Int height )
  1090. {
  1091. // if the local player does not have a radar then we can't draw anything
  1092. Player *player = ThePlayerList->getLocalPlayer();
  1093. if( !player->hasRadar() && !TheRadar->isRadarForced() )
  1094. return;
  1095. //
  1096. // given a upper left corner at pixelX|Y and a width and height to draw into, figure out
  1097. // where we should start and end the image so that the final drawn image has the
  1098. // same ratio as the map and isn't stretched or distorted
  1099. //
  1100. ICoord2D ul, lr;
  1101. findDrawPositions( pixelX, pixelY, width, height, &ul, &lr );
  1102. Int scaledWidth = lr.x - ul.x;
  1103. Int scaledHeight = lr.y - ul.y;
  1104. // draw black border areas where we need map
  1105. Color fillColor = GameMakeColor( 0, 0, 0, 255 );
  1106. Color lineColor = GameMakeColor( 50, 50, 50, 255 );
  1107. if( m_mapExtent.width()/width >= m_mapExtent.height()/height )
  1108. {
  1109. // draw horizontal bars at top and bottom
  1110. TheDisplay->drawFillRect( pixelX, pixelY, width, ul.y - pixelY - 1, fillColor );
  1111. TheDisplay->drawFillRect( pixelX, lr.y + 1, width, pixelY + height - lr.y - 1, fillColor);
  1112. TheDisplay->drawLine(pixelX, ul.y, pixelX + width, ul.y, 1, lineColor);
  1113. TheDisplay->drawLine(pixelX, lr.y + 1, pixelX + width, lr.y + 1, 1, lineColor);
  1114. } // end if
  1115. else
  1116. {
  1117. // draw vertical bars to the left and right
  1118. TheDisplay->drawFillRect( pixelX, pixelY, ul.x - pixelX - 1, height, fillColor );
  1119. TheDisplay->drawFillRect( lr.x + 1, pixelY, width - (lr.x - pixelX) - 1, height, fillColor );
  1120. TheDisplay->drawLine(ul.x, pixelY, ul.x, pixelY + height, 1, lineColor);
  1121. TheDisplay->drawLine(lr.x + 1, pixelY, lr.x + 1, pixelY + height, 1, lineColor);
  1122. } // end else
  1123. // draw the terrain texture
  1124. TheDisplay->drawImage( m_terrainImage, ul.x, ul.y, lr.x, lr.y );
  1125. // refresh the overlay texture once every so many frames
  1126. if( TheGameClient->getFrame() % OVERLAY_REFRESH_RATE == 0 )
  1127. {
  1128. // reset the overlay texture
  1129. SurfaceClass *surface = m_overlayTexture->Get_Surface_Level();
  1130. surface->Clear();
  1131. REF_PTR_RELEASE(surface);
  1132. // rebuild the object overlay
  1133. renderObjectList( getObjectList(), m_overlayTexture );
  1134. renderObjectList( getLocalObjectList(), m_overlayTexture, TRUE );
  1135. } // end if
  1136. // draw the overlay image
  1137. TheDisplay->drawImage( m_overlayImage, ul.x, ul.y, lr.x, lr.y );
  1138. // draw the shroud image
  1139. #if defined(_DEBUG) || defined(_INTERNAL)
  1140. if( TheGlobalData->m_shroudOn )
  1141. #else
  1142. if (true)
  1143. #endif
  1144. {
  1145. TheDisplay->drawImage( m_shroudImage, ul.x, ul.y, lr.x, lr.y );
  1146. }
  1147. // draw any icons
  1148. drawIcons( ul.x, ul.y, scaledWidth, scaledHeight );
  1149. // draw any radar events
  1150. drawEvents( ul.x, ul.y, scaledWidth, scaledHeight );
  1151. // see if we need to reconstruct the view box
  1152. if( TheTacticalView->getZoom() != m_viewZoom )
  1153. m_reconstructViewBox = TRUE;
  1154. if( TheTacticalView->getAngle() != m_viewAngle )
  1155. m_reconstructViewBox = TRUE;
  1156. if( m_reconstructViewBox == TRUE )
  1157. reconstructViewBox();
  1158. // draw the view region on top of the radar reconstructing if necessary
  1159. drawViewBox( ul.x, ul.y, scaledWidth, scaledHeight );
  1160. } // end draw
  1161. // ------------------------------------------------------------------------------------------------
  1162. // ------------------------------------------------------------------------------------------------
  1163. void W3DRadar::refreshTerrain( TerrainLogic *terrain )
  1164. {
  1165. // extend base class
  1166. Radar::refreshTerrain( terrain );
  1167. // rebuild the entire terrain texture
  1168. buildTerrainTexture( terrain );
  1169. } // end refreshTerrain