/* ** 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 : WWPhys * * * * $Archive:: /Commando/Code/wwphys/camerashakesystem.cpp $* * * * Original Author:: Greg Hjelstrom * * * * $Author:: Greg_h $* * * * $Modtime:: 6/12/01 10:25a $* * * * $Revision:: 3 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "camerashakesystem.h" #include "camera.h" #include "wwmemlog.h" /* ** (gth) According to my "research" the artists say that there are several factors that ** go into a good camera shake. ** - The motion should be sinusoidal. ** - Camera rotation is more effective than camera motion (good, I won't use any translation) ** - The camera should pitch up and down a lot more than it yaws left and right. */ DEFINE_AUTO_POOL(CameraShakeSystemClass::CameraShakerClass,256); const float MIN_OMEGA = DEG_TO_RADF(12.5f*360.0f); const float MAX_OMEGA = DEG_TO_RADF(15.0f*360.0f); const float END_OMEGA = DEG_TO_RADF(360.0f); const float MIN_PHI = DEG_TO_RADF(0.0f); const float MAX_PHI = DEG_TO_RADF(360.0f); const Vector3 AXIS_ROTATION = Vector3(DEG_TO_RADF(7.5f),DEG_TO_RADF(15.0f),DEG_TO_RADF(5.0f)); /************************************************************************************************ ** ** CameraShakeSystemClass::CameraShakerClass Implementation ** ************************************************************************************************/ CameraShakeSystemClass::CameraShakerClass::CameraShakerClass ( const Vector3 & position, float radius, float duration, float intensity ) : Position(position), Radius(radius), Duration(duration), Intensity(intensity), ElapsedTime(0.0f) { /* ** Initialize random sinusoid values */ Omega.X = WWMath::Random_Float(MIN_OMEGA,MAX_OMEGA); Omega.Y = WWMath::Random_Float(MIN_OMEGA,MAX_OMEGA); Omega.Z = WWMath::Random_Float(MIN_OMEGA,MAX_OMEGA); Phi.X = WWMath::Random_Float(MIN_PHI,MAX_PHI); Phi.Y = WWMath::Random_Float(MIN_PHI,MAX_PHI); Phi.Z = WWMath::Random_Float(MIN_PHI,MAX_PHI); } CameraShakeSystemClass::CameraShakerClass::~CameraShakerClass(void) { } void CameraShakeSystemClass::CameraShakerClass::Compute_Rotations(const Vector3 & camera_position, Vector3 * set_angles) { WWASSERT(set_angles != NULL); /* ** We want several different sinusiods, each with a different phase shift and ** frequency. The frequency is a function of time as well, stretching the ** sine wave out. These waves are modulated based on the distance from the ** center of the "shake", the intensity of the shake, and based on the axis ** being affected. The vertical axis should have about 3x the amplitude of ** the horizontal axis. */ float len2 = (camera_position - Position).Length2(); if (len2 > Radius*Radius) { return; } /* ** f(t) = intensity(t,pos) * sin( omega(t) * t + phi ); ** intensity(t,pos) = intensity * (radius/distance) * timeremaing/totaltime ** omega(t) = start_omega + (end_omega - start_omega) * t ** phi = random(0..start_omega) */ float intensity = Intensity * (1.0f - WWMath::Sqrt(len2) / Radius) * (1.0f - ElapsedTime / Duration); for (int i=0; i<3; i++) { float omega = Omega[i] + (END_OMEGA - Omega[i]) * ElapsedTime; (*set_angles)[i] += AXIS_ROTATION[i] * intensity * WWMath::Sin(omega * ElapsedTime + Phi[i]); } } /************************************************************************************************ ** ** CameraShakeSystemClass Implementation ** ************************************************************************************************/ CameraShakeSystemClass::CameraShakeSystemClass(void) { } CameraShakeSystemClass::~CameraShakeSystemClass(void) { /* ** delete all of the objects out of the list */ while (!CameraShakerList.Is_Empty()) { CameraShakerClass * obj = CameraShakerList.Remove_Head(); CameraShakerList.Remove(obj); delete obj; } } void CameraShakeSystemClass::Add_Camera_Shake ( const Vector3 & position, float radius, float duration, float power ) { WWMEMLOG(MEM_PHYSICSDATA); /* ** Allocate a new camera shaker object. Note that these are mem-pooled so the allocation ** is very cheap. */ CameraShakerClass * shaker = new CameraShakerClass(position,radius,duration,power); CameraShakerList.Add(shaker); } void CameraShakeSystemClass::Timestep(float dt) { /* ** Allow each camera shaker to timestep. Any that expire are added to a temporary ** list for deletion. */ MultiListClass deletelist; MultiListIterator iterator(&CameraShakerList); for (iterator.First(); !iterator.Is_Done(); iterator.Next()) { CameraShakerClass * obj = iterator.Peek_Obj(); obj->Timestep(dt); if (obj->Is_Expired()) { deletelist.Add(obj); } } /* ** Remove and delete all the ones that expired */ while (!deletelist.Is_Empty()) { CameraShakerClass * obj = deletelist.Remove_Head(); CameraShakerList.Remove(obj); delete obj; } } void CameraShakeSystemClass::Update_Camera(CameraClass & camera) { MultiListIterator iterator(&CameraShakerList); Vector3 angles(0,0,0); Vector3 camera_position; Matrix3D camera_transform; camera_transform = camera.Get_Transform(); camera_transform.Get_Translation(&camera_position); /* ** Accumulate the effects of any active camera shakers */ for (iterator.First(); !iterator.Is_Done(); iterator.Next()) { iterator.Peek_Obj()->Compute_Rotations(camera_position,&angles); } /* ** Clamp the result */ for (int i=0; i<3; i++) { WWMath::Clamp(angles[i],-AXIS_ROTATION[i],AXIS_ROTATION[i]); } /* ** Apply to the camera */ camera_transform.Rotate_X(angles.X); camera_transform.Rotate_Y(angles.Y); camera_transform.Rotate_Z(angles.Z); camera.Set_Transform(camera_transform); }