//============================================================================= // Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. // // File: RailCam.cpp // // Description: Implement RailCam // // History: 17/07/2002 + Created -- Cary Brisebois (Borrowed and adapted from J-L Duprat) // //============================================================================= //======================================== // System Includes //======================================== // Foundation Tech #include #include //======================================== // Project Includes //======================================== #ifndef WORLD_BUILDER #include #include #include #include #include #include #include //TODO: I only really need the tuner variables... Break this file up. #include #else #include "RailCam.h" #include "isupercamtarget.h" #include "supercamcontroller.h" #include "supercamconstants.h" #include "../../../tools/globalcode/utility/GLExt.h" #include class tPointCamera { public: void GetFOV( float* fov, float* aspect ) { *fov = 1.5707f; *aspect = 4.0f / 3.0f; }; }; namespace CharacterTune { static float sfMaxSpeed; static float sfDashBurstMax; }; #endif #ifdef DEBUGWATCH float RAIL_CAM_MIN_FOV = SUPERCAM_DEFAULT_MIN_FOV; #else const float RAIL_CAM_MIN_FOV = SUPERCAM_DEFAULT_MIN_FOV; #endif const float RAIL_CAM_FOV_LAG = SUPERCAM_DEFAULT_FOV_LAG; //****************************************************************************** // // Global Data, Local Data, Local Classes // //****************************************************************************** const float DEFAULT_MIN_RADIUS = 6.0f; const float DEFAULT_MAX_RADIUS = 12.0f; const char* const RailCam::BehaviourNames[] = { "Distance", "Projection" }; #ifdef DEBUGWATCH float MAX_STEP = 0.25f; #else const float MAX_STEP = 0.25f; #endif //****************************************************************************** // // Public Member Functions // //****************************************************************************** //============================================================================== // RailCam::RailCam //============================================================================== // Description: Constructor. // // Parameters: None. // // Return: N/A. // //============================================================================== RailCam::RailCam() : mTarget( NULL ), mBehaviour( PROJECTION ), mMinRadius( DEFAULT_MIN_RADIUS ), mMaxRadius( DEFAULT_MAX_RADIUS ), mTrackDist( 0.0f ), mStartU( -1.0f ), mU( 0.0f ), mStep( MAX_STEP ), mPositionLag( 0.04f ), mTargetLag( 0.04f ), mFOVDelta( 0.0f ), mMaxFOV( 0.0f ), mFOVLag( RAIL_CAM_FOV_LAG ), mTrackRail( false ), mReverseSensing( false ), mDrawRail( false ), mDrawHull( false ), mDrawCylinder( false ), mDrawIntersections( false ), mAllowUpdate( true ), mReset( false ), mResetting( false ) { CLASSTRACKER_CREATE( RailCam ); mQ.SetBasis( rmt::Spline::BSpline ); mQd.SetBasis( rmt::Spline::DBSpline ); mTargetOffset.Set( 0.0f, 0.0f, 0.0f ); mPosition.Set( 0.0f, 0.0f, 0.0f ); mPositionDelta.Set( 0.0f, 0.0f, 0.0f ); mTargetPosition.Set( 0.0f, 0.0f, 0.0f ); mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f ); } //============================================================================== // RailCam::~RailCam //============================================================================== // Description: Destructor. // // Parameters: None. // // Return: N/A. // //============================================================================== RailCam::~RailCam() { CLASSTRACKER_DESTROY( RailCam ); } //============================================================================= // RailCam::OnInit //============================================================================= // Description: Comment // // Parameters: () // // Return: void // //============================================================================= void RailCam::OnInit() { //Override the Near plane... SetNearPlane( mMinRadius ); InitMyController(); if ( !mReset ) { //This is to make the cameras move to a better place before interpolation. DenyUpdate(); DoFirstTime(); DoCameraCut(); Update( 16 ); AllowUpdate(); } else { mResetting = true; } } //============================================================================= // RailCam::OnShutdown //============================================================================= // Description: Comment // // Parameters: () // // Return: void // //============================================================================= void RailCam::OnShutdown() { //Reset me to the origin of the spline. mU = 0.0f; } //============================================================================= // RailCam::Update //============================================================================= // Description: Comment // // Parameters: ( unsigned int milliseconds ) // // Return: void // //============================================================================= void RailCam::Update( unsigned int milliseconds ) { float timeMod = milliseconds / 16.0f; rmt::Vector targetPos; GetTargetPosition( &targetPos, true ); // rmt::Vector camPos; // GetPosition( &camPos ); rmt::Vector desiredPos; if ( mResetting ) { mU = 0.0f; mResetting = false; } // compute camera position switch(mBehaviour) { case DISTANCE: default: desiredPos = FindCameraPosition_Distance(targetPos, mMaxRadius); break; case PROJECTION: desiredPos = FindCameraPosition_Projection(targetPos, mMaxRadius); break; } rmt::Vector desiredTarget; desiredTarget = FindCameraLookAt( targetPos, desiredPos ); bool cut, firstTime; cut = GetFlag( (Flag)CUT ); firstTime = GetFlag( (Flag)FIRST_TIME ); if ( cut || firstTime ) { //Reset the smoothing stuff. mPosition = desiredPos; mPositionDelta.Set( 0.0f, 0.0f, 0.0f ); mTargetPosition = desiredTarget; mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f ); //SetFOV( mMaxFOV ); mFOVDelta = 0.0f; if ( cut ) { if ( GetFlag( (Flag)START_TRANSITION ) ) { SetFlag( (Flag)START_TRANSITION, false ); } else if ( GetFlag( (Flag)TRANSITION ) ) { SetFlag( (Flag)END_TRANSITION, true ); } SetFlag( (Flag)CUT, false ); } else { //Not cutting, so let's try this. float fov, aspect = 0.0f; GetCamera()->GetFOV( &fov, &aspect ); if ( mAllowUpdate ) { SetFOV( fov ); } } SetFlag( (Flag)FIRST_TIME, false ); } else { //Let's smooth out the motion... float posLag = mPositionLag * timeMod; CLAMP_TO_ONE(posLag); MotionCubic( &mPosition.x, &mPositionDelta.x, desiredPos.x, posLag ); MotionCubic( &mPosition.y, &mPositionDelta.y, desiredPos.y, posLag ); MotionCubic( &mPosition.z, &mPositionDelta.z, desiredPos.z, posLag ); float targLag = mTargetLag * timeMod; CLAMP_TO_ONE(targLag); MotionCubic( &mTargetPosition.x, &mTargetPositionDelta.x, desiredTarget.x, targLag ); MotionCubic( &mTargetPosition.y, &mTargetPositionDelta.y, desiredTarget.y, targLag ); MotionCubic( &mTargetPosition.z, &mTargetPositionDelta.z, desiredTarget.z, targLag ); } //--------- Goofin' with the FOV if ( GetFlag( (Flag)TRANSITION ) || mTarget->IsCar() ) { if ( mAllowUpdate ) { SetFOV( mMaxFOV ); } mFOVDelta = 0.0f; //reset } else { #ifndef WORLD_BUILDER float zoom = mController->GetValue( SuperCamController::zToggle ); #else float zoom = 0.0f; #endif float FOV = GetFOV(); FilterFov( zoom, RAIL_CAM_MIN_FOV, mMaxFOV, FOV, mFOVDelta, mFOVLag, timeMod ); if ( mAllowUpdate ) { SetFOV( FOV ); } } //--------- Set values. if ( mAllowUpdate ) { SetCameraValues( milliseconds, mPosition, mTargetPosition ); } DrawRail( true ); DrawHull( true ); DrawCylinder( targetPos ); } //============================================================================= // RailCam::InitController //============================================================================= // Description: Comment // // Parameters: () // // Return: void // //============================================================================= void RailCam::InitController() { } //============================================================================= // RailCam::LoadSettings //============================================================================= // Description: Comment // // Parameters: ( unsigned char* settings ) // // Return: void // //============================================================================= void RailCam::LoadSettings( unsigned char* settings ) { } //****************************************************************************** // // Private Member Functions // //****************************************************************************** //============================================================================= // RailCam::OnRegisterDebugControls //============================================================================= // Description: Comment // // Parameters: () // // Return: void // //============================================================================= void RailCam::OnRegisterDebugControls() { #ifdef DEBUGWATCH char nameSpace[256]; sprintf( nameSpace, "SuperCam\\Player%d\\Rail", GetPlayerID() ); radDbgWatchAddUnsignedInt( ((unsigned int*)(&mBehaviour)), "Behaviour", nameSpace, NULL, NULL, RailCam::DISTANCE, RailCam::PROJECTION ); radDbgWatchAddFloat( &mMinRadius, "Min. Radius", nameSpace, NULL, NULL, 0.0f, 12.0f ); radDbgWatchAddFloat( &mMaxRadius, "Max. Radius", nameSpace, NULL, NULL, 0.0f, 12.0f ); radDbgWatchAddBoolean( &mTrackRail, "Track Rail", nameSpace, NULL, NULL ); radDbgWatchAddFloat( &mTrackDist, "Track Distance", nameSpace, NULL, NULL, -10.0f, 10.0f ); radDbgWatchAddBoolean( &mReverseSensing, "Reverse Sense", nameSpace, NULL, NULL ); radDbgWatchAddVector( &mTargetOffset.x, "Target Offset", nameSpace, NULL, NULL, -5.0f, 5.0f ); radDbgWatchAddVector( &mAxisPlay.x, "Axis Play", nameSpace, NULL, NULL, 0.0f, rmt::PI_BY2 ); radDbgWatchAddBoolean( &mDrawRail, "Draw Rails", nameSpace, NULL, NULL ); radDbgWatchAddBoolean( &mDrawHull, "Draw Hulls", nameSpace, NULL, NULL ); radDbgWatchAddBoolean( &mDrawCylinder, "Draw Cylinder", nameSpace, NULL, NULL ); radDbgWatchAddBoolean( &mDrawIntersections, "Draw Intersections", nameSpace, NULL, NULL ); radDbgWatchAddFloat( &MAX_STEP, "Max Step", nameSpace, NULL, NULL, 0.0f, 1.0f ); radDbgWatchAddFloat( &mPositionLag, "Position Lag Factor", nameSpace, NULL, NULL, 0.0f, 1.0f ); radDbgWatchAddFloat( &mTargetLag, "Target Lag Factor", nameSpace, NULL, NULL, 0.0f, 1.0f ); radDbgWatchAddFloat( &mFOVLag, "FOV Lag", nameSpace, NULL, NULL, 0.0f, 1.0f ); radDbgWatchAddFloat( &RAIL_CAM_MIN_FOV, "FOV Min", nameSpace, NULL, NULL, 0.0f, rmt::PI_BY2 ); #endif } //============================================================================= // RailCam::OnUnregisterDebugControls //============================================================================= // Description: Comment // // Parameters: () // // Return: void // //============================================================================= void RailCam::OnUnregisterDebugControls() { #ifdef DEBUGWATCH radDbgWatchDelete( &mBehaviour ); radDbgWatchDelete( &mMinRadius ); radDbgWatchDelete( &mMaxRadius ); radDbgWatchDelete( &mTrackRail ); radDbgWatchDelete( &mTrackDist ); radDbgWatchDelete( &mReverseSensing ); radDbgWatchDelete( &mTargetOffset.x ); radDbgWatchDelete( &mAxisPlay.x ); radDbgWatchDelete( &mDrawRail ); radDbgWatchDelete( &mDrawHull ); radDbgWatchDelete( &mDrawCylinder ); radDbgWatchDelete( &mDrawIntersections ); radDbgWatchDelete( &MAX_STEP ); radDbgWatchDelete( &mPositionLag ); radDbgWatchDelete( &mTargetLag ); radDbgWatchDelete( &RAIL_CAM_MIN_FOV ); radDbgWatchDelete( &mFOVLag ); #endif } //============================================================================= // RailCam::GetTargetSpeedModifier //============================================================================= // Description: Comment // // Parameters: () // // Return: float // //============================================================================= float RailCam::GetTargetSpeedModifier() { rmt::Vector vel; mTarget->GetVelocity( &vel ); float speed = vel.Magnitude(); //Square root! //TODO:I wish this was a const. if ( speed < CharacterTune::sfMaxSpeed || rmt::Epsilon( speed, CharacterTune::sfMaxSpeed, 0.01f ) ) { return 1.0f; } float maxMod = CharacterTune::sfMaxSpeed + CharacterTune::sfDashBurstMax / CharacterTune::sfMaxSpeed; float modifier = (speed - CharacterTune::sfMaxSpeed) / CharacterTune::sfDashBurstMax * maxMod; rAssert( modifier > 0.01f ); return modifier; } //============================================================================= // RailCam::GetTargetPosition //============================================================================= // Description: Comment // // Parameters: ( rmt::Vector* position, bool withOffset ) // // Return: void // //============================================================================= void RailCam::GetTargetPosition( rmt::Vector* position, bool withOffset ) { rAssert( mTarget ); mTarget->GetPosition( position ); } //============================================================================= // RailCam::IntervalClamp //============================================================================= // Description: Comment // // Parameters: ( float &t ) // // Return: SolutionType // //============================================================================= RailCam::SolutionType RailCam::IntervalClamp( float &t ) const { if(t>1.0f) { t=1.0f; return WORSTCASE; } if(t<0.0f) { t=0.0f; return WORSTCASE; } return APPROX; } //============================================================================= // RailCam::ProjectPointOnLine //============================================================================= // Description: Comment // // Parameters: (const rmt::Vector& A, const rmt::Vector& B, const rmt::Vector& O, float& t) // // Return: float // //============================================================================= float& RailCam::ProjectPointOnLine(const rmt::Vector& A, const rmt::Vector& B, const rmt::Vector& O, float& t) const { // Projects the point O on the line that goes through A and B // Caller needs to clamp the parameter t such that it always lies between A and B if( A == B ) t = 0.0f; // not really a segment... else t = -((B.x-A.x)*(A.x-O.x) + (B.y-A.y)*(A.y-O.y) + (B.z-A.z)*(A.z-O.z))/((B.x-A.x)*(B.x-A.x) + (B.y-A.y)*(B.y-A.y) + (B.z-A.z)*(B.z-A.z)); return t; } //============================================================================= // RailCam::IntersectLineCylinder //============================================================================= // Description: Comment // // Parameters: (const int segment, const rmt::Vector& origin, const float radius, const rmt::Vector& neighbour, float& t) // // Return: RailCam // //============================================================================= RailCam::SolutionType RailCam::IntersectLineCylinder(const int segment, const rmt::Vector& origin, const float radius, const rmt::Vector& neighbour, float& t) { // Intersects the line segment defined by the knots at segment and segment+1 with a // infinitely long cylinder centered on origin and of specified radius. If there are // multiple solutions (potentially an infinity), pick the one closest to neighbour. // Returns true if there is a solution and t is set to the parametric position of intersection // along the line segment... If there is no solution, return false and t is set to the closest // point to the cylinder along the line segment // To simplify the math, we consider that the spline segments are straight lines between the knots // we return the t at which the intersection occured on the line, but camera will be moved to that // parametric position along the curve. // vertical axis of cylinder is Y rmt::Vector A = mQ.Evaluate(float(segment)); rmt::Vector B = mQ.Evaluate(float(segment+1)); if(rmt::Epsilon(B.x, A.x) && rmt::Epsilon(B.z, A.z)) { // curve segment is parallel to cylinder (zero or infinity of intersections) // project origin on segment instead ProjectPointOnLine(A, B, origin, t); rmt::Vector L(A.x, origin.y, A.z); if(rmt::Epsilon(L.Magnitude(), radius)) return EXACT; // edge is along the cylinder else { return IntervalClamp(t); // edge is inside the cylinder } } // There are 0, 1 or 2 intersections along the segment // Replace the parametrized line equation into the equation for the cylinder // and solve for the parameter t. The algebra is ugly, but here goes: We are // solving a quadratic and use the expected names for variables... float a = (A.x-B.x)*(A.x-B.x)+(A.z-B.z)*(A.z-B.z); float b = 2.0f* (- A.x*A.x - A.z*A.z + A.x*B.x + A.z*B.z + A.x*origin.x - B.x*origin.x + A.z*origin.z - B.z*origin.z); float c = (A.x-origin.x)*(A.x-origin.x)+(A.z-origin.z)*(A.z-origin.z)-radius*radius; float b2m4ac = b*b-4.0f*a*c; if(b2m4ac>0.0f) { float t1 = (-b+rmt::Sqrt(b2m4ac))/(2.0f*a); float t2 = (-b-rmt::Sqrt(b2m4ac))/(2.0f*a); if((t1<0.0f || t1>1.0f) && (t2<0.0f || t2>1.0f)) { // both intersections are outside of the segment, project origin on segment return IntervalClamp(ProjectPointOnLine(A, B, origin, t)); } if(t1<0.0f || t1>1.0f) { // only t2 is on the segment t = t2; return EXACT; } if(t2<0.0f || t2>1.0f) { // only t1 is on the segment t = t1; return EXACT; } // we have two intersections on the segment, pick the one closest to neighbour rmt::Vector X1; X1.Interpolate(A, B, t1); rmt::Vector L; L.Sub(neighbour, X1); float l1 = L.MagnitudeSqr(); rmt::Vector X2; X2.Interpolate(A, B, t2); L.Sub(neighbour, X2); float l2 = L.MagnitudeSqr(); if(l1mQ.GetEndParam()/2.0f) mCandidates[index].pDist = rmt::Abs(mCandidates[index].pDist-mQ.GetEndParam()); // its shorter going the other way! } } rmt::Vector railPosition; // Decide whether to use the exact or approx solution to minimize camera travel if(mCandidates[EXACT].segment != -1 && mCandidates[EXACT].pDist>mStep) { // closest intersection point is quite far from current parametric position and would cause camera to jump. // if approximate solution is closer use that instead... if(mCandidates[APPROX].pDist<=mStep) { railPosition = FinalizeRailPosition(APPROX); } else { // Pick the closest (parameter space) point and move towards it. SolutionType index = (mCandidates[EXACT].pDist<=mCandidates[APPROX].pDist)?EXACT:APPROX; railPosition = FinalizeRailPosition(index); // we never considered WorstCase solutions } } else if(mCandidates[EXACT].segment != -1) { // use the proper intersection, its close enough railPosition = FinalizeRailPosition(EXACT); } else if(mCandidates[APPROX].segment != -1) { // use the closest approximation railPosition = FinalizeRailPosition(APPROX); } else railPosition = FinalizeRailPosition(WORSTCASE); return railPosition; } //============================================================================= // RailCam::FindCameraPosition_Projection //============================================================================= // Description: Comment // // Parameters: (const rmt::Vector& target, const float pOffset) // // Return: rmt // //============================================================================= rmt::Vector RailCam::FindCameraPosition_Projection(const rmt::Vector& target, const float pOffset) { // finds the position of camera along rail where the camera is at fixed parametric distance from // from projection of target on the rail. Each segment provides a candidate projection and the closest // one to previous camera position is used... if(mQ.GetNumSegments()==0) return rmt::Vector(0.0f, 0.0f, 0.0f); unsigned int i; for(i=0; i<3; i++) mCandidates[i].Reset(); if( mReverseSensing ) mU=mQ.GetEndParam()-mU; // we need to know where the camera really is for the evaluation rmt::Vector prevRailPos; if( GetFlag( (Flag)CUT ) || GetFlag( (Flag)FIRST_TIME ) ) { // when initializing camera minimize distance to actor prevRailPos = target; } else { // when not initializing camera we want to minize movement between frames prevRailPos = mQ.Evaluate(mU); // previous camera position on the rail } for(i=0; i mQ.GetEndParam()) { if(mQ.GetClosed()) while(curveT>mQ.GetEndParam()) curveT -= mQ.GetEndParam(); else curveT = 0.0f; } rmt::Vector p=mQ.Evaluate(curveT); //rmt::Vector delta(p.x-prevRailPos.x, p.y-prevRailPos.y, p.z-prevRailPos.z); rmt::Vector delta( p.x-target.x, p.y-target.y, p.z-target.z); float len = delta.MagnitudeSqr(); #ifdef DEBUGINFO_ENABLED if( mDrawIntersections ) { // mark the actual intersection of the cylinder and the spline curve tColour col; if(EXACT==index) col.Set(255,255,255); // this should not happen in corridors else if(APPROX==index) col.Set(255,255,0); else col.Set(255,0,0); if ( DEBUGINFO_PUSH_SECTION( "Rail Cam" ) ) { DEBUGINFO_ADDSTAR( mQ.Evaluate(i+segT), col, 0.5f ); } DEBUGINFO_POP_SECTION(); } #endif if(lenmQ.GetEndParam()/2.0f) mCandidates[index].pDist = rmt::Abs(mCandidates[index].pDist-mQ.GetEndParam()); // its shorter going the other way! } } // TODO: this could be bad because if the second intersection is a better choice than // the best approximation it will be ignored... rmt::Vector railPosition; // Decide whether to use the exact or approx solution to minimize camera travel if(mCandidates[EXACT].segment != -1 && mCandidates[EXACT].pDist>mStep) { // closest intersection point is quite far from current parametric position and would cause camera to jump. // if approximate solution is closer use that instead... if(mCandidates[APPROX].pDist<=mStep) { railPosition = FinalizeRailPosition(APPROX); } else { // Pick the closest (parameter space) point and move towards it. SolutionType index = (mCandidates[EXACT].pDist<=mCandidates[APPROX].pDist)?EXACT:APPROX; railPosition = FinalizeRailPosition(index); // we never considered WorstCase solutions } } else if(mCandidates[EXACT].segment != -1) { // use the proper intersection, its close enough railPosition = FinalizeRailPosition(EXACT); } else if(mCandidates[APPROX].segment != -1) { // use the closest approximation railPosition = FinalizeRailPosition(APPROX); } else railPosition = FinalizeRailPosition(WORSTCASE); return railPosition; } //============================================================================= // RailCam::FinalizeRailPosition //============================================================================= // Description: Comment // // Parameters: (SolutionType index) // // Return: rmt // //============================================================================= rmt::Vector RailCam::FinalizeRailPosition(SolutionType index) { rAssert(mCandidates[index].segment!=-1); if ( GetFlag( (Flag)CUT ) ) { // when we are cutting to a camera we can jump straight to the parametric position selected mU = mCandidates[index].u+mCandidates[index].segment; if(mReverseSensing) { mU=mQ.GetEndParam()-mU; // reverse camera if needed return mQ.Evaluate(mU); } else { return mCandidates[index].pu; } } //if(mCandidates[index].pDist>0.5f) //{ // if(mReverseSensing) // mU=mQ.GetEndParam()-mU; // reverse camera if needed // return mQ.Evaluate(mU); // target is too far along rail, just stay put. //} // TODO: take care of acceleration for start/stop on the rail if(mCandidates[index].pDist<=mStep) { // target is within step size, snap to it mU = mCandidates[index].u+mCandidates[index].segment; if(mReverseSensing) { mU=mQ.GetEndParam()-mU; // reverse camera if needed return mQ.Evaluate(mU); } else { return mCandidates[index].pu; } return mCandidates[index].pu; } // target is too far, walk towards it if(mCandidates[index].u+mCandidates[index].segment>mU) mU+=mStep; else mU-=mStep; if(mReverseSensing) mU=mQ.GetEndParam()-mU; // reverse camera if needed return mQ.Evaluate(mU); } //============================================================================= // RailCam::FindCameraLookAt //============================================================================= // Description: Comment // // Parameters: (const rmt::Vector& target, const rmt::Vector& desiredPos) // // Return: rmt // //============================================================================= rmt::Vector RailCam::FindCameraLookAt(const rmt::Vector& target, const rmt::Vector& desiredPos) { rmt::Vector lookAt(target); if(mTrackRail && rmt::Abs(mTrackDist)>0.001f) { float trackU = mU + mTrackDist; lookAt = TestEval( trackU ); } /* #ifdef DEBUGINFO_ENABLED if(mTrackRail && mDrawRail) { if ( DEBUGINFO_PUSH_SECTION( "Rail Cam" ) ) { DEBUGINFO_ADDLINE(mQ.Evaluate(trackU), lookAt, tColour(0,0,255)); DEBUGINFO_ADDSTAR(lookAt, tColour(0,0,255), 0.5f); } DEBUGINFO_POP_SECTION(); } #endif */ //Take the offset and apply it to the look at. //If the trackDist is 0, then do a little track dist anyway and use it to calculate the heading. if ( mTargetOffset.x != 0 || mTargetOffset.y != 0 || mTargetOffset.z != 0 ) { rmt::Vector offset; //Make a transformation that puts the offset into the rotation space of //the camera target. offset = lookAt - desiredPos; rmt::Vector vup = UpdateVUP( desiredPos, lookAt ); rmt::Matrix mat; mat.Identity(); mat.FillHeading( offset, vup ); //reuse offset. offset = mTargetOffset; offset.Transform( mat ); lookAt.Add( offset ); CorrectDist( desiredPos, lookAt ); } return lookAt; } //============================================================================= // RailCam::TestEval //============================================================================= // Description: Comment // // Parameters: ( float u ) // // Return: rmt // //============================================================================= rmt::Vector RailCam::TestEval( float u ) { rmt::Vector lookAt; if(u > mQ.GetEndParam()) { // past end of curve if(mQ.GetClosed()) { u -= mQ.GetEndParam(); // cycle on closed curve lookAt = mQ.Evaluate(u); } else { // Interpolate along curve tangent at endpoint float mult = rmt::Floor(u/mQ.GetEndParam()); u -= mult*mQ.GetEndParam(); // distance beyond end of curve // if curve has triple end-knots, derivative there is zero so move in... float evalAt=mQ.GetEndParam(); int index = mQ.GetNumVertices(); if(mQ.GetCntrlVertex(index-1) == mQ.GetCntrlVertex(index-2) && mQ.GetCntrlVertex(index-2) == mQ.GetCntrlVertex(index-3)) evalAt -= 1.0f; rmt::Vector p = mQ.Evaluate(mQ.GetEndParam()); // point at the end of the spline rmt::Vector t = mQd.Evaluate(evalAt); // tangent at the end of the spline t.Scale(u); lookAt.Add(p, t); } } else if(u < 0.0f) { // before begining of curve if(mQ.GetClosed()) { u += mQ.GetEndParam(); // cycle on closed curve lookAt = mQ.Evaluate(u); } else { // Interpolate along curve tangent at endpoint float mult = rmt::Floor(-u/mQ.GetEndParam()); u += mult*mQ.GetEndParam(); // distance beyond end of curve // if curve has triple end-knots, derivative there is zero so move in... float evalAt=0.0f; if(mQ.GetCntrlVertex(0) == mQ.GetCntrlVertex(1) && mQ.GetCntrlVertex(1) == mQ.GetCntrlVertex(2)) evalAt = 1.0f; rmt::Vector p = mQ.Evaluate(0.0f); // point at the end of the spline rmt::Vector t = mQd.Evaluate(evalAt); // tangent at the end of the spline t.Scale(u); lookAt.Add(p, t); } } else { // along the curve this is easy lookAt = mQ.Evaluate(u); } return lookAt; } //============================================================================= // RailCam::DrawRail //============================================================================= // Description: Comment // // Parameters: (bool active) // // Return: void // //============================================================================= void RailCam::DrawRail(bool active) { #ifdef DEBUGINFO_ENABLED if(!mDrawRail) return; // not set to draw if ( DEBUGINFO_PUSH_SECTION( "Rail Cam" ) ) { tColour cRail; if(active) cRail.Set(255,255,255); else cRail.Set(255,255,0); // spline path const unsigned int numSteps = 10; rmt::Vector p0, p1; p0=mQ.InitForwardDifferencing(numSteps); unsigned int j; for(j=0; jAddCircle(bottomC, m_minRadius, cCyln); DEBUGINFO_ADDCIRCLE(bottomC, mMaxRadius, cCyln); DEBUGINFO_ADDCIRCLE(bottomC, mMinRadius, cCyln); // top cap //g_pDebug->AddCircle(topC, m_minRadius, cCyln); DEBUGINFO_ADDCIRCLE(topC, mMaxRadius, cCyln); DEBUGINFO_ADDCIRCLE(topC, mMinRadius, cCyln); // sides int max = 8; int i; for(i=0; i