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

1520 lines
46 KiB
C++

//=============================================================================
// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
//
// File: walkercam.cpp
//
// Description: Implement WalkerCam
//
// History: 25/04/2002 + Created -- Cary Brisebois
//
//=============================================================================
//========================================
// System Includes
//========================================
// Foundation Tech
//These are included in the precompiled headers on XBOX and GC
#include <raddebug.hpp>
#include <raddebugwatch.hpp>
#include <p3d/pointcamera.hpp>
#include <p3d/utility.hpp>
//========================================
// Project Includes
//========================================
#include <camera/WalkerCam.h>
#include <camera/WalkerCamData.h>
#include <camera/WalkerCamDataChunk.h>
#include <camera/isupercamtarget.h>
#include <camera/supercamcentral.h>
#include <camera/supercamcontroller.h>
#include <camera/supercammanager.h>
#include <memory/srrmemory.h>
#include <events/eventmanager.h>
#include <events/eventenum.h>
#include <main/game.h>
#include <worldsim/character/character.h>
#include <worldsim/character/charactertarget.h>
#include <worldsim/character/charactermanager.h>
#include <worldsim/ped/pedestrianmanager.h>
#include <mission/gameplaymanager.h>
#include <mission/charactersheet/charactersheetmanager.h>
#include <mission/animatedicon.h>
#include <ai/actor/intersectionlist.h>
#include <choreo/utility.hpp>
//******************************************************************************
//
// Global Data, Local Data, Local Classes
//
//******************************************************************************
#ifdef DEBUGWATCH
float SLIDE_INTERVAL = 0.06f;
float AVERAGE_LAG = 1.0f;
float PED_MAX_DIST = 5.0f;
float PED_MIN_DIST = 0.0f;
float PED_ZOOM_OFFSET = 0.49f;
float PED_FOV_TEST = 0.567424f; //~33 deg
unsigned int MIN_PED_ACCUM = 1500;
static float CREEPY_TWIST_WALKER = 0.176f;
rmt::Vector gLookFromR;
rmt::Vector gLookFromL;
rmt::Vector gLookToR;
rmt::Vector gLookToL;
rmt::Vector gLookFromU;
rmt::Vector gLookFromD;
rmt::Vector gLookToU;
rmt::Vector gLookToD;
#else
const float SLIDE_INTERVAL = 0.06f;
const float AVERAGE_LAG = 1.0f;
const float PED_MAX_DIST = 5.0f;
const float PED_MIN_DIST = 0.0f;
const float PED_ZOOM_OFFSET = 0.49f;
const float PED_FOV_TEST = 0.567424f; //~33 deg
const unsigned int MIN_PED_ACCUM = 1500;
static const float CREEPY_TWIST_WALKER = 0.176f;
#endif
//******************************************************************************
//
// Public Member Functions
//
//******************************************************************************
//==============================================================================
// WalkerCam::WalkerCam
//==============================================================================
// Description: Constructor.
//
// Parameters: None.
//
// Return: N/A.
//
//==============================================================================
WalkerCam::WalkerCam() :
mFOVDelta( 0.0f ),
mMagnitude( 0.0f ),
mMagnitudeDelta( 0.0f ),
mElevation( 0.0f ),
mElevationDelta( 0.0f ),
mIsAirborn( false ),
mLandingCountdown( 0 ),
mCameraHeight( 0.0f ),
mNumCollisions( 0 ),
mLastCollisionFrame( 0 ),
mLastCharacter( 0 ),
mPedTimeAccum( 0 ),
mXAxis( 0.0f ),
mOldMagnitude( 0.0f ),
mCollectible( NULL )
{
mTarget = NULL;
mTargetPos.Set(0.0f, 0.0f, 0.0f);
mTargetPosDelta.Set(0.0f, 0.0f, 0.0f);
mPosition.Set( 0.0f, 0.0f, 0.0f );
mDesiredPositionDelta.Set( 0.0f, 0.0f, 0.0f );
mGroundOffset.Set( 0.0f, 0.0f, 0.0f );
}
//==============================================================================
// WalkerCam::~WalkerCam
//==============================================================================
// Description: Destructor.
//
// Parameters: None.
//
// Return: N/A.
//
//==============================================================================
WalkerCam::~WalkerCam()
{
}
//=============================================================================
// WalkerCam::Update
//=============================================================================
// Description: Comment
//
// Parameters: ( unsigned int milliseconds )
//
// Return: void
//
//=============================================================================
void WalkerCam::Update( unsigned int milliseconds )
{
if ( mTarget->IsUnstable() )
{
return;
}
//This is to adjust interpolation when we're running substeps.
float timeMod = 1.0f;
timeMod = (float)milliseconds / 16.0f;
UpdatePositionNormal( milliseconds, timeMod );
//--------- Adjust where we look
rmt::Vector targetPos;
rmt::Vector desiredTargetPos;
GetTargetPosition( &targetPos );
#ifdef RAD_DEBUG
rAssert( !rmt::IsNan( targetPos.x ) );
rAssert( !rmt::IsNan( targetPos.y ) );
rAssert( !rmt::IsNan( targetPos.z ) );
#endif
desiredTargetPos = targetPos;
desiredTargetPos.y = mTargetPos.y;
//Look up
rmt::Vector centre = targetPos;
centre.y += mData.GetUpAngle();
float lookUp = mController->GetValue( SuperCamController::lookToggle );
if ( !GetCamera()->SphereVisible( centre, 1.0f ) && lookUp <= 0.5f )
{
desiredTargetPos = targetPos;
}
else
{
//Look down
centre = targetPos;
centre.y -= mData.GetUpAngle();
if ( !GetCamera()->SphereVisible( centre, 1.0f ) && lookUp <= 0.5f )
{
desiredTargetPos = targetPos;
}
else
{
//Look normal
if ( !mTarget->IsCar() )
{
if ( lookUp > 0.5f )
{
const float howFarUp = 3.0f;
float addY = howFarUp * lookUp;
targetPos.y += addY;
desiredTargetPos = targetPos;
}
}
rmt::Vector camPos;
GetPosition( &camPos );
rmt::Vector camToTarg;
camToTarg.Sub( targetPos, camPos );
float mag, theta, phi;
rmt::CartesianToSpherical( camToTarg.x, camToTarg.z, camToTarg.y, &mag, &theta, &phi );
if ( phi > 1.65f )
{
desiredTargetPos = targetPos;
}
}
}
rmt::Vector targetVelocity;
mTarget->GetVelocity( &targetVelocity );
float targLag = mData.GetTargetLag() * timeMod;
if ( mTarget->IsCar() || targetVelocity.y < -1.0f )
{
targLag *= 2.0f;
}
CLAMP_TO_ONE(targLag);
MotionCubic( &mTargetPos.x, &mTargetPosDelta.x, desiredTargetPos.x, targLag );
MotionCubic( &mTargetPos.y, &mTargetPosDelta.y, desiredTargetPos.y, targLag );
MotionCubic( &mTargetPos.z, &mTargetPosDelta.z, desiredTargetPos.z, targLag );
//--------- Goofin' with the FOV
if ( !mTarget->IsCar() )
{
float zoom = mController->GetValue( SuperCamController::zToggle );
float FOV = GetFOV();
float pedPCT = IsTargetNearPed( milliseconds );
float offset = PED_ZOOM_OFFSET * pedPCT;
if ( GetFlag((Flag)CUT ) )
{
FilterFov( zoom, mData.GetMinFOV(), mData.GetMaxFOV(), FOV, mFOVDelta, 1.0f, timeMod, offset );
}
else
{
FilterFov( zoom, mData.GetMinFOV(), mData.GetMaxFOV(), FOV, mFOVDelta, mData.GetFOVLag(), timeMod, offset );
}
SetFOV( FOV );
//--------- Goofin' with the twist
//Are we on level 7?
if ( GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L7 && pedPCT > 0.0f)
{
rmt::Vector velocity;
mTarget->GetVelocity( &velocity );
float minCharSpeed = CharacterTune::sfMaxSpeed / 3.0f;
minCharSpeed *= minCharSpeed;
if ( velocity.MagnitudeSqr() < minCharSpeed )
{
SetTwist( CREEPY_TWIST_WALKER * pedPCT );
}
else
{
SetTwist( 0.0f );
}
}
else
{
SetTwist( 0.0f );
}
}
//--------- Set values.
#ifdef RAD_DEBUG
rAssert( !rmt::IsNan( mPosition.x ) );
rAssert( !rmt::IsNan( mPosition.y ) );
rAssert( !rmt::IsNan( mPosition.z ) );
rAssert( !rmt::IsNan( mTargetPos.x ) );
rAssert( !rmt::IsNan( mTargetPos.y ) );
rAssert( !rmt::IsNan( mTargetPos.z ) );
#endif
SetCameraValues( milliseconds, mPosition, mTargetPos );
}
//=============================================================================
// WalkerCam::UpdateForPhysics
//=============================================================================
// Description: Comment
//
// Parameters: ( unsigned int milliseconds )
//
// Return: void
//
//=============================================================================
void WalkerCam::UpdateForPhysics( unsigned int milliseconds )
{
//If we're in collision OR we're just coming out of collision.
bool hasGroundOffset = !rmt::Epsilon( mGroundOffset.MagnitudeSqr(), 0.0f, 0.01f);
if ( mNumCollisions || hasGroundOffset || GetFlag( (Flag)CUT ) )
{
float timeMod = 1.0f;
timeMod = (float)milliseconds / 16.0f;
if ( mNumCollisions )
{
//Adjust the mPosition and the mMagnitude if the camera is colliding.
UpdatePositionInCollision( milliseconds, timeMod );
}
if ( hasGroundOffset )
{
mPosition.Add( mGroundOffset );
rmt::Vector posToTarg;
posToTarg.Sub( mTargetPos, mPosition );
mMagnitude = posToTarg.Magnitude();
}
//This should test that the magnitude of the rod isn't too long. If it
//is, we ray cast backwards and move there.
if ( mMagnitude > (mData.GetMaxMagnitude() * 1.2f) ||
mTargetPos.y - mPosition.y > mData.GetMinMagnitude() ||
GetFlag( (Flag)CUT ) )
{
rmt::Vector lookFrom;
GetTargetPosition( &lookFrom );
rmt::Vector lookTo;
GetPosition( &lookTo );
rmt::Vector fromTo;
fromTo.Sub( lookTo, lookFrom );
fromTo.NormalizeSafe();
fromTo.Scale( mData.GetMinMagnitude() );
lookTo.Add( lookFrom, fromTo );
IntersectionList& iList = GetSuperCamManager()->GetSCC( GetPlayerID() )->GetIntersectionList();
if ( iList.LineOfSight( lookFrom, lookTo, 0.1f, true ) )
{
//Go here!
mPosition = lookTo;
mMagnitude = mData.GetMinMagnitude();
}
else
{
rmt::Vector intersection( 0.0f, 0.0f, 0.0f );
iList.TestIntersectionStatics( lookFrom, lookTo, &intersection );
rmt::Vector fromToIntersection;
fromToIntersection.Sub( intersection, lookFrom );
if ( rmt::Epsilon( intersection.MagnitudeSqr(), 0.0f, 0.01f ) ||
fromToIntersection.MagnitudeSqr() < (mData.GetMinMagnitude() * mData.GetMinMagnitude() ) )
{
//Trouble.
GetTargetPosition( &mPosition );
mPosition.y += 2.0f;
mMagnitude = 2.0f;
}
else
{
mPosition = intersection;
mMagnitude = fromToIntersection.Magnitude();
}
}
}
SetCameraValues( 0, mPosition, mTargetPos ); //No extra transition
}
else if ( GetFlag( (Flag)IN_COLLISION ) && !IsPushingStick() )
{
SetFlag( (Flag)IN_COLLISION, false );
//Get the old position.
GetPosition( &mOldPos );
mOldMagnitude = mMagnitude;
}
SetFlag( (Flag)CUT, false );
}
//=============================================================================
// WalkerCam::LoadSettings
//=============================================================================
// Description: Comment
//
// Parameters: ( unsigned char* settings )
//
// Return: void
//
//=============================================================================
void WalkerCam::LoadSettings( unsigned char* settings )
{
}
//=============================================================================
// WalkerCam::SetTarget
//=============================================================================
// Description: Comment
//
// Parameters: ( ISuperCamTarget* target )
//
// Return: void
//
//=============================================================================
void WalkerCam::SetTarget( ISuperCamTarget* target )
{
rAssert( target );
mTarget = target;
//Load the camera data for this guy since he is the primary
//dood.
p3d::inventory->PushSection();
p3d::inventory->SelectSection( SuperCamCentral::CAMERA_INVENTORY_SECTION );
HeapMgr()->PushHeap( GMA_TEMP );
tInventory::Iterator<WalkerCamDataChunk> it;
HeapMgr()->PopHeap( GMA_TEMP );
WalkerCamDataChunk* wcD = it.First();
while( wcD )
{
if ( wcD->mID == target->GetID() )
{
//Load the data.
mData.SetMaxMagnitude( wcD->mMaxMagnitude);
mData.SetMaxMagnitude( wcD->mMaxMagnitude);
mData.SetElevation( wcD->mElevation );
p3d::inventory->Remove( wcD );
p3d::inventory->PopSection();
return;
}
wcD = it.Next();
}
p3d::inventory->PopSection();
rDebugString( "There should have been camera data loaded!\n" );
}
//=============================================================================
// WalkerCam::AddTarget
//=============================================================================
// Description: Comment
//
// Parameters: ( ISuperCamTarget* target )
//
// Return: void
//
//=============================================================================
void WalkerCam::AddTarget( ISuperCamTarget* target )
{
//We don't care.
return;
}
//******************************************************************************
//
// Protected Member Functions
//
//******************************************************************************
//=============================================================================
// WalkerCam::OnDisplay
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void WalkerCam::OnDisplay() const
{
#ifdef DEBUGWATCH
if ( mNumCollisions == 1 && mPlaneIntersectPoint.MagnitudeSqr() != 0.0f )
{
rmt::Vector camPos;
rmt::Vector targPos;
GetPosition( &camPos );
GetTargetPosition( &targPos );
rmt::Vector normal = mCollisionOffset[ 0 ];
rmt::Vector pointInPlane;
pointInPlane.Add( mPlaneIntersectPoint, normal );
float D = -(normal.DotProduct( pointInPlane ));
rmt::Plane collisionPlane( normal, D );
normal.NormalizeSafe();
rmt::Vector left;
left.CrossProduct( rmt::Vector( 0.0f, 1.0f, 0.0f ), normal );
rmt::Vector line;
line.Add( mPlaneIntersectPoint, normal );
pddiPrimStream* stream;
stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINES, PDDI_V_C, 6);
line.Add( mPlaneIntersectPoint, normal );
stream->Colour( tColour( 0, 255, 0 ) );
stream->Coord(line.x, line.y, line.z);
normal.Scale( -1.0f );
line.Add( mPlaneIntersectPoint, normal );
stream->Colour( tColour( 0, 255, 0 ) );
stream->Coord(line.x, line.y, line.z);
line.Add( mPlaneIntersectPoint, left );
stream->Colour( tColour( 0, 255, 0 ) );
stream->Coord(line.x, line.y, line.z);
left.Scale( -1.0f );
line.Add( mPlaneIntersectPoint, left );
stream->Colour( tColour( 0, 255, 0 ) );
stream->Coord(line.x, line.y, line.z);
stream->Colour( tColour( 0, 0, 255 ) );
stream->Coord(camPos.x, camPos.y, camPos.z);
stream->Colour( tColour( 0, 0, 255 ) );
stream->Coord(targPos.x, targPos.y, targPos.z);
p3d::pddi->EndPrims(stream);
}
pddiPrimStream* stream;
stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINES, PDDI_V_C, 2);
stream->Colour( tColour( 0, 255, 0 ) );
stream->Coord( gLookFromR.x, gLookFromR.y, gLookFromR.z );
stream->Coord( gLookToR.x, gLookToR.y, gLookToR.z );
p3d::pddi->EndPrims(stream);
stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINES, PDDI_V_C, 2);
stream->Colour( tColour( 0, 255, 0 ) );
stream->Coord( gLookFromL.x, gLookFromL.y, gLookFromL.z );
stream->Coord( gLookToL.x, gLookToL.y, gLookToL.z );
p3d::pddi->EndPrims(stream);
stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINES, PDDI_V_C, 2);
stream->Colour( tColour( 0, 255, 0 ) );
stream->Coord( gLookFromU.x, gLookFromU.y, gLookFromU.z );
stream->Coord( gLookToU.x, gLookToU.y, gLookToU.z );
p3d::pddi->EndPrims(stream);
stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINES, PDDI_V_C, 2);
stream->Colour( tColour( 0, 255, 0 ) );
stream->Coord( gLookFromD.x, gLookFromD.y, gLookFromD.z );
stream->Coord( gLookToD.x, gLookToD.y, gLookToD.z );
p3d::pddi->EndPrims(stream);
#endif
}
//******************************************************************************
//
// Private Member Functions
//
//******************************************************************************
//=============================================================================
// WalkerCam::UpdatePositionNormal
//=============================================================================
// Description: Comment
//
// Parameters: ( unsigned int milliseconds, float timeMod )
//
// Return: void
//
//=============================================================================
void WalkerCam::UpdatePositionNormal( unsigned int milliseconds, float timeMod )
{
rmt::Vector targetPos, camPos;
rmt::Vector desiredPosition;
rmt::Vector desiredTargetPos;
rmt::Vector rod;
GetTargetPosition( &targetPos );
GetPosition( &camPos );
if ( mTarget->IsAirborn() && !mIsAirborn )
{
//Going Airborn!
mIsAirborn = true;
mCameraHeight = camPos.y;
}
else if ( !mTarget->IsAirborn() && mIsAirborn )
{
//Come down!
mIsAirborn = false;
}
desiredPosition = targetPos;
desiredTargetPos = targetPos;
float desiredMagnitude, rotation, elevation;
desiredMagnitude = mMagnitude;
if ( GetFlag( (Flag)FIRST_TIME ) )
{
mTargetPos = targetPos;
mMagnitude = mData.GetMagnitude();
mPosition = camPos;
mTargetPosDelta.Set( 0.0f, 0.0f, 0.0f );
mDesiredPositionDelta.Set( 0.0f, 0.0f, 0.0f );
}
bool topRayNotBlocked = false;
bool bottomRayNotBlocked = false;
if ( GetFlag( (Flag)CUT ) )
{
//Reset the FOV.
SetFOV( mData.GetMinFOV() + (mData.GetMaxFOV() - mData.GetMinFOV()) );
mMagnitude = mData.GetMagnitude();
rotation = mData.GetRotation();
mElevation = mData.GetElevation();
mElevationDelta = 0.0f;
mTargetPos = targetPos;
mTargetPosDelta.Set( 0.0f, 0.0f, 0.0f );
mDesiredPositionDelta.Set( 0.0f, 0.0f, 0.0f );
//Reset the deltas
mFOVDelta = 0.0f;
mMagnitudeDelta = 0.0f;
}
else
{
//Try to look at the collectible
bool lookAtCollectible = false;
if ( mCollectible )
{
rmt::Vector collectiblePos;
mCollectible->GetPosition( collectiblePos );
rmt::Vector targToCollect;
targToCollect.Sub( collectiblePos, targetPos );
if ( targToCollect.MagnitudeSqr() < 100.0f )
{
//We should look at the collectible
lookAtCollectible = true;
rod = targToCollect;
rod.NormalizeSafe();
rod.Scale( mData.GetMagnitude() );
rod.x *= -1.0f;
rod.z *= -1.0f;
}
}
if ( !lookAtCollectible )
{
rod.Sub( camPos, targetPos );
}
float magWaste;
rmt::CartesianToSpherical( rod.x, rod.z, rod.y, &magWaste, &rotation, &elevation );
if ( GetFlag( (Flag)FIRST_TIME ) )
{
mElevation = elevation;
}
}
SetFlag( (Flag)FIRST_TIME, false );
//--------- Adjust the camera according to user input...
if ( !GetFlag((Flag)CUT ) )
{
float xAxis = mController->GetAxisValue( SuperCamController::stickX );
float zAxis = mController->GetAxisValue( SuperCamController::stickY );
if ( GetSuperCamManager()->GetSCC( GetPlayerID() )->IsInvertedCameraEnabled() )
{
xAxis *= -1.0f; //Invert
}
#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
if ( mController->IsWheel() )
{
//This is a wheel. No left right on wheels.
xAxis *= 3.0f;
rmt::Clamp( xAxis, -1.0f, 1.0f );
}
#endif
if ( GetFlag( (Flag)IN_COLLISION ) && IsPushingStick() )
{
xAxis = 0.0f;
}
//Auto avoid visual impediments
if ( !mTarget->IsCar() &&
( IsStickStill() ||
( GetFlag( (Flag)IN_COLLISION ) && IsPushingStick() ) ) )
{
float nearPlane = GetNearPlane();
float radius = GetCollisionRadius() * 0.8f;
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 lookFrom( radius, 0.0f, nearPlane );
lookFrom.Transform( camMat );
lookFrom.Add( camPos );
rmt::Vector lookTo( 0.5f, 0.0f, -0.5f );
lookTo.Transform( camMat );
lookTo.Add( targetPos );
#ifdef DEBUGWATCH
gLookFromR = lookFrom;
gLookToR = lookTo;
#endif
IntersectionList& iList = GetSuperCamManager()->GetSCC( GetPlayerID() )->GetIntersectionList();
if ( !iList.LineOfSight( lookFrom, lookTo, 0.1f ) )
{
xAxis += -0.25f;
}
lookFrom.Set( -radius, 0.0f, nearPlane );
lookFrom.Transform( camMat );
lookFrom.Add( camPos );
lookTo.Set( -0.5f, 0.0f, -0.5f );
lookTo.Transform( camMat );
lookTo.Add( targetPos );
#ifdef DEBUGWATCH
gLookFromL = lookFrom;
gLookToL = lookTo;
#endif
if ( !iList.LineOfSight( lookFrom, lookTo, 0.1f ) )
{
xAxis += 0.25f;
}
//Look to see if we can go up.
lookFrom.Set( 0.0f, radius, nearPlane );
lookFrom.Transform( camMat );
lookFrom.Add( camPos );
lookTo.Set( 0.0f, 0.5f, -0.5f );
lookTo.Transform( camMat );
lookTo.Add( targetPos );
#ifdef DEBUGWATCH
gLookFromU = lookFrom;
gLookToU = lookTo;
#endif
topRayNotBlocked = iList.LineOfSight( lookFrom, lookTo, 0.1f );
//Do this one the other way since we want the ray to maintain it's length.
lookTo = targetPos;
lookTo.y = targetPos.y + 0.5f;
lookFrom.Set( 0.0f, 0.0, -0.2f );
lookFrom.Transform( camMat );
lookFrom.x += camPos.x;
lookFrom.y = lookTo.y;
lookFrom.z += camPos.z;
rmt::Vector toFrom;
toFrom.Sub( lookFrom, lookTo );
toFrom.Normalize();
toFrom.Scale( mMagnitude + 0.5f );
lookFrom.Add( lookTo, toFrom );
#ifdef DEBUGWATCH
gLookFromD = lookFrom;
gLookToD = lookTo;
#endif
bottomRayNotBlocked = iList.LineOfSight( lookFrom, lookTo, 0.1f );
}
rmt::Clamp( xAxis, -1.0f, 1.0f );
rotation += ( xAxis * mData.GetRotationIncrement() * timeMod );
const unsigned int frameTest = 0;
float lookUp = mController->GetValue( SuperCamController::lookToggle );
if ( rmt::Fabs( zAxis ) == 1.0f && mLastCollisionFrame == 0 ||
( mLastCollisionFrame + frameTest < GetGame()->GetFrameCount() ) )
{
float halfMag = (mData.GetMaxMagnitude() - mData.GetMinMagnitude()) / 2.0f;
desiredMagnitude -= ( zAxis * halfMag );
if ( desiredMagnitude < mData.GetMinMagnitude() )
{
desiredMagnitude = mData.GetMinMagnitude();
}
else if ( desiredMagnitude > mData.GetMaxMagnitude() )
{
desiredMagnitude = mData.GetMaxMagnitude();
}
float lag = mData.GetLag() * timeMod;
CLAMP_TO_ONE(lag);
MotionCubic( &mMagnitude, &mMagnitudeDelta, desiredMagnitude, lag );
mLastCollisionFrame = 0;
}
}
//--------- Adjust the camera according to where the target is...
if ( GetFlag( (Flag)CUT ) )
{
//Transform the rod to have the angle rmt::PI be behind the target...
rmt::Matrix mat;
rmt::Vector heading, vup;
mTarget->GetHeading( &heading );
// Dusit:
// assume vup of target is always 0,1,0 because
// the code won't work otherwise, especially now
// that the player character can transit into simulation control
// and tumble about in physics.
//mTarget->GetVUP( &vup );
vup.Set( 0.0f, 1.0f, 0.0f );
mat.Identity();
mat.FillHeading( heading, vup );
mElevation = mData.GetElevation();
rmt::SphericalToCartesian( mMagnitude, rotation, mElevation, &rod.x, &rod.z, &rod.y );
mElevationDelta = 0.0f;
rod.Transform( mat );
desiredPosition.Add( rod );
mPosition = desiredPosition;
}
else
{
float elevation = mData.GetElevation();
rmt::Vector groundNormal, pos;
mTarget->GetTerrainIntersect( pos, groundNormal );
if ( groundNormal.y < 0.97f )
{
elevation -= 0.3f;
}
else if ( topRayNotBlocked && !bottomRayNotBlocked )
{
elevation -= 0.2f;
}
float elevlag = SLIDE_INTERVAL * timeMod;
CLAMP_TO_ONE(elevlag);
MotionCubic( &mElevation, &mElevationDelta, elevation, elevlag );
rmt::SphericalToCartesian( mMagnitude, rotation, mElevation, &rod.x, &rod.z, &rod.y );
if ( mMagnitude < GetNearPlane() + 1.5f )
{
rod.x *= -1.0f;
rod.z *= -1.0f;
desiredPosition.Add( rod );
mPosition = desiredPosition;
mPosition.Add( mGroundOffset ); //Add the ground correction.
mMagnitude = mData.GetMinMagnitude();
mMagnitudeDelta = 0.0f;
mDesiredPositionDelta.Set( 0.0f, 0.0f, 0.0f );
//Tell the character controller that we're cutting to a new position.
GetEventManager()->TriggerEvent( EVENT_CAMERA_CHANGE, this );
}
else
{
float lag = mData.GetLag() * timeMod;
CLAMP_TO_ONE(lag);
desiredPosition.Add( rod );
if ( mIsAirborn )
{
desiredPosition.y = mCameraHeight;
}
MotionCubic( &mPosition.x, &mDesiredPositionDelta.x, desiredPosition.x, lag );
MotionCubic( &mPosition.y, &mDesiredPositionDelta.y, desiredPosition.y, lag );
MotionCubic( &mPosition.z, &mDesiredPositionDelta.z, desiredPosition.z, lag );
}
}
}
//=============================================================================
// WalkerCam::UpdatePositionInCollision
//=============================================================================
// Description: Comment
//
// Parameters: ( unsigned int milliseconds, float timeMod )
//
// Return: void
//
//=============================================================================
void WalkerCam::UpdatePositionInCollision( unsigned int milliseconds, float timeMod )
{
//Store the new collision and the num of collisions;
mLastCollisionFrame = GetGame()->GetFrameCount();
rmt::Vector camPos;
GetPosition( &camPos );
if ( mNumCollisions == 1 )
{
UpdatePositionOneCollsion( milliseconds, timeMod );
}
else
{
UpdatePositionMultipleCollision( milliseconds, timeMod );
}
SetFlag( (Flag)IN_COLLISION, true );
mXAxis = mController->GetAxisValue( SuperCamController::stickX );
}
//=============================================================================
// WalkerCam::UpdatePositionOneCollsion
//=============================================================================
// Description: Comment
//
// Parameters: ( unsigned int milliseconds, float timeMod, unsigned int collisionIndex )
//
// Return: bool
//
//=============================================================================
bool WalkerCam::UpdatePositionOneCollsion( unsigned int milliseconds, float timeMod, unsigned int collisionIndex )
{
rmt::Vector camPos;
GetPosition( &camPos );
rmt::Vector targetPos;
mTarget->GetPosition( &targetPos );
rmt::Vector newPos;
mPlaneIntersectPoint.Set( 0.0f, 0.0f, 0.0f );
newPos.Add( camPos, mCollisionOffset[ collisionIndex ] );
mPosition = newPos;
mDesiredPositionDelta.Set( 0.0f, 0.0f, 0.0f );
rmt::Vector camToTarg;
camToTarg.Sub( targetPos, mPosition );
mMagnitude = camToTarg.Magnitude();
mMagnitudeDelta = 0.0f;
/*
bool speedUp = false;
float xAxis = mController->GetValue( SuperCamController::stickX );
if ( GetFlag( (Flag)IN_COLLISION ) )
{
if ( IsPushingStick() )
{
return false;
}
else if ( !IsStickStill() )
{
speedUp = true;
}
}
else
{
if ( !IsStickStill() )
{
mPosition = mOldPos;
mMagnitude = mOldMagnitude;
return false;
}
}
rmt::Vector camPos, targetPos;
GetPosition( &camPos );
GetTargetPosition( &targetPos );
//Let's use the plane equation Ax+By+Cz+D = 0
//Where x,y,z are points in 3D and A,B,C are the plane normal
//D is the distance to the origin. YEAH!
rmt::Vector normal = mCollisionOffset[ collisionIndex ];
rmt::Vector normalScaled = normal;
normalScaled.NormalizeSafe();
normalScaled.Scale( -GetNearPlane() * 1.2f );
rmt::Vector pointInPlane;
pointInPlane.Add( camPos, normal );
pointInPlane.Add( normalScaled );
float D = -(normal.DotProduct( pointInPlane ));
rmt::Plane collisionPlane( normal, D );
rmt::Vector camToTargDir;
camToTargDir.Sub( targetPos, camPos );
camToTargDir.NormalizeSafe();
rmt::Vector newPos;
mPlaneIntersectPoint.Set( 0.0f, 0.0f, 0.0f );
newPos.Add( camPos, mCollisionOffset[ collisionIndex ] );
if ( collisionPlane.Intersect( camPos, camToTargDir, &mPlaneIntersectPoint ) )
{
rmt::Vector camToIntersect;
camToIntersect.Sub( mPlaneIntersectPoint, camPos );
//Test to ignore intersects that are on the wrong side of the plane.
if ( camToIntersect.DotProduct( camToTargDir ) > 0.0f && camToIntersect.MagnitudeSqr() < 16.0f )
{
rmt::Vector intersectPointOffset = mPlaneIntersectPoint;
intersectPointOffset.Sub( normalScaled );
newPos = intersectPointOffset;
}
else
{
mPlaneIntersectPoint.Set( 0.0f, 0.0f, 0.0f );
}
}
else
{
mPlaneIntersectPoint.Set( 0.0f, 0.0f, 0.0f );
}
float lag = mData.GetCollisionLag() * timeMod;
CLAMP_TO_ONE( lag );
// if ( speedUp )
// {
// lag = 1.0f;
// }
mDesiredPositionDelta.Set( 0.0f, 0.0f, 0.0f );
MotionCubic( &mPosition.x, &mDesiredPositionDelta.x, newPos.x, lag );
MotionCubic( &mPosition.y, &mDesiredPositionDelta.y, newPos.y, lag );
MotionCubic( &mPosition.z, &mDesiredPositionDelta.z, newPos.z, lag );
rmt::Vector newDir;
newDir.Sub( mPosition, targetPos );
float mag = newDir.Magnitude();
bool panic = false;
if ( mag > mData.GetMaxMagnitude() )
{
//Build a new rod.
newDir.NormalizeSafe();
newDir.Scale( mData.GetMagnitude() );
mPosition.Add( targetPos, newDir );
mag = mData.GetMagnitude();
}
if ( mag < GetNearPlane() + 1.5f )
{
MotionCubic( &mPosition.x, &mDesiredPositionDelta.x, mOldPos.x, lag );
MotionCubic( &mPosition.y, &mDesiredPositionDelta.y, mOldPos.y, lag );
MotionCubic( &mPosition.z, &mDesiredPositionDelta.z, mOldPos.z, lag );
rmt::Vector targToPos;
targToPos.Sub( mPosition, targetPos );
mag = targToPos.Magnitude();
}
mMagnitude = mag;
mMagnitudeDelta = 0.0f;
*/
return false;
}
struct OldCamData
{
rmt::Vector position;
rmt::Vector positionDelta;
float elevation;
float elevationDelta;
float magnitude;
float magnitudeDelta;
};
//=============================================================================
// WalkerCam::UpdatePositionMultipleCollision
//=============================================================================
// Description: Comment
//
// Parameters: ( unsigned int milliseconds, float timeMod )
//
// Return: void
//
//=============================================================================
void WalkerCam::UpdatePositionMultipleCollision( unsigned int milliseconds, float timeMod )
{
OldCamData origData;
origData.elevation = mElevation;
origData.elevationDelta = mElevationDelta;
origData.magnitude = mMagnitude;
origData.magnitudeDelta = mMagnitudeDelta;
origData.position = mPosition;
origData.positionDelta = mDesiredPositionDelta;
float desiredElevation = 0.0f;
float desiredMagnitude = 0.0f;
rmt::Vector desiredPosition( 0.0f, 0.0f, 0.0f );
unsigned int i;
for ( i = 0; i < mNumCollisions; ++i )
{
UpdatePositionOneCollsion( milliseconds, timeMod, i );
desiredElevation += mElevation;
desiredMagnitude += mMagnitude;
desiredPosition.Add( mPosition );
//Reset
mElevation = origData.elevation;
mElevationDelta = origData.elevationDelta;
mMagnitude = origData.magnitude;
mMagnitudeDelta = origData.magnitudeDelta;
mPosition = origData.position;
mDesiredPositionDelta = origData.positionDelta;
}
mElevationDelta = 0.0f;
mDesiredPositionDelta.Set( 0.0f, 0.0f, 0.0f );
mMagnitudeDelta = 0.0f;
desiredElevation += origData.elevation;
desiredMagnitude += origData.magnitude;
desiredPosition.x += origData.position.x;
desiredPosition.y += origData.position.y;
desiredPosition.z += origData.position.z;
desiredElevation /= mNumCollisions + 1;
desiredMagnitude /= mNumCollisions + 1;
desiredPosition.x /= mNumCollisions + 1;
desiredPosition.y /= mNumCollisions + 1;
desiredPosition.z /= mNumCollisions + 1;
float newLag = AVERAGE_LAG * timeMod;
CLAMP_TO_ONE(newLag);
MotionCubic( &mElevation, &mElevationDelta, desiredElevation, newLag );
MotionCubic( &mPosition.x, &mDesiredPositionDelta.x, desiredPosition.x, newLag );
MotionCubic( &mPosition.y, &mDesiredPositionDelta.y, desiredPosition.y, newLag );
MotionCubic( &mPosition.z, &mDesiredPositionDelta.z, desiredPosition.z, newLag );
MotionCubic( &mMagnitude, &mMagnitudeDelta, desiredMagnitude, newLag );
}
//=============================================================================
// WalkerCam::OnRegisterDebugControls
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void WalkerCam::OnRegisterDebugControls()
{
#ifdef DEBUGWATCH
char nameSpace[256];
sprintf( nameSpace, "SuperCam\\Player%d\\Walker", GetPlayerID() );
radDbgWatchAddFloat( &mData.mMinFOV, "Min FOV", nameSpace, NULL, NULL, 0.0f, rmt::PI );
radDbgWatchAddFloat( &mData.mMaxFOV, "Max FOV", nameSpace, NULL, NULL, 0.0f, rmt::PI );
radDbgWatchAddFloat( &mData.mLag, "Main lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
radDbgWatchAddFloat( &mData.mFOVLag, "FOV lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
radDbgWatchAddFloat( &mData.mMinMagnitude, "Min Magnitude", nameSpace, NULL, NULL, 1.0f, 100.0f );
radDbgWatchAddFloat( &mData.mMaxMagnitude, "Max Magnitude", nameSpace, NULL, NULL, 1.0f, 100.0f );
radDbgWatchAddFloat( &mData.mElevation, "Elevation (M)", nameSpace, NULL, NULL, 0.0f, 5.0 );
radDbgWatchAddFloat( &mData.mRotation, "Rotation", nameSpace, NULL, NULL, 0.0f, rmt::PI_2 );
radDbgWatchAddFloat( &mData.mMagnitude, "Magnitude", nameSpace, NULL, NULL, 0.0f, 100.0f );
radDbgWatchAddFloat( &mData.mRotationIncrement, "Rotation Increment", nameSpace, NULL, NULL, 0.0f, rmt::PI_BY2 );
radDbgWatchAddVector( &mData.mTargetOffset.x, "Target Offset (M)", nameSpace, NULL, NULL, 0.0f, 2.0f );
radDbgWatchAddFloat( &mData.mTargetLag, "Target Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
radDbgWatchAddFloat( &mData.mJumpLag, "Jump Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
radDbgWatchAddUnsignedInt( &mData.mLandingTransitionTime, "Landing Transition", nameSpace, NULL, NULL, 0, 10000 );
radDbgWatchAddFloat( &mData.mUpAngle, "Up Height (M)", nameSpace, NULL, NULL, 0.0f, 5.0f );
radDbgWatchAddFloat( &mData.mCollisionLag, "Collision Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
radDbgWatchAddFloat( &SLIDE_INTERVAL, "Slide Interval", nameSpace, NULL, NULL, 0.0f, 1.0f );
radDbgWatchAddFloat( &AVERAGE_LAG, "Average Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
radDbgWatchAddFloat( &PED_MIN_DIST, "Ped Min Dist", nameSpace, NULL, NULL, 0.0f, 10.0f );
radDbgWatchAddFloat( &PED_MAX_DIST, "Ped Max Dist", nameSpace, NULL, NULL, 0.0f, 10.0f );
radDbgWatchAddFloat( &PED_ZOOM_OFFSET, "Ped Zoom", nameSpace, NULL, NULL, 0.0f, rmt::PI_BY2 );
radDbgWatchAddFloat( &PED_FOV_TEST, "Ped FOV Test", nameSpace, NULL, NULL, 0.01f, rmt::PI );
radDbgWatchAddUnsignedInt( &MIN_PED_ACCUM, "Min Ped Time Acc", nameSpace, NULL, NULL, 0, 10000 );
radDbgWatchAddFloat( &CREEPY_TWIST_WALKER, "Twist", nameSpace, NULL, NULL, 0.0f, rmt::PI_2 );
#endif
}
//=============================================================================
// WalkerCam::OnUnregisterDebugControls
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void WalkerCam::OnUnregisterDebugControls()
{
#ifdef DEBUGWATCH
radDbgWatchDelete( &mData.mMinFOV );
radDbgWatchDelete( &mData.mMaxFOV );
radDbgWatchDelete( &mData.mLag );
radDbgWatchDelete( &mData.mFOVLag );
radDbgWatchDelete( &mData.mMinMagnitude );
radDbgWatchDelete( &mData.mMaxMagnitude );
radDbgWatchDelete( &mData.mElevation );
radDbgWatchDelete( &mData.mRotation );
radDbgWatchDelete( &mData.mMagnitude );
radDbgWatchDelete( &mData.mRotationIncrement );
radDbgWatchDelete( &mData.mTargetOffset.x );
radDbgWatchDelete( &mData.mTargetLag );
radDbgWatchDelete( &mData.mJumpLag );
radDbgWatchDelete( &mData.mLandingTransitionTime );
radDbgWatchDelete( &mData.mUpAngle );
radDbgWatchDelete( &mData.mCollisionLag );
radDbgWatchDelete( &SLIDE_INTERVAL );
radDbgWatchDelete( &AVERAGE_LAG );
radDbgWatchDelete( &PED_MIN_DIST );
radDbgWatchDelete( &PED_MAX_DIST );
radDbgWatchDelete( &PED_ZOOM_OFFSET );
radDbgWatchDelete( &PED_FOV_TEST );
radDbgWatchDelete( &MIN_PED_ACCUM );
radDbgWatchDelete( &CREEPY_TWIST_WALKER );
#endif
}
//=============================================================================
// WalkerCam::IsPushingStick
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: bool
//
//=============================================================================
bool WalkerCam::IsPushingStick()
{
float xAxis = mController->GetAxisValue( SuperCamController::stickX );
return ( !rmt::Epsilon( xAxis, 0.0f, 0.001f ) &&
rmt::Sign( xAxis ) == rmt::Sign( mXAxis ) );
}
//=============================================================================
// WalkerCam::IsStickStill
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: bool
//
//=============================================================================
bool WalkerCam::IsStickStill()
{
float xAxis = mController->GetAxisValue( SuperCamController::stickX );
return rmt::Epsilon( xAxis, 0.0f, 0.01f );
}
//=============================================================================
// WalkerCam::GetTargetPosition
//=============================================================================
// Description: Comment
//
// Parameters: ( rmt::Vector* position, bool withOffset )
//
// Return: void
//
//=============================================================================
void WalkerCam::GetTargetPosition( rmt::Vector* position,
bool withOffset ) const
{
rAssert( mTarget );
mTarget->GetPosition( position );
rAssert( !rmt::IsNan( position->x ) );
rAssert( !rmt::IsNan( position->y ) );
rAssert( !rmt::IsNan( position->z ) );
if ( withOffset )
{
rmt::Vector offset;
mData.GetTargetOffset( &offset );
//Now put the offset in the target's space
/* rmt::Matrix mat;
rmt::Vector targetHeading, targetVUP;
mTarget->GetHeading( &targetHeading );
#ifdef RAD_DEBUG
rAssert( !rmt::IsNan( targetHeading.x ) );
rAssert( !rmt::IsNan( targetHeading.y ) );
rAssert( !rmt::IsNan( targetHeading.z ) );
#endif
// Dusit:
// assume vup of target is always 0,1,0 because
// the code won't work otherwise, especially now
// that the player character can transit into simulation control
// and tumble about in physics.
//mTarget->GetVUP( &targetVUP );
targetVUP.Set( 0.0f, 1.0f, 0.0f );
mat.Identity();
mat.FillHeading( targetHeading, targetVUP );
offset.Transform( mat );
*/
(*position).Add( offset );
}
}
//=============================================================================
// WalkerCam::IsTargetNearPed
//=============================================================================
// Description: Comment
//
// Parameters: ( unsigned int milliseconds )
//
// Return: float
//
//=============================================================================
float WalkerCam::IsTargetNearPed( unsigned int milliseconds )
{
if ( mPedTimeAccum - static_cast<int>(milliseconds) <= 0 )
{
mPedTimeAccum = 0;
}
else
{
mPedTimeAccum -= milliseconds;
}
if ( mPedTimeAccum == 0 )
{
//Reset the time.
mPedTimeAccum = MIN_PED_ACCUM;
//Retest for a new ped
CharacterManager* cm = GetCharacterManager();
rmt::Vector targetPos;
mTarget->GetPosition( &targetPos );
int i;
float pedMagSqr = 10000.0f;
mLastCharacter = 0;
//Find the closest, visible character.
float oldFOV, oldAspect;
GetCamera()->GetFOV( &oldFOV, &oldAspect );
GetCameraNonConst()->SetFOV( PED_FOV_TEST, oldAspect );
for ( i = 0; i < cm->GetMaxCharacters(); ++i )
{
Character* tempCharacter = cm->GetCharacter( i );
//If it's valid and not the player.
if ( tempCharacter != NULL &&
static_cast<ISuperCamTarget*>(tempCharacter->GetTarget()) != mTarget &&
!PedestrianManager::GetInstance()->IsPed( tempCharacter ) )
{
rmt::Vector charPos;
tempCharacter->GetPosition( &charPos );
if ( GetCamera()->SphereVisible( charPos, 1.0f ) )
{
rmt::Vector targetToPed;
targetToPed.Sub( charPos, targetPos );
if ( targetToPed.MagnitudeSqr() < pedMagSqr )
{
if ( targetToPed.MagnitudeSqr() < PED_MAX_DIST * PED_MAX_DIST )
{
pedMagSqr = targetToPed.MagnitudeSqr();
mLastCharacter = tempCharacter->GetUID();
}
}
}
}
}
//Reset the FOV.
GetCameraNonConst()->SetFOV( oldFOV, oldAspect );
}
//If we have a candidate ped...
if ( mLastCharacter != static_cast< tUID >( 0 ) )
{
Character* c = GetCharacterManager()->GetCharacterByName(mLastCharacter);
if(c)
{
rmt::Vector charPos;
c->GetPosition( &charPos );
rmt::Vector targetPos;
mTarget->GetPosition( &targetPos );
rmt::Vector targetToPed;
targetToPed.Sub( charPos, targetPos );
float distNormal = targetToPed.Magnitude() / (PED_MAX_DIST - PED_MIN_DIST); //Square root!
CLAMP_TO_ONE( distNormal );
distNormal = 1.0f - distNormal;
return distNormal;
}
}
return 0.0f;
}
//=============================================================================
// WalkerCam::GetWatcherName
//=============================================================================
// Description: the name of the class for the watcher or other debug purposes
//
// Parameters: NONE
//
// Return: const char* - the name of the class
//
//=============================================================================
#ifdef DEBUGWATCH
const char* WalkerCam::GetWatcherName() const
{
return "WalkerCam";
}
#endif