mapctrl.cpp 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246
  1. /*
  2. ** Command & Conquer Renegade(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. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Combat *
  23. * *
  24. * $Archive:: /Commando/Code/wwui/mapctrl.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 1/10/02 6:39p $*
  29. * *
  30. * $Revision:: 15 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "mapctrl.h"
  36. #include "assetmgr.h"
  37. #include "refcount.h"
  38. #include "font3d.h"
  39. #include "mousemgr.h"
  40. #include "ww3d.h"
  41. #include "dialogmgr.h"
  42. #include "dialogbase.h"
  43. #include "stylemgr.h"
  44. #include "ffactory.h"
  45. ////////////////////////////////////////////////////////////////
  46. // Local constants
  47. ////////////////////////////////////////////////////////////////
  48. static const float ZOOM_RATE = 2.0F;
  49. static const float MARKER_WIDTH = 16.0F;
  50. static const float MARKER_HEIGHT = 16.0F;
  51. static const RectClass ZoomInUVRect (1, 33, 25, 57);
  52. static const RectClass ZoomOutUVRect (26, 33, 53, 57);
  53. static const Vector2 ButtonTextureSize (64, 64);
  54. static const RectClass EdgeTopUVRect (1, 2, 31, 30);
  55. static const RectClass EdgeRightUVRect (34, 1, 62, 31);
  56. static const RectClass EdgeLeftUVRect (2, 31, 30, 63);
  57. static const RectClass EdgeBottomUVRect (31, 34, 63, 62);
  58. ////////////////////////////////////////////////////////////////
  59. //
  60. // MapCtrlClass
  61. //
  62. ////////////////////////////////////////////////////////////////
  63. MapCtrlClass::MapCtrlClass (void) :
  64. Zoom (1.0F),
  65. ScrollPos (0, 0),
  66. MapSize (0, 0),
  67. IsDragging (false),
  68. IsZoomingIn (false),
  69. IsZoomingOut (false),
  70. IsUsingOverlay (false),
  71. InitialMousePos (0, 0),
  72. InitialScrollPos (0, 0),
  73. ZoomInButtonRect (0, 0, 0, 0),
  74. ZoomOutButtonRect (0, 0, 0, 0),
  75. MapCenterPoint (0, 0),
  76. MapScale (1, 1),
  77. CloudVector (NULL),
  78. CloudSize (0, 0),
  79. OverlayOpacity (1.0F),
  80. PulseDirection (-1.0F)
  81. {
  82. //
  83. // Configure the renderers
  84. //
  85. StyleMgrClass::Assign_Font (&TextRenderer, StyleMgrClass::FONT_CONTROLS);
  86. StyleMgrClass::Configure_Renderer (&ControlRenderer);
  87. StyleMgrClass::Configure_Renderer (&ButtonRenderer);
  88. StyleMgrClass::Configure_Renderer (&MapRenderer);
  89. StyleMgrClass::Configure_Renderer (&MapOverlayRenderer);
  90. StyleMgrClass::Configure_Renderer (&CloudRenderer);
  91. StyleMgrClass::Configure_Renderer (&EdgeRenderer);
  92. StyleMgrClass::Configure_Renderer (&IconRenderer);
  93. TextureClass *texture = WW3DAssetManager::Get_Instance ()->Get_Texture ("map_edges.tga", TextureClass::MIP_LEVELS_1);
  94. EdgeRenderer.Set_Texture (texture);
  95. REF_PTR_RELEASE (texture);
  96. texture = WW3DAssetManager::Get_Instance ()->Get_Texture ("mapicons.tga", TextureClass::MIP_LEVELS_1);
  97. ButtonRenderer.Set_Texture (texture);
  98. REF_PTR_RELEASE (texture);
  99. return ;
  100. }
  101. ////////////////////////////////////////////////////////////////
  102. //
  103. // ~MapCtrlClass
  104. //
  105. ////////////////////////////////////////////////////////////////
  106. MapCtrlClass::~MapCtrlClass (void)
  107. {
  108. Free_Cloud_Data ();
  109. return ;
  110. }
  111. ////////////////////////////////////////////////////////////////
  112. //
  113. // Create_Text_Renderers
  114. //
  115. ////////////////////////////////////////////////////////////////
  116. void
  117. MapCtrlClass::Create_Text_Renderers (void)
  118. {
  119. //
  120. // Start fresh
  121. //
  122. TextRenderer.Reset ();
  123. //
  124. // Draw the text
  125. //
  126. //StyleMgrClass::Render_Text (Title, &TextRenderer, TextRect, true, true);
  127. return ;
  128. }
  129. ////////////////////////////////////////////////////////////////
  130. //
  131. // Create_Control_Renderers
  132. //
  133. ////////////////////////////////////////////////////////////////
  134. void
  135. MapCtrlClass::Create_Control_Renderers (void)
  136. {
  137. Render2DClass &renderer = ControlRenderer;
  138. //
  139. // Configure this renderer
  140. //
  141. renderer.Reset ();
  142. renderer.Enable_Texturing (false);
  143. //
  144. // Determine which color to draw the outline in
  145. //
  146. int color = StyleMgrClass::Get_Line_Color ();
  147. int bkcolor = StyleMgrClass::Get_Bk_Color ();
  148. if (IsEnabled == false) {
  149. color = StyleMgrClass::Get_Disabled_Line_Color ();
  150. bkcolor = StyleMgrClass::Get_Disabled_Bk_Color ();
  151. }
  152. //
  153. // Draw the control's outline
  154. //
  155. renderer.Add_Outline (Rect, 1.0F, color);
  156. renderer.Add_Outline (ZoomInButtonRect, 1.0F, color);
  157. renderer.Add_Outline (ZoomOutButtonRect, 1.0F, color);
  158. //
  159. // Shrink the rectangle
  160. //
  161. RectClass rect = Rect;
  162. rect.Right -= 1;
  163. rect.Bottom -= 1;
  164. //
  165. // Calculate what the UV rectangle should be given the
  166. // current scroll and zoom factors.
  167. //
  168. RectClass map_uv_rect (0, 0, 1, 1);
  169. //
  170. // Calculate the center of the image
  171. //
  172. Vector2 center = (MapSize / 2) + ScrollPos;
  173. //
  174. // Calculate the 'zoomed' rectangle (in pixels)
  175. //
  176. float width = Rect.Width ();
  177. float height = Rect.Height ();
  178. map_uv_rect.Left = center.X - ((width / 2) / Zoom);
  179. map_uv_rect.Top = center.Y - ((height / 2) / Zoom);
  180. map_uv_rect.Right = center.X + ((width / 2) / Zoom);
  181. map_uv_rect.Bottom = center.Y + ((height / 2) / Zoom);
  182. //
  183. // Render any markers that are in view
  184. //
  185. IconRenderer.Reset ();
  186. for (int index = 0; index < MarkerList.Count (); index ++) {
  187. const MapMarkerClass &marker = MarkerList[index];
  188. //
  189. // Calculate what pixel position this marker is located at.
  190. //
  191. Vector2 map_pos;
  192. map_pos.X = MapCenterPoint.X + (marker.Get_Position ().X * MapScale.X);
  193. map_pos.Y = MapCenterPoint.Y - (marker.Get_Position ().Y * MapScale.Y);
  194. //
  195. // Is this marker currently visible?
  196. //
  197. if (map_uv_rect.Contains (map_pos)) {
  198. //
  199. // Calculate what percentage of the visible map the marker's position is
  200. //
  201. Vector2 percent;
  202. percent.X = (map_pos.X - map_uv_rect.Left) / map_uv_rect.Width ();
  203. percent.Y = (map_pos.Y - map_uv_rect.Top) / map_uv_rect.Height ();
  204. //
  205. // Convert the perctange into a screen position
  206. //
  207. Vector2 screen_pos;
  208. screen_pos.X = rect.Left + (percent.X * rect.Width ());
  209. screen_pos.Y = rect.Top + (percent.Y * rect.Height ());
  210. //
  211. // Build a screen rectangle we'll render into
  212. //
  213. RectClass screen_rect;
  214. screen_rect.Left = int(screen_pos.X - (marker.Get_Rect ().Width () / 2));
  215. screen_rect.Top = int(screen_pos.Y - (marker.Get_Rect ().Height () / 2));
  216. screen_rect.Right = int(screen_rect.Left + marker.Get_Rect ().Width ());
  217. screen_rect.Bottom = int(screen_rect.Top + marker.Get_Rect ().Height ());
  218. //
  219. // Convert the pixel coordinates into normalized UVs
  220. //
  221. RectClass marker_uv_rect = marker.Get_Rect ();
  222. marker_uv_rect.Inverse_Scale (Vector2 (IconTextureSize.X, IconTextureSize.Y));
  223. //
  224. // Clip the rectangle as necessary
  225. //
  226. RectClass clipped_rect;
  227. clipped_rect.Left = max (screen_rect.Left, Rect.Left);
  228. clipped_rect.Right = min (screen_rect.Right, Rect.Right);
  229. clipped_rect.Top = max (screen_rect.Top, Rect.Top);
  230. clipped_rect.Bottom = min (screen_rect.Bottom, Rect.Bottom);
  231. //
  232. // Clip the texture to the specified area
  233. //
  234. RectClass clipped_uv_rect;
  235. float clip_percent = ((clipped_rect.Left - screen_rect.Left) / screen_rect.Width ());
  236. clipped_uv_rect.Left = marker_uv_rect.Left + (marker_uv_rect.Width () * clip_percent);
  237. clip_percent = ((clipped_rect.Right - screen_rect.Left) / screen_rect.Width ());
  238. clipped_uv_rect.Right = marker_uv_rect.Left + (marker_uv_rect.Width () * clip_percent);
  239. clip_percent = ((clipped_rect.Top - screen_rect.Top) / screen_rect.Height ());
  240. clipped_uv_rect.Top = marker_uv_rect.Top + (marker_uv_rect.Height () * clip_percent);
  241. clip_percent = ((clipped_rect.Bottom - screen_rect.Top) / screen_rect.Height ());
  242. clipped_uv_rect.Bottom = marker_uv_rect.Top + (marker_uv_rect.Height () * clip_percent);
  243. //
  244. // Use the clipped rectangles to render
  245. //
  246. screen_rect = clipped_rect;
  247. marker_uv_rect = clipped_uv_rect;
  248. //
  249. // Render the marker
  250. //
  251. IconRenderer.Add_Quad (screen_rect, marker_uv_rect, marker.Get_Color ());
  252. }
  253. }
  254. //
  255. // Render the buttons
  256. //
  257. ButtonRenderer.Reset ();
  258. RectClass temp_rect1;
  259. temp_rect1.Left = int(ZoomInButtonRect.Center ().X - (ZoomInUVRect.Width () / 2));
  260. temp_rect1.Top = int(ZoomInButtonRect.Center ().Y - (ZoomInUVRect.Height () / 2));
  261. temp_rect1.Right = int(temp_rect1.Left + ZoomInUVRect.Width ());
  262. temp_rect1.Bottom = int(temp_rect1.Top + ZoomInUVRect.Height ());
  263. RectClass temp_rect2;
  264. temp_rect2.Left = int(ZoomOutButtonRect.Center ().X - (ZoomInUVRect.Width () / 2));
  265. temp_rect2.Top = int(ZoomOutButtonRect.Center ().Y - (ZoomInUVRect.Height () / 2));
  266. temp_rect2.Right = int(temp_rect2.Left + ZoomInUVRect.Width ());
  267. temp_rect2.Bottom = int(temp_rect2.Top + ZoomInUVRect.Height ());
  268. RectClass temp_uv_rect1 = ZoomInUVRect;
  269. RectClass temp_uv_rect2 = ZoomOutUVRect;
  270. temp_uv_rect1.Inverse_Scale (Vector2 (ButtonTextureSize.X, ButtonTextureSize.Y));
  271. temp_uv_rect2.Inverse_Scale (Vector2 (ButtonTextureSize.X, ButtonTextureSize.Y));
  272. ButtonRenderer.Add_Quad (temp_rect1, temp_uv_rect1);
  273. ButtonRenderer.Add_Quad (temp_rect2, temp_uv_rect2);
  274. //
  275. // Normalize the map UVs
  276. //
  277. map_uv_rect.Inverse_Scale (Vector2 (MapSize.X , MapSize.Y));
  278. //
  279. // Render the map
  280. //
  281. MapRenderer.Reset ();
  282. MapOverlayRenderer.Reset ();
  283. MapRenderer.Enable_Alpha (false);
  284. MapOverlayRenderer.Enable_Alpha (true);
  285. MapRenderer.Add_Quad (rect, map_uv_rect);
  286. MapOverlayRenderer.Add_Quad (rect, map_uv_rect);
  287. return ;
  288. }
  289. ////////////////////////////////////////////////////////////////
  290. //
  291. // Create_Cloud_Renderer
  292. //
  293. ////////////////////////////////////////////////////////////////
  294. void
  295. MapCtrlClass::Create_Cloud_Renderer (void)
  296. {
  297. CloudRenderer.Reset ();
  298. EdgeRenderer.Reset ();
  299. CloudRenderer.Enable_Texturing (false);
  300. //
  301. // Calculate the dimensions of the cloud at this zoom factor
  302. //
  303. float cloud_width = (MapSize.X / CloudSize.I) * Zoom;
  304. float cloud_height = (MapSize.Y / CloudSize.J) * Zoom;
  305. Vector2 center = (MapSize / 2) - ScrollPos;
  306. float delta_x = ((MapSize.X * Zoom) - Rect.Width ()) / 2;
  307. float delta_y = ((MapSize.Y * Zoom) - Rect.Height ()) / 2;
  308. float cloud_x_pos = Rect.Left - (delta_x + (ScrollPos.X * Zoom));
  309. float cloud_y_pos = Rect.Top - (delta_y + (ScrollPos.Y * Zoom));
  310. //
  311. // Loop over all the cells
  312. //
  313. for (int cell_y = 0; cell_y < CloudSize.J; cell_y ++) {
  314. //
  315. // Reset the x position
  316. //
  317. cloud_x_pos = Rect.Left - (delta_x + (ScrollPos.X * Zoom));
  318. for (int cell_x = 0; cell_x < CloudSize.I; cell_x ++) {
  319. //
  320. // Build the rectangle for this cloud section
  321. //
  322. RectClass cloud_rect;
  323. cloud_rect.Left = cloud_x_pos;
  324. cloud_rect.Top = cloud_y_pos;
  325. cloud_rect.Right = cloud_rect.Left + cloud_width;
  326. cloud_rect.Bottom = cloud_rect.Top + cloud_height;
  327. //
  328. // Clip the rectangle
  329. //
  330. cloud_rect.Left = max (cloud_rect.Left, Rect.Left);
  331. cloud_rect.Top = max (cloud_rect.Top, Rect.Top);
  332. cloud_rect.Right = min (cloud_rect.Right, Rect.Right);
  333. cloud_rect.Bottom = min (cloud_rect.Bottom, Rect.Bottom);
  334. //
  335. // Don't render anything if this cell is completely clipped.
  336. //
  337. if (cloud_rect.Width () > 0 && cloud_rect.Height () > 0) {
  338. //
  339. // Is this cell clouded?
  340. //
  341. if (Is_Cell_Shrouded (cell_x, cell_y)) {
  342. //
  343. // Render a black square in this cell
  344. //
  345. CloudRenderer.Add_Quad (cloud_rect, RGB_TO_INT32 (0, 0, 0));
  346. } else {
  347. RectClass delta_rect;
  348. delta_rect.Left = ((cloud_rect.Left - cloud_x_pos) / cloud_width);
  349. delta_rect.Top = ((cloud_rect.Top - cloud_y_pos) / cloud_height);
  350. delta_rect.Right = ((cloud_rect.Right - (cloud_x_pos + cloud_width)) / cloud_width);
  351. delta_rect.Bottom = ((cloud_rect.Bottom - (cloud_y_pos + cloud_height)) / cloud_height);
  352. if (Is_Cell_Shrouded (cell_x - 1, cell_y)) {
  353. RectClass uv_rect = EdgeLeftUVRect;
  354. //
  355. // Clip the uv-rectangle to fit the color rectangle
  356. //
  357. uv_rect.Left += uv_rect.Width () * delta_rect.Left;
  358. uv_rect.Right += uv_rect.Width () * delta_rect.Right;
  359. uv_rect.Top += uv_rect.Height () * delta_rect.Top;
  360. uv_rect.Bottom += uv_rect.Height () * delta_rect.Bottom;
  361. uv_rect.Inverse_Scale (Vector2 (64.0F, 64.0F));
  362. EdgeRenderer.Add_Quad (cloud_rect, uv_rect);
  363. }
  364. if (Is_Cell_Shrouded (cell_x + 1, cell_y)) {
  365. RectClass uv_rect = EdgeRightUVRect;
  366. //
  367. // Clip the uv-rectangle to fit the color rectangle
  368. //
  369. uv_rect.Left += uv_rect.Width () * delta_rect.Left;
  370. uv_rect.Right += uv_rect.Width () * delta_rect.Right;
  371. uv_rect.Top += uv_rect.Height () * delta_rect.Top;
  372. uv_rect.Bottom += uv_rect.Height () * delta_rect.Bottom;
  373. uv_rect.Inverse_Scale (Vector2 (64.0F, 64.0F));
  374. EdgeRenderer.Add_Quad (cloud_rect, uv_rect);
  375. }
  376. if (Is_Cell_Shrouded (cell_x, cell_y - 1)) {
  377. RectClass uv_rect = EdgeTopUVRect;
  378. //
  379. // Clip the uv-rectangle to fit the color rectangle
  380. //
  381. uv_rect.Left += uv_rect.Width () * delta_rect.Left;
  382. uv_rect.Right += uv_rect.Width () * delta_rect.Right;
  383. uv_rect.Top += uv_rect.Height () * delta_rect.Top;
  384. uv_rect.Bottom += uv_rect.Height () * delta_rect.Bottom;
  385. uv_rect.Inverse_Scale (Vector2 (64.0F, 64.0F));
  386. EdgeRenderer.Add_Quad (cloud_rect, uv_rect);
  387. }
  388. if (Is_Cell_Shrouded (cell_x, cell_y + 1)) {
  389. RectClass uv_rect = EdgeBottomUVRect;
  390. //
  391. // Clip the uv-rectangle to fit the color rectangle
  392. //
  393. uv_rect.Left += uv_rect.Width () * delta_rect.Left;
  394. uv_rect.Right += uv_rect.Width () * delta_rect.Right;
  395. uv_rect.Top += uv_rect.Height () * delta_rect.Top;
  396. uv_rect.Bottom += uv_rect.Height () * delta_rect.Bottom;
  397. uv_rect.Inverse_Scale (Vector2 (64.0F, 64.0F));
  398. EdgeRenderer.Add_Quad (cloud_rect, uv_rect);
  399. }
  400. }
  401. }
  402. //
  403. // Advance one column
  404. //
  405. cloud_x_pos += cloud_width;
  406. }
  407. //
  408. // Advance one row
  409. //
  410. cloud_y_pos += cloud_height;
  411. }
  412. return ;
  413. }
  414. ////////////////////////////////////////////////////////////////
  415. //
  416. // On_Set_Cursor
  417. //
  418. ////////////////////////////////////////////////////////////////
  419. void
  420. MapCtrlClass::On_Set_Cursor (const Vector2 &mouse_pos)
  421. {
  422. //
  423. // Change the mouse cursor
  424. //
  425. if ( ZoomInButtonRect.Contains (mouse_pos) ||
  426. ZoomOutButtonRect.Contains (mouse_pos) ||
  427. (::GetAsyncKeyState (VK_CONTROL) < 0) ||
  428. Marker_From_Pos (mouse_pos) != -1)
  429. {
  430. MouseMgrClass::Set_Cursor (MouseMgrClass::CURSOR_ACTION);
  431. } else {
  432. MouseMgrClass::Set_Cursor (MouseMgrClass::CURSOR_PAN_UP);
  433. }
  434. return ;
  435. }
  436. ////////////////////////////////////////////////////////////////
  437. //
  438. // Update_Client_Rect
  439. //
  440. ////////////////////////////////////////////////////////////////
  441. void
  442. MapCtrlClass::Update_Client_Rect (void)
  443. {
  444. //
  445. // Set the client area
  446. //
  447. ClientRect = Rect;
  448. //
  449. // Build the zoom button rectangles
  450. //
  451. ZoomOutButtonRect;
  452. ZoomOutButtonRect.Left = int(Rect.Right - (ZoomInUVRect.Width () + 2));
  453. ZoomOutButtonRect.Top = int(Rect.Bottom - (ZoomInUVRect.Height () + 2));
  454. ZoomOutButtonRect.Right = int(Rect.Right);
  455. ZoomOutButtonRect.Bottom = int(Rect.Bottom);
  456. ZoomInButtonRect = ZoomOutButtonRect;
  457. ZoomInButtonRect.Left = int(ZoomInButtonRect.Left - (ZoomInUVRect.Width () + 2));
  458. ZoomInButtonRect.Right = int(ZoomInButtonRect.Right - (ZoomInUVRect.Width () + 2));
  459. Set_Dirty ();
  460. return ;
  461. }
  462. ////////////////////////////////////////////////////////////////
  463. //
  464. // Render
  465. //
  466. ////////////////////////////////////////////////////////////////
  467. void
  468. MapCtrlClass::Render (void)
  469. {
  470. Update_Pulse ();
  471. //
  472. // Recreate the renderers (if necessary)
  473. //
  474. if (IsDirty) {
  475. Create_Cloud_Renderer ();
  476. Create_Control_Renderers ();
  477. Create_Text_Renderers ();
  478. }
  479. //
  480. // Render the background and text for the current state
  481. //
  482. TextRenderer.Render ();
  483. MapRenderer.Render ();
  484. if (IsUsingOverlay) {
  485. MapOverlayRenderer.Render ();
  486. }
  487. IconRenderer.Render ();
  488. CloudRenderer.Render ();
  489. EdgeRenderer.Render ();
  490. ButtonRenderer.Render ();
  491. ControlRenderer.Render ();
  492. DialogControlClass::Render ();
  493. return ;
  494. }
  495. ////////////////////////////////////////////////////////////////
  496. //
  497. // On_LButton_Down
  498. //
  499. ////////////////////////////////////////////////////////////////
  500. void
  501. MapCtrlClass::On_LButton_Down (const Vector2 &mouse_pos)
  502. {
  503. IsDragging = false;
  504. IsZoomingIn = false;
  505. IsZoomingOut = false;
  506. if (ZoomInButtonRect.Contains (mouse_pos)) {
  507. IsZoomingIn = true;
  508. Set_Capture ();
  509. } else if (ZoomOutButtonRect.Contains (mouse_pos)) {
  510. IsZoomingOut = true;
  511. Set_Capture ();
  512. } else {
  513. //
  514. // If the user held the control as they clicked, then
  515. // notify the advise sinks that a position is being requested
  516. //
  517. if (::GetAsyncKeyState (VK_CONTROL) < 0) {
  518. Vector3 world_pos = Position_To_Coord (mouse_pos);
  519. ADVISE_NOTIFY (On_MapCtrl_Pos_Clicked (this, Get_ID (), world_pos));
  520. } else {
  521. //
  522. // Begin panning
  523. //
  524. IsDragging = true;
  525. Set_Capture ();
  526. InitialMousePos = mouse_pos;
  527. InitialScrollPos = ScrollPos;
  528. }
  529. }
  530. return ;
  531. }
  532. ////////////////////////////////////////////////////////////////
  533. //
  534. // On_LButton_Up
  535. //
  536. ////////////////////////////////////////////////////////////////
  537. void
  538. MapCtrlClass::On_LButton_Up (const Vector2 &mouse_pos)
  539. {
  540. Release_Capture ();
  541. IsDragging = false;
  542. IsZoomingIn = false;
  543. IsZoomingOut = false;
  544. return ;
  545. }
  546. ////////////////////////////////////////////////////////////////
  547. //
  548. // Set_Map_Texture
  549. //
  550. ////////////////////////////////////////////////////////////////
  551. void
  552. MapCtrlClass::Set_Map_Texture (const char *filename)
  553. {
  554. //
  555. // Load the texture
  556. //
  557. TextureClass *texture = WW3DAssetManager::Get_Instance ()->Get_Texture (filename, TextureClass::MIP_LEVELS_1);
  558. if (texture != NULL) {
  559. //
  560. // Get the dimensions of the texture
  561. //
  562. MapSize.X = texture->Get_Width();
  563. MapSize.Y = texture->Get_Height();
  564. texture->Set_U_Addr_Mode (TextureClass::TEXTURE_ADDRESS_CLAMP);
  565. texture->Set_V_Addr_Mode (TextureClass::TEXTURE_ADDRESS_CLAMP);
  566. //
  567. // Set the texture for the map
  568. //
  569. MapRenderer.Set_Texture (texture);
  570. REF_PTR_RELEASE (texture);
  571. //
  572. // Force a repaint
  573. //
  574. Set_Dirty ();
  575. }
  576. //
  577. // Try to find a texture to use as an overlay...
  578. //
  579. StringClass overlay_texture_name = filename;
  580. int len = overlay_texture_name.Get_Length ();
  581. overlay_texture_name.Erase (len - 4, 4);
  582. overlay_texture_name += "a.tga";
  583. //
  584. // Check to see if the overlay exists
  585. //
  586. IsUsingOverlay = false;
  587. FileClass *file = _TheFileFactory->Get_File (overlay_texture_name);
  588. if (file != NULL) {
  589. bool is_valid = true;
  590. //
  591. // If the file doesn't exist as a TGA try to find it as a DDS
  592. //
  593. if (file->Is_Available () == false) {
  594. is_valid = false;
  595. _TheFileFactory->Return_File (file);
  596. len = overlay_texture_name.Get_Length ();
  597. overlay_texture_name.Erase (len - 3, 3);
  598. overlay_texture_name += "dds";
  599. file = _TheFileFactory->Get_File (overlay_texture_name);
  600. if (file != NULL && file->Is_Available ()) {
  601. is_valid = true;
  602. }
  603. }
  604. if (is_valid) {
  605. texture = WW3DAssetManager::Get_Instance ()->Get_Texture (overlay_texture_name, TextureClass::MIP_LEVELS_1);
  606. //
  607. // Configure the overlay renderer
  608. //
  609. if (texture != NULL) {
  610. texture->Set_U_Addr_Mode (TextureClass::TEXTURE_ADDRESS_CLAMP);
  611. texture->Set_V_Addr_Mode (TextureClass::TEXTURE_ADDRESS_CLAMP);
  612. MapOverlayRenderer.Set_Texture (texture);
  613. REF_PTR_RELEASE (texture);
  614. IsUsingOverlay = true;
  615. }
  616. }
  617. if (file != NULL) {
  618. _TheFileFactory->Return_File (file);
  619. }
  620. }
  621. return ;
  622. }
  623. ////////////////////////////////////////////////////////////////
  624. //
  625. // On_Set_Focus
  626. //
  627. ////////////////////////////////////////////////////////////////
  628. void
  629. MapCtrlClass::On_Set_Focus (void)
  630. {
  631. DialogControlClass::On_Set_Focus ();
  632. return ;
  633. }
  634. ////////////////////////////////////////////////////////////////
  635. //
  636. // On_Kill_Focus
  637. //
  638. ////////////////////////////////////////////////////////////////
  639. void
  640. MapCtrlClass::On_Kill_Focus (DialogControlClass *focus)
  641. {
  642. IsDragging = false;
  643. IsZoomingIn = false;
  644. IsZoomingOut = false;
  645. DialogControlClass::On_Kill_Focus (focus);
  646. return ;
  647. }
  648. ////////////////////////////////////////////////////////////////
  649. //
  650. // On_Mouse_Move
  651. //
  652. ////////////////////////////////////////////////////////////////
  653. void
  654. MapCtrlClass::On_Mouse_Move (const Vector2 &mouse_pos)
  655. {
  656. //
  657. // Is the user "dragging" inside the control
  658. //
  659. if (HasFocus) {
  660. if (IsDragging) {
  661. Vector2 delta = (InitialMousePos - mouse_pos);
  662. //
  663. // Update the scroll position
  664. //
  665. ScrollPos.X = InitialScrollPos.X + (delta.X / Zoom);
  666. ScrollPos.Y = InitialScrollPos.Y + (delta.Y / Zoom);
  667. Clamp_Scroll_Pos ();
  668. //
  669. // Force a repaint
  670. //
  671. Set_Dirty ();
  672. }
  673. }
  674. //
  675. // Notify the parent
  676. //
  677. if (IsDragging == false) {
  678. int index = Marker_From_Pos (mouse_pos);
  679. ADVISE_NOTIFY (On_MapCtrl_Marker_Hilighted (this, Get_ID (), index));
  680. }
  681. return ;
  682. }
  683. ////////////////////////////////////////////////////////////////
  684. //
  685. // On_Frame_Update
  686. //
  687. ////////////////////////////////////////////////////////////////
  688. void
  689. MapCtrlClass::On_Frame_Update (void)
  690. {
  691. if (HasFocus) {
  692. if (IsZoomingIn) {
  693. //
  694. // Zoom-In
  695. //
  696. Zoom += (DialogMgrClass::Get_Frame_Time () / 1000.F) * ZOOM_RATE;
  697. Zoom = WWMath::Clamp (Zoom, 0.5F, 1.5F);
  698. Set_Dirty ();
  699. } else if (IsZoomingOut) {
  700. //
  701. // Zoom-Out
  702. //
  703. Zoom -= (DialogMgrClass::Get_Frame_Time () / 1000.0F) * ZOOM_RATE;
  704. Zoom = WWMath::Clamp (Zoom, 0.5F, 1.5F);
  705. Set_Dirty ();
  706. }
  707. }
  708. return ;
  709. }
  710. ////////////////////////////////////////////////////////////////
  711. //
  712. // Add_Marker
  713. //
  714. ////////////////////////////////////////////////////////////////
  715. int
  716. MapCtrlClass::Add_Marker
  717. (
  718. const WCHAR * name,
  719. const Vector3 & pos,
  720. const RectClass & uv_rect,
  721. int color
  722. )
  723. {
  724. //
  725. // Add this marker to our list
  726. //
  727. MapMarkerClass marker;
  728. marker.Set_Name (name);
  729. marker.Set_Position (pos);
  730. marker.Set_Rect (uv_rect);
  731. marker.Set_Color (color);
  732. MarkerList.Add (marker);
  733. //
  734. // Return the new marker's index
  735. //
  736. return (MarkerList.Count () - 1);
  737. }
  738. ////////////////////////////////////////////////////////////////
  739. //
  740. // Get_Marker_Data
  741. //
  742. ////////////////////////////////////////////////////////////////
  743. uint32
  744. MapCtrlClass::Get_Marker_Data (int index)
  745. {
  746. return MarkerList[index].Get_User_Data ();
  747. }
  748. ////////////////////////////////////////////////////////////////
  749. //
  750. // Set_Marker_Data
  751. //
  752. ////////////////////////////////////////////////////////////////
  753. void
  754. MapCtrlClass::Set_Marker_Data (int index, uint32 user_data)
  755. {
  756. MarkerList[index].Set_User_Data (user_data);
  757. return ;
  758. }
  759. ////////////////////////////////////////////////////////////////
  760. //
  761. // Remove_Marker
  762. //
  763. ////////////////////////////////////////////////////////////////
  764. void
  765. MapCtrlClass::Remove_Marker (int index)
  766. {
  767. MarkerList.Delete (index);
  768. return ;
  769. }
  770. ////////////////////////////////////////////////////////////////
  771. //
  772. // Set_Marker_Texture
  773. //
  774. ////////////////////////////////////////////////////////////////
  775. void
  776. MapCtrlClass::Set_Marker_Texture (const char *filename)
  777. {
  778. //
  779. // Load the texture
  780. //
  781. TextureClass *texture = WW3DAssetManager::Get_Instance ()->Get_Texture (filename, TextureClass::MIP_LEVELS_1);
  782. if (texture != NULL) {
  783. //
  784. // Get the dimensions of the texture
  785. //
  786. // SurfaceClass::SurfaceDescription surface_desc;
  787. // texture->Get_Level_Description (surface_desc);
  788. // IconTextureSize.X = surface_desc.Width;
  789. // IconTextureSize.Y = surface_desc.Height;
  790. IconTextureSize.X = texture->Get_Width ();
  791. IconTextureSize.Y = texture->Get_Height ();
  792. //
  793. // Set the texture for the map
  794. //
  795. IconRenderer.Set_Texture (texture);
  796. REF_PTR_RELEASE (texture);
  797. //
  798. // Force a repaint
  799. //
  800. Set_Dirty ();
  801. }
  802. return ;
  803. }
  804. ////////////////////////////////////////////////////////////////
  805. //
  806. // Center_View_About_Marker
  807. //
  808. ////////////////////////////////////////////////////////////////
  809. void
  810. MapCtrlClass::Center_View_About_Marker (int marker_index)
  811. {
  812. const MapMarkerClass &marker = MarkerList[marker_index];
  813. //
  814. // Calculate what pixel position this marker is located at.
  815. //
  816. Vector2 map_pos;
  817. map_pos.X = MapCenterPoint.X + (marker.Get_Position ().X * MapScale.X);
  818. map_pos.Y = MapCenterPoint.Y - (marker.Get_Position ().Y * MapScale.Y);
  819. //
  820. // Now calculate what scroll offset we'd need to be centered
  821. // about this position
  822. //
  823. ScrollPos = map_pos - (MapSize * 0.5F);
  824. Clamp_Scroll_Pos ();
  825. //
  826. // Force a repaint
  827. //
  828. Set_Dirty ();
  829. return ;
  830. }
  831. ////////////////////////////////////////////////////////////////
  832. //
  833. // Marker_From_Pos
  834. //
  835. ////////////////////////////////////////////////////////////////
  836. int
  837. MapCtrlClass::Marker_From_Pos (const Vector2 &mouse_pos)
  838. {
  839. int retval = -1;
  840. Vector2 center = (MapSize / 2) + ScrollPos;
  841. //
  842. // Loop over all the markers in our list
  843. //
  844. for (int index = 0; index < MarkerList.Count (); index ++) {
  845. const MapMarkerClass &marker = MarkerList[index];
  846. //
  847. // Calculate what texel position this marker is located at.
  848. //
  849. Vector2 map_pos;
  850. map_pos.X = MapCenterPoint.X + (marker.Get_Position ().X * MapScale.X);
  851. map_pos.Y = MapCenterPoint.Y - (marker.Get_Position ().Y * MapScale.Y);
  852. //
  853. // Get the screen position of this objective
  854. //
  855. Vector2 screen_pos = Rect.Center () + ((map_pos - center) * Zoom);
  856. if (Rect.Contains (screen_pos)) {
  857. //
  858. // Build a screen rectangle of the marker
  859. //
  860. RectClass screen_rect;
  861. screen_rect.Left = screen_pos.X - (marker.Get_Rect ().Width () / 2);
  862. screen_rect.Top = screen_pos.Y - (marker.Get_Rect ().Height () / 2);
  863. screen_rect.Right = screen_rect.Left + marker.Get_Rect ().Width ();
  864. screen_rect.Bottom = screen_rect.Top + marker.Get_Rect ().Height ();
  865. //
  866. // Is the mouse over this marker?
  867. //
  868. if (screen_rect.Contains (mouse_pos)) {
  869. retval = index;
  870. break;
  871. }
  872. }
  873. }
  874. return retval;
  875. }
  876. ////////////////////////////////////////////////////////////////
  877. //
  878. // Clamp_Scroll_Pos
  879. //
  880. ////////////////////////////////////////////////////////////////
  881. void
  882. MapCtrlClass::Clamp_Scroll_Pos (void)
  883. {
  884. float width = Rect.Width ();
  885. float height = Rect.Height ();
  886. //
  887. // Determine what our scroll offset should be
  888. // at this zoom factor
  889. //
  890. float max_x_offset = WWMath::Clamp (MapSize.X - width, 0, MapSize.X);
  891. float max_y_offset = WWMath::Clamp (MapSize.Y - height, 0, MapSize.Y);
  892. float offset_x = ScrollPos.X / Zoom;
  893. float offset_y = ScrollPos.Y / Zoom;
  894. offset_x = WWMath::Clamp (offset_x, -max_x_offset, max_x_offset);
  895. offset_y = WWMath::Clamp (offset_y, -max_y_offset, max_y_offset);
  896. //
  897. // Re-adjust our scroll position so it doesn't go off the page
  898. //
  899. ScrollPos.X = offset_x * Zoom;
  900. ScrollPos.Y = offset_y * Zoom;
  901. return ;
  902. }
  903. ////////////////////////////////////////////////////////////////
  904. //
  905. // Initialize_Cloud
  906. //
  907. ////////////////////////////////////////////////////////////////
  908. void
  909. MapCtrlClass::Initialize_Cloud (int cells_x, int cells_y)
  910. {
  911. Free_Cloud_Data ();
  912. CloudSize.I = cells_x;
  913. CloudSize.J = cells_y;
  914. //
  915. // Allocate a bit vector large enough to hold the cells
  916. //
  917. CloudVector = new uint32[((CloudSize.I * CloudSize.J) / sizeof (uint32)) + 1];
  918. return ;
  919. }
  920. ////////////////////////////////////////////////////////////////
  921. //
  922. // Reset_Cloud
  923. //
  924. ////////////////////////////////////////////////////////////////
  925. void
  926. MapCtrlClass::Reset_Cloud (void)
  927. {
  928. ::memset (CloudVector, 0xFF, sizeof (uint32) * ((CloudSize.I * CloudSize.J) / sizeof (uint32)) + 1);
  929. return ;
  930. }
  931. ////////////////////////////////////////////////////////////////
  932. //
  933. // Set_Cloud_Cell
  934. //
  935. ////////////////////////////////////////////////////////////////
  936. void
  937. MapCtrlClass::Set_Cloud_Cell (int cell_x, int cell_y, bool is_visible)
  938. {
  939. if (CloudVector == NULL) {
  940. return ;
  941. }
  942. //
  943. // Calculate where in our bit-vector the cell lies
  944. //
  945. int bit_offset = (cell_y * CloudSize.I) + cell_x;
  946. int index = bit_offset / 32;
  947. int bit = (bit_offset - (index * 32)) + 1;
  948. //
  949. // Set (or clear) the bit
  950. //
  951. if (is_visible) {
  952. CloudVector[index] &= ~(1 << bit);
  953. } else {
  954. CloudVector[index] |= (1 << bit);
  955. }
  956. return ;
  957. }
  958. ////////////////////////////////////////////////////////////////
  959. //
  960. // Free_Cloud_Data
  961. //
  962. ////////////////////////////////////////////////////////////////
  963. void
  964. MapCtrlClass::Free_Cloud_Data (void)
  965. {
  966. if (CloudVector != NULL) {
  967. delete CloudVector;
  968. CloudVector = NULL;
  969. }
  970. CloudSize.Set (0, 0);
  971. return ;
  972. }
  973. ////////////////////////////////////////////////////////////////
  974. //
  975. // Position_To_Coord
  976. //
  977. ////////////////////////////////////////////////////////////////
  978. Vector3
  979. MapCtrlClass::Position_To_Coord (const Vector2 &mouse_pos)
  980. {
  981. Vector2 percent;
  982. percent.X = (mouse_pos.X - Rect.Left) / Rect.Width ();
  983. percent.Y = (mouse_pos.Y - Rect.Top) / Rect.Height ();
  984. //
  985. // Calculate the center of the image
  986. //
  987. RectClass map_uv_rect (0, 0, 1, 1);
  988. Vector2 center = (MapSize / 2) + ScrollPos;
  989. //
  990. // Calculate the 'zoomed' rectangle (in pixels)
  991. //
  992. float width = Rect.Width ();
  993. float height = Rect.Height ();
  994. map_uv_rect.Left = center.X - ((width / 2) / Zoom);
  995. map_uv_rect.Top = center.Y - ((height / 2) / Zoom);
  996. map_uv_rect.Right = center.X + ((width / 2) / Zoom);
  997. map_uv_rect.Bottom = center.Y + ((height / 2) / Zoom);
  998. Vector2 map_pos;
  999. map_pos.X = map_uv_rect.Left + (map_uv_rect.Width () * percent.X);
  1000. map_pos.Y = map_uv_rect.Top + (map_uv_rect.Height () * percent.Y);
  1001. //
  1002. // Now convert the map position into a world-space position
  1003. //
  1004. Vector3 result;
  1005. result.X = (map_pos.X - MapCenterPoint.X) / MapScale.X;
  1006. result.Y = (MapCenterPoint.Y - map_pos.Y) / MapScale.Y;
  1007. result.Z = 0;
  1008. return result;
  1009. }
  1010. ////////////////////////////////////////////////////////////////
  1011. //
  1012. // Update_Pulse
  1013. //
  1014. ////////////////////////////////////////////////////////////////
  1015. void
  1016. MapCtrlClass::Update_Pulse (void)
  1017. {
  1018. if (IsUsingOverlay == false) {
  1019. return ;
  1020. }
  1021. const float PULSE_RATE = 0.7F;
  1022. const float MIN_OPACITY = 0.5F;
  1023. const float MAX_OPACITY = 1.0F;
  1024. //
  1025. // Pulse the bar
  1026. //
  1027. float delta = PULSE_RATE * (DialogMgrClass::Get_Frame_Time () / 1000.0F);
  1028. OverlayOpacity += PulseDirection * delta;
  1029. //
  1030. // Clamp the opacity
  1031. //
  1032. if (OverlayOpacity <= MIN_OPACITY) {
  1033. OverlayOpacity = MIN_OPACITY;
  1034. PulseDirection = 1.0F;
  1035. } else if (OverlayOpacity >= MAX_OPACITY) {
  1036. OverlayOpacity = MAX_OPACITY;
  1037. PulseDirection = -1.0F;
  1038. }
  1039. int overlay_color = VRGBA_TO_INT32 (Vector4 (1.0F, 1.0F, 1.0F, OverlayOpacity));
  1040. //
  1041. // Update the color vector array
  1042. //
  1043. DynamicVectorClass<unsigned long> &color_array = MapOverlayRenderer.Get_Color_Array ();
  1044. for (int index = 0; index < color_array.Count (); index ++) {
  1045. color_array[index] = overlay_color;
  1046. }
  1047. return ;
  1048. }