The-Simpsons-Hit-and-Run/game/code/camera/pccam.cpp

505 lines
16 KiB
C++

//=============================================================================
// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
//
// File: pccam.cpp
//
// Description: Implement PCCam
//
// History: 7/25/2003 + Created -- Cary Brisebois
//
//=============================================================================
//========================================
// System Includes
//========================================
// Foundation Tech
#include <raddebug.hpp>
#include <radmath/radmath.hpp>
//========================================
// Project Includes
//========================================
#include <camera/pccam.h>
#include <camera/isupercamtarget.h>
#include <camera/supercammanager.h>
#include <camera/supercamcentral.h>
#include <camera/supercamconstants.h>
#include <camera/supercamcontroller.h>
#include <worldsim/character/character.h>
#include <worldsim/character/charactercontroller.h>
#include <worldsim/character/charactermappable.h>
#include <worldsim/avatarmanager.h>
#include <worldsim/avatar.h>
#include <ai/actor/intersectionlist.h>
#include <choreo/utility.hpp>
#include <input/inputmanager.h>
#include <input/usercontrollerwin32.h>
#include <input/mouse.h>
#include <input/realcontroller.h>
#include <roads/geometry.h>
//*****************************************************************************
//
// Global Data, Local Data, Local Classes
//
//*****************************************************************************
const float DEFAULT_LENGTH = 5.0f;
const float gMaxRodLength = DEFAULT_LENGTH;
const float gMinRodLength = 1.0f;
const rmt::Vector gOffset( 0.0f, 2.0f, 0.0f );
static float gDefaultElevation = 1.57079f;
const float gTopElevation = 3.0;
const float gBottomElevation = 1.57079f;
const float gElevationLag = 0.05f;
const float gElevationIncrement = 0.1f;
const float gMagAdjust = 0.0f;
const float gMagIncrement = 0.1f;
const float gMaxLookUp = 2.0f;
static float gDefaultPitch = 0.0f;
static float gPitchIncrement = 0.02f;
static float gPitchLag = 0.05f;
//*****************************************************************************
//
// Public Member Functions
//
//*****************************************************************************
//=============================================================================
// PCCam::PCCam
//=============================================================================
// Description: Constructor.
//
// Parameters: None.
//
// Return: N/A.
//
//=============================================================================
PCCam::PCCam() :
mTarget( NULL ),
m_fAngularSpeed(0.0f),
m_fYawVelocity(0.0f),
m_fPitchVelocity(0.0f),
mFOVDelta( 0.0f ),
mLastPCCamFacing( 0 ),
m_fInterpolationSpeed( 0.1f )
{
mGroundOffset.Set( 0.0f, 0.0f, 0.0f );
}
//=============================================================================
// PCCam::~PCCam
//=============================================================================
// Description: Destructor.
//
// Parameters: None.
//
// Return: N/A.
//
//=============================================================================
PCCam::~PCCam()
{
}
//=============================================================================
// PCCam::Update
//=============================================================================
// Description: Comment
//
// Parameters: ( unsigned int milliseconds )
//
// Return: void
//
//=============================================================================
void PCCam::Update( unsigned int milliseconds )
{
float timeMod = milliseconds / 16.0f;
rAssert( mTarget );
if ( GetFlag( (Flag)CUT ) )
{
mFOVDelta = 0.0f;
}
// Update the angular speed modifier.
static float decelerationSpeed = 0.7f;
m_fAngularSpeed = 1.0f * timeMod;
m_fYawVelocity *= decelerationSpeed;
m_fPitchVelocity *= decelerationSpeed;
rmt::Vector targetPos;
mTarget->GetPosition( &targetPos );
rmt::Vector rod( 0.0f, 0.0f, 0.0f );
float elevation = gDefaultElevation;
//Calculate the length and the elevation based on the mouse values.
if ( mController )
{
//---- Do the Elevation calculation
float mouseUp = mController->GetValue( SuperCamController::mouseLookUp );
float mouseDown = mController->GetAxisValue( SuperCamController::mouseLookDown );
float yAxis = ( mouseUp > mouseDown ) ? -mouseUp : mouseDown;
m_fYawVelocity += yAxis;
//rDebugPrintf( " yAxis = %g xAxis = %g \n", yAxis, xAxis );
static float Y_SENSE_MOD = 0.1f;
float mouseSense = static_cast<Mouse*>(GetInputManager()->GetController( 0 )->GetRealController( MOUSE ))->getSensitivityY();
gDefaultElevation += gElevationIncrement * m_fYawVelocity * m_fAngularSpeed * mouseSense * Y_SENSE_MOD;
//rDebugPrintf( "Angular Velocity = %g \n", m_fAngularSpeed );
if ( gDefaultElevation > gTopElevation )
{
gDefaultElevation = gTopElevation;
elevation = gTopElevation;
}
if ( gDefaultElevation < gBottomElevation )
{
elevation = gBottomElevation;
}
}
//---- Do the Pitch calculation
// If target is walking towards camera, let them...
Character* player = GetAvatarManager()->GetAvatarForPlayer(0)->GetCharacter();
float pitch = 0.0f;
if ( player->mPCCamFacing == 2 )
{
if ( mLastPCCamFacing != 2 )
{
//Reset the pitch to be the direction we're already facing.
rmt::Vector camHeading;
GetHeading( &camHeading );
camHeading.y = 0.0f;
camHeading *= -1.0f;
gDefaultPitch = choreo::GetWorldAngle( camHeading.x, camHeading.z );
}
float mouseSenseX = static_cast<Mouse*>(GetInputManager()->GetController( 0 )->GetRealController( MOUSE ))->getSensitivityX();
gDefaultPitch += gPitchIncrement * m_fPitchVelocity * m_fAngularSpeed * mouseSenseX;
pitch = gDefaultPitch;
}
rmt::SphericalToCartesian( -gMaxRodLength, pitch, elevation, &rod.z, &rod.x, &rod.y );
///////////////
// Do camera rotation as necessary
if( player->mPCCamFacing == 0 || player->mPCCamFacing == 1 )
{
static float ROT_DIR = 4.0f;
static float X_SENSE_MOD = 0.01f;
float mouseSense = static_cast<Mouse*>(GetInputManager()->GetController( 0 )->GetRealController( MOUSE ))->getSensitivityX();
float fScaleFactor = 1.0f;
if( player->IsTurbo() )
{
rmt::Vector playerVel;
player->GetVelocity( playerVel );
fScaleFactor *= playerVel.Magnitude();
}
rmt::Vector camHeading;
GetHeading( &camHeading );
float currAngle = choreo::GetWorldAngle( camHeading.x, camHeading.z );
currAngle += ROT_DIR * mouseSense * X_SENSE_MOD * GetAngularSpeed() * GetPitchVelocity();
player->SetDesiredDir( currAngle );
rmt::Vector newCamHeading;
newCamHeading = choreo::DEFAULT_FACING_VECTOR;
choreo::RotateYVector( currAngle, newCamHeading );
rmt::Matrix mat;
mat.Identity();
mat.FillHeading( newCamHeading, rmt::Vector( 0.0f, 1.0f, 0.0f ) );
rod.Transform( mat );
}
/*
rmt::Vector targetHeading;
mTarget->GetHeading( &targetHeading );
player->GetParentTransform().RotateVector( targetHeading, &targetHeading );
rmt::Vector camHeading;
GetHeading( &camHeading );
// Compensate targetHeading so PC_CAM doesn't rotate behind the character
// when they're walking towards the camera or walking to the left or
// walking to the right.
switch( player->mPCCamFacing )
{
case 1: // character facing backwards
targetHeading *= -1.0f;
case 0: // character facing in cam's direction
{
//Orient according to the target.
rmt::Matrix mat;
mat.Identity();
mat.FillHeading( targetHeading, rmt::Vector( 0.0f, 1.0f, 0.0f ) );
rod.Transform( mat );
}
break;
case 2: // character somewhere else
break;
}
*/
rod.Add( gOffset );
rod.Add( targetPos );
rmt::Vector lookTo = targetPos;
lookTo.Add( gOffset );
if ( gDefaultElevation < gBottomElevation )
{
if ( gDefaultElevation < 0.0f )
{
gDefaultElevation = 0.0f;
}
float lookUpMod = ( rmt::PI_BY2 - gDefaultElevation ) / rmt::PI_BY2;
rmt::Vector lookUpOffset;
lookUpOffset.Set( 0.0f, gMaxLookUp * lookUpMod, 0.0f );
lookTo.Add( lookUpOffset );
}
//--------- Goofin' with the FOV
float zoom = mController->GetValue( SuperCamController::zToggle );
float FOV = GetFOV();
if ( GetFlag((Flag)CUT ) )
{
FilterFov( zoom, SUPERCAM_DEFAULT_MIN_FOV, SUPERCAM_DEFAULT_MAX_FOV, FOV, mFOVDelta, 1.0f, timeMod );
}
else
{
FilterFov( zoom, SUPERCAM_DEFAULT_MIN_FOV, SUPERCAM_DEFAULT_MAX_FOV, FOV, mFOVDelta, SUPERCAM_DEFAULT_FOV_LAG, timeMod );
}
SetFOV( FOV );
SetCameraValues( milliseconds, rod, lookTo );
mLastPCCamFacing = player->mPCCamFacing;
}
//=============================================================================
// PCCam::UpdateForPhysics
//=============================================================================
// Description: Comment
//
// Parameters: ( unsigned int milliseconds )
//
// Return: void
//
//=============================================================================
void PCCam::UpdateForPhysics( unsigned int milliseconds )
{
/* float nearPlane = GetNearPlane();
float radius = GetCollisionRadius()*2.0f;
rmt::Matrix camMat;
camMat.IdentityTranslation();
rmt::Vector heading;
GetHeading( &heading );
rmt::Vector vup;
GetCameraUp( &vup );
camMat.FillHeading( heading, vup ); //I do this because the tCamera may not be the same as I think it is.
//Test the sides and rotate if there's a problem.
rmt::Vector camPos;
GetPosition( &camPos );
rmt::Vector camTargPos;
mTarget->GetPosition( &camTargPos );
rmt::Vector lookFrom( radius, 0.0f, nearPlane );
lookFrom.Transform( camMat );
lookFrom.Add( camTargPos );
lookFrom.Add( gOffset );
//rmt::Vector lookTo;
rmt::Vector lookTo( 0.5f, 0.0f, -0.5f );
lookTo.Transform( camMat );
lookTo.Add( camPos );
//GetPosition( &lookTo );*/
rmt::Vector lookFrom;
mTarget->GetPosition( &lookFrom );
lookFrom.Add( gOffset );
rmt::Vector lookTo;
GetPosition( &lookTo );
static float NEARCLIP_OFFSET = GetNearPlane()*0.8f;
// Offset the lookFrom to compensate for the near clip plane. (Thanks for the math help Ian)
// lookFrom = (Normalize(LT-LF) * NEARCLIP_OFFSET) + LF
//rmt::Vector offsetVector = (((lookTo-lookFrom)/(lookTo-lookFrom).Magnitude()) * NEARCLIP_OFFSET)+lookFrom;
//rmt::Vector offsetVector = (((lookFrom-lookTo)/(lookFrom-lookTo).Magnitude()) * NEARCLIP_OFFSET)+lookTo;
rmt::Vector newPos;
newPos = lookTo;
newPos.Add( mGroundOffset );
/*
if ( mNumCollisions )
{
//Deal with collisions too;
unsigned int i;
unsigned int collCount = 0;
rmt::Vector collisionOffset( 0.0f, 0.0f, 0.0f );
for ( i = 0; i < mNumCollisions; ++i )
{
if ( mCollisionOffset[ i ].y >= 0.0f )
{
collisionOffset.Add( mCollisionOffset[ i ] );
++collCount;
}
}
if ( collCount )
{
collisionOffset.x /= collCount;
collisionOffset.y /= collCount;
collisionOffset.z /= collCount;
newPos.Add( collisionOffset );
}
}
*/
IntersectionList& iList = GetSuperCamManager()->GetSCC( GetPlayerID() )->GetIntersectionList();
static float ZOOMIN_FACTOR = 0.5f;
static float ZOOMOUT_FACTOR = 0.9f;
rmt::Vector intersection( 0.0f, 0.0f, 0.0f );
if ( !iList.LineOfSight( lookFrom, lookTo, 0.01f, true ) )
{
if ( iList.TestIntersection( lookFrom, lookTo, &intersection, 1.0f ) )
{
// Offset the intersection to compensate for the near clip plane.
// F = D + [( D-S )/ (|| D-S ||)] * NEARCLIP_OFFSET
rmt::Vector offSettedDest = intersection + ((intersection-newPos)/((intersection-newPos).Magnitude())*(NEARCLIP_OFFSET));
//offSettedDest = lookFrom-((lookFrom-offSettedDest)/((lookFrom-offSettedDest).Magnitude())*(NEARCLIP_OFFSET*2.0f));
if( (lookFrom-offSettedDest).Magnitude() < 5.0f )
{
offSettedDest.Add( gOffset );
}
newPos = offSettedDest;
//newPos = intersection;
// Now interpolate between the current position and the intersection,
// to achieve a smoother translation towards the character.
//newPos.Interpolate( offSettedDest, m_fInterpolationSpeed );
//interpolation not used at the moment.
m_fInterpolationSpeed /= ZOOMIN_FACTOR;
if( m_fInterpolationSpeed >= 1.0f ) m_fInterpolationSpeed = 1.0f;
}
}
else
{
if( iList.TestIntersectionAnimPhys( lookFrom, lookTo, &intersection ) )
{
// Offset the intersection to compensate for the near clip plane.
// F = D + [( D-S )/ (|| D-S ||)] * NEARCLIP_OFFSET
rmt::Vector offSettedDest = intersection + ((intersection-newPos)/((intersection-newPos).Magnitude())*(NEARCLIP_OFFSET));
newPos = intersection;
// Now interpolate between the current position and the intersection,
// to achieve a smoother translation towards the character.
//newPos.Interpolate( offSettedDest, m_fInterpolationSpeed );
m_fInterpolationSpeed /= ZOOMIN_FACTOR;
if( m_fInterpolationSpeed >= 1.0f ) m_fInterpolationSpeed = 1.0f;
}
else
{
m_fInterpolationSpeed *= ZOOMOUT_FACTOR;
if( m_fInterpolationSpeed <= 0.1f ) m_fInterpolationSpeed = 0.1f;
}
}
// rmt::Vector newToTarg;
// newToTarg.Sub( lookFrom, newPos );
// float mag = newToTarg.Magnitude();
// mag -= 0.5f; //This is for the size of the character.
// SetNearPlane( rmt::Clamp( mag, 0.1f, SUPERCAM_NEAR ) );
SetNearPlane( 0.1f );
rmt::Vector camTarg;
GetTarget( &camTarg );
SetCameraValues( 0, newPos, camTarg );
//rDebugPrintf( "mNumCollisions = %d \n", mNumCollisions );
// rDebugPrintf( "m_fInterpolationSpeed = %g \n", m_fInterpolationSpeed );
}
//=============================================================================
// PCCam::SetCollisionOffset
//=============================================================================
// Description: Comment
//
// Parameters: ( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset )
//
// Return: void
//
//=============================================================================
void PCCam::SetCollisionOffset( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset )
{
mCollisionOffset = offset;
mNumCollisions = numCollisions;
mGroundOffset = groundOffset;
}
//=============================================================================
// PCCam::GetIntersectionRadius
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: float
//
//=============================================================================
float PCCam::GetIntersectionRadius() const
{
return gMaxRodLength;
}
//*****************************************************************************
//
// Private Member Functions
//
//*****************************************************************************