| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711 |
- /*
- ** Command & Conquer Renegade(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /***********************************************************************************************
- *** 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 ***
- ***********************************************************************************************
- * *
- * Project Name : Combat *
- * *
- * $Archive:: /Commando/Code/wwui/viewerctrl.cpp $*
- * *
- * Author:: Patrick Smith *
- * *
- * $Modtime:: 11/06/01 4:23p $*
- * *
- * $Revision:: 9 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- // Disable warning about exception handling not being enabled. It's used as part of STL - in a part of STL we don't use.
- #pragma warning(disable : 4530)
- #include "viewerctrl.h"
- #include "assetmgr.h"
- #include "refcount.h"
- #include "mousemgr.h"
- #include "ww3d.h"
- #include "dialogmgr.h"
- #include "dialogbase.h"
- #include "stylemgr.h"
- #include "scene.h"
- #include "camera.h"
- #include "rendobj.h"
- #include "hanim.h"
- #include "mesh.h"
- #include "meshmdl.h"
- #include "light.h"
- ////////////////////////////////////////////////////////////////
- // Local constants
- ////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////
- //
- // ViewerCtrlClass
- //
- ////////////////////////////////////////////////////////////////
- ViewerCtrlClass::ViewerCtrlClass (void) :
- Scene (NULL),
- Camera (NULL),
- Model (NULL),
- Distance (0),
- ZRotation (0),
- MinCameraDist (0),
- RotationRate (DEG_TO_RADF (0.0f)),
- BoundingBox (Vector3 (0, 0, 0), Vector3 (0, 0, 0)),
- IsCameraDirty (true),
- IsBackgroundVisible (true),
- InterfaceMode (Z_ROTATION)
- {
- Vector3 ambientcolor (0.2f, 0.2f, 0.2f);
-
- LastMousePosition = DialogMgrClass::Get_Mouse_Pos();
- //
- // Configure the 2D renderer
- //
- StyleMgrClass::Configure_Renderer (&ControlRenderer);
- //
- // Create a scene to use to render the model
- //
- Scene = new SimpleSceneClass;
- Scene->Set_Ambient_Light (ambientcolor);
- //
- // Create a camera to use in the scene
- // NOTE: Near and far clip planes are set to values which will suit a large range of object sizes.
- // However, very small objects may pass thru the near clip plane and large objects may pass
- // thru the far clip plane (and Z-fighting will result if the ratio is too high). If this
- // proves to problematic replace the constant settings with a function of object size that
- // can be set on a per object basis.
- //
- Camera = new CameraClass;
- Camera->Set_Clip_Planes (0.25F, 300.0F);
- // Create and add a light.
- Light = NEW_REF (LightClass, ());
- Light->Set_Intensity (1.0f);
- Light->Set_Ambient (Vector3 (0.0f, 0.0f, 0.0f));
- Light->Set_Diffuse (Vector3 (1.0f, 1.0f, 1.0f));
- Scene->Add_Render_Object (Light);
-
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // ~ViewerCtrlClass
- //
- ////////////////////////////////////////////////////////////////
- ViewerCtrlClass::~ViewerCtrlClass (void)
- {
- Free_Model ();
- REF_PTR_RELEASE (Light);
- REF_PTR_RELEASE (Camera);
- REF_PTR_RELEASE (Scene);
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Create_Control_Renderer
- //
- ////////////////////////////////////////////////////////////////
- void
- ViewerCtrlClass::Create_Control_Renderer (void)
- {
- Render2DClass &renderer = ControlRenderer;
- //
- // Configure this renderer
- //
- renderer.Reset ();
- renderer.Enable_Texturing (false);
- ShaderClass *shader = renderer.Get_Shader ();
- renderer.Set_Z_Value (1.0F);
- shader->Set_Depth_Compare (ShaderClass::PASS_ALWAYS);
- shader->Set_Depth_Mask (ShaderClass::DEPTH_WRITE_ENABLE);
- shader->Set_Color_Mask (ShaderClass::COLOR_WRITE_ENABLE);
- //
- // Determine which color to draw the outline in
- //
- int color = StyleMgrClass::Get_Line_Color ();
- int bkcolor = StyleMgrClass::Get_Bk_Color ();
- if (IsEnabled == false) {
- color = StyleMgrClass::Get_Disabled_Line_Color ();
- bkcolor = StyleMgrClass::Get_Disabled_Bk_Color ();
- }
- //
- // Draw the window frame
- //
- renderer.Add_Rect (Rect, 1.0F, color, bkcolor);
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // On_Set_Cursor
- //
- ////////////////////////////////////////////////////////////////
- void
- ViewerCtrlClass::On_Set_Cursor (const Vector2 &mouse_pos)
- {
- //
- // Change the mouse cursor
- //
- MouseMgrClass::Set_Cursor (MouseMgrClass::CURSOR_ROTATE);
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Update_Client_Rect
- //
- ////////////////////////////////////////////////////////////////
- void
- ViewerCtrlClass::Update_Client_Rect (void)
- {
- //
- // Set the client area
- //
- ClientRect = Rect;
- //
- // Calculate what the horizontal and vertical field of view
- // should be for this window.
- //
- float hfov = 0;
- float vfov = 0;
- float cx = Rect.Width ();
- float cy = Rect.Height ();
- if (cy > cx) {
- vfov = DEG_TO_RADF (45.0F);
- hfov = (cx / cy) * vfov;
- } else {
- hfov = DEG_TO_RADF (45.0F);
- vfov = (cy / cx) * hfov;
- }
- //
- // Set the new view plane for the camera
- //
- Camera->Set_View_Plane (hfov, vfov);
- //
- // Calculate a viewport that we can use to restraint
- // the model to our window.
- //
- const RectClass &screen_rect = Render2DClass::Get_Screen_Resolution ();
- Vector2 upper_left = Rect.Upper_Left ();
- Vector2 lower_right = Rect.Lower_Right ();
- upper_left.X /= screen_rect.Width ();
- lower_right.X /= screen_rect.Width ();
- upper_left.Y /= screen_rect.Height ();
- lower_right.Y /= screen_rect.Height ();
- //
- // Set the viewport to render only to our window
- //
- Camera->Set_Viewport (upper_left, lower_right);
- Set_Dirty ();
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Render
- //
- ////////////////////////////////////////////////////////////////
- void
- ViewerCtrlClass::Render (void)
- {
- //
- // Recreate the renderers (if necessary)
- //
- if (IsDirty) {
- Create_Control_Renderer ();
- }
- //
- // Render the background
- //
- if (IsBackgroundVisible) {
- ControlRenderer.Render ();
- }
- //
- // Render the 3D object
- //
- if (Scene != NULL) {
- WW3D::Render (Scene, Camera);
- }
- DialogControlClass::Render ();
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // On_LButton_Down
- //
- ////////////////////////////////////////////////////////////////
- void
- ViewerCtrlClass::On_LButton_Down (const Vector2 &mouse_pos)
- {
- // Put the viewer interface into virtual trackball mode.
- Set_Interface_Mode (ViewerCtrlClass::VIRTUAL_TRACKBALL);
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // On_LButton_Up
- //
- ////////////////////////////////////////////////////////////////
- void
- ViewerCtrlClass::On_LButton_Up (const Vector2 &mouse_pos)
- {
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Set_Model
- //
- ////////////////////////////////////////////////////////////////
- void
- ViewerCtrlClass::Set_Model (const char *model_name)
- {
- //
- // Load the new model
- //
- RenderObjClass *new_model = WW3DAssetManager::Get_Instance ()->Create_Render_Obj (model_name);
- Set_Model (new_model);
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Set_Model
- //
- ////////////////////////////////////////////////////////////////
- void
- ViewerCtrlClass::Set_Model (RenderObjClass *new_model)
- {
- //
- // Start fresh
- //
- Free_Model ();
- if (new_model != NULL) {
- Model = new_model;
- //
- // Notify any advise sinks
- //
- ADVISE_NOTIFY (On_ViewerCtrl_Model_Loaded (this, Get_ID (), Model));
- //
- // Force the high LOD
- //
- int count = Model->Get_LOD_Count ();
- Model->Set_LOD_Level (max (count-1, 0));
- Model->Set_Transform (Matrix3D (1));
- //
- // Add the model to the scene
- //
- Scene->Add_Render_Object (Model);
- //
- // Force a camera update
- //
- IsCameraDirty = true;
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Calculate_Camera_Position
- //
- ////////////////////////////////////////////////////////////////
- void
- ViewerCtrlClass::Calculate_Camera_Position (void)
- {
- if (Model == NULL) {
- return ;
- }
- Model->Update_Obj_Space_Bounding_Volumes ();
- Model->Update_Sub_Object_Transforms ();
- //
- // Get the bounds of this object
- //
- bool is_first = true;
- BoundingBox.Center.Set (0, 0, 0);
- BoundingBox.Extent.Set (0, 0, 0);
- Get_Visible_Bounding_Box (&BoundingBox, Model, is_first);
- //
- // Get information about the camera
- //
- Vector2 vpmin, vpmax;
- Camera->Get_View_Plane (vpmin, vpmax);
- float znear = 0;
- float zfar = 0;
- Camera->Get_Clip_Planes (znear, zfar);
- //
- // Calculate the closest distance we can get
- // without clipping the rotating object
- //
- float width = WWMath::Sqrt (BoundingBox.Extent.X * BoundingBox.Extent.X * 4 + BoundingBox.Extent.Y * BoundingBox.Extent.Y * 4);
- float height = BoundingBox.Extent.Z * 2;
- float near_width = (vpmax.X - vpmin.X) * znear;
- float far_width = (vpmax.X - vpmin.X) * zfar;
- float near_height = (vpmax.Y - vpmin.Y) * znear;
- float far_height = (vpmax.Y - vpmin.Y) * zfar;
- float percent1 = (width - near_width) / (far_width - near_width);
- float percent2 = (height - near_height) / (far_height - near_height);
- float test_z1 = znear + (zfar - znear) * percent1;
- float test_z2 = znear + (zfar - znear) * percent2;
- Distance = (max (test_z1, test_z2) * 1.2F);
- Distance = max (MinCameraDist, Distance);
- //
- // Reposition the camera to be looking at this position
- //
- Vector3 center = BoundingBox.Center;
- Vector3 pos = center + Vector3 (Distance, 0, 0);
- Matrix3D tm;
- tm.Look_At (pos, center, 0);
- Camera->Set_Transform (tm);
- //
- // Remember that's we've updated
- //
- IsCameraDirty = false;
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Set_Animation
- //
- ////////////////////////////////////////////////////////////////
- void
- ViewerCtrlClass::Set_Animation (const char *anim_name)
- {
- if (Model == NULL) {
- return ;
- }
- if (anim_name[0] != 0) {
- //
- // Play the animation on the model
- //
- HAnimClass *anim = WW3DAssetManager::Get_Instance ()->Get_HAnim (anim_name);
- if (anim != NULL) {
- Model->Set_Animation (anim, 0, RenderObjClass::ANIM_MODE_LOOP);
- REF_PTR_RELEASE (anim);
- }
- } else {
- //
- // Stop the animation
- //
- Model->Set_Animation ();
- }
- //
- // Force a camera update
- //
- IsCameraDirty = true;
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Free_Model
- //
- ////////////////////////////////////////////////////////////////
- void
- ViewerCtrlClass::Free_Model (void)
- {
- if (Model != NULL) {
- Model->Remove ();
- REF_PTR_RELEASE (Model);
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Set_Interface_Mode
- //
- ////////////////////////////////////////////////////////////////
- void ViewerCtrlClass::Set_Interface_Mode (InterfaceModeEnum mode, float rotationrate)
- {
- // If already in the desired mode do not reset the rotation.
- if (mode != InterfaceMode) {
- InterfaceMode = mode;
- ZRotation = DEG_TO_RADF (0.0f);
- }
- RotationRate = DEG_TO_RADF (rotationrate);
- }
- ////////////////////////////////////////////////////////////////
- //
- // On_Frame_Update
- //
- ////////////////////////////////////////////////////////////////
- void
- ViewerCtrlClass::On_Frame_Update (void)
- {
- //
- // Update the camera's data if necessary
- //
- if (IsCameraDirty) {
- Calculate_Camera_Position ();
- }
- switch (InterfaceMode) {
- case Z_ROTATION:
- {
- float delta = ((DialogMgrClass::Get_Frame_Time () / 1000.0F) * RotationRate);
- //
- // Adjust our rotation
- //
- ZRotation -= delta;
- if (ZRotation < 0) {
- ZRotation += DEG_TO_RADF (360);
- }
- //
- // Rotate the camera's position about the object
- //
- Matrix3D rotation_tm (1);
- rotation_tm.Rotate_Z (ZRotation);
- Vector3 position = rotation_tm.Rotate_Vector (Vector3 (Distance, 0, 0)) + BoundingBox.Center;
- //
- // Update the camera's position
- //
- Matrix3D tm;
- tm.Look_At (position, BoundingBox.Center, 0);
- Camera->Set_Transform (tm);
- break;
- }
-
- case VIRTUAL_TRACKBALL:
- if (DialogMgrClass::Is_Button_Down (VK_LBUTTON)) {
- const RectClass &rect = Get_Window_Rect();
- const Vector2 mouseposition (DialogMgrClass::Get_Mouse_Pos().X, DialogMgrClass::Get_Mouse_Pos().Y);
- if (rect.Contains (mouseposition)) {
- int width, height, bits;
- bool windowed;
- float oow, ooh;
- Vector2 viewportmin, viewportmax, viewportextent, viewportcenter;
- float a, b;
- Vector2 p0, p1;
- Quaternion rotation;
- Matrix3D transform, inversetransform;
- Vector3 objectcenter;
- WW3D::Get_Render_Target_Resolution (width, height, bits, windowed);
- oow = 1.0f / width;
- ooh = 1.0f / height;
- Camera->Get_Viewport (viewportmin, viewportmax);
- viewportextent = viewportmax - viewportmin;
- viewportcenter = 0.5f * (viewportmin + viewportmax);
- a = 1.0f / viewportextent.X;
- b = -1.0f / viewportextent.Y;
-
- p0.Set (LastMousePosition.X, LastMousePosition.Y);
- p0.Scale (oow, ooh);
- p0 -= viewportcenter;
- p0.Scale (a, b);
-
- p1.Set (DialogMgrClass::Get_Mouse_Pos().X, DialogMgrClass::Get_Mouse_Pos().Y);
- p1.Scale (oow, ooh);
- p1 -= viewportcenter;
- p1.Scale (a, b);
-
- rotation = ::Trackball (p0.X, p0.Y, p1.X, p1.Y, 0.5f);
-
- transform = Camera->Get_Transform();
- transform.Get_Orthogonal_Inverse (inversetransform);
- objectcenter = inversetransform * BoundingBox.Center;
- transform.Translate (objectcenter);
- Matrix3D::Multiply (transform, Build_Matrix3D (rotation), &transform);
- transform.Translate (-objectcenter);
- Camera->Set_Transform (transform);
- }
- }
- break;
- }
- // Place the light source at the camera position.
- Light->Set_Transform (Camera->Get_Transform());
-
- LastMousePosition = DialogMgrClass::Get_Mouse_Pos();
- DialogControlClass::On_Frame_Update ();
- return ;
- }
- ///////////////////////////////////////////////////////////////
- //
- // Get_Visible_Bounding_Box
- //
- ///////////////////////////////////////////////////////////////
- void
- ViewerCtrlClass::Get_Visible_Bounding_Box (AABoxClass *box, RenderObjClass *render_obj, bool &is_first)
- {
- if (render_obj == NULL) {
- return ;
- }
- //
- // Recursively walk through the subobjects
- //
- for (int index = 0; index < render_obj->Get_Num_Sub_Objects (); index ++) {
- RenderObjClass *sub_obj = render_obj->Get_Sub_Object (index);
- //
- // Recurse into this sub-object
- //
- if (sub_obj != NULL && (sub_obj->Is_Hidden () == false)) {
- Get_Visible_Bounding_Box (box, sub_obj, is_first);
- }
- REF_PTR_RELEASE (sub_obj);
- }
- //
- // Is this a mesh?
- //
- if (render_obj->Class_ID () == RenderObjClass::CLASSID_MESH) {
- MeshClass *mesh = reinterpret_cast<MeshClass *> (render_obj);
- //
- // Dig out the mesh's model
- //
- MeshModelClass *mesh_model = mesh->Get_Model ();
- if (mesh_model != NULL) {
- //
- // Get the vertex count
- //
- int vertex_count = mesh_model->Get_Vertex_Count ();
- Vector3 *vertex_array = NULL;
- //
- // Get a pointer to the vertices
- //
- bool is_skin = (mesh_model->Get_Flag (MeshGeometryClass::SKIN) != 0);
- if (is_skin) {
- vertex_array = new Vector3[vertex_count];
- mesh->Get_Deformed_Vertices (vertex_array);
- } else {
- vertex_array = mesh_model->Get_Vertex_Array ();
- }
-
- //
- // Loop over all the verts inside this model
- //
- for (int index = 0; index < vertex_count; index ++) {
-
- //
- // Get the world-space vertex position
- //
- Vector3 vertex_pos = vertex_array[index];
- if (is_skin == false) {
- vertex_pos = mesh->Get_Transform () * vertex_array[index];
- }
- //
- // Expand our box to account for this vertex
- //
- if (is_first) {
- box->Center = vertex_pos;
- is_first = false;
- } else {
- box->Add_Point (vertex_pos);
- }
- }
- //
- // Free the vertex array (if necessary)
- //
- if (is_skin) {
- delete [] vertex_array;
- vertex_array = NULL;
- }
- REF_PTR_RELEASE (mesh_model);
- }
- }
- return ;
- }
|