/* ** 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 . */ /*********************************************************************************************** *** 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 : WW3D * * * * $Archive:: /Commando/Code/ww3d2/camera.cpp $* * * * Author:: Greg_h * * * * $Modtime:: 11/26/01 5:30p $* * * * $Revision:: 23 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * CameraClass::CameraClass -- constructor * * CameraClass::CameraClass -- copy constructor * * CameraClass::operator == -- assignment operator * * CameraClass::~CameraClass -- destructor * * CameraClass::Clone -- virtual copy constructor * * CameraClass::Add -- add the camera to the world? * * CameraClass::Remove -- Remove the camera from the world? * * CameraClass::Get_Obj_Space_Bounding_Sphere -- returns the object space bounding sphere * * CameraClass::Get_Object_Space_Bounding_Box -- returns the object space bounding box * * CameraClass::Set_Transform -- set the transform of the camera * * CameraClass::Set_Position -- Set the position of the camera * * CameraClass::Set_Animation -- set the animation state of the camera * * CameraClass::Set_Animation -- Set the animation state of the camera * * CameraClass::Set_Animation -- set the animation state of the camera * * CameraClass::Set_Animation -- set the animation state of the camera * * CameraClass::Set_View_Plane -- control over the view plane * * CameraClass::Set_View_Plane -- set the viewplane using fov angles * * CameraClass::Set_Focal_Length -- set the view plane using focal length and aspect ratio * * CameraClass::Get_View_Plane -- get the corners of the current view plane * * CameraClass::Project -- project a point from ws to the view plane * * CameraClass::Project_Camera_Space_Point -- Project a point that is already in camera spac * * CameraClass::Un_Project -- "unproject" a point from the view plane to world space * * CameraClass::Transform_To_View_Space -- transforms the given world space point to camera * * CameraClass::Rotate_To_View_Space -- rotates the given world space vector to camera space * * CameraClass::Get_Near_Clip_Bounding_Box -- returns an obb that contains near clip plane * * CameraCLass::Update_Frustum -- updates the frustum parameters * * CameraClass::Cull_Box -- tests whether the given box can be culled * * CameraClass::Device_To_View_Space -- converts the given device coordinate to view space * * CameraClass::Device_To_World_Space -- converts given device coord to world space * * CameraClass::Camera_Push -- pushes the camera's parameters into the given GERD * * CameraClass::Camera_Pop -- pops the camera's parameters from the given GERD * * CameraClass::Set_Aspect_Ratio -- sets the aspect ratio of the camera * * CameraClass::Apply_D3D_State -- sets the D3D states controlled by the camera * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "camera.h" #include "ww3d.h" #include "matrix4.h" #include "dx8wrapper.h" /*********************************************************************************************** * CameraClass::CameraClass -- constructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 3/21/98 GTH : Created. * *=============================================================================================*/ CameraClass::CameraClass(void) : Projection(PERSPECTIVE), Viewport(Vector2(0,0),Vector2(1,1)), // pixel viewport to render into AspectRatio(4.0f/3.0f), ZNear(1.0f), // near clip plane distance ZFar(1000.0f), // far clip plane distance ZBufferMin(0.0f), // smallest value we'll write into the z-buffer ZBufferMax(1.0f), // largest value we'll write into the z-buffer FrustumValid(false) { Set_Transform(Matrix3D(1)); Set_View_Plane(DEG_TO_RADF(50.0f)); } /*********************************************************************************************** * CameraClass::CameraClass -- copy constructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 3/21/98 GTH : Created. * * 4/13/2001 hy : added in copy code for new member functions * *=============================================================================================*/ CameraClass::CameraClass(const CameraClass & src) : RenderObjClass(src), Projection(src.Projection), Viewport(src.Viewport), ViewPlane(src.ViewPlane), ZNear(src.ZNear), ZFar(src.ZFar), FrustumValid(src.FrustumValid), Frustum(src.Frustum), NearClipBBox(src.NearClipBBox), ProjectionTransform(src.ProjectionTransform), CameraInvTransform(src.CameraInvTransform), AspectRatio(src.AspectRatio), ZBufferMin(src.ZBufferMin), ZBufferMax(src.ZBufferMax) { // just being paraniod in case any parent class doesn't completely copy the entire state... FrustumValid = false; } /*********************************************************************************************** * CameraClass::operator == -- assignment operator * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 3/21/98 GTH : Created. * *=============================================================================================*/ CameraClass & CameraClass::operator = (const CameraClass & that) { if (this != &that) { RenderObjClass::operator = (that); Projection = that.Projection; Viewport = that.Viewport; ViewPlane = that.ViewPlane; ZNear = that.ZNear; ZFar = that.ZFar; FrustumValid = that.FrustumValid; Frustum = that.Frustum; NearClipBBox = that.NearClipBBox; ProjectionTransform = that.ProjectionTransform; CameraInvTransform = that.CameraInvTransform; // just being paraniod in case any parent class doesn't completely copy the entire state... FrustumValid = false; } return * this; } /*********************************************************************************************** * CameraClass::~CameraClass -- destructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 3/21/98 GTH : Created. * *=============================================================================================*/ CameraClass::~CameraClass(void) { } /*********************************************************************************************** * CameraClass::Clone -- virtual copy constructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 3/21/98 GTH : Created. * *=============================================================================================*/ RenderObjClass * CameraClass::Clone(void) const { return NEW_REF( CameraClass, (*this) ); } /*********************************************************************************************** * CameraClass::Get_Obj_Space_Bounding_Sphere -- returns the object space bounding sphere * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/29/98 GTH : Created. * *=============================================================================================*/ void CameraClass::Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const { sphere.Center.Set(0,0,0); sphere.Radius = ZFar; // could optimize this but its not really used. } /*********************************************************************************************** * CameraClass::Get_Object_Space_Bounding_Box -- returns the object space bounding box * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/29/98 GTH : Created. * *=============================================================================================*/ void CameraClass::Get_Obj_Space_Bounding_Box(AABoxClass & box) const { box.Center.Set(0,0,0); box.Extent.Set(ZFar,ZFar,ZFar); // could optimize this but its not really used. } /*********************************************************************************************** * CameraClass::Set_Transform -- set the transform of the camera * * * * This is over-ridden to invalidate the cached frustum parameters * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 5/29/98 GTH : Created. * *=============================================================================================*/ void CameraClass::Set_Transform(const Matrix3D &m) { RenderObjClass::Set_Transform(m); FrustumValid = false; } /*********************************************************************************************** * CameraClass::Set_Position -- Set the position of the camera * * * * This is overriden to invalidate the cached frustum parameters * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 5/29/98 GTH : Created. * *=============================================================================================*/ void CameraClass::Set_Position(const Vector3 &v) { RenderObjClass::Set_Position(v); FrustumValid = false; } /*********************************************************************************************** * CameraClass::Set_View_Plane -- control over the view plane * * * * INPUT: * * min - x,y of the upper left corner of the view rectangle (dist is assumed to be 1.0) * * max - x,y of the lower right corner of the view rectangle * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 3/21/98 GTH : Created. * *=============================================================================================*/ void CameraClass::Set_View_Plane(const Vector2 & vmin,const Vector2 & vmax) { ViewPlane.Min = vmin; ViewPlane.Max = vmax; AspectRatio = (vmax.X - vmin.X) / (vmax.Y - vmin.Y); FrustumValid = false; } /*********************************************************************************************** * CameraClass::Set_View_Plane -- set the viewplane using fov angles * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 3/21/98 GTH : Created. * *=============================================================================================*/ void CameraClass::Set_View_Plane(float hfov,float vfov) { float width_half = tan(hfov/2.0); float height_half = 0.0f; if (vfov == -1) { height_half = (1.0f / AspectRatio) * width_half; // use the aspect ratio } else { height_half = tan(vfov/2.0); AspectRatio = width_half / height_half; // or, initialize the aspect ratio } ViewPlane.Min.Set(-width_half,-height_half); ViewPlane.Max.Set(width_half,height_half); FrustumValid = false; } /*********************************************************************************************** * CameraClass::Set_Aspect_Ratio -- sets the aspect ratio of the camera * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/29/2001 gth : Created. * *=============================================================================================*/ void CameraClass::Set_Aspect_Ratio(float width_to_height) { AspectRatio = width_to_height; ViewPlane.Min.Y = ViewPlane.Min.X / AspectRatio; ViewPlane.Max.Y = ViewPlane.Max.X / AspectRatio; FrustumValid = false; } /*********************************************************************************************** * CameraClass::Get_View_Plane -- get the corners of the current view plane * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 3/21/98 GTH : Created. * *=============================================================================================*/ void CameraClass::Get_View_Plane(Vector2 & set_min,Vector2 & set_max) const { set_min = ViewPlane.Min; set_max = ViewPlane.Max; } /*********************************************************************************************** * CameraClass::Project -- project a point from ws to the view plane * * * * INPUT: * * dest - will be set to a point on the normalized view plane. x,y, and z range from -1 to 1 * * ws_point - input point in world space * * * * OUTPUT: * * ProjectionResType indicating whether the point was in the frustum * * * * WARNINGS: * * When the input is behind the near clip plane, 0,0,0 is returned. * * * * HISTORY: * * 3/21/98 GTH : Created. * *=============================================================================================*/ CameraClass::ProjectionResType CameraClass::Project(Vector3 & dest,const Vector3 & ws_point) const { Update_Frustum(); Vector3 cam_point; Matrix3D::Transform_Vector(CameraInvTransform,ws_point,&cam_point); if (cam_point.Z > -ZNear) { dest.Set(0,0,0); return OUTSIDE_NEAR_CLIP; } Vector4 view_point = ProjectionTransform * cam_point; float oow = 1.0f / view_point.W; dest.X = view_point.X * oow; dest.Y = view_point.Y * oow; dest.Z = view_point.Z * oow; if (dest.Z > 1.0f) { return OUTSIDE_FAR_CLIP; } if ((dest.X < -1.0f) || (dest.X > 1.0f) || (dest.Y < -1.0f) || (dest.Y > 1.0f)) { return OUTSIDE_FRUSTUM; } return INSIDE_FRUSTUM; } /*********************************************************************************************** * CameraClass::Project_Camera_Space_Point -- Project a point that is already in camera space * * * * INPUT: * * dest - will be set to a point on the normalized view plane. x,y, and z range from -1 to 1 * * cam_point - input point in camera space * * * * OUTPUT: * * ProjectionResType indicating whether the point was in the frustum * * * * WARNINGS: * * When the input is behind the near clip plane, 0,0,0 is returned. * * * * HISTORY: * * 11/17/2000 gth : Created. * *=============================================================================================*/ CameraClass::ProjectionResType CameraClass::Project_Camera_Space_Point(Vector3 & dest,const Vector3 & cam_point) const { Update_Frustum(); if ( cam_point.Z > -ZNear + WWMATH_EPSILON) { dest.Set(0,0,0); return OUTSIDE_NEAR_CLIP; } Vector4 view_point = ProjectionTransform * cam_point; float oow = 1.0f / view_point.W; dest.X = view_point.X * oow; dest.Y = view_point.Y * oow; dest.Z = view_point.Z * oow; if (dest.Z > 1.0f) { return OUTSIDE_FAR_CLIP; } if ((dest.X < -1.0f) || (dest.X > 1.0f) || (dest.Y < -1.0f) || (dest.Y > 1.0f)) { return OUTSIDE_FRUSTUM; } return INSIDE_FRUSTUM; } /*********************************************************************************************** * CameraClass::Un_Project -- "unproject" a point from the view plane to world space * * * * The given point is assumed to be on the view_plane at z=-1. The 3D world space point that * * this represents will be returned in dest. * * * * INPUT: * * dest - will be filled in with the 3D world space point that the given point represents * * view_point - point on the view_plane to be un-projected * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 3/21/98 GTH : Created. * *=============================================================================================*/ void CameraClass::Un_Project(Vector3 & dest,const Vector2 & view_point) const { /* ** map view_point.X from -1..1 to ViewPlaneMin.X..ViewPlaneMax.X ** map view_point.Y from -1..1 to ViewPlaneMin.X..ViewPlaneMax.X */ float vpdx = ViewPlane.Max.X - ViewPlane.Min.X; float vpdy = ViewPlane.Max.Y - ViewPlane.Min.Y; Vector3 point; point.X = ViewPlane.Min.X + vpdx * (view_point.X + 1.0f) * 0.5f; point.Y = ViewPlane.Min.Y + vpdy * (view_point.Y + 1.0f) * 0.5f; point.Z = -1.0f; Matrix3D::Transform_Vector(Transform,point,&dest); } /*********************************************************************************************** * CameraClass::Transform_To_View_Space -- transforms the given world space point to camera sp * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 2/22/2001 gth : Created. * *=============================================================================================*/ void CameraClass::Transform_To_View_Space(Vector3 & dest,const Vector3 & ws_point) const { Update_Frustum(); Matrix3D::Transform_Vector(CameraInvTransform,ws_point,&dest); } /*********************************************************************************************** * CameraClass::Rotate_To_View_Space -- rotates the given world space vector to camera space * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 2/22/2001 gth : Created. * *=============================================================================================*/ void CameraClass::Rotate_To_View_Space(Vector3 & dest,const Vector3 & ws_vector) const { Update_Frustum(); Matrix3D::Rotate_Vector(CameraInvTransform,ws_vector,&dest); } /*********************************************************************************************** * CameraClass::Get_Near_Clip_Bounding_Box -- returns an obb that contains near clip plane * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 8/25/99 GTH : Created. * *=============================================================================================*/ const OBBoxClass & CameraClass::Get_Near_Clip_Bounding_Box(void) const { Update_Frustum(); return NearClipBBox; } /*********************************************************************************************** * CameraClass::Cull_Box -- tests whether the given box can be culled * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 12/8/98 GTH : Created. * *=============================================================================================*/ bool CameraClass::Cull_Box(const AABoxClass & box) const { const FrustumClass & frustum = Get_Frustum(); return CollisionMath::Overlap_Test(frustum,box) == CollisionMath::OUTSIDE; } /*********************************************************************************************** * CameraClass::Update_Frustum -- updates the frustum parameters * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 5/29/98 GTH : Created. * *=============================================================================================*/ void CameraClass::Update_Frustum(void) const { if (FrustumValid) return; Vector2 vpmin,vpmax; float znear,zfar; float znear_dist,zfar_dist; Matrix3D cam_mat = Get_Transform(); Get_View_Plane(vpmin, vpmax); // Normalized view plane at a depth of 1.0 Get_Clip_Planes(znear_dist, zfar_dist); // Forward is negative Z in our viewspace coordinate system. znear = -znear_dist; zfar = -zfar_dist; // Update the frustum FrustumValid = true; Frustum.Init(cam_mat,vpmin,vpmax,znear,zfar); ViewSpaceFrustum.Init(Matrix3D(1),vpmin,vpmax,znear,zfar); // Update the OBB around the near clip rectangle NearClipBBox.Center = cam_mat * Vector3(0,0,znear); NearClipBBox.Extent.X = (vpmax.X - vpmin.X) * (-znear) * 0.5f; // (near_clip_x / |znear|) == (vpmin.X / 1.0f)... NearClipBBox.Extent.Y = (vpmax.Y - vpmin.Y) * (-znear) * 0.5f; NearClipBBox.Extent.Z = 0.01f; NearClipBBox.Basis.Set(cam_mat); // Update the inverse camera matrix Transform.Get_Inverse(CameraInvTransform); // Update the projection matrix if (Projection == PERSPECTIVE) { ProjectionTransform.Init_Perspective( vpmin.X*znear_dist, vpmax.X*znear_dist, vpmin.Y*znear_dist, vpmax.Y*znear_dist, znear_dist, zfar_dist ); } else { ProjectionTransform.Init_Ortho( vpmin.X,vpmax.X,vpmin.Y,vpmax.Y,znear_dist,zfar_dist); } } /*********************************************************************************************** * CameraClass::Device_To_View_Space -- converts the given device coordinate to view space * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 12/8/98 GTH : Created. * *=============================================================================================*/ void CameraClass::Device_To_View_Space(const Vector2 & device_coord,Vector3 * set_view) { int res_width; int res_height; int res_bits; bool windowed; WW3D::Get_Render_Target_Resolution(res_width,res_height,res_bits,windowed); // convert the device coordinates into normalized device coordinates: Vector2 ndev; ndev.X = device_coord.X / (float)res_width; ndev.Y = device_coord.Y / (float)res_height; // view space rectangle which corresponds to the viewport Vector2 vs_min; Vector2 vs_max; Get_View_Plane(vs_min,vs_max); // mapping from the viewport coordinates to view space coordinates set_view->X = vs_min.X + (ndev.X - Viewport.Min.X) * (vs_max.X - vs_min.X) / (Viewport.Width()); set_view->Y = vs_max.Y - (ndev.Y - Viewport.Min.Y) * (vs_max.Y - vs_min.Y) / (Viewport.Height()); set_view->Z = -1.0f; } /*********************************************************************************************** * CameraClass::Device_To_World_Space -- converts given device coord to world space * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 12/8/98 GTH : Created. * *=============================================================================================*/ void CameraClass::Device_To_World_Space(const Vector2 & device_coord,Vector3 * world_coord) { Vector3 vs; Device_To_View_Space(device_coord,&vs); Matrix3D::Transform_Vector(Transform,vs,world_coord); } /*********************************************************************************************** * CameraClass::Apply -- sets the D3D states controlled by the camera * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/29/2001 gth : Created. * *=============================================================================================*/ void CameraClass::Apply(void) { Update_Frustum(); int width,height,bits; bool windowed; WW3D::Get_Render_Target_Resolution(width,height,bits,windowed); D3DVIEWPORT8 vp; vp.X = (DWORD)(Viewport.Min.X * (float)width); vp.Y = (DWORD)(Viewport.Min.Y * (float)height); vp.Width = (DWORD)((Viewport.Max.X - Viewport.Min.X) * (float)width); vp.Height = (DWORD)((Viewport.Max.Y - Viewport.Min.Y) * (float)height); vp.MinZ = ZBufferMin; vp.MaxZ = ZBufferMax; DX8Wrapper::Set_Viewport(&vp); Matrix4 d3dprojection; Get_D3D_Projection_Matrix(&d3dprojection); DX8Wrapper::Set_Projection_Transform_With_Z_Bias(d3dprojection,ZNear,ZFar); DX8Wrapper::Set_Transform(D3DTS_VIEW,CameraInvTransform); } void CameraClass::Set_Clip_Planes(float znear,float zfar) { FrustumValid = false; ZNear = znear; ZFar = zfar; } void CameraClass::Get_Clip_Planes(float & znear,float & zfar) const { znear = ZNear; zfar = ZFar; } float CameraClass::Get_Horizontal_FOV(void) const { float width = ViewPlane.Max.X - ViewPlane.Min.X; return 2*WWMath::Atan2(width,2.0); } float CameraClass::Get_Vertical_FOV(void) const { float height = ViewPlane.Max.Y - ViewPlane.Min.Y; return 2*WWMath::Atan2(height,2.0); } float CameraClass::Get_Aspect_Ratio(void) const { return AspectRatio; } void CameraClass::Get_Projection_Matrix(Matrix4 * set_tm) { WWASSERT(set_tm != NULL); Update_Frustum(); *set_tm = ProjectionTransform; } void CameraClass::Get_D3D_Projection_Matrix(Matrix4 * set_tm) { WWASSERT(set_tm != NULL); Update_Frustum(); *set_tm = ProjectionTransform; /* ** We need to flip the handed-ness of the projection matrix and ** move the z-range to 0