/* ** 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 : Commando * * * * $Archive:: /Commando/Code/Combat/ccamera.cpp $* * * * $Author:: Greg_h $* * * * $Modtime:: 6/14/02 10:44a $* * * * $Revision:: 162 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "ccamera.h" #include "timemgr.h" #include "physicalgameobj.h" #include "physcoltest.h" #include "pscene.h" #include "combat.h" #include "input.h" #include "debug.h" #include "assets.h" #include "slist.h" #include #include "vector3.h" #include "phys.h" #include "vehicle.h" #include "soldier.h" #include "weapons.h" #include "gameobjmanager.h" #include "htree.h" #include "hanim.h" #include "hudinfo.h" #include "listener.h" #include "wwaudio.h" #include "soundscene.h" #include "wwprofile.h" #include "diaglog.h" #include "gametype.h" #include "wwphysids.h" #include "buildingaggregate.h" #include "persistfactory.h" #define MIN_FOV 0.02f #define MAX_FOV 2.6f #define CCAMERA_NEARZ 0.26f // near clip plane distance #define CCAMERA_FARZ 300.0f // far clip plane distance #define CCAMERA_SHRINK_NEARZ_DIST 0.5f // third person distance below which we start shrinking nearz #define CCAMERA_MIN_NEARZ 0.2f // how small nearz gets when we're up against a wall Vector3 FirstPersonOffsetTweak( 0, 0, 0 ); /* ** Speed at which the camera pushes back out to its default position ** after a collision. 1.0 = goes all the way in 1 second */ const float CAMERA_UNWIND_SPEED = 1.0f; /* ** Camera Profiles */ //SList ProfileList; HashTemplateClass ProfileHash; #define CAMERAS_INI_FILENAME "CAMERAS.INI" #define SECTION_PROFILE_LIST "Profile_List" #define ENTRY_NAME "Name" #define ENTRY_FOV "FOV" #define ENTRY_HEIGHT "Height" #define ENTRY_VIEWTILT "ViewTilt" #define ENTRY_TRANSLATIONTILT "TranslationTilt" #define ENTRY_DISTANCE "Distance" #define ENTRY_LAG_UP "LagUp" #define ENTRY_LAG_LEFT "LagLeft" #define ENTRY_LAG_FORWARD "LagForward" #define ENTRY_TILTTWEAK "TiltTweak" /* ** */ class CCameraProfileClass { public: static void Init( void ); static void Shutdown( void ); void Set_Zoom( float amount ); float Get_Zoom( void ); void Set_Height( float height ) { Height = height; } float Get_Height( void ) { return Height; } void Set_Distance( float distance ) { Distance = distance; } float Get_Distance( void ) { return Distance; } protected: CCameraProfileClass( void ); ~CCameraProfileClass( void ); const CCameraProfileClass & operator = ( const CCameraProfileClass & ); void Lerp( const CCameraProfileClass & a, const CCameraProfileClass & b, float lerp ); // StringClass Name; float FOV; // field of view for the camera float Height; // height above the origin of "focus" object float ViewTilt; // default tilt of the camera float TiltTweak; // default tilt tweak of the camera float TranslationTilt; // tilt of translation vector for the camera (off of the z axis) float Distance; // how far back the camera wants to be normally Vector3 Lag; // the camera lag static bool _ProfilesInitted; static CCameraProfileClass * Find( const char * name ); friend CCameraClass; }; bool CCameraProfileClass::_ProfilesInitted = false; /* ** */ CCameraProfileClass::CCameraProfileClass( void ) : FOV( DEG_TO_RADF( 65.0f ) ), Height( 1.95f ), ViewTilt( DEG_TO_RADF(20.0f) ), TiltTweak( 0.6f ), TranslationTilt( DEG_TO_RADF( 19.9f ) ), Distance( 3.1f ), Lag( 0,0,0 ) { } CCameraProfileClass::~CCameraProfileClass( void ) { } void CCameraProfileClass::Set_Zoom( float amount ) { amount = WWMath::Sqrt(amount); amount = WWMath::Clamp( (1-amount), 0, 1 ); FOV = MIN_FOV + ((0.8f - MIN_FOV) * amount); } float CCameraProfileClass::Get_Zoom( void ) { // TSS - by my reckoning this is the actual zoom factor return 0.8 / FOV; } #define Get_Camera_Profile_Radians( v, e ) \ profile->v = DEG_TO_RADF( camerasINI->Get_Float( section_name, e, RAD_TO_DEGF( profile->v ) ) ) #define Get_Camera_Profile_Float( v, e ) \ profile->v = camerasINI->Get_Float( section_name, e, profile->v ) //#define Get_Camera_Profile_String( v, e ) \ // profile->v = camerasINI->Get_String( section_name, e, profile->v ) void CCameraProfileClass::Init( void ) { if (_ProfilesInitted) { CCameraProfileClass::Shutdown(); } INIClass * camerasINI = Get_INI( CAMERAS_INI_FILENAME ); if (camerasINI != NULL) { WWASSERT( camerasINI && camerasINI->Section_Count() > 0 ); int count, entry; count = camerasINI->Entry_Count( SECTION_PROFILE_LIST ); for ( entry = 0; entry < count; entry++ ) { StringClass section_name(0,true); camerasINI->Get_String( section_name, SECTION_PROFILE_LIST, camerasINI->Get_Entry( SECTION_PROFILE_LIST, entry) ); CCameraProfileClass * profile = new CCameraProfileClass(); WWASSERT( profile ); StringClass name(true); camerasINI->Get_String( name, section_name, ENTRY_NAME, name ); // Get_Camera_Profile_String( name, ENTRY_NAME ); Get_Camera_Profile_Radians( FOV, ENTRY_FOV ); Get_Camera_Profile_Float( Height, ENTRY_HEIGHT ); Get_Camera_Profile_Radians( ViewTilt, ENTRY_VIEWTILT ); Get_Camera_Profile_Float( TiltTweak, ENTRY_TILTTWEAK ); Get_Camera_Profile_Radians( TranslationTilt, ENTRY_TRANSLATIONTILT ); Get_Camera_Profile_Float( Distance, ENTRY_DISTANCE ); Get_Camera_Profile_Float( Lag.Y, ENTRY_LAG_UP ); Get_Camera_Profile_Float( Lag.X, ENTRY_LAG_LEFT ); Get_Camera_Profile_Float( Lag.Z, ENTRY_LAG_FORWARD ); // ProfileList.Add_Tail( profile ); // Convert to lower case _strlwr(name.Peek_Buffer()); ProfileHash.Insert(name,profile); } Release_INI( camerasINI ); } else { Debug_Say(("CCameraProfileClass::Init - Unable to load %s\n", CAMERAS_INI_FILENAME)); } _ProfilesInitted = true; } void CCameraProfileClass::Shutdown( void ) { // while( ProfileList.Get_Count() ) { // delete ProfileList.Remove_Head(); // } HashTemplateIterator ite(ProfileHash); for (ite.First();!ite.Is_Done();ite.Next()) { CCameraProfileClass * profile=ite.Peek_Value(); delete profile; } ProfileHash.Remove_All(); _ProfilesInitted = false; } const CCameraProfileClass & CCameraProfileClass::operator = ( const CCameraProfileClass & src ) { // Name = src.Name; FOV = src.FOV; Height = src.Height; ViewTilt = src.ViewTilt; TiltTweak = src.TiltTweak; TranslationTilt = src.TranslationTilt; Distance = src.Distance; Lag = src.Lag; return *this; } float RadianLerp( float a, float b, float lerp ) { float diff = b - a; // diff should be -180 .. 180 diff = WWMath::Wrap( diff, DEG_TO_RADF( -180 ), DEG_TO_RADF( 180 ) ); // diff += DEG_TO_RAD( 360 ) * WWMath::Sign( -diff ); // Take the long way return a + diff * lerp; } void CCameraProfileClass::Lerp( const CCameraProfileClass & a, const CCameraProfileClass & b, float lerp ) { *this = a; // Start by taking A FOV = WWMath::Lerp( a.FOV, b.FOV, lerp ); Height = WWMath::Lerp( a.Height, b.Height, lerp ); ViewTilt = WWMath::Lerp( a.ViewTilt, b.ViewTilt, lerp ); TiltTweak = WWMath::Lerp( a.TiltTweak, b.TiltTweak, lerp ); TranslationTilt= WWMath::Lerp( a.TranslationTilt, b.TranslationTilt, lerp ); Distance = WWMath::Lerp( a.Distance, b.Distance, lerp ); } CCameraProfileClass * CCameraProfileClass::Find( const char * name ) { // Compare lower case strings char tmp[256]; strncpy(tmp,name,sizeof(tmp)); _strlwr(tmp); StringClass tmp_string(tmp,true); CCameraProfileClass* profile = ProfileHash.Get(tmp_string); return profile; // SLNode *node; // for ( node = ProfileList.Head(); node; node = node->Next()) { // if ( !stricmp( node->Data()->Name, name ) ) { // return node->Data(); // } // } // return NULL; } /* ** CCameraClass */ CCameraClass::CCameraClass() : CameraClass(), HostModel( NULL ), AnchorPosition(0,0,0), IsValid( false ), Tilt( 0 ), Heading( 0 ), DistanceFraction(1.0f), Enable2DTargeting( false ), EnableWeaponHelp( false ), CameraTarget2DOffset( 0.5f, 0.5f ), LerpTimeTotal( 0.0f ), LerpTimeRemaining( 0.0f ), LastAnchorPosition(0,0,0), LastHeading( 0 ), CurrentProfile( NULL ), LastProfile( NULL ), DefaultProfile( NULL ), NearClipPlane( CCAMERA_NEARZ ), FarClipPlane( CCAMERA_FARZ ), IsStarSniping( false ), WasStarSniping( false ), CinematicSnipingEnabled( false ), CinematicSnipingDesiredZoom( 0 ), SniperZoom( 0 ), SniperDistance( 0 ), SniperListener( NULL ), SnapShotMode( SNAPSHOT_OFF ), WeaponHelpTimer( 0 ), WeaponHelpTargetID( 0 ), LagPersistTimer( 0 ), DisableLag( false ) { Set_Clip_Planes( NearClipPlane, FarClipPlane ); Set_View_Plane( DEG_TO_RAD( 90.0 ) ); SniperListener = new Listener3DClass; DefaultProfile = CCameraProfileClass::Find( "default" ); DefaultProfileName="default"; Use_Default_Profile(); } CCameraClass::~CCameraClass(void) { Set_Host_Model( NULL ); // // Remove the sniper listener (if necessary) from the world // if ( SniperListener != NULL && WWAudioClass::Get_Instance() != NULL && WWAudioClass::Get_Instance()->Get_Sound_Scene() != NULL) { if ( WWAudioClass::Get_Instance()->Get_Sound_Scene()->Peek_2nd_Listener() != NULL ) { WWAudioClass::Get_Instance()->Get_Sound_Scene ()->Set_2nd_Listener( NULL ); } } REF_PTR_RELEASE (SniperListener); } /* ** */ void CCameraClass::Init( void ) { CCameraProfileClass::Init(); } void CCameraClass::Shutdown( void ) { CCameraProfileClass::Shutdown(); } /* ** Save and Load */ enum { XXXCHUNKID_PARENT = 416012036, CHUNKID_VARIABLES, MICROCHUNKID_HOST_MODEL = 1, MICROCHUNKID_ANCHOR_POSITION, MICROCHUNKID_IS_VALID, MICROCHUNKID_TILT, MICROCHUNKID_HEADING, MICROCHUNKID_DISTANCE_FRACTION, MICROCHUNKID_ENABLE_2D_TARGETING, MICROCHUNKID_ENABLE_WEAPON_HELP, MICROCHUNKID_WEAPON_HELP_TIMER, MICROCHUNKID_WEAPON_HELP_TARGET_ID, MICROCHUNKID_STAR_TARGETING_POSITION, MICROCHUNKID_CAMERA_TARGET_2D_OFFSET, MICROCHUNKID_LERP_TIME_TOTAL, MICROCHUNKID_LERP_TIME_REMAINING, MICROCHUNKID_LAST_ANCHOR_POSITION, MICROCHUNKID_LAST_HEADING, MICROCHUNKID_CURRENT_PROFILE, MICROCHUNKID_LAST_PROFILE, MICROCHUNKID_DEFAULT_PROFILE, MICROCHUNKID_NEAR_CLIP_PLANE, MICROCHUNKID_FAR_CLIP_PLANE, MICROCHUNKID_IS_STAR_SNIPING, MICROCHUNKID_WAS_STAR_SNIPING, MICROCHUNKID_SNIPER_ZOOM, MICROCHUNKID_SNIPER_DISTANCE, XXXMICROCHUNKID_IS_SNIPER_LOCKED, XXXMICROCHUNKID_SNIPER_TARGET_NAME, XXXMICROCHUNKID_SNIPER_LISTENER, MICROCHUNKID_SNAP_SHOT_MODE, MICROCHUNKID_CINEMATIC_SNIPING_ENABLED, MICROCHUNKID_CINEMATIC_SNIPING_DESIRED_ZOOM, }; //------------------------------------------------------------------------------------ bool CCameraClass::Save( ChunkSaveClass & csave ) { csave.Begin_Chunk( CHUNKID_VARIABLES ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HOST_MODEL, HostModel ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ANCHOR_POSITION, AnchorPosition ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_IS_VALID, IsValid ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TILT, Tilt ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HEADING, Heading ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DISTANCE_FRACTION, DistanceFraction ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ENABLE_2D_TARGETING, Enable2DTargeting ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ENABLE_WEAPON_HELP, EnableWeaponHelp ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_WEAPON_HELP_TIMER, WeaponHelpTimer ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_WEAPON_HELP_TARGET_ID, WeaponHelpTargetID ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAR_TARGETING_POSITION,StarTargetingPosition ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_CAMERA_TARGET_2D_OFFSET,CameraTarget2DOffset ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LERP_TIME_TOTAL, LerpTimeTotal ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LERP_TIME_REMAINING, LerpTimeRemaining ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LAST_ANCHOR_POSITION, LastAnchorPosition ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LAST_HEADING, LastHeading ); if ( CurrentProfile != NULL ) { // StringClass name = CurrentProfile->Name; WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_CURRENT_PROFILE, CurrentProfileName ); } if ( LastProfile != NULL ) { // StringClass name = LastProfile->Name; WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_LAST_PROFILE, LastProfileName ); } if ( DefaultProfile != NULL ) { // StringClass name = DefaultProfile->Name; WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_DEFAULT_PROFILE, DefaultProfileName ); } // (gth) don't save and load NearClipPlane and FarClipPlane // WRITE_MICRO_CHUNK( csave, MICROCHUNKID_NEAR_CLIP_PLANE, NearClipPlane ); // WRITE_MICRO_CHUNK( csave, MICROCHUNKID_FAR_CLIP_PLANE, FarClipPlane ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_IS_STAR_SNIPING, IsStarSniping ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_WAS_STAR_SNIPING, WasStarSniping ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_CINEMATIC_SNIPING_ENABLED, CinematicSnipingEnabled ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_CINEMATIC_SNIPING_DESIRED_ZOOM, CinematicSnipingDesiredZoom ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SNIPER_ZOOM, SniperZoom ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SNIPER_DISTANCE, SniperDistance ); // WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SNIPER_LISTENER, SniperListener ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SNAP_SHOT_MODE, SnapShotMode ); csave.End_Chunk(); return true; } //------------------------------------------------------------------------------------ bool CCameraClass::Load( ChunkLoadClass &cload ) { StringClass current_name; StringClass last_name; StringClass default_name; while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case CHUNKID_VARIABLES: while (cload.Open_Micro_Chunk()) { switch(cload.Cur_Micro_Chunk_ID()) { READ_MICRO_CHUNK( cload, MICROCHUNKID_HOST_MODEL, HostModel ); READ_MICRO_CHUNK( cload, MICROCHUNKID_ANCHOR_POSITION, AnchorPosition ); READ_MICRO_CHUNK( cload, MICROCHUNKID_IS_VALID, IsValid ); READ_MICRO_CHUNK( cload, MICROCHUNKID_TILT, Tilt ); READ_MICRO_CHUNK( cload, MICROCHUNKID_HEADING, Heading ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DISTANCE_FRACTION, DistanceFraction ); READ_MICRO_CHUNK( cload, MICROCHUNKID_ENABLE_2D_TARGETING, Enable2DTargeting ); READ_MICRO_CHUNK( cload, MICROCHUNKID_ENABLE_WEAPON_HELP, EnableWeaponHelp ); READ_MICRO_CHUNK( cload, MICROCHUNKID_WEAPON_HELP_TIMER, WeaponHelpTimer ); READ_MICRO_CHUNK( cload, MICROCHUNKID_WEAPON_HELP_TARGET_ID, WeaponHelpTargetID ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAR_TARGETING_POSITION, StarTargetingPosition ); READ_MICRO_CHUNK( cload, MICROCHUNKID_CAMERA_TARGET_2D_OFFSET, CameraTarget2DOffset ); READ_MICRO_CHUNK( cload, MICROCHUNKID_LERP_TIME_TOTAL, LerpTimeTotal ); READ_MICRO_CHUNK( cload, MICROCHUNKID_LERP_TIME_REMAINING, LerpTimeRemaining ); READ_MICRO_CHUNK( cload, MICROCHUNKID_LAST_ANCHOR_POSITION, LastAnchorPosition ); READ_MICRO_CHUNK( cload, MICROCHUNKID_LAST_HEADING, LastHeading ); READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_CURRENT_PROFILE,current_name ); READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_LAST_PROFILE, last_name ); READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_DEFAULT_PROFILE,default_name ); // (gth) don't save and load NearClipPlane and FarClipPlane // READ_MICRO_CHUNK( cload, MICROCHUNKID_NEAR_CLIP_PLANE, NearClipPlane ); // READ_MICRO_CHUNK( cload, MICROCHUNKID_FAR_CLIP_PLANE, FarClipPlane ); READ_MICRO_CHUNK( cload, MICROCHUNKID_IS_STAR_SNIPING, IsStarSniping ); READ_MICRO_CHUNK( cload, MICROCHUNKID_WAS_STAR_SNIPING, WasStarSniping ); READ_MICRO_CHUNK( cload, MICROCHUNKID_CINEMATIC_SNIPING_ENABLED, CinematicSnipingEnabled ); READ_MICRO_CHUNK( cload, MICROCHUNKID_CINEMATIC_SNIPING_DESIRED_ZOOM, CinematicSnipingDesiredZoom ); READ_MICRO_CHUNK( cload, MICROCHUNKID_SNIPER_ZOOM, SniperZoom ); READ_MICRO_CHUNK( cload, MICROCHUNKID_SNIPER_DISTANCE, SniperDistance ); // READ_MICRO_CHUNK( cload, MICROCHUNKID_SNIPER_LISTENER, SniperListener ); READ_MICRO_CHUNK( cload, MICROCHUNKID_SNAP_SHOT_MODE, SnapShotMode ); default: Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Micro_Chunk(); } break; default: Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Chunk(); } if ( !current_name.Is_Empty() ) { CurrentProfile = CCameraProfileClass::Find( current_name ); CurrentProfileName=current_name; } if ( !last_name.Is_Empty() ) { LastProfile = CCameraProfileClass::Find( last_name ); LastProfileName=last_name; } if ( !default_name.Is_Empty() ) { DefaultProfile = CCameraProfileClass::Find( default_name ); DefaultProfileName=default_name; } if ( HostModel != NULL ) { REQUEST_REF_COUNTED_POINTER_REMAP ((RefCountClass **)&HostModel); } return true; } /* ** */ void CCameraClass::Set_Host_Model( RenderObjClass * host ) { DisableLag = true; static StringClass SavedProfileName; // If dropping host if ( HostModel != NULL && host == NULL ) { if ( SavedProfileName.Is_Empty() ) { Use_Default_Profile(); } else { Use_Profile( SavedProfileName ); // Return to saved profile } } // If getting host if ( HostModel == NULL && host != NULL ) { SavedProfileName = CurrentProfileName; Use_Profile( "Cinematic" ); // CurrentProfile->Set_Zoom( 0 ); CurrentProfile->FOV = DEG_TO_RADF( 75.0f ); CinematicSnipingEnabled = false; } if ( HostModel ) { HostModel->Release_Ref(); IsValid = false; } HostModel = host; if ( HostModel ) { HostModel->Add_Ref(); IsValid = true; } // If clearing host, force facing to match the star if ( host == NULL && COMBAT_STAR != NULL ) { Force_Heading( COMBAT_STAR->Get_Facing() ); } // When the camera is controlled, activeate cinematic freeze GameObjManager::Activate_Cinematic_Freeze( host != NULL ); } void Convert_World_To_Camera( Matrix3D * tm ) { tm->Rotate_Y( DEG_TO_RADF(-90.0) ); tm->Rotate_Z( DEG_TO_RADF(-90.0) ); } void CCameraClass::Use_Host_Model( void ) { WWASSERT( HostModel ); Matrix3D tm = HostModel->Get_Bone_Transform( "CAMERA" ); Set_Transform( tm ); if ( !CinematicSnipingEnabled ) { CurrentProfile->FOV = DEG_TO_RADF( 75.0f ); } Set_View_Plane( CurrentProfile->FOV ); #ifdef ATI_DEMO_HACK static GameObjReference DemoFocusObject; /* ** Focus change key pressed: */ if (Input::Get_State(INPUT_FUNCTION_PROFILE_RESET)) { if (DemoFocusObject.Get_Ptr() == NULL) { /* ** Count the soldiers in the level (ignoring the human controlled commando) */ int soldier_count = 0; SLNode * smart_objnode; for (smart_objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head(); smart_objnode; smart_objnode = smart_objnode->Next()) { SmartGameObj * obj = smart_objnode->Data(); if ((obj->As_SoldierGameObj() != NULL) && (obj->Is_Human_Controlled() == false)) { soldier_count++; } } /* ** Pick a random soldier to watch */ int random_soldier = rand() % soldier_count; soldier_count = 0; for (smart_objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head(); smart_objnode; smart_objnode = smart_objnode->Next()) { SmartGameObj * obj = smart_objnode->Data(); if ((obj->As_SoldierGameObj() != NULL) && (obj->Is_Human_Controlled() == false)) { if (soldier_count == random_soldier) { DemoFocusObject = obj; break; } soldier_count++; } } } else { /* ** Toggle out of demo focus mode */ DemoFocusObject = NULL; } } /* ** Position the camera on this soldier */ if (DemoFocusObject.Get_Ptr() != NULL) { Vector3 position; DemoFocusObject.Get_Ptr()->Get_Position(&position); Matrix3D camera_tm; camera_tm.Look_At(position + Vector3(2.0f,0.0f,1.75f),position + Vector3(0.0f,0.0f,1.0f),0.0f); Set_Transform(camera_tm); } #endif } /* ** */ void CCameraClass::Set_Anchor_Position( Vector3 pos ) { AnchorPosition = pos; // If this is the first anchor point, don't lag the camera if ( !IsValid ) { LastAnchorPosition = pos; IsValid = true; } } /* ** */ void Ignore_Star_And_Vehicle( void ) { if (COMBAT_STAR != NULL) { COMBAT_STAR->Peek_Physical_Object()->Inc_Ignore_Counter(); VehicleGameObj * vehicle = COMBAT_STAR->Get_Profile_Vehicle(); if ( vehicle ) { vehicle->Peek_Physical_Object()->Inc_Ignore_Counter(); } } } void Unignore_Star_And_Vehicle( void ) { if (COMBAT_STAR != NULL) { COMBAT_STAR->Peek_Physical_Object()->Dec_Ignore_Counter(); VehicleGameObj * vehicle = COMBAT_STAR->Get_Profile_Vehicle(); if ( vehicle ) { vehicle->Peek_Physical_Object()->Dec_Ignore_Counter(); } } } /* ** */ void CCameraClass::Update() { Handle_Input(); WWASSERT( CurrentProfile ); if ( SnapShotMode != SNAPSHOT_OFF ) { Handle_Snap_Shot_Mode(); return; } if ( Is_Using_Host_Model() ) { // if using a host model, update the Use_Host_Model(); // camera from it return; } Vector3 anchor_position = AnchorPosition; float camera_heading = Heading; CCameraProfileClass profile; bool interpolating = false; if ( LerpTimeTotal ) { interpolating = true; float frame_time = TimeManager::Get_Frame_Seconds(); float lerp = WWMath::Clamp( LerpTimeRemaining / LerpTimeTotal, 0, 1 ); LerpTimeRemaining -= frame_time; if ( LerpTimeRemaining <= 0 ) { LerpTimeRemaining = 0.0f; LerpTimeTotal = 0.0f; // Set up last, so out Lag code doesn't use the old LastAnchorPosition = anchor_position; LastHeading = Heading; } profile = *CurrentProfile; if ( LastProfile ) { profile.Lerp( *CurrentProfile, *LastProfile, lerp ); } anchor_position = ::Lerp( anchor_position, LastAnchorPosition, lerp ); camera_heading = RadianLerp( camera_heading, LastHeading, lerp ); } else { profile = *CurrentProfile; LastProfile = CurrentProfile; LastProfileName=CurrentProfileName; if ( profile.Lag.Length() > 0 ) { // This is an attempt to not lag when in an elevator bool lag_ok = false; if ( COMBAT_STAR == NULL || COMBAT_STAR->Is_Airborne() ) { lag_ok = true; LagPersistTimer = 1; // Persist the lag for 1 second } else { if ( LagPersistTimer > 0 ) { lag_ok = true; LagPersistTimer -= TimeManager::Get_Frame_Seconds(); // Debug_Say(( "Lag Persist\n" )); } } if ( DisableLag ) { lag_ok = false; DisableLag = false; } if ( lag_ok ) { // Get position local to the camera Vector3 local_last; Vector3 local_current; Matrix3D tm = Get_Transform(); Matrix3D::Inverse_Transform_Vector( tm, LastAnchorPosition, &local_last ); Matrix3D::Inverse_Transform_Vector( tm, anchor_position, &local_current ); // Lerp // ( left/right, up/down, forward/back ) Vector3 lerp = profile.Lag * WWMath::Clamp( LagPersistTimer, 0, 1); //( 0, 0.5f, 0.2f ); // ( y, z, x ) lerp.X = ::pow( lerp.X, 10 * TimeManager::Get_Frame_Seconds() ); lerp.Y = ::pow( lerp.Y, 10 * TimeManager::Get_Frame_Seconds() ); lerp.Z = ::pow( lerp.Z, 10 * TimeManager::Get_Frame_Seconds() ); lerp = Vector3( 1,1,1 ) - lerp; local_current.X = local_last.X + ( local_current.X - local_last.X ) * lerp.X; local_current.Y = local_last.Y + ( local_current.Y - local_last.Y ) * lerp.Y; local_current.Z = local_last.Z + ( local_current.Z - local_last.Z ) * lerp.Z; Matrix3D::Transform_Vector( tm, local_current, &anchor_position ); Vector3 diff = anchor_position - LastAnchorPosition; if ( diff.Length() < 5.0f ) { // Don't lerp over long distances anchor_position = ::Lerp( anchor_position, LastAnchorPosition, 0.25f ); } else { LagPersistTimer = 0; DisableLag = true; } } } LastAnchorPosition = anchor_position; LastHeading = Heading; } Set_View_Plane( profile.FOV ); // Apply Zoom // Calculate the Camera Transform Matrix3D tm(1); // Setup base position tm.Translate( anchor_position ); tm.Translate( Vector3( 0, 0, profile.Height ) ); Convert_World_To_Camera( &tm ); // Setup orientation tm.Rotate_Y( camera_heading ); // Apply rotations tm.Rotate_X( -profile.ViewTilt - Tilt); // Only do this when the profile has a distance value if ( profile.Distance != 0 ) { // Translate along Z so that our near clip plane is behind the head float nearz,farz; const float HEAD_RADIUS = 0.2f; // HEAD_RADIUS should be renamed and become part of the profile? Get_Clip_Planes(nearz,farz); tm.Translate_Z(nearz + HEAD_RADIUS); } Vector3 intermediate_pos = tm.Get_Translation(); // Save base position Matrix3D intermediate_tm = tm; // Save base transform // Generate a translation path for the camera which is 'tilt' off of the z-axis Vector3 camera_move(0,0,profile.Distance); camera_move.Rotate_X(-profile.TranslationTilt); camera_move.Rotate_X( -(WWMath::Max( -Tilt * profile.TiltTweak, 0 )) ); tm.Translate(camera_move); // Pull back Vector3 end_pos = tm.Get_Translation(); // Save the end position // Sweep the view plane back until it hits something if ( profile.Distance != 0 ) { WWPROFILE( "Sweep" ); // (gth) FIXME! // Sort of a hack here, trying to make the camera not collide with the star // Really this should make sure we don't collide with whatever the camera is starting inside Ignore_Star_And_Vehicle(); // Collide the bounding box of the near clip plane // Have to Set_Transform so that the camera can calculate the box for us CastResultStruct res; Set_Transform(intermediate_tm); Set_Clip_Planes( NearClipPlane,FarClipPlane ); OBBoxClass box = Get_Near_Clip_Bounding_Box(); PhysOBBoxCollisionTestClass boxtest( box, end_pos - intermediate_pos, &res, DEFAULT_COLLISION_GROUP, COLLISION_TYPE_CAMERA); PhysicsSceneClass::Get_Instance()->Cast_OBBox(boxtest); // Solve the problem of the camera when getting out of the car if ( res.StartBad && interpolating && boxtest.CollidedPhysObj ) { // ignore what we hit and do it again PhysClass * hit = boxtest.CollidedPhysObj; hit->Inc_Ignore_Counter(); res.Reset(); PhysicsSceneClass::Get_Instance()->Cast_OBBox(boxtest); hit->Dec_Ignore_Counter(); } // Move the camera to the collision if needed. if (res.Fraction < DistanceFraction) { DistanceFraction = res.Fraction; // always pull camera in if a collision occured } if (res.Fraction > DistanceFraction) { float dt = TimeManager::Get_Frame_Seconds(); DistanceFraction += WWMath::Min(res.Fraction-DistanceFraction,CAMERA_UNWIND_SPEED * dt); } if (DistanceFraction < 1.0f) { tm.Set_Translation(intermediate_pos + DistanceFraction * (end_pos - intermediate_pos)); end_pos = tm.Get_Translation(); } // When Physics_Debug is on, draw the camera collision box in its start point #ifdef WWDEBUG PhysicsSceneClass * scene = PhysicsSceneClass::Get_Instance(); if (scene && scene->Is_Debug_Display_Enabled()) { scene->Add_Debug_OBBox(box,Vector3(1,0,0)); } #endif // Now put the star back to his original 'ignore' state Unignore_Star_And_Vehicle(); } else { WWPROFILE( "No Sweep" ); // This is a camera which doesn't translate back. Just check its near clip plane for intersection // with the world and if it does intersect, pull the near clip plane in to its minimum. Ignore_Star_And_Vehicle(); // Collide the bounding box of the near clip plane // Have to Set_Transform so that the camera can calculate the box for us CastResultStruct res; Set_Transform(tm); Set_Clip_Planes(NearClipPlane,FarClipPlane); OBBoxClass box = Get_Near_Clip_Bounding_Box(); Vector3 null_vec(0.0f,0.0f,0.0f); PhysOBBoxCollisionTestClass boxtest( box, null_vec, &res, DEFAULT_COLLISION_GROUP, COLLISION_TYPE_CAMERA); PhysicsSceneClass::Get_Instance()->Cast_OBBox(boxtest); // Set the near clip plane depending on whether the default near clip plane // intersected any geometry if (res.StartBad) { Set_Clip_Planes(CCAMERA_MIN_NEARZ,FarClipPlane); } else { Set_Clip_Planes(NearClipPlane,FarClipPlane); } Unignore_Star_And_Vehicle(); } Set_Transform( tm ); // Set our new transform // Debug_Say(( "Camera at %1.2f, %1.2f, %1.2f\n", tm.Get_Translation().X, tm.Get_Translation().Y, tm.Get_Translation().Z )); // First, set the aiming point to where the camera is looking if ( Determine_Targeting_Position() == false ) { WWPROFILE( "Help" ); // Then, modify the aiming point for weapon help, if not on a target Apply_Weapon_Help(); } // Lastly, pass the aiming point to the star bool is_star_determining_target = CombatManager::Is_Star_Determining_Target(); if ( COMBAT_STAR && is_star_determining_target ) { // and tell the star what we are looking at... #if 0 // This didn't work with X key.... // Make sure it is forward in star space Matrix3D star_tm = COMBAT_STAR->Get_Transform(); Vector3 star_space_pos; Matrix3D::Inverse_Transform_Vector( star_tm, StarTargetingPosition, &star_space_pos ); if ( star_space_pos.X <= 0 ) { star_space_pos.X = MAX( star_space_pos.X, 1 ); } Debug_Say(( "SSP %f %f %f\n", star_space_pos.X, star_space_pos.Y, star_space_pos.Z )); Matrix3D::Transform_Vector( star_tm, star_space_pos, &StarTargetingPosition ); #endif COMBAT_STAR->Set_Targeting( StarTargetingPosition ); } } /* ** Determine_Targeting_Position finds the World position and object the player is looking/pointing at ** The details get stored in HUDInfo */ bool CCameraClass::Determine_Targeting_Position( void ) { WWPROFILE( "Determining Targeting" ); bool looking_at_object = false; Matrix3D tm = Get_Transform(); /* ** */ SoldierGameObj * star = COMBAT_STAR; bool is_star_determining_target = CombatManager::Is_Star_Determining_Target(); if ( star && is_star_determining_target ) { // and tell the star what we are looking at... // Now cast to determine what we are looking/pointing at... Vector3 cast_start = tm.Get_Translation(); float range = 100; if ( star && star->Get_Weapon() ) { range = star->Get_Weapon()->Get_Range(); } if ( star && star->Get_Vehicle() && star->Get_Vehicle()->Get_Weapon() ) { range = star->Get_Vehicle()->Get_Weapon()->Get_Range(); } range += CurrentProfile->Distance; Vector2 view_plane; view_plane.X = CameraTarget2DOffset.X; view_plane.Y = CameraTarget2DOffset.Y; Vector3 world_pos; COMBAT_CAMERA->Un_Project( world_pos, view_plane ); Vector3 cast_end = world_pos - cast_start; cast_end.Normalize(); cast_end *= range; cast_end += cast_start; LineSegClass ray; if ( !Enable2DTargeting ) { // start the ray in front of the character + 0.5m float move_forward_dist = DistanceFraction * CurrentProfile->Distance + 0.5f; cast_start += move_forward_dist * -(tm.Get_Z_Vector()); ray.Set( cast_start, cast_end ); } else { // for 2D, start at the camera ray.Set( cast_start, cast_end ); } // trim off Near Clip from Beginning float start_fraction = NearClipPlane / ray.Get_Length(); ray.Compute_Point( start_fraction, &cast_start ); ray.Set( cast_start, cast_end ); CastResultStruct result; PhysRayCollisionTestClass raytest(ray, &result, BULLET_COLLISION_GROUP, COLLISION_TYPE_PROJECTILE); WWASSERT(COMBAT_SCENE != NULL); Ignore_Star_And_Vehicle(); { WWPROFILE( "Cast Ray" ); COMBAT_SCENE->Cast_Ray( raytest ); } Unignore_Star_And_Vehicle(); // Determine the Camera Target Point and object ray.Compute_Point( raytest.Result->Fraction, &StarTargetingPosition ); // // (gth) "hack" to prevent the target point from ever ending up behind the player // this sometimes happens when looking up at an extreme angle in third person // if (COMBAT_STAR != NULL) { Vector3 player_pos(0,0,0); if (COMBAT_STAR->Get_Profile_Vehicle() != NULL) { COMBAT_STAR->Get_Profile_Vehicle()->Get_Position(&player_pos); } else { COMBAT_STAR->Get_Position(&player_pos); } float player_dx = player_pos.X - cast_start.X; float player_dy = player_pos.Y - cast_start.Y; float target_dx = StarTargetingPosition.X - cast_start.X; float target_dy = StarTargetingPosition.Y - cast_start.Y; float xy_dist_to_player2 = player_dx * player_dx + player_dy * player_dy; float xy_dist_to_target2 = target_dx * target_dx + target_dy * target_dy; if (xy_dist_to_target2 < xy_dist_to_player2) { // Move the point along the raycast until until // target is in front of player. float ray_parameter = 1.1f * WWMath::Sqrt(xy_dist_to_player2) / WWMath::Sqrt(xy_dist_to_target2); StarTargetingPosition = ray.Get_P0() + ray.Get_Dir() * ray_parameter; } } //Debug_Say(( "Targeting %f %f %f\n", StarTargetingPosition.X, StarTargetingPosition.Y, StarTargetingPosition.Z )); if ( raytest.CollidedPhysObj != NULL && raytest.CollidedPhysObj->Get_Observer() != NULL ) { DamageableGameObj * obj = ((CombatPhysObserverClass *)raytest.CollidedPhysObj->Get_Observer())->As_DamageableGameObj(); bool MCT = false; if ( obj->As_BuildingGameObj() ) { // Check for MCT collision if (raytest.CollidedPhysObj->Get_Factory().Chunk_ID() == PHYSICS_CHUNKID_BUILDINGAGGREGATE) { if (((BuildingAggregateClass *)raytest.CollidedPhysObj)->Is_MCT()) { MCT = true; } } } if ( obj->Is_Targetable() == false ) { obj = NULL; } // if stealthed and enemy, it's not targetable if ( obj && obj->As_SmartGameObj() && obj->As_SmartGameObj()->Is_Stealthed() && COMBAT_STAR && obj->Is_Enemy( COMBAT_STAR ) ) { obj = NULL; } looking_at_object = ( obj != NULL ); HUDInfo::Set_Info_Object( obj, MCT ); // Update the HUDInfo Info Object HUDInfo::Set_Weapon_Target_Object( obj ); } if ( raytest.CollidedPhysObj ) { Set_Sniper_Distance( raytest.Result->Fraction * ray.Get_Length() ); } else { Set_Sniper_Distance( 0 ); } } return looking_at_object; } /* ** Apply_Weapon_Help : Use weapon Help to update the StarTargetingPosition ); ** Should only be called if not already on a target object ** We will only do the search periodically, so we will save the last valid target, ** and track him until we search again. */ #define WEAPON_HELP_CHEAT 0 void CCameraClass::Apply_Weapon_Help( void ) { WWPROFILE( "Weapon Help" ); WeaponHelpTimer -= TimeManager::Get_Frame_Seconds(); if ( WeaponHelpTimer <= 0 ) { #if WEAPON_HELP_CHEAT WeaponHelpTimer = 0.01f; #else WeaponHelpTimer = 0.5f; // Re-check every 1/2 second #endif WeaponHelpTargetID = 0; // Asuume we find nothing SmartGameObj * star = COMBAT_STAR; // if ( star && EnableWeaponHelp ) { #if WEAPON_HELP_CHEAT if ( star ) { #else if ( star && CombatManager::Get_Difficulty_Level() == 0 && IS_MISSION ) { // if on easy difficulty #endif // Make sure dot is off #if WEAPON_HELP_CHEAT if ( !CombatManager::Is_Hit_Reticle_Enabled() ) { #else if ( CombatManager::Is_Hit_Reticle_Enabled() ) { #endif CombatManager::Toggle_Hit_Reticle_Enabled(); } float weapon_range = 100; if ( star->Get_Weapon() ) { weapon_range = star->Get_Weapon()->Get_Range(); } Vector3 star_pos; star->Get_Position( &star_pos ); SmartGameObj * best_obj = NULL; #if WEAPON_HELP_CHEAT float best_distance = 10; #else float best_distance = 0.1f; #endif // Find a target object near the camera target vector SLNode * smart_objnode; for (smart_objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head(); smart_objnode; smart_objnode = smart_objnode->Next()) { SmartGameObj * obj = smart_objnode->Data(); if ( obj == COMBAT_STAR ) { continue; } #if WEAPON_HELP_CHEAT if ( !obj->Is_Enemy( COMBAT_STAR ) ) { continue; } #endif Vector3 pos = obj->Get_Bullseye_Position(); float range = (pos - star_pos).Length(); if ( range > weapon_range ) { continue; } Vector3 twoDpos; if ( Project( twoDpos, pos ) == CameraClass::INSIDE_FRUSTUM ) { twoDpos.X -= CameraTarget2DOffset.X; twoDpos.Y -= CameraTarget2DOffset.Y; twoDpos.Z = 0; float distance = twoDpos.Length(); if ( distance < best_distance ) { // This is the best target so far, cast a Ray and see if it is a possible target // We are seeing if the camera can see him, not if the gun can hit him... Vector3 cast_start = Get_Transform().Get_Translation(); Vector3 cast_end = pos; LineSegClass ray; ray.Set( cast_start, cast_end ); #if 0 // trim off Near Clip from Beginning float start_fraction = NearClipPlane / ray.Get_Length(); ray.Compute_Point( start_fraction, &cast_start ); ray.Set( cast_start, cast_end ); #endif CastResultStruct result; PhysRayCollisionTestClass raytest(ray, &result, BULLET_COLLISION_GROUP, COLLISION_TYPE_PROJECTILE); WWASSERT(COMBAT_SCENE != NULL); Ignore_Star_And_Vehicle(); { WWPROFILE( "Cast Ray" ); COMBAT_SCENE->Cast_Ray( raytest ); } Unignore_Star_And_Vehicle(); //Debug_Say(( "Targeting %f %f %f\n", StarTargetingPosition.X, StarTargetingPosition.Y, StarTargetingPosition.Z )); if ( raytest.CollidedPhysObj != NULL && raytest.CollidedPhysObj->Get_Observer() != NULL && ((CombatPhysObserverClass *)raytest.CollidedPhysObj->Get_Observer())->As_PhysicalGameObj() ) { best_distance = distance; best_obj = obj; } else { // If this is the Info Target, forget him! if ( obj == HUDInfo::Get_Info_Object() ) { HUDInfo::Set_Info_Object( NULL ); } } } } } if ( best_obj != NULL ) { // remember this guy WeaponHelpTargetID = best_obj->Get_ID(); StarTargetingPosition = best_obj->Get_Bullseye_Position(); HUDInfo::Set_Info_Object( best_obj ); // Update HUD Info Object } } } else { // If we had a target if ( WeaponHelpTargetID != 0 ) { SmartGameObj * target = GameObjManager::Find_SmartGameObj( WeaponHelpTargetID ); // and we still have him if ( target != NULL ) { // update the targting pos StarTargetingPosition = target->Get_Bullseye_Position(); } else { // else, forget about him WeaponHelpTargetID = 0; } } } } void CCameraClass::Use_Profile( const char * name ) { if ( name ) { // quick reject if the same if ( CurrentProfile && !stricmp( CurrentProfileName, name ) ) { return; } CCameraProfileClass *profile = CCameraProfileClass::Find( name ); if ( profile ) { CurrentProfile = profile; CurrentProfileName=name; } } else { CurrentProfile = NULL; } } void CCameraClass::Use_Default_Profile() { WWASSERT( DefaultProfile ); CurrentProfile = DefaultProfile; CurrentProfileName=DefaultProfileName; } void CCameraClass::Set_Profile_Height( float height ) { CurrentProfile->Set_Height( height ); } void CCameraClass::Set_Profile_Distance( float distance ) { CurrentProfile->Set_Distance( distance ); } float CCameraClass::Get_Profile_Zoom( void ) { return CurrentProfile->Get_Zoom(); } /* ** */ void CCameraClass::Handle_Input( void ) { #define TILT_ADJUST (float)DEG_TO_RAD(30.0f) #define MAX_TILT (float)DEG_TO_RAD(80.0f) #define MIN_TILT (float)DEG_TO_RAD(-30.0f) #define DISTANCE_ADJUST 5.0f #define MIN_DISTANCE -5.0f #define MAX_DISTANCE 200.0f #define FOV_ADJUST 1.0f #define HEIGHT_ADJUST 0.5f #define MIN_HEIGHT -3.0f #define MAX_HEIGHT 4.0f #define FP_TWEAK_RATE 0.1f; float dt = TimeManager::Get_Frame_Real_Seconds(); // Hold F9 to control the camera assert( CurrentProfile != NULL ); #ifdef WWDEBUG if ( CombatManager::Is_First_Person() ) { Vector3 tweak(0,0,0); tweak.X = ( Input::Get_State( INPUT_FUNCTION_CAMERA_TRANSTILT_INC ) ? 1 : 0 ) + ( Input::Get_State( INPUT_FUNCTION_CAMERA_TRANSTILT_DEC ) ? -1 : 0 ); tweak.Y = ( Input::Get_State( INPUT_FUNCTION_DEBUG_FAR_CLIP_IN ) ? -1 : 0 ) + ( Input::Get_State( INPUT_FUNCTION_DEBUG_FAR_CLIP_OUT ) ? 1 : 0 ); tweak.Z = ( Input::Get_State( INPUT_FUNCTION_CAMERA_DIST_INC ) ? -1 : 0 ) + ( Input::Get_State( INPUT_FUNCTION_CAMERA_DIST_DEC ) ? 1 : 0 ); tweak *= dt * FP_TWEAK_RATE; if ( tweak.Length() ) { FirstPersonOffsetTweak += tweak; Vector3 offset( 0,0,0 ); if ( COMBAT_STAR != NULL && COMBAT_STAR->Get_Weapon() != NULL ) { offset = COMBAT_STAR->Get_Weapon()->Get_First_Person_Model_Offset(); } offset += FirstPersonOffsetTweak; Debug_Say(( "First Person Offset ( %1.3f, %1.3f, %1.3f )\n", offset.X, offset.Y, offset.Z )); // Debug_Say(( "First Person Tweak ( %1.3f, %1.3f, %1.3f )\n", FirstPersonOffsetTweak.X, FirstPersonOffsetTweak.Y, FirstPersonOffsetTweak.Z )); } } else { float tilt_amount = ( Input::Get_State( INPUT_FUNCTION_CAMERA_TRANSTILT_INC ) ? 1 : 0 ) + ( Input::Get_State( INPUT_FUNCTION_CAMERA_TRANSTILT_DEC ) ? -1 : 0 ); CurrentProfile->TranslationTilt += TILT_ADJUST * tilt_amount * dt; CurrentProfile->TranslationTilt = WWMath::Clamp( CurrentProfile->TranslationTilt, MIN_TILT, MAX_TILT ); float view_tilt_amount = ( Input::Get_State( INPUT_FUNCTION_CAMERA_VIEWTILT_INC ) ? 1 : 0 ) + ( Input::Get_State( INPUT_FUNCTION_CAMERA_VIEWTILT_DEC ) ? -1 : 0 ); CurrentProfile->ViewTilt += TILT_ADJUST * view_tilt_amount * dt; CurrentProfile->ViewTilt = WWMath::Clamp( CurrentProfile->ViewTilt, MIN_TILT, MAX_TILT ); float distance_amount = ( Input::Get_State( INPUT_FUNCTION_CAMERA_DIST_INC ) ? 1 : 0 ) + ( Input::Get_State( INPUT_FUNCTION_CAMERA_DIST_DEC ) ? -1 : 0 ); CurrentProfile->Distance += DISTANCE_ADJUST * distance_amount * dt; CurrentProfile->Distance = WWMath::Clamp( CurrentProfile->Distance, MIN_DISTANCE, MAX_DISTANCE ); float fov_amount = ( Input::Get_State( INPUT_FUNCTION_CAMERA_FOV_INC ) ? 1 : 0 ) + ( Input::Get_State( INPUT_FUNCTION_CAMERA_FOV_DEC ) ? -1 : 0 ); CurrentProfile->FOV += FOV_ADJUST * fov_amount * dt; CurrentProfile->FOV = WWMath::Clamp( CurrentProfile->FOV, MIN_FOV, MAX_FOV ); float height_amount = ( Input::Get_State( INPUT_FUNCTION_CAMERA_HEIGHT_INC ) ? 1 : 0 ) + ( Input::Get_State( INPUT_FUNCTION_CAMERA_HEIGHT_DEC ) ? -1 : 0 ); CurrentProfile->Height += HEIGHT_ADJUST * height_amount * dt; CurrentProfile->Height = WWMath::Clamp( CurrentProfile->Height, MIN_HEIGHT, MAX_HEIGHT ); if ( ( tilt_amount != 0 ) || (view_tilt_amount != 0) || ( distance_amount != 0 ) || ( fov_amount != 0 ) || ( height_amount != 0 ) ) { Debug_Say(( " Dist: %3.1f Height: %3.1f FOV %3.1f TransTilt: %3.1f ViewTilt: %3.1f\n", CurrentProfile->Distance, CurrentProfile->Height, RAD_TO_DEG( CurrentProfile->FOV ), RAD_TO_DEG( CurrentProfile->TranslationTilt ), RAD_TO_DEG( CurrentProfile->ViewTilt ) )); } if ( Input::Get_State( INPUT_FUNCTION_DEBUG_FAR_CLIP_IN ) || Input::Get_State( INPUT_FUNCTION_DEBUG_FAR_CLIP_OUT ) ) { float fog_start, fog_stop; COMBAT_SCENE->Get_Fog_Range( &fog_start, &fog_stop ); if ( Input::Get_State( INPUT_FUNCTION_DEBUG_FAR_CLIP_OUT ) ) { FarClipPlane *= 1.1f; fog_start *= 1.1f; } else { FarClipPlane /= 1.1f; fog_start /= 1.1f; } COMBAT_SCENE->Set_Fog_Range( fog_start, FarClipPlane ); Debug_Say(( "FarClipPlane %f\n", FarClipPlane )); Set_Clip_Planes( NearClipPlane, FarClipPlane ); } } #endif if ( CinematicSnipingEnabled ) { float zoom_change = CinematicSnipingDesiredZoom - SniperZoom; float max = TimeManager::Get_Frame_Seconds(); zoom_change = WWMath::Clamp( zoom_change, -max, max ); SniperZoom += zoom_change; CurrentProfile->Set_Zoom( SniperZoom ); } #ifdef WWDEBUG if ( Input::Get_State( INPUT_FUNCTION_TOGGLE_SNAP_SHOT_MODE ) ) { Set_Snap_Shot_Mode( Is_Snap_Shot_Mode() == SNAPSHOT_OFF ); Debug_Say(( "Snap Shot Mode %s\n", Is_Snap_Shot_Mode() ? "ON" : "OFF" )); } #endif if ( COMBAT_STAR != NULL && COMBAT_STAR->Is_Control_Enabled() == false ) { // Don't move camera if control is disabled return; } // if Snipinging Changed, update tilt to match old target if ( WasStarSniping != IsStarSniping ) { WasStarSniping = IsStarSniping; SmartGameObj * star = COMBAT_STAR; if ( star ) { Vector3 target_pos = star->Get_Targeting_Pos(); Vector3 camera_pos = AnchorPosition; camera_pos.Z += CurrentProfile->Height; camera_pos -= target_pos; float drop = camera_pos.Z; float dist = camera_pos.Length(); Tilt = WWMath::Asin( drop/dist ); Tilt -= CurrentProfile->ViewTilt; } } // Sniper Control float sniping_scale = 1; if ( !Is_Using_Host_Model() && Is_Star_Sniping() ) { float amount = Input::Get_Amount( INPUT_FUNCTION_ZOOM_IN ) - Input::Get_Amount( INPUT_FUNCTION_ZOOM_OUT ); SniperZoom += amount * TimeManager::Get_Frame_Seconds(); SniperZoom = WWMath::Clamp( SniperZoom, 0, 1 ); // Scale weapon tilt and turn from Zoom float sniper_scale = 0.3f; float scale = 1 - SniperZoom * (1 - sniper_scale); // Debug_Say(( "scale %f\n", scale )); sniping_scale = scale * scale; // Set Zoom CurrentProfile->Set_Zoom( SniperZoom ); // Update Zooming Sound..... } // Adjust Tilt float tilt_amount = Input::Get_Amount( INPUT_FUNCTION_WEAPON_UP ) - Input::Get_Amount( INPUT_FUNCTION_WEAPON_DOWN ); // Adjust Facing float turn_amount = Input::Get_Amount( INPUT_FUNCTION_WEAPON_LEFT ) - Input::Get_Amount( INPUT_FUNCTION_WEAPON_RIGHT ); if ( Input::Get_State( INPUT_FUNCTION_TURN_AROUND ) ) { // turn_amount += DEG_TO_RADF( 180.0F ) / dt; Heading = Heading + DEG_TO_RADF( 180.0F ); if ( Heading > DEG_TO_RADF( 360.0F ) ) { Heading -= DEG_TO_RADF( 360.0F ); } if ( COMBAT_STAR ) { Vector3 pos; COMBAT_STAR->Get_Position( &pos ); DIAG_LOG(( "SPUS", "%1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z )); } } if ( Enable2DTargeting ) { // Adjust Targeting Offset if ( !( Input::Get_Mouse_2D_Invert() ^ Input::Get_Mouse_Invert() ) ) { tilt_amount = -tilt_amount; } CameraTarget2DOffset.X += -turn_amount * dt; CameraTarget2DOffset.Y += -tilt_amount * dt; #define _2D_TARGETING_LIMIT 0.85f CameraTarget2DOffset.X = WWMath::Clamp( CameraTarget2DOffset.X, -_2D_TARGETING_LIMIT, _2D_TARGETING_LIMIT ); CameraTarget2DOffset.Y = WWMath::Clamp( CameraTarget2DOffset.Y, -_2D_TARGETING_LIMIT, _2D_TARGETING_LIMIT ); } else { Tilt += -tilt_amount * dt * sniping_scale; Heading += turn_amount * dt * sniping_scale; CameraTarget2DOffset.X = 0.0f; CameraTarget2DOffset.Y = 0.0f; } // Clamp the tilt to this arbitrary value Tilt = WWMath::Clamp( Tilt, DEG_TO_RADF( -80 ), DEG_TO_RADF( 80 ) ); // if soldier tries to turn, turn the camera float soldier_turn = 0; if ( COMBAT_STAR != NULL && COMBAT_STAR->Get_Vehicle() == NULL ) { // only do this if the soldier is not in a vehicle soldier_turn = Input::Get_Amount( INPUT_FUNCTION_TURN_LEFT ) - Input::Get_Amount( INPUT_FUNCTION_TURN_RIGHT ); } Heading += soldier_turn * dt * sniping_scale; } void CCameraClass::Force_Look( const Vector3 & target ) { Vector3 diff = Get_Transform().Get_Translation(); diff -= target; Heading = WWMath::Atan2( -diff.Y, -diff.X ); float drop = diff.Z; float dist = diff.Length(); Tilt = WWMath::Asin( drop/dist ); Tilt -= CurrentProfile->ViewTilt; } void CCameraClass::Set_Lerp_Time( float time ) { LerpTimeTotal = time; LerpTimeRemaining = time; } Vector3 CCameraClass::Get_First_Person_Offset_Tweak( void ) { return FirstPersonOffsetTweak; } void CCameraClass::Reset_First_Person_Offset_Tweak( void ) { FirstPersonOffsetTweak = Vector3(0,0,0); } bool CCameraClass::Draw_Sniper( void ) { if ( Is_Using_Host_Model() ) { return CinematicSnipingEnabled; } else { return IsStarSniping; } } void CCameraClass::Cinematic_Sniper_Control( bool enabled, float zoom ) { Debug_Say(( "Camera Sniper Control %d, %f \n", enabled, zoom )); CinematicSnipingEnabled = enabled; CinematicSnipingDesiredZoom = zoom; } void CCameraClass::Set_Is_Star_Sniping( bool onoff ) { if (IsStarSniping != onoff) { IsStarSniping = onoff; // // Add or remove the sniper listener from the world // if ( SniperListener != NULL && WWAudioClass::Get_Instance() != NULL ) { if ( WWAudioClass::Get_Instance()->Get_Sound_Scene()->Peek_2nd_Listener() != NULL ) { WWAudioClass::Get_Instance()->Get_Sound_Scene()->Set_2nd_Listener( NULL ); } else { WWAudioClass::Get_Instance()->Get_Sound_Scene()->Set_2nd_Listener( SniperListener ); // // Update the listener's position in the world // Update_Sniper_Listener_Pos(); } } if ( COMBAT_STAR ) { Vector3 pos; COMBAT_STAR->Get_Position( &pos ); int ammo = 0; if ( COMBAT_STAR->Get_Weapon() ) { ammo = COMBAT_STAR->Get_Weapon()->Get_Total_Rounds(); } if ( IsStarSniping ) { DIAG_LOG(( "SNEN", "%1.2f; %1.2f; %1.2f; %d ", pos.X, pos.Y, pos.Z, ammo )); } else { DIAG_LOG(( "SNEX", "%1.2f; %1.2f; %1.2f; %d ", pos.X, pos.Y, pos.Z, ammo )); } } } return ; } void CCameraClass::Update_Sniper_Listener_Pos( void ) { if ( SniperListener != NULL ) { // // Get the camera's transform // Matrix3D tm = Get_Transform(); // // Calculate what point in world space the sniper is zoomed into // float dist = ::tan (1.45F + ((1.5672F - 1.45F) * SniperZoom)); dist = min (dist, SniperDistance); Vector3 pos = tm.Get_Translation() - (tm.Get_Z_Vector() * dist); // // Update the sniper's position // SniperListener->Set_Transform( Matrix3D( pos ) ); } return ; } void CCameraClass::Set_Sniper_Distance( float dist ) { // // If the distance has changed, update the sniper's listener // if ( SniperDistance != dist ) { SniperDistance = dist; Update_Sniper_Listener_Pos(); } return ; } /* ** */ void CCameraClass::Handle_Snap_Shot_Mode( void ) { float dt = TimeManager::Get_Frame_Real_Seconds(); static float tilt = 0; static float turn = 0; float tilt_amount = Input::Get_Amount( INPUT_FUNCTION_WEAPON_UP ) - Input::Get_Amount( INPUT_FUNCTION_WEAPON_DOWN ); float turn_amount = Input::Get_Amount( INPUT_FUNCTION_WEAPON_LEFT ) - Input::Get_Amount( INPUT_FUNCTION_WEAPON_RIGHT ); if (turn_amount == 0) { turn_amount = Input::Get_Amount( INPUT_FUNCTION_TURN_LEFT ) - Input::Get_Amount( INPUT_FUNCTION_TURN_RIGHT ); } tilt += tilt_amount * dt; turn += turn_amount * dt; Matrix3D tm = Get_Transform(); Vector3 pos = tm.Get_Translation(); tm.Make_Identity(); tm.Set_Translation( pos ); tm.Rotate_Z( turn ); tm.Rotate_X( tilt ); float forward = Input::Get_Amount( INPUT_FUNCTION_MOVE_FORWARD ) - Input::Get_Amount( INPUT_FUNCTION_MOVE_BACKWARD ); float left = Input::Get_Amount( INPUT_FUNCTION_MOVE_LEFT ) - Input::Get_Amount( INPUT_FUNCTION_MOVE_RIGHT ); float up = Input::Get_Amount( INPUT_FUNCTION_MOVE_UP ) - Input::Get_Amount( INPUT_FUNCTION_MOVE_DOWN ); SnapShotMode = Input::Get_State( INPUT_FUNCTION_SNAP_SHOT_ADVANCE ) ? SNAPSHOT_PROGRESS : SNAPSHOT_ON; dt *= 4; tm.Translate( Vector3( -left * dt, up * dt, -forward * dt ) ); Set_Transform( tm ); }