viewerctrl.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  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/viewerctrl.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 11/06/01 4:23p $*
  29. * *
  30. * $Revision:: 9 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. // Disable warning about exception handling not being enabled. It's used as part of STL - in a part of STL we don't use.
  36. #pragma warning(disable : 4530)
  37. #include "viewerctrl.h"
  38. #include "assetmgr.h"
  39. #include "refcount.h"
  40. #include "mousemgr.h"
  41. #include "ww3d.h"
  42. #include "dialogmgr.h"
  43. #include "dialogbase.h"
  44. #include "stylemgr.h"
  45. #include "scene.h"
  46. #include "camera.h"
  47. #include "rendobj.h"
  48. #include "hanim.h"
  49. #include "mesh.h"
  50. #include "meshmdl.h"
  51. #include "light.h"
  52. ////////////////////////////////////////////////////////////////
  53. // Local constants
  54. ////////////////////////////////////////////////////////////////
  55. ////////////////////////////////////////////////////////////////
  56. //
  57. // ViewerCtrlClass
  58. //
  59. ////////////////////////////////////////////////////////////////
  60. ViewerCtrlClass::ViewerCtrlClass (void) :
  61. Scene (NULL),
  62. Camera (NULL),
  63. Model (NULL),
  64. Distance (0),
  65. ZRotation (0),
  66. MinCameraDist (0),
  67. RotationRate (DEG_TO_RADF (0.0f)),
  68. BoundingBox (Vector3 (0, 0, 0), Vector3 (0, 0, 0)),
  69. IsCameraDirty (true),
  70. IsBackgroundVisible (true),
  71. InterfaceMode (Z_ROTATION)
  72. {
  73. Vector3 ambientcolor (0.2f, 0.2f, 0.2f);
  74. LastMousePosition = DialogMgrClass::Get_Mouse_Pos();
  75. //
  76. // Configure the 2D renderer
  77. //
  78. StyleMgrClass::Configure_Renderer (&ControlRenderer);
  79. //
  80. // Create a scene to use to render the model
  81. //
  82. Scene = new SimpleSceneClass;
  83. Scene->Set_Ambient_Light (ambientcolor);
  84. //
  85. // Create a camera to use in the scene
  86. // NOTE: Near and far clip planes are set to values which will suit a large range of object sizes.
  87. // However, very small objects may pass thru the near clip plane and large objects may pass
  88. // thru the far clip plane (and Z-fighting will result if the ratio is too high). If this
  89. // proves to problematic replace the constant settings with a function of object size that
  90. // can be set on a per object basis.
  91. //
  92. Camera = new CameraClass;
  93. Camera->Set_Clip_Planes (0.25F, 300.0F);
  94. // Create and add a light.
  95. Light = NEW_REF (LightClass, ());
  96. Light->Set_Intensity (1.0f);
  97. Light->Set_Ambient (Vector3 (0.0f, 0.0f, 0.0f));
  98. Light->Set_Diffuse (Vector3 (1.0f, 1.0f, 1.0f));
  99. Scene->Add_Render_Object (Light);
  100. return ;
  101. }
  102. ////////////////////////////////////////////////////////////////
  103. //
  104. // ~ViewerCtrlClass
  105. //
  106. ////////////////////////////////////////////////////////////////
  107. ViewerCtrlClass::~ViewerCtrlClass (void)
  108. {
  109. Free_Model ();
  110. REF_PTR_RELEASE (Light);
  111. REF_PTR_RELEASE (Camera);
  112. REF_PTR_RELEASE (Scene);
  113. return ;
  114. }
  115. ////////////////////////////////////////////////////////////////
  116. //
  117. // Create_Control_Renderer
  118. //
  119. ////////////////////////////////////////////////////////////////
  120. void
  121. ViewerCtrlClass::Create_Control_Renderer (void)
  122. {
  123. Render2DClass &renderer = ControlRenderer;
  124. //
  125. // Configure this renderer
  126. //
  127. renderer.Reset ();
  128. renderer.Enable_Texturing (false);
  129. ShaderClass *shader = renderer.Get_Shader ();
  130. renderer.Set_Z_Value (1.0F);
  131. shader->Set_Depth_Compare (ShaderClass::PASS_ALWAYS);
  132. shader->Set_Depth_Mask (ShaderClass::DEPTH_WRITE_ENABLE);
  133. shader->Set_Color_Mask (ShaderClass::COLOR_WRITE_ENABLE);
  134. //
  135. // Determine which color to draw the outline in
  136. //
  137. int color = StyleMgrClass::Get_Line_Color ();
  138. int bkcolor = StyleMgrClass::Get_Bk_Color ();
  139. if (IsEnabled == false) {
  140. color = StyleMgrClass::Get_Disabled_Line_Color ();
  141. bkcolor = StyleMgrClass::Get_Disabled_Bk_Color ();
  142. }
  143. //
  144. // Draw the window frame
  145. //
  146. renderer.Add_Rect (Rect, 1.0F, color, bkcolor);
  147. return ;
  148. }
  149. ////////////////////////////////////////////////////////////////
  150. //
  151. // On_Set_Cursor
  152. //
  153. ////////////////////////////////////////////////////////////////
  154. void
  155. ViewerCtrlClass::On_Set_Cursor (const Vector2 &mouse_pos)
  156. {
  157. //
  158. // Change the mouse cursor
  159. //
  160. MouseMgrClass::Set_Cursor (MouseMgrClass::CURSOR_ROTATE);
  161. return ;
  162. }
  163. ////////////////////////////////////////////////////////////////
  164. //
  165. // Update_Client_Rect
  166. //
  167. ////////////////////////////////////////////////////////////////
  168. void
  169. ViewerCtrlClass::Update_Client_Rect (void)
  170. {
  171. //
  172. // Set the client area
  173. //
  174. ClientRect = Rect;
  175. //
  176. // Calculate what the horizontal and vertical field of view
  177. // should be for this window.
  178. //
  179. float hfov = 0;
  180. float vfov = 0;
  181. float cx = Rect.Width ();
  182. float cy = Rect.Height ();
  183. if (cy > cx) {
  184. vfov = DEG_TO_RADF (45.0F);
  185. hfov = (cx / cy) * vfov;
  186. } else {
  187. hfov = DEG_TO_RADF (45.0F);
  188. vfov = (cy / cx) * hfov;
  189. }
  190. //
  191. // Set the new view plane for the camera
  192. //
  193. Camera->Set_View_Plane (hfov, vfov);
  194. //
  195. // Calculate a viewport that we can use to restraint
  196. // the model to our window.
  197. //
  198. const RectClass &screen_rect = Render2DClass::Get_Screen_Resolution ();
  199. Vector2 upper_left = Rect.Upper_Left ();
  200. Vector2 lower_right = Rect.Lower_Right ();
  201. upper_left.X /= screen_rect.Width ();
  202. lower_right.X /= screen_rect.Width ();
  203. upper_left.Y /= screen_rect.Height ();
  204. lower_right.Y /= screen_rect.Height ();
  205. //
  206. // Set the viewport to render only to our window
  207. //
  208. Camera->Set_Viewport (upper_left, lower_right);
  209. Set_Dirty ();
  210. return ;
  211. }
  212. ////////////////////////////////////////////////////////////////
  213. //
  214. // Render
  215. //
  216. ////////////////////////////////////////////////////////////////
  217. void
  218. ViewerCtrlClass::Render (void)
  219. {
  220. //
  221. // Recreate the renderers (if necessary)
  222. //
  223. if (IsDirty) {
  224. Create_Control_Renderer ();
  225. }
  226. //
  227. // Render the background
  228. //
  229. if (IsBackgroundVisible) {
  230. ControlRenderer.Render ();
  231. }
  232. //
  233. // Render the 3D object
  234. //
  235. if (Scene != NULL) {
  236. WW3D::Render (Scene, Camera);
  237. }
  238. DialogControlClass::Render ();
  239. return ;
  240. }
  241. ////////////////////////////////////////////////////////////////
  242. //
  243. // On_LButton_Down
  244. //
  245. ////////////////////////////////////////////////////////////////
  246. void
  247. ViewerCtrlClass::On_LButton_Down (const Vector2 &mouse_pos)
  248. {
  249. // Put the viewer interface into virtual trackball mode.
  250. Set_Interface_Mode (ViewerCtrlClass::VIRTUAL_TRACKBALL);
  251. return ;
  252. }
  253. ////////////////////////////////////////////////////////////////
  254. //
  255. // On_LButton_Up
  256. //
  257. ////////////////////////////////////////////////////////////////
  258. void
  259. ViewerCtrlClass::On_LButton_Up (const Vector2 &mouse_pos)
  260. {
  261. return ;
  262. }
  263. ////////////////////////////////////////////////////////////////
  264. //
  265. // Set_Model
  266. //
  267. ////////////////////////////////////////////////////////////////
  268. void
  269. ViewerCtrlClass::Set_Model (const char *model_name)
  270. {
  271. //
  272. // Load the new model
  273. //
  274. RenderObjClass *new_model = WW3DAssetManager::Get_Instance ()->Create_Render_Obj (model_name);
  275. Set_Model (new_model);
  276. return ;
  277. }
  278. ////////////////////////////////////////////////////////////////
  279. //
  280. // Set_Model
  281. //
  282. ////////////////////////////////////////////////////////////////
  283. void
  284. ViewerCtrlClass::Set_Model (RenderObjClass *new_model)
  285. {
  286. //
  287. // Start fresh
  288. //
  289. Free_Model ();
  290. if (new_model != NULL) {
  291. Model = new_model;
  292. //
  293. // Notify any advise sinks
  294. //
  295. ADVISE_NOTIFY (On_ViewerCtrl_Model_Loaded (this, Get_ID (), Model));
  296. //
  297. // Force the high LOD
  298. //
  299. int count = Model->Get_LOD_Count ();
  300. Model->Set_LOD_Level (max (count-1, 0));
  301. Model->Set_Transform (Matrix3D (1));
  302. //
  303. // Add the model to the scene
  304. //
  305. Scene->Add_Render_Object (Model);
  306. //
  307. // Force a camera update
  308. //
  309. IsCameraDirty = true;
  310. }
  311. return ;
  312. }
  313. ////////////////////////////////////////////////////////////////
  314. //
  315. // Calculate_Camera_Position
  316. //
  317. ////////////////////////////////////////////////////////////////
  318. void
  319. ViewerCtrlClass::Calculate_Camera_Position (void)
  320. {
  321. if (Model == NULL) {
  322. return ;
  323. }
  324. Model->Update_Obj_Space_Bounding_Volumes ();
  325. Model->Update_Sub_Object_Transforms ();
  326. //
  327. // Get the bounds of this object
  328. //
  329. bool is_first = true;
  330. BoundingBox.Center.Set (0, 0, 0);
  331. BoundingBox.Extent.Set (0, 0, 0);
  332. Get_Visible_Bounding_Box (&BoundingBox, Model, is_first);
  333. //
  334. // Get information about the camera
  335. //
  336. Vector2 vpmin, vpmax;
  337. Camera->Get_View_Plane (vpmin, vpmax);
  338. float znear = 0;
  339. float zfar = 0;
  340. Camera->Get_Clip_Planes (znear, zfar);
  341. //
  342. // Calculate the closest distance we can get
  343. // without clipping the rotating object
  344. //
  345. float width = WWMath::Sqrt (BoundingBox.Extent.X * BoundingBox.Extent.X * 4 + BoundingBox.Extent.Y * BoundingBox.Extent.Y * 4);
  346. float height = BoundingBox.Extent.Z * 2;
  347. float near_width = (vpmax.X - vpmin.X) * znear;
  348. float far_width = (vpmax.X - vpmin.X) * zfar;
  349. float near_height = (vpmax.Y - vpmin.Y) * znear;
  350. float far_height = (vpmax.Y - vpmin.Y) * zfar;
  351. float percent1 = (width - near_width) / (far_width - near_width);
  352. float percent2 = (height - near_height) / (far_height - near_height);
  353. float test_z1 = znear + (zfar - znear) * percent1;
  354. float test_z2 = znear + (zfar - znear) * percent2;
  355. Distance = (max (test_z1, test_z2) * 1.2F);
  356. Distance = max (MinCameraDist, Distance);
  357. //
  358. // Reposition the camera to be looking at this position
  359. //
  360. Vector3 center = BoundingBox.Center;
  361. Vector3 pos = center + Vector3 (Distance, 0, 0);
  362. Matrix3D tm;
  363. tm.Look_At (pos, center, 0);
  364. Camera->Set_Transform (tm);
  365. //
  366. // Remember that's we've updated
  367. //
  368. IsCameraDirty = false;
  369. return ;
  370. }
  371. ////////////////////////////////////////////////////////////////
  372. //
  373. // Set_Animation
  374. //
  375. ////////////////////////////////////////////////////////////////
  376. void
  377. ViewerCtrlClass::Set_Animation (const char *anim_name)
  378. {
  379. if (Model == NULL) {
  380. return ;
  381. }
  382. if (anim_name[0] != 0) {
  383. //
  384. // Play the animation on the model
  385. //
  386. HAnimClass *anim = WW3DAssetManager::Get_Instance ()->Get_HAnim (anim_name);
  387. if (anim != NULL) {
  388. Model->Set_Animation (anim, 0, RenderObjClass::ANIM_MODE_LOOP);
  389. REF_PTR_RELEASE (anim);
  390. }
  391. } else {
  392. //
  393. // Stop the animation
  394. //
  395. Model->Set_Animation ();
  396. }
  397. //
  398. // Force a camera update
  399. //
  400. IsCameraDirty = true;
  401. return ;
  402. }
  403. ////////////////////////////////////////////////////////////////
  404. //
  405. // Free_Model
  406. //
  407. ////////////////////////////////////////////////////////////////
  408. void
  409. ViewerCtrlClass::Free_Model (void)
  410. {
  411. if (Model != NULL) {
  412. Model->Remove ();
  413. REF_PTR_RELEASE (Model);
  414. }
  415. return ;
  416. }
  417. ////////////////////////////////////////////////////////////////
  418. //
  419. // Set_Interface_Mode
  420. //
  421. ////////////////////////////////////////////////////////////////
  422. void ViewerCtrlClass::Set_Interface_Mode (InterfaceModeEnum mode, float rotationrate)
  423. {
  424. // If already in the desired mode do not reset the rotation.
  425. if (mode != InterfaceMode) {
  426. InterfaceMode = mode;
  427. ZRotation = DEG_TO_RADF (0.0f);
  428. }
  429. RotationRate = DEG_TO_RADF (rotationrate);
  430. }
  431. ////////////////////////////////////////////////////////////////
  432. //
  433. // On_Frame_Update
  434. //
  435. ////////////////////////////////////////////////////////////////
  436. void
  437. ViewerCtrlClass::On_Frame_Update (void)
  438. {
  439. //
  440. // Update the camera's data if necessary
  441. //
  442. if (IsCameraDirty) {
  443. Calculate_Camera_Position ();
  444. }
  445. switch (InterfaceMode) {
  446. case Z_ROTATION:
  447. {
  448. float delta = ((DialogMgrClass::Get_Frame_Time () / 1000.0F) * RotationRate);
  449. //
  450. // Adjust our rotation
  451. //
  452. ZRotation -= delta;
  453. if (ZRotation < 0) {
  454. ZRotation += DEG_TO_RADF (360);
  455. }
  456. //
  457. // Rotate the camera's position about the object
  458. //
  459. Matrix3D rotation_tm (1);
  460. rotation_tm.Rotate_Z (ZRotation);
  461. Vector3 position = rotation_tm.Rotate_Vector (Vector3 (Distance, 0, 0)) + BoundingBox.Center;
  462. //
  463. // Update the camera's position
  464. //
  465. Matrix3D tm;
  466. tm.Look_At (position, BoundingBox.Center, 0);
  467. Camera->Set_Transform (tm);
  468. break;
  469. }
  470. case VIRTUAL_TRACKBALL:
  471. if (DialogMgrClass::Is_Button_Down (VK_LBUTTON)) {
  472. const RectClass &rect = Get_Window_Rect();
  473. const Vector2 mouseposition (DialogMgrClass::Get_Mouse_Pos().X, DialogMgrClass::Get_Mouse_Pos().Y);
  474. if (rect.Contains (mouseposition)) {
  475. int width, height, bits;
  476. bool windowed;
  477. float oow, ooh;
  478. Vector2 viewportmin, viewportmax, viewportextent, viewportcenter;
  479. float a, b;
  480. Vector2 p0, p1;
  481. Quaternion rotation;
  482. Matrix3D transform, inversetransform;
  483. Vector3 objectcenter;
  484. WW3D::Get_Render_Target_Resolution (width, height, bits, windowed);
  485. oow = 1.0f / width;
  486. ooh = 1.0f / height;
  487. Camera->Get_Viewport (viewportmin, viewportmax);
  488. viewportextent = viewportmax - viewportmin;
  489. viewportcenter = 0.5f * (viewportmin + viewportmax);
  490. a = 1.0f / viewportextent.X;
  491. b = -1.0f / viewportextent.Y;
  492. p0.Set (LastMousePosition.X, LastMousePosition.Y);
  493. p0.Scale (oow, ooh);
  494. p0 -= viewportcenter;
  495. p0.Scale (a, b);
  496. p1.Set (DialogMgrClass::Get_Mouse_Pos().X, DialogMgrClass::Get_Mouse_Pos().Y);
  497. p1.Scale (oow, ooh);
  498. p1 -= viewportcenter;
  499. p1.Scale (a, b);
  500. rotation = ::Trackball (p0.X, p0.Y, p1.X, p1.Y, 0.5f);
  501. transform = Camera->Get_Transform();
  502. transform.Get_Orthogonal_Inverse (inversetransform);
  503. objectcenter = inversetransform * BoundingBox.Center;
  504. transform.Translate (objectcenter);
  505. Matrix3D::Multiply (transform, Build_Matrix3D (rotation), &transform);
  506. transform.Translate (-objectcenter);
  507. Camera->Set_Transform (transform);
  508. }
  509. }
  510. break;
  511. }
  512. // Place the light source at the camera position.
  513. Light->Set_Transform (Camera->Get_Transform());
  514. LastMousePosition = DialogMgrClass::Get_Mouse_Pos();
  515. DialogControlClass::On_Frame_Update ();
  516. return ;
  517. }
  518. ///////////////////////////////////////////////////////////////
  519. //
  520. // Get_Visible_Bounding_Box
  521. //
  522. ///////////////////////////////////////////////////////////////
  523. void
  524. ViewerCtrlClass::Get_Visible_Bounding_Box (AABoxClass *box, RenderObjClass *render_obj, bool &is_first)
  525. {
  526. if (render_obj == NULL) {
  527. return ;
  528. }
  529. //
  530. // Recursively walk through the subobjects
  531. //
  532. for (int index = 0; index < render_obj->Get_Num_Sub_Objects (); index ++) {
  533. RenderObjClass *sub_obj = render_obj->Get_Sub_Object (index);
  534. //
  535. // Recurse into this sub-object
  536. //
  537. if (sub_obj != NULL && (sub_obj->Is_Hidden () == false)) {
  538. Get_Visible_Bounding_Box (box, sub_obj, is_first);
  539. }
  540. REF_PTR_RELEASE (sub_obj);
  541. }
  542. //
  543. // Is this a mesh?
  544. //
  545. if (render_obj->Class_ID () == RenderObjClass::CLASSID_MESH) {
  546. MeshClass *mesh = reinterpret_cast<MeshClass *> (render_obj);
  547. //
  548. // Dig out the mesh's model
  549. //
  550. MeshModelClass *mesh_model = mesh->Get_Model ();
  551. if (mesh_model != NULL) {
  552. //
  553. // Get the vertex count
  554. //
  555. int vertex_count = mesh_model->Get_Vertex_Count ();
  556. Vector3 *vertex_array = NULL;
  557. //
  558. // Get a pointer to the vertices
  559. //
  560. bool is_skin = (mesh_model->Get_Flag (MeshGeometryClass::SKIN) != 0);
  561. if (is_skin) {
  562. vertex_array = new Vector3[vertex_count];
  563. mesh->Get_Deformed_Vertices (vertex_array);
  564. } else {
  565. vertex_array = mesh_model->Get_Vertex_Array ();
  566. }
  567. //
  568. // Loop over all the verts inside this model
  569. //
  570. for (int index = 0; index < vertex_count; index ++) {
  571. //
  572. // Get the world-space vertex position
  573. //
  574. Vector3 vertex_pos = vertex_array[index];
  575. if (is_skin == false) {
  576. vertex_pos = mesh->Get_Transform () * vertex_array[index];
  577. }
  578. //
  579. // Expand our box to account for this vertex
  580. //
  581. if (is_first) {
  582. box->Center = vertex_pos;
  583. is_first = false;
  584. } else {
  585. box->Add_Point (vertex_pos);
  586. }
  587. }
  588. //
  589. // Free the vertex array (if necessary)
  590. //
  591. if (is_skin) {
  592. delete [] vertex_array;
  593. vertex_array = NULL;
  594. }
  595. REF_PTR_RELEASE (mesh_model);
  596. }
  597. }
  598. return ;
  599. }