1716 lines
53 KiB
C++
1716 lines
53 KiB
C++
/*
|
|
** 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 : 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 <string.h>
|
|
#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<CCameraProfileClass> ProfileList;
|
|
HashTemplateClass<StringClass, CCameraProfileClass*> 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<StringClass,CCameraProfileClass*> 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<CCameraProfileClass> *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<SmartGameObj> * 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<SmartGameObj> * 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 );
|
|
}
|
|
|