//============================================================================= // Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. // // File: followcam.cpp // // Description: Implement FollowCam // This is the main target following camera mode. It maintains a rod // that orients itself in 3D according to rules on the 3-axis. // This mode can handle one main target and possibly 2-others in a // limited fashion. // // History: 22/04/2002 + Created -- Cary Brisebois // //============================================================================= //======================================== // System Includes //======================================== // Foundation Tech //These are included in the precompiled headers on XBOX and GC #include #include #include #include //======================================== // Project Includes //======================================== #include #include #include #include #include #include #include
#include #include #include #include //****************************************************************************** // // Global Data, Local Data, Local Classes // //****************************************************************************** void SetDirtyCB( void* userData ) { FollowCam* fc = (FollowCam*)userData; fc->SetDirty(); } static bool reverseTest = false; #ifdef DEBUGWATCH float MIN_REVERSE_VELOCITY = 45.0f; static float CREEPY_TWIST = 6.107f; float LOOK_OFFSET_DIST = 5.0f; float LOOK_OFFSET_HEIGHT = 1.0f; float LOOK_OFFSET_BACK = 0.0f; float LOOK_ROT = rmt::PI_BY2; #else const float MIN_REVERSE_VELOCITY = 45.0f; static const float CREEPY_TWIST = 6.107f; const float LOOK_OFFSET_DIST = 5.0f; const float LOOK_OFFSET_HEIGHT = 1.0f; const float LOOK_OFFSET_BACK = 0.0f; const float LOOK_ROT = rmt::PI_BY2; #endif const unsigned int FOLLOW_ID_OFFSET = 256; //Only uncomment ONE of these. //#define TURN_LOOK //#define CUT_LOOK #define EXTRA_ROT //****************************************************************************** // // Public Member Functions // //****************************************************************************** //============================================================================== // FollowCam::FollowCam //============================================================================== // Description: Constructor. // // Parameters: None. // // Return: N/A. // //============================================================================== FollowCam::FollowCam( FollowType type ) : mNumTargets( 0 ), mActiveTarget( 0 ), mUnstableDelayTimeLeft( 0 ), mQuickTurnTimeLeft( 0 ), mFollowType( type ), mXAxis( 0.0f ) { unsigned int i; for ( i = 0; i < MAX_TARGETS; ++i ) { mTargets[ i ] = NULL; } mUnstablePosition.Set( 0.0f, 0.0f, 0.0f ); mUnstableTarget.Set( 0.0f, 0.0f, 0.0f ); mRotationAngleXZ = mData.GetRotationXZ(); mRotationAngleY = mData.GetRotationY(); mRotationAngleXZDelta = 0.0f; mRotationAngleYDelta = 0.0f; mMagnitude = mData.GetMagnitude(); mMagnitudeDelta = 0.0f; mTargetPosition.Set( 0.0f, 0.0f, 0.0f ); mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f ); mGroundOffset.Set( 0.0f, 0.0f, 0.0f ); } //============================================================================== // FollowCam::~FollowCam //============================================================================== // Description: Destructor. // // Parameters: None. // // Return: N/A. // //============================================================================== FollowCam::~FollowCam() { } //============================================================================= // FollowCam::Update //============================================================================= // Description: Comment // // Parameters: ( unsigned int milliseconds ) // // Return: void // //============================================================================= void FollowCam::Update( unsigned int milliseconds ) { BEGIN_PROFILE("FollowCam::Update") rAssert( mTargets[ mActiveTarget ] ); //This is to adjust interpolation when we're running substeps. float timeMod = (float)milliseconds / EXPECTED_FRAME_RATE; rmt::Vector rod; ISuperCamTarget* target = mTargets[ mActiveTarget ]; #ifdef CUT_LOOK #if defined(RAD_GAMECUBE) || defined(RAD_XBOX) float leftRight = mController->GetValue( SuperCamController::stickX ); #elif defined(RAD_WIN32) float left = mController->GetValue( SuperCamController::carLookLeft ); float right = mController->GetValue( SuperCamController::carLookRight ); float leftRight = ( right > left ) ? right : -left; #else //This is PS2 float leftRight = mController->GetValue( SuperCamController::r2 ) - mController->GetValue( SuperCamController::l2 ); #endif #if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32) if ( mController->IsWheel() ) { //This is a wheel. No left right on wheels. leftRight = 0.0f; } #endif if ( rmt::Fabs(leftRight) > 0.2f && !GetFlag( (Flag)UNSTABLE ) ) { SetFlag( (Flag)LOOKING_SIDEWAYS, true ); SetFlag( (Flag)CUT, true ); } else if ( rmt::Fabs(leftRight) <= 0.2f && GetFlag( (Flag)LOOKING_SIDEWAYS ) ) { SetFlag( (Flag)LOOKING_SIDEWAYS, false ); SetFlag( (Flag)CUT, true ); } else #endif if ( GetFlag( (Flag)LOOK_BACK ) && !GetFlag( (Flag)LOOKING_BACK ) && !GetFlag( (Flag)UNSTABLE ) ) { //Turn on SetFlag( (Flag)LOOKING_BACK, true ); SetFlag( (Flag)CUT, true ); } else if ( GetFlag( (Flag)LOOKING_BACK ) && !GetFlag( (Flag)LOOK_BACK ) && !GetFlag( (Flag)UNSTABLE ) ) { //Turn off. SetFlag( (Flag)LOOKING_BACK, false ); SetFlag( (Flag)CUT, true ); } if ( GetFlag( (Flag)CUT ) ) { DoCameraCut(); } if ( GetFlag( (Flag)UNSTABLE ) ) { UpdateUnstable( milliseconds ); } //--------- Test to see if the target is still unstable if ( !GetFlag((Flag)FIRST_TIME) && !GetFlag((Flag)CUT) && ( target->IsUnstable() || target->IsQuickTurn() || target->IsAirborn() ) && !GetSuperCamManager()->GetSCC( GetPlayerID() )->IsCutCam() ) { if ( target->IsQuickTurn() ) { //Say that going unstable is due to quick turn, //so when we come out of unstable, speed up interpolation. SetFlag( (Flag)QUICK_TURN_ALERT, true ); } if ( mUnstableDelayTimeLeft == 0 ) { //Going unstable anew InitUnstable(); } //Reset the unstable clock. mUnstableDelayTimeLeft = mData.GetUnstableDelay(); SetFlag((Flag)UNSTABLE, true ); } BEGIN_PROFILE("Cam::Rod") //--------- Figure out the new rod thing. if ( GetFlag( (Flag)UNSTABLE ) ) { //Get the unstable rod. rod = mUnstablePosition; //Deal with collision //rAssert( mNumCollisions == 0 ); } else { CalculateRod( &rod, milliseconds, timeMod ); } END_PROFILE("Cam::Rod") rmt::Vector desiredPosition = rod; //--------- Get the position in space of the target to apply to desired values. rmt::Vector targetPos; target->GetPosition( &targetPos ); //--------- Set the desired position of the camera desiredPosition.Add( targetPos ); BEGIN_PROFILE("FollowCam::Target") //--------- Set the desired position of the camera target rmt::Vector desiredTarget; GetTargetPosition( &desiredTarget ); if ( GetFlag( (Flag)UNSTABLE ) ) { desiredTarget.Add( targetPos, mUnstableTarget ); } else { CalculateTarget( &desiredTarget, milliseconds, timeMod ); } //TODO: Connect this camera to the physics system. END_PROFILE("FollowCam::Target") if ( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_SPEED_CAM ) ) { SetFOV( 1.608495f ); } else { SetFOV( mData.GetFOV() ); } bool stabilizingToggle = false; rmt::Vector lookFrom = targetPos; rmt::Vector lookTo = desiredPosition; IntersectionList& iList = GetSuperCamManager()->GetSCC( GetPlayerID() )->GetIntersectionList(); if ( !iList.LineOfSight( lookFrom, lookTo, 0.01f, true ) ) { bool badSituation = false; if ( GetFlag( (Flag)LOOK_BACK ) ) { //Go somewhere else. rmt::Vector intersection; if ( iList.TestIntersectionStatics( lookFrom, lookTo, &intersection ) ) { desiredPosition = intersection; rmt::Vector localPos = desiredPosition; localPos.Sub( targetPos ); rmt::CartesianToSpherical( localPos.x, localPos.z, localPos.y, &mMagnitude, &mRotationAngleXZ, &mRotationAngleY ); mRotationAngleXZDelta = 0.0f; mRotationAngleYDelta = 0.0f; mMagnitudeDelta = 0.0f; lookFrom = targetPos; lookTo = desiredPosition; if ( !iList.LineOfSight( lookFrom, lookTo, 0.01f, true ) ) { badSituation = true; } } } else { SetFlag( (Flag)CUT, true ); DoCameraCut(); CalculateRod( &rod, milliseconds, timeMod ); desiredPosition = rod; desiredPosition.Add( targetPos ); lookFrom = targetPos; lookTo = desiredPosition; if ( !iList.LineOfSight( lookFrom, lookTo, 0.01f, true ) ) { badSituation = true; } SetFlag( (Flag)LOS_CORRECTED, true ); } if ( badSituation ) { iList.TestIntersectionStatics( lookTo, lookFrom, &desiredPosition ); rmt::Vector distVec; distVec.Sub( targetPos, desiredPosition ); float minDist = mData.GetMagnitude() - 2.0f; //Arbitrary? if ( distVec.MagnitudeSqr() < ( minDist * minDist ) ) { desiredPosition = targetPos; desiredPosition.Add( rmt::Vector( 0.0f, 6.0f, 0.0f ) ); //BAD! /* rmt::Vector heading; mTargets[ 0 ]->GetHeading( &heading ); if ( GetFlag( (Flag)LOOKING_BACK ) ) { heading.x *= -1.0f; heading.z *= -1.0f; } heading.Scale( 2.0f ); desiredTarget.Add( desiredPosition, heading ); */ } } //Better clear the unstable flag, just in case. //Clear the unstable flag. SetFlag( (Flag)UNSTABLE, false ); stabilizingToggle = true; mUnstableDelayTimeLeft = 0; } //--------- Set the new camera values BEGIN_PROFILE("FollowCam::SetCameraValues") SetCameraValues( milliseconds, desiredPosition, desiredTarget ); END_PROFILE("FollowCam::SetCameraValues") SetFlag( (Flag)FIRST_TIME, false ); SetFlag( (Flag)LOOK_BACK, false ); SetFlag( (Flag)STABILIZING, stabilizingToggle ); SetFlag( (Flag)CUT, stabilizingToggle ); //SetFlag( (Flag)LOOKING_SIDEWAYS, false ); END_PROFILE("FollowCam::Update") //Save the old position of the car in case we go unstable next update. GetTargetPosition( &mOldTargetPos, false ); } //============================================================================= // FollowCam::UpdateForPhysics //============================================================================= // Description: Comment // // Parameters: ( unsigned int milliseconds ) // // Return: void // //============================================================================= void FollowCam::UpdateForPhysics( unsigned int milliseconds ) { bool collision = false; //This is to correct for collisions. //Adjust for collision; rmt::Vector currentPosition, desiredPosition, delta; GetPosition( ¤tPosition ); desiredPosition = currentPosition; delta.Set( 0.0f, 0.0f, 0.0f ); unsigned int i; for ( i = 0; i < mNumCollisions; ++i ) { rmt::Vector camHeading; GetHeading( &camHeading ); camHeading.NormalizeSafe(); if ( camHeading.DotProduct( mCollisionOffset[ i ] ) >= -0.7f ) { desiredPosition.Add( mCollisionOffset[ i ] ); } else if ( !collision ) { rmt::Vector fakedCollision = camHeading; fakedCollision *= GetNearPlane() * 1.5f; desiredPosition.Add( fakedCollision ); } collision = true; } float lag = mData.GetCollisionLag(); lag *= milliseconds / 16.0f; CLAMP_TO_ONE( lag ); MotionCubic( ¤tPosition.x, &delta.x, desiredPosition.x, lag ); MotionCubic( ¤tPosition.y, &delta.y, desiredPosition.y, lag ); MotionCubic( ¤tPosition.z, &delta.z, desiredPosition.z, lag ); //------- Check to see if we've intersected anything if ( mGroundOffset.MagnitudeSqr() > 0.001f ) { currentPosition.Add( mGroundOffset ); collision = true; } rmt::Vector targetPos; GetTarget( &targetPos ); SetCameraValues( 0, currentPosition, targetPos ); //0.0f, no extra transition please. SetFlag((Flag)COLLIDING, collision ); if ( collision ) { mXAxis = mController->GetAxisValue( SuperCamController::stickX ); } } //============================================================================= // FollowCam::LoadSettings //============================================================================= // Description: Comment // // Parameters: ( unsigned char* settings ) // // Return: void // //============================================================================= void FollowCam::LoadSettings( unsigned char* settings ) { } //============================================================================= // FollowCam::CopyToData //============================================================================= // Description: Comment // // Parameters: () // // Return: void // //============================================================================= void FollowCam::CopyToData() { // dlong: it's fun to misuse other people's code mData.SetAspect( GetAspect() ); } //============================================================================= // FollowCam::SetTarget //============================================================================= // Description: This selects the current active target. // // Parameters: ( ISuperCamTarget* target ) // // Return: void // //============================================================================= void FollowCam::SetTarget( ISuperCamTarget* target ) { rAssert( target ); mTargets[ 0 ] = target; mNumTargets = 1; mActiveTarget = 0;//Set it to the first one. FollowCamDataChunk* fcD = SuperCamCentral::FindFCD( target->GetID() + static_cast(mFollowType) * FOLLOW_ID_OFFSET ); if ( fcD ) { //Load the data. //mData.SetRotationXZ( fcD->mRotation ); mData.SetRotationY( fcD->mElevation ); mData.SetMagnitude( fcD->mMagnitude ); mData.SetTargetOffset( fcD->mTargetOffset ); return; } rDebugString( "There should have been camera data loaded!" ); } //============================================================================= // FollowCam::AddTarget //============================================================================= // Description: Comment // // Parameters: ( ISuperCamTarget* target ) // // Return: void // //============================================================================= void FollowCam::AddTarget( ISuperCamTarget* target ) { rAssert( mNumTargets < MAX_TARGETS ); rAssert( target ); if ( mNumTargets < MAX_TARGETS ) { //Add the target mTargets[ mNumTargets ] = target; ++mNumTargets; } } //============================================================================= // FollowCam::EnableShake //============================================================================= // Description: Comment // // Parameters: () // // Return: void // //============================================================================= void FollowCam::EnableShake() { SetShaker( &mSineCosShaker ); SuperCam::EnableShake(); } //============================================================================= // FollowCam::DisableShake //============================================================================= // Description: Comment // // Parameters: () // // Return: void // //============================================================================= void FollowCam::DisableShake() { SuperCam::DisableShake(); } //============================================================================= // FollowCam::ShouldReverse //============================================================================= // Description: Comment // // Parameters: () // // Return: bool // //============================================================================= bool FollowCam::ShouldReverse() const { rAssert( mTargets[0] != NULL ); rmt::Vector v; mTargets[0]->GetVelocity( &v ); return !mTargets[0]->IsAirborn() && !mTargets[0]->IsUnstable() && mTargets[0]->IsInReverse() && ((v.MagnitudeSqr() > MIN_REVERSE_VELOCITY ) || mNumCollisions > 0 ); } //****************************************************************************** // // Private Member Functions // //****************************************************************************** //============================================================================= // FollowCam::OnRegisterDebugControls //============================================================================= // Description: Comment // // Parameters: () // // Return: void // //============================================================================= void FollowCam::OnRegisterDebugControls() { #ifdef DEBUGWATCH char nameSpace[256]; sprintf( nameSpace, "SuperCam\\Player%d\\Follow", GetPlayerID() ); radDbgWatchAddFloat( &mData.mRotationXZ, "Rotation XZ", nameSpace, &SetDirtyCB, this, 0.0f, rmt::PI_2 ); radDbgWatchAddFloat( &mData.mRotationY, "Rotation Y", nameSpace, &SetDirtyCB, this, 0.001f, rmt::PI_BY2 ); radDbgWatchAddFloat( &mData.mMagnitude, "Magnitude", nameSpace, &SetDirtyCB, this, 2.0f, 50.0f ); radDbgWatchAddFloat( &mData.mCameraLagXZ, "Camera Lag XZ", nameSpace, &SetDirtyCB, this, 0.0f, 1.0f ); radDbgWatchAddFloat( &mData.mCameraLagY, "Camera Lag Y", nameSpace, &SetDirtyCB, this, 0.0f, 1.0f ); radDbgWatchAddFloat( &mData.mMagnitudeLag, "Magnitude Lag", nameSpace, &SetDirtyCB, this, 0.0f, 1.0f ); radDbgWatchAddFloat( &mData.mCollisionLag, "Collision Lag", nameSpace, &SetDirtyCB, this, 0.0f, 1.0f ); radDbgWatchAddVector( &mData.mTargetOffset.x, "Target Offset", nameSpace, &SetDirtyCB, this, 0.0f, 100.0f ); radDbgWatchAddFloat( &mData.mTargetLagXZ, "Target Lag XZ", nameSpace, &SetDirtyCB, this, 0.0f, 1.0f ); radDbgWatchAddFloat( &mData.mTargetLagY, "Target Lag Y", nameSpace, &SetDirtyCB, this, 0.0f, 1.0f ); radDbgWatchAddUnsignedInt( &mData.mUnstableDelay, "Unstable Delay", nameSpace, &SetDirtyCB, this, 0, 1000 ); radDbgWatchAddUnsignedInt( &mData.mQuickTurnDelay, "Quick-turn Delay", nameSpace, &SetDirtyCB, this, 0, 1000 ); radDbgWatchAddFloat( &mData.mQuickTurnModifier, "Quick-turn Modifier", nameSpace, &SetDirtyCB, this, 0.0f, 1.0f ); radDbgWatchAddFloat( &MIN_REVERSE_VELOCITY, "Min reverse v", nameSpace, NULL, this, 10.0f, 300.0f ); radDbgWatchAddFloat( &CREEPY_TWIST, "Twist", nameSpace, NULL, NULL, 0.0f, rmt::PI_2 ); #if defined(LOOK_TURN) || defined(CUT_LOOK) radDbgWatchAddFloat( &LOOK_OFFSET_DIST, "Side/Up Look Dist", nameSpace, NULL, NULL, 0.0f, 20.0f ); radDbgWatchAddFloat( &LOOK_OFFSET_HEIGHT, "Side/Up Look height", nameSpace, NULL, NULL, 0.0f, 5.0f ); radDbgWatchAddFloat( &LOOK_OFFSET_BACK, "Side/Up Look pos Z", nameSpace, NULL, NULL, 0.0f, -5.0f ); #endif #ifdef EXTRA_ROT radDbgWatchAddFloat( &LOOK_ROT, "Side Look Rot", nameSpace, NULL, NULL, 0.01f, rmt::PI_BY2 ); #endif #endif } //============================================================================= // FollowCam::OnUnregisterDebugControls //============================================================================= // Description: Comment // // Parameters: () // // Return: void // //============================================================================= void FollowCam::OnUnregisterDebugControls() { #ifdef DEBUGWATCH radDbgWatchDelete( &mData.mRotationXZ ); radDbgWatchDelete( &mData.mRotationY ); radDbgWatchDelete( &mData.mMagnitude ); radDbgWatchDelete( &mData.mCameraLagXZ ); radDbgWatchDelete( &mData.mCameraLagY ); radDbgWatchDelete( &mData.mMagnitudeLag ); radDbgWatchDelete( &mData.mCollisionLag ); radDbgWatchDelete( &mData.mTargetOffset.x ); radDbgWatchDelete( &mData.mTargetLagXZ ); radDbgWatchDelete( &mData.mTargetLagY ); radDbgWatchDelete( &mData.mUnstableDelay ); radDbgWatchDelete( &mData.mQuickTurnDelay ); radDbgWatchDelete( &mData.mQuickTurnModifier ); radDbgWatchDelete( &MIN_REVERSE_VELOCITY ); radDbgWatchDelete( &CREEPY_TWIST ); #if defined(LOOK_TURN) || defined(CUT_LOOK) radDbgWatchDelete( &LOOK_OFFSET_DIST ); radDbgWatchDelete( &LOOK_OFFSET_HEIGHT ); radDbgWatchDelete( &LOOK_OFFSET_BACK ); #endif #ifdef EXTRA_ROT radDbgWatchDelete( &LOOK_ROT ); #endif #endif } //============================================================================= // FollowCam::DoCameraCut //============================================================================= // Description: Comment // // Parameters: () // // Return: void // //============================================================================= void FollowCam::DoCameraCut() { SetFlag( (Flag)CUT, true ); //We're doing a camera cut so let's set the position to the default. rmt::Matrix mat; rmt::Vector targetHeading, targetVUP; mTargets[ mActiveTarget ]->GetHeading( &targetHeading ); mTargets[ mActiveTarget ]->GetVUP( &targetVUP ); mat.Identity(); mat.FillHeading( targetHeading, targetVUP ); rmt::Vector desiredRod; GetDesiredRod( &desiredRod ); //Get the rod into the target's space desiredRod.Transform( mat ); rmt::CartesianToSpherical( desiredRod.x, desiredRod.z, desiredRod.y, &mMagnitude, &mRotationAngleXZ, &mRotationAngleY ); //Also, reset the deltas. mRotationAngleXZDelta = 0.0f; mRotationAngleYDelta = 0.0f; mMagnitudeDelta = 0.0f; #ifdef CUT_LOOK if ( GetFlag( (Flag)LOOKING_SIDEWAYS ) ) { GetTargetPosition( &mTargetPosition, false ); mTargetPosition.Add( rmt::Vector( 0.0f, LOOK_OFFSET_HEIGHT, 0.0f ) ); } else { //mTargetPosition.Set( 0.0f, 0.0f, 0.0f ); GetTargetPosition( &mTargetPosition ); } #else //mTargetPosition.Set( 0.0f, 0.0f, 0.0f ); GetTargetPosition( &mTargetPosition ); #endif mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f ); //Reset the FOV and aspect ratio for this camera. SetFOV( mData.GetFOV() ); SetAspect( mData.GetAspect() ); SetFlag( (Flag)UNSTABLE, false ); } //============================================================================= // FollowCam::InitUnstable //============================================================================= // Description: Comment // // Parameters: () // // Return: void // //============================================================================= void FollowCam::InitUnstable() { //First time unstable since recovered. //Set up the unstable rod rmt::Vector targetPosition; //GetTargetPosition( &targetPosition, false ); targetPosition = mOldTargetPos; rmt::Vector camPosition; GetPosition( &camPosition ); mUnstablePosition.Sub( camPosition, targetPosition ); rmt::Vector camTarget; GetTarget( &camTarget ); mUnstableTarget.Sub( camTarget, targetPosition ); //Set the unstable flag SetFlag( (Flag)UNSTABLE, true ); } //============================================================================= // FollowCam::UpdateUnstable //============================================================================= // Description: Comment // // Parameters: () // // Return: void // //============================================================================= void FollowCam::UpdateUnstable( unsigned int milliseconds ) { mUnstableDelayTimeLeft -= milliseconds; if ( mUnstableDelayTimeLeft <= 0 ) { //Finished being unstable. Let the camera drift back to it's normal //position. If this is a speed up camera situation, we should //accelerate the rotation. mUnstableDelayTimeLeft = 0; mUnstablePosition.Set( 0.0f, 0.0f, 0.0f ); mUnstableTarget.Set( 0.0f, 0.0f, 0.0f ); mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f ); //Clear the unstable flag. SetFlag( (Flag)UNSTABLE, false ); if ( GetFlag( (Flag)QUICK_TURN_ALERT ) ) { SetFlag( (Flag)QUICK_TURN_ALERT, false ); SetFlag( (Flag)QUICK_TURN, true ); InitQuickTurn(); } //This is to internally reset the state of the follow cam. By doing //The unstable stuff we've hosed the "memory" of the rotations. SetFlag( (Flag)STABILIZING, true ); } } //============================================================================= // FollowCam::InitQuickTurn //============================================================================= // Description: Comment // // Parameters: () // // Return: void // //============================================================================= void FollowCam::InitQuickTurn() { mQuickTurnTimeLeft = mData.GetQuickTurnDelay(); } //============================================================================= // FollowCam::UpdateQuickTurn //============================================================================= // Description: Comment // // Parameters: ( unsigned int milliseconds ) // // Return: void // //============================================================================= void FollowCam::UpdateQuickTurn( unsigned int milliseconds ) { mQuickTurnTimeLeft -= milliseconds; if ( mQuickTurnTimeLeft <= 0 ) { //Done the quick turn, back to normal speed now. SetFlag( (Flag)QUICK_TURN, false ); mQuickTurnTimeLeft = 0; } } //============================================================================= // FollowCam::GetTargetPosition //============================================================================= // Description: Comment // // Parameters: ( rmt::Vector* position, bool withOffset ) // // Return: void // //============================================================================= void FollowCam::GetTargetPosition( rmt::Vector* position, bool withOffset ) { mTargets[ mActiveTarget ]->GetPosition( position ); if ( withOffset ) { rmt::Vector offset; mData.GetTargetOffset( &offset ); if ( GetFlag( (Flag)LOOK_BACK ) ) { offset.x *= -1.0f; offset.z *= -1.0f; } //Add offset according to controller input. float lookUp = 0.0f; #ifdef TURN_LOOK float lookLeftRight = 0.0f; #if defined(RAD_GAMECUBE) || defined(RAD_XBOX) lookUp = mController->GetValue( SuperCamController::stickY ); lookLeftRight = mController->GetValue( SuperCamController::stickX ); #elif defined(RAD_WIN32) lookUp = mController->GetValue( SuperCamController::carLookUp ); float left = mController->GetValue( SuperCamController::carLookLeft ); float right = mController->GetValue( SuperCamController::carLookRight ); lookLeftRight = ( right > left ) ? right : -left; #else //This is PS2 lookUp = mController->GetValue( SuperCamController::lookToggle ); lookLeftRight = mController->GetValue( SuperCamController::r2 ) - mController->GetValue( SuperCamController::l2 ); #endif #else #if defined(RAD_GAMECUBE) || defined(RAD_XBOX) lookUp = mController->GetValue( SuperCamController::stickY ); #elif defined(RAD_WIN32) lookUp = mController->GetValue( SuperCamController::carLookUp ); #else //This is PS2 lookUp = mController->GetValue( SuperCamController::lookToggle ); #endif #endif #ifdef TURN_LOOK if ( GetCharacterSheetManager()->QueryInvertedCameraSetting() ) { //Invert this! lookLeftRight *= -1.0f; } offset.x += ( LOOK_OFFSET_DIST * lookLeftRight ); #endif if ( lookUp > 0.2f ) { offset.y += ( LOOK_OFFSET_DIST * 1.0f ); //Make it digital. } //Now put the offset in the target's space rmt::Matrix mat; rmt::Vector targetHeading, targetVUP; mTargets[ mActiveTarget ]->GetHeading( &targetHeading ); mTargets[ mActiveTarget ]->GetVUP( &targetVUP ); mat.Identity(); mat.FillHeading( targetHeading, targetVUP ); offset.Transform( mat ); (*position).Add( offset ); } } //============================================================================= // FollowCam::CalculateRod //============================================================================= // Description: Comment // // Parameters: ( rmt::Vector* rod, unsigned int milliseconds, float timeMod ) // // Return: void // //============================================================================= void FollowCam::CalculateRod( rmt::Vector* rod, unsigned int milliseconds, float timeMod ) { //Where is the camera in spherical coordinates? //This is the targets matrix and inverse matrix for rotation. rmt::Matrix mat; rmt::Vector targetHeading, targetVUP; mTargets[ mActiveTarget ]->GetHeading( &targetHeading ); mTargets[ mActiveTarget ]->GetVUP( &targetVUP ); //Never go upside down. targetVUP.y = rmt::Fabs( targetVUP.y ); mat.Identity(); mat.FillHeading( targetHeading, targetVUP ); rmt::Vector desiredRod; GetDesiredRod( &desiredRod ); desiredRod.Transform( mat ); //--------- Do the look back if( GetFlag( (Flag)LOOK_BACK ) ) { //reverse the rod. desiredRod.x = -desiredRod.x; desiredRod.z = -desiredRod.z; } //This puts us in spherical space, also note the z and y switch. float desiredRotXZ, desiredRotY, desiredMag; rmt::CartesianToSpherical( desiredRod.x, desiredRod.z, desiredRod.y, &desiredMag, &desiredRotXZ, &desiredRotY ); #ifdef EXTRA_ROT float invertMod = GetSuperCamManager()->GetSCC( GetPlayerID() )->IsInvertedCameraEnabled() ? -1.0f : 1.0f; #if defined(RAD_GAMECUBE) || defined(RAD_XBOX) float leftRight = -mController->GetAxisValue( SuperCamController::stickX ); #elif defined(RAD_WIN32) float left = mController->GetValue( SuperCamController::carLookLeft ); float right = mController->GetValue( SuperCamController::carLookRight ); float leftRight = ( right > left ) ? -right : left; #else //This is PS2 float leftRight = mController->GetValue( SuperCamController::l2 ) - mController->GetValue( SuperCamController::r2 ); #endif if ( GetFlag( (Flag)LOS_CORRECTED ) && IsPushingStick() ) { leftRight = 0.0f; } else { SetFlag( (Flag)LOS_CORRECTED, false ); } #if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32) if ( mController->IsWheel() ) { //This is a wheel. No left right on wheels. //Or we are stopping the user from continuing to force the stick into a wall. leftRight = 0.0f; } #endif if ( !GetFlag( (Flag)LOOK_BACK ) ) { #ifdef RAD_WIN32 // this retarded move is thanks to vs.net optimization. desiredRotXZ += ( invertMod * leftRight * rmt::PI_BY2 ); #else desiredRotXZ += ( invertMod * leftRight * LOOK_ROT ); #endif } #endif //--------- Setup the desired angle and the current angle. if ( !GetFlag((Flag)CUT) && (GetFlag( (Flag)FIRST_TIME ) || GetFlag( (Flag)STABILIZING ) || GetFlag((Flag)COLLIDING )) ) { //Let's try to interpolate from it's current position. rmt::Vector position; GetPosition( &position ); rmt::Vector target; if ( GetFlag( (Flag)FIRST_TIME ) ) { mTargets[ mActiveTarget]->GetPosition( &target ); } else { //Use the old position since we're stabilizing from badness. target = mOldTargetPos; } rmt::Vector targToPos; targToPos.Sub( position, target ); rmt::CartesianToSpherical( targToPos.x, targToPos.z, targToPos.y, &mMagnitude, &mRotationAngleXZ, &mRotationAngleY ); mMagnitudeDelta = 0.0f; mRotationAngleXZDelta = 0.0f; mRotationAngleYDelta = 0.0f; } //We only want to interpolate to a rotation via the fewest number of degrees. AdjustAngles( &desiredRotXZ, &mRotationAngleXZ, &mRotationAngleXZDelta ); AdjustAngles( &desiredRotY, &mRotationAngleY, &mRotationAngleYDelta ); rmt::Clamp( desiredRotY, rmt::PI + rmt::PI_BY2, rmt::PI_BY2 ); //--------- Interpolate to the desired position and target //This is the normal interpolation stage. float cameraLagXZ = mData.GetCameraLagXZ() * timeMod; float cameraLagY = mData.GetCameraLagY() * timeMod; float magnitudeLag = mData.GetMagnitudeLag() * timeMod; UpdateQuickTurn( milliseconds ); if ( GetFlag( (Flag)QUICK_TURN ) ) { //Change the speed of interpolation to the adjusted value. cameraLagXZ *= mData.GetQuickTurnModifier(); CLAMP_TO_ONE(cameraLagXZ); } if ( GetFlag( (Flag)CUT ) ) { cameraLagXZ = 1.0f; cameraLagY = 1.0f; magnitudeLag = 1.0f; } else { CLAMP_TO_ONE( cameraLagXZ ); CLAMP_TO_ONE( cameraLagY ); CLAMP_TO_ONE( magnitudeLag ); } MotionCubic( &mRotationAngleXZ, &mRotationAngleXZDelta, desiredRotXZ, cameraLagXZ ); MotionCubic( &mRotationAngleY, &mRotationAngleYDelta, desiredRotY, cameraLagY ); MotionCubic( &mMagnitude, &mMagnitudeDelta, desiredMag, magnitudeLag ); float x, y, z; rmt::SphericalToCartesian( mMagnitude, mRotationAngleXZ, mRotationAngleY, &x, &z, &y ); rod->Set( x, y, z ); } //============================================================================= // FollowCam::CalculateTarget //============================================================================= // Description: Comment // // Parameters: ( rmt::Vector* desiredTarget, // unsigned int milliseconds, // float timeMod ) // // Return: void // //============================================================================= void FollowCam::CalculateTarget( rmt::Vector* desiredTarget, unsigned int milliseconds, float timeMod ) { #ifdef CUT_LOOK if ( GetFlag( (Flag)LOOKING_SIDEWAYS ) ) { GetTargetPosition( &mTargetPosition, false ); mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f ); *desiredTarget = mTargetPosition; desiredTarget->Add( rmt::Vector( 0.0f, LOOK_OFFSET_HEIGHT, 0.0f ) ); return; } #endif if ( !GetFlag((Flag)CUT) && (GetFlag( (Flag)FIRST_TIME ) || (GetFlag( (Flag)STABILIZING ))) ) { GetTarget( &mTargetPosition ); mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f ); } GetTargetPosition( desiredTarget ); //Here's the target position and interpolation. float targetLagXZ = mData.GetTargetLagXZ() * timeMod; CLAMP_TO_ONE(targetLagXZ); float targetLagY = mData.GetTargetLagY() * timeMod; CLAMP_TO_ONE(targetLagY); MotionCubic( &mTargetPosition.x, &mTargetPositionDelta.x, desiredTarget->x, targetLagXZ ); MotionCubic( &mTargetPosition.y, &mTargetPositionDelta.y, desiredTarget->y, targetLagY ); MotionCubic( &mTargetPosition.z, &mTargetPositionDelta.z, desiredTarget->z, targetLagXZ ); //Set the target position *desiredTarget = mTargetPosition; } //============================================================================= // FollowCam::GetDesiredRod //============================================================================= // Description: Comment // // Parameters: ( rmt::Vector* rod) // // Return: bool - this says whether the rod is in world space or not. // //============================================================================= bool FollowCam::GetDesiredRod( rmt::Vector* rod) { #ifdef CUT_LOOK if ( !GetFlag( (Flag)LOOKING_SIDEWAYS ) ) { //No movement. mData.GetRod( rod ); return false; } else { float invertMod = GetSuperCamManager()->GetSCC( GetPlayerID() )->IsInvertedCameraEnabled() ? -1.0f : 1.0f; #if defined(RAD_GAMECUBE) || defined(RAD_XBOX) float leftRight = mController->GetValue( SuperCamController::stickX ); #elif defined(RAD_WIN32) float left = mController->GetValue( SuperCamController::carLookLeft ); float right = mController->GetValue( SuperCamController::carLookRight ); float leftRight = ( right > left ) ? right : -left; #else //This is PS2 float leftRight = mController->GetValue( SuperCamController::r2 ) - mController->GetValue( SuperCamController::l2 ); #endif float dir = rmt::Sign( leftRight ); //Look right so move left (I like it inverted here.) rod->Set( dir * invertMod * LOOK_OFFSET_DIST, LOOK_OFFSET_HEIGHT, LOOK_OFFSET_BACK ); return false; } #endif mData.GetRod( rod ); return false; } //============================================================================= // FollowCam::IsPushingStick //============================================================================= // Description: Comment // // Parameters: () // // Return: bool // //============================================================================= bool FollowCam::IsPushingStick() { #if defined(RAD_XBOX) || defined(RAD_GAMECUBE) float xAxis = mController->GetValue( SuperCamController::stickX ); #elif defined(RAD_WIN32) float left = mController->GetValue( SuperCamController::carLookLeft ); float right = mController->GetValue( SuperCamController::carLookRight ); float xAxis = ( right > left ) ? right : -left; #else float xAxis = mController->GetValue( SuperCamController::l2 ) - mController->GetValue( SuperCamController::r2 ); #endif return ( !rmt::Epsilon( xAxis, 0.0f, 0.001f ) && rmt::Sign( xAxis ) == rmt::Sign( mXAxis ) ); }