//**************************************************************************** //* MODULE: Gel/Components //* FILENAME: SkaterCameraComponent.cpp //* OWNER: Dave Cowling //* CREATION DATE: 02/21/03 //**************************************************************************** #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Obj { const float SKATERCAMERACOMPONENT_LEAN_TO_SKATERCAM_LEAN = (( Mth::PI / 6.0f ) / -4096.0f ); const float SKATERCAMERACOMPONENT_INTERPOLATOR_TIMESTEP = ( 1.0f / 60.0f ); // This is the value that mAbove should tend to when the behind value is closer than mBehind. const float VERT_AIR_LANDED_TIME = 10.0f / 60.0f; const float TILT_MAX = 0.34907f; // 20 degees. const float TILT_MIN = -0.34907f; // -20 degees. const float TILT_INCREMENT = 2.0f * ( TILT_MAX / 60.0f ); // 40 degrees/second. const float TILT_DECREMENT = 2.0f * ( TILT_MAX / 60.0f ); // 40 degrees/second. const float TILT_RESTORE = 8.0f * ( TILT_MAX / 60.0f ); // 160 degrees/second. /******************************************************************/ /* */ /* */ /******************************************************************/ static void _xrotlocal ( Mth::Matrix& m, float a ) { m.RotateXLocal( a ); } /******************************************************************/ /* */ /* */ /******************************************************************/ CBaseComponent* CSkaterCameraComponent::s_create() { return static_cast< CBaseComponent* >( new CSkaterCameraComponent ); } /******************************************************************/ /* */ /* */ /******************************************************************/ CSkaterCameraComponent::CSkaterCameraComponent() : CBaseComponent() { SetType( CRC_SKATERCAMERA ); mMode = SKATERCAM_MODE_UNDEFINED; // Create the SLERP interpolator here. mpSlerp = new Mth::SlerpInterpolator; // Set current zoom and LERP rates. mCurrentZoom = 1.0f; mZoomLerp = 0.0625f; mLastDot = 1.0f; mLerpXZ = 0.25f; mLerpY = 0.5f; mVertAirLerpXZ = 1.0f; mVertAirLerpY = 1.0f; mGrindLerp = 0.1f; mpSkater= NULL; mpSkaterStateComponent = NULL; mpSkaterPhysicsControlComponent = NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ CSkaterCameraComponent::~CSkaterCameraComponent() { delete mpSlerp; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::Finalize ( ) { mp_lookaround_component = GetCameraLookAroundComponentFromObject(GetObject()); Dbg_Assert(mp_lookaround_component); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::Enable( bool enable ) { CCameraComponent *p_cam_comp = GetCameraComponentFromObject( GetObject()); Dbg_MsgAssert( p_cam_comp, ( "SkaterCameraComponent requires CameraComponent attached to parent" )); p_cam_comp->Enable( enable ); } /******************************************************************/ /* */ /* */ /******************************************************************/ Gfx::Camera* CSkaterCameraComponent::GetCamera( void ) { CCameraComponent *p_cam_comp = GetCameraComponentFromObject( GetObject()); Dbg_MsgAssert( p_cam_comp, ( "SkaterCameraComponent requires CameraComponent attached to parent" )); return p_cam_comp->GetCamera(); } /******************************************************************/ /* */ /* */ /******************************************************************/ const Mth::Vector& CSkaterCameraComponent::GetPosition( void ) const { CCameraComponent *p_cam_comp = GetCameraComponentFromObject( GetObject()); Dbg_MsgAssert( p_cam_comp, ( "SkaterCameraComponent requires CameraComponent attached to parent" )); return p_cam_comp->GetPosition(); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::SetPosition( Mth::Vector& pos ) { CCameraComponent *p_cam_comp = GetCameraComponentFromObject( GetObject()); Dbg_MsgAssert( p_cam_comp, ( "SkaterCameraComponent requires CameraComponent attached to parent" )); p_cam_comp->SetPosition( pos ); } /******************************************************************/ /* */ /* */ /******************************************************************/ const Mth::Matrix& CSkaterCameraComponent::GetMatrix( void ) const { CCameraComponent *p_cam_comp = GetCameraComponentFromObject( GetObject()); Dbg_MsgAssert( p_cam_comp, ( "SkaterCameraComponent requires CameraComponent attached to parent" )); return p_cam_comp->GetMatrix(); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::InitFromStructure( Script::CStruct* pParams ) { // NOT We need a CCameraComponent attached in order to get camera details. // CCameraComponent *p_cam_comp = GetCameraComponentFromObject( GetObject()); // Dbg_MsgAssert( p_cam_comp, ( "CSkaterCameraComponent needs CCameraComponent attached to parent" )); // Clear the frame matrix, so that future slerps make sense. //Mth::Matrix& frame_matrix = p_cam_comp->GetMatrix(); Mth::Matrix& frame_matrix = GetObject()->GetMatrix(); frame_matrix.Ident(); mLastActualRight = frame_matrix[X]; mLastActualUp = frame_matrix[Y]; mLastActualAt = frame_matrix[Z]; // The camera requires a "CameraTarget" checksum parameter uint32 target_id = 0 ; pParams->GetChecksum("CameraTarget", &target_id, true); Dbg_MsgAssert(target_id >=0 && target_id <= 15,("Bad CameraTarget 0x%x in SkaterCameraComponent",target_id)); CSkater* p_skater = static_cast(CCompositeObjectManager::Instance()->GetObjectByID(target_id)); Dbg_MsgAssert(p_skater,("Can't find skater %d in SkaterCameraComponent",target_id)); SetSkater(p_skater); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::RefreshFromStructure( Script::CStruct* pParams ) { // Reinitialise. InitFromStructure( pParams ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::Commit( void ) { // Under the normal operation of components, the CSkaterCameraComponent updates the values // in CCameraComponent during its Update() function. The CCameraComponent in turn will set // the Gfx::CCamera values during it's Update() function. // In some cases however, we need the camera changes to be committed to the Gfx::Camera // immediately, usually when logic is paused, and the component Update()'s are not taking place. CCameraComponent *p_cam_component = GetCameraComponentFromObject( GetObject()); if( p_cam_component ) { p_cam_component->Update(); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::ReadyForActivation ( const SCameraState& state ) { Dbg_MsgAssert(mpSkater, ("Skater camera (%s) has NULL target", Script::FindChecksumName(GetObject()->GetID()))); mLastActualRight = state.lastActualMatrix[X]; mLastActualUp = state.lastActualMatrix[Y]; mLastActualAt = state.lastActualMatrix[Z]; mLastTripodPos = state.lastTripodPos; mLastDot = state.lastDot; mCurrentZoom = state.lastZoom; mVertAirLandedTimer = 0.0f; mInstantCount = 0; mp_lookaround_component->mLookaroundHeading = 0.0f; mp_lookaround_component->mLookaroundLock = false; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::Update( void ) { if( mpSkater == NULL ) { return; } // optimization KLUDGE if (mpSkaterPhysicsControlComponent && mpSkaterPhysicsControlComponent->IsDriving()) { GetObject()->Pause(true); return; } // We need a CCameraComponent attached in order to get camera details. CCameraComponent *p_cam_comp = GetCameraComponentFromObject( GetObject()); Dbg_MsgAssert( p_cam_comp, ( "CSkaterCameraComponent needs CCameraComponent attached to parent" )); // This used to be passed in as a param for the old CSkaterCam::Update(). // Now we get it from a flag in the skater bool instantly = false; Dbg_Assert( mpSkater ); if (mpSkater->HasTeleported()) { instantly = true; } if( instantly ) { mInstantCount = 3; } if( mInstantCount > 0 ) { --mInstantCount; instantly = true; } // if (instantly) // { // printf ("%d: Instantly!\n",(int)Tmr::GetRenderFrame()); // } // Length of last frame in seconds. float delta_time = Tmr::FrameLength(); // Update any values we are currently interpolating. UpdateInterpolators(); // Would have used a SetPos() function, but the camera update in rwviewer.cpp is spread out all over the place, // so it's not easy to do. We'll just store the old position before updating anything... // Gfx::Camera::StoreOldPos(); p_cam_comp->StoreOldPosition(); // Mth::Matrix& frame_matrix = Gfx::Camera::GetMatrix(); Mth::Matrix& frame_matrix = p_cam_comp->GetMatrix(); frame_matrix[X] = mLastActualRight; frame_matrix[Y] = mLastActualUp; frame_matrix[Z] = mLastActualAt; Mth::Vector current_at_vector( frame_matrix[Z][X], frame_matrix[Z][Y], frame_matrix[Z][Z] ); // Obtain skater state. EStateType state = mpSkaterStateComponent->GetState(); Mth::Matrix target; if( state == WALL ) { # ifdef DEBUG_CAMERA printf ("SkaterCam Using mLastPreWallSkaterMatrix, as wallriding\n"); # endif target = mLastPreWallSkaterMatrix; } else { # ifdef DEBUG_CAMERA printf ("SkaterCam Using GetCameraDisplayMatrix\n"); # endif // target = static_cast< CCompositeObject* >(mpSkater)->GetDisplayMatrix(); if (mpSkater->IsLocalClient()) { target = mpSkater->GetDisplayMatrix(); } else { target = static_cast< CCompositeObject* >(mpSkater)->GetDisplayMatrix(); } mLastPreWallSkaterMatrix = target; } // Lerp towards the required lean. float cam_lean = 0.0f; if( state == RAIL && mpSkater->IsLocalClient()) { // Need to translate the lean value [-4096, 4096] into a reasonable camera lean angle. cam_lean = GetSkaterBalanceTrickComponentFromObject(mpSkater)->mGrind.mManualLean * SKATERCAMERACOMPONENT_LEAN_TO_SKATERCAM_LEAN; } if( cam_lean != mLean ) { if( instantly ) { mLean = cam_lean; } else { mLean += (( cam_lean - mLean ) * mGrindLerp ); } } Mth::Vector skater_up; if (state != GROUND) { skater_up = target[Y]; } else { skater_up = mpSkaterStateComponent->GetCameraDisplayNormal(); } // Gfx::AddDebugLine( p_skater->m_pos, ( target[X] * 72.0f ) + p_skater->m_pos, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 4 ); // Gfx::AddDebugLine( p_skater->m_pos, ( target[Y] * 72.0f ) + p_skater->m_pos, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ), 4 ); // Gfx::AddDebugLine( p_skater->m_pos, ( target[Z] * 72.0f ) + p_skater->m_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 4 ); // Use vel for forward facing, if moving in xz plane. Mth::Vector vel_xz( mpSkater->GetVel().GetX(), 0.0f, mpSkater->GetVel().GetZ()); bool use_vert_cam = UseVertCam(); // use velocity if physics not paused, and if we are moving fast enough, or if we are doing vert cam // or if we are doing spine physics if( !(mpSkaterPhysicsControlComponent && mpSkaterPhysicsControlComponent->IsPhysicsSuspended()) && (vel_xz.Length() > 0.1f || use_vert_cam || mpSkaterStateComponent->GetFlag( SPINE_PHYSICS )) ) { if( use_vert_cam ) { // If it's vert, then set target direction to near straight down. target[Z].Set( 0.0f, -1.0f, 0.0f ); // Zero lookaround when in vert air (and not in lookaround locked or override mode, or doing a lip trick). if( !mp_lookaround_component->mLookaroundLock && !mp_lookaround_component->mLookaroundOverride && ( state != LIP )) { mp_lookaround_component->mLookaroundTilt = 0.0f; mp_lookaround_component->mLookaroundHeading = 0.0f; } } else { target[Z] = mpSkater->GetVel(); # if 1 // for use with non-straight spine transfer physics if( mpSkaterStateComponent->GetFlag( SPINE_PHYSICS )) { // Just use the up velocity, plus the velocity over the spine will be straight down when coming down the other side. target[Z][X] = 0.0f; target[Z][Z] = 0.0f; target[Z] += mpSkaterStateComponent->GetSpineVel(); } # endif target[Z].Normalize(); // printf ("Using Velocity (%f, %f, %f)\n", target[Z][X],target[Z][Y], target[Z][Z]); // Gfx::AddDebugLine( mpSkater->m_pos, ( target[Z] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 128, 128, 128 ), MAKE_RGB( 128, 128, 128 ), 4 ); // Flatten out the velocity component perpendicular to the ground. However, if the skater is in the air, the // m_last_normal will contain the last ground normal, which we don't want, so set it to be 'up'. float dot; Mth::Vector normal; if( state == AIR ) { // Patch for spine physics // only reduce component perpendicular to plane by 60% // so we still get some up/down camera movmeent // only do this when moving down though, otherwise it looks poo if (mpSkaterStateComponent->GetFlag(SPINE_PHYSICS) && target[Z][Y] < 0.0f) { normal.Set( 0.0f, 1.0f, 0.0f ); dot = 0.7f * Mth::DotProduct(target[Z],normal); // change this to 0.8 to look down when coming down other side of a spine } else { normal.Set( 0.0f, 1.0f, 0.0f ); dot = target[Z].GetY(); } // Set world up as the target vector. target[Y].Set( 0.0f, 1.0f, 0.0f ); } else if( state == WALL ) { normal.Set( 0.0f, 1.0f, 0.0f ); dot = target[Z].GetY(); } else { normal = mpSkaterStateComponent->GetCameraCurrentNormal(); dot = 0.8f * Mth::DotProduct(target[Z],normal); } target[Z] -= normal * dot; target[Z].Normalize(); // Gfx::AddDebugLine( mpSkater->m_pos, ( target[Z] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 256, 256, 256 ), MAKE_RGB( 256, 256, 256 ), 4 ); } // We need to orthonormalize in a specific order, as when the velocity is going backwards, then // the X orient will be wrong, and so plonk the skater upside down. target[X] = Mth::CrossProduct( target[Y], target[Z] ); target[X].Normalize(); target[Y] = Mth::CrossProduct( target[Z], target[X] ); target[Y].Normalize(); // Gfx::AddDebugLine( mpSkater->m_pos, ( target[X] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 4 ); // Gfx::AddDebugLine( mpSkater->m_pos, ( target[Y] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ), 4 ); // Gfx::AddDebugLine( mpSkater->m_pos, ( target[Z] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 4 ); } if (mpSkater->IsLocalClient() && mpSkaterStateComponent->GetFlag(IN_ACID_DROP)) { CSkaterCorePhysicsComponent* pSkaterPhysicsComponent = GetSkaterCorePhysicsComponentFromObject(mpSkater); Dbg_Assert(pSkaterPhysicsComponent); target = pSkaterPhysicsComponent->m_acid_drop_camera_matrix; } // Tilt further if in regular air (going off a big jump, for example), or doing a lip trick. if( state == AIR ) { mTiltAddition += TILT_INCREMENT * Tmr::FrameRatio(); if( mTiltAddition > TILT_MAX ) { mTiltAddition = TILT_MAX; } } else if( mTiltAddition > 0.0f ) { mTiltAddition -= TILT_RESTORE * Tmr::FrameRatio(); if( mTiltAddition < 0.0f ) { mTiltAddition = 0.0f; } } else if( mTiltAddition < 0.0f ) { mTiltAddition += TILT_RESTORE * Tmr::FrameRatio(); if( mTiltAddition > 0.0f ) { mTiltAddition = 0.0f; } } // PJR: Needed to do this to fix a compiler crash on NGC. Hopefully SN will // fix this & I can revert this hack... target.RotateYLocal( mp_lookaround_component->mLookaroundHeading + mp_lookaround_component->mLookaroundHeadingStartingPoint ); // Now tilt the matrix down a bit. if( state == LIP ) { // target.RotateYLocal( 0.8f ); // target.RotateXLocal( mLipTrickTilt + mTiltAddition ); _xrotlocal ( target, mLipTrickTilt + mTiltAddition ); } else if( !(mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT ) && !mpSkaterStateComponent->GetFlag(SPINE_PHYSICS) ) /*|| !mpSkaterStateComponent->GetFlag(IN_ACID_DROP)*/) { // target.RotateXLocal( mTilt + mTiltAddition ); _xrotlocal ( target, mTilt + mTiltAddition ); } // Now adjust for 'lookaround'. // target.RotateXLocal( mLookaroundTilt ); _xrotlocal( target, mp_lookaround_component->mLookaroundTilt ); static float lip_rotate = 0.0f; if( state == LIP ) { lip_rotate = Mth::PI / 2.0f; if( mLipSideDecided == 0 ) { target.RotateY( lip_rotate ); // Do collision check. CFeeler feeler; Mth::Vector horiz_z( target[Z][X], 0.0f, target[Z][Z] ); horiz_z.Normalize(); Mth::Vector a = mpSkater->m_pos - ( target[Z] * 3.0f ); Mth::Vector b = a - ( horiz_z * 72.0f ); feeler.SetIgnore(IGNORE_FACE_FLAGS_1, IGNORE_FACE_FLAGS_0); bool collision = feeler.GetCollision(a, b, true); if( !collision ) { // Side 1 is fine. mLipSideDecided = 1; } else { float dist = feeler.GetDist(); target.RotateY( -lip_rotate * 2.0f ); horiz_z.Set( target[Z][X], 0.0f, target[Z][Z] ); horiz_z.Normalize(); a = mpSkater->m_pos - ( target[Z] * 3.0f ); b = a - ( horiz_z * 72.0f ); collision = feeler.GetCollision(a, b, true); if( !collision ) { // Side 2 is fine. mLipSideDecided = 2; } else { if( feeler.GetDist() < dist ) { // Side 2 is better than side 1. mLipSideDecided = 2; } else { // Side 1 is better than side 2. target.RotateY( lip_rotate * 2.0f ); mLipSideDecided = 1; } } } } else { if( mLipSideDecided == 1 ) { target.RotateY( lip_rotate ); } else { target.RotateY( -lip_rotate ); } } } else { mLipSideDecided = 0; } // Set skater flag to indicate when the liptrick camera has spun to the opposite side. if( mpSkater && ( mLipSideDecided == 2 )) { mpSkater->mScriptFlags |= ( 1 << Script::GetInteger( 0x16b8e4cb /* "FLAG_SKATER_LIPTRICK_CAM_REVERSED" */ )); } else { mpSkater->mScriptFlags &= ~( 1 << Script::GetInteger( 0x16b8e4cb /* "FLAG_SKATER_LIPTRICK_CAM_REVERSED" */ )); } // Gfx::AddDebugLine( mpSkater->m_pos, ( target[X] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 4 ); // Gfx::AddDebugLine( mpSkater->m_pos, ( target[Y] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ), 4 ); // Gfx::AddDebugLine( mpSkater->m_pos, ( target[Z] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 4 ); // Why doing this, when setting below? Mth::Matrix t_matrix(0.0f, 0.0f, 0.0f); // Since camera points in -Z, but player in +Z, we must negate the X and Z axes t_matrix[X][X] = -target[0][0]; t_matrix[X][Y] = -target[0][1]; t_matrix[X][Z] = -target[0][2]; t_matrix[X][W] = 0.0f; t_matrix[Y][X] = target[1][0]; t_matrix[Y][Y] = target[1][1]; t_matrix[Y][Z] = target[1][2]; t_matrix[Y][W] = 0.0f; t_matrix[Z][X] = -target[2][0]; t_matrix[Z][Y] = -target[2][1]; t_matrix[Z][Z] = -target[2][2]; t_matrix[Z][W] = 0.0f; t_matrix[W][X] = 0.0f; t_matrix[W][Y] = 0.0f; t_matrix[W][Z] = 0.0f; t_matrix[W][W] = 1.0f; frame_matrix[W][X] = 0.0f; frame_matrix[W][Y] = 0.0f; frame_matrix[W][Z] = 0.0f; frame_matrix[W][W] = 1.0f; // if we want an instant update, then just move it if (instantly) { frame_matrix = t_matrix; } float dotx = Mth::DotProduct( t_matrix[X], frame_matrix[X] ); float doty = Mth::DotProduct( t_matrix[Y], frame_matrix[Y] ); float dotz = Mth::DotProduct( t_matrix[Z], frame_matrix[Z] ); if( dotx > CAMERA_SLERP_STOP && doty > CAMERA_SLERP_STOP && dotz > CAMERA_SLERP_STOP ) { // Do nothing, camera has reached the target so we just leave the frame matrix exactly where it is. // frame_matrix = t_matrix; } else { // Initialise the SLERP interpolator, with the current matrix as the start, and the ideal // matrix as the end (target). // GARY: is this right? i assume p_matrix = &t_matrix mpSlerp->setMatrices( &frame_matrix, &t_matrix ); // Copy flags and shit (why copying all this when most of the values will be overwritten?) Mth::Matrix stored_frame; stored_frame = frame_matrix; frame_matrix = t_matrix; // Continue to slerp towards the target, updating frame_matrix with the slerped values. if( !(mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT ) && !mpSkaterStateComponent->GetFlag(SPINE_PHYSICS))) { // If the skater has just landed from big air, use the big air land slerp value. if(( mpSkaterStateComponent->GetState() == GROUND ) && ( mVertAirLandedTimer >= delta_time )) { mVertAirLandedTimer -= delta_time; mpSlerp->getMatrix( &frame_matrix, GetTimeAdjustedSlerp( mVertAirLandedSlerp, delta_time )); } else { // Clear the vert air landed timer. mVertAirLandedTimer = 0.0f; mpSlerp->getMatrix( &frame_matrix, GetTimeAdjustedSlerp( mSlerp, delta_time )); // At this point we can check to see the angle that the camera's 'at' vector moved through this frame. // If this angle is too large, we need to limit it, regardless of the slerp. (This is primarily to avoid // the nasty jerk when transitioning rails etc. float this_dot = ( current_at_vector.GetX() * frame_matrix[Mth::AT][X] ) + ( current_at_vector.GetY() * frame_matrix[Mth::AT][Y] ) + ( current_at_vector.GetZ() * frame_matrix[Mth::AT][Z] ); // Dot values will be in the range [0,1], with 1 being no change. if( this_dot < ( mLastDot * 0.9998f )) { // The angular change this frame is too big. Need to recalculate with an adjusted slerp value. float new_slerp = mSlerp * ( this_dot / ( mLastDot * 0.9998f )); mpSlerp->setMatrices( &stored_frame, &t_matrix ); mpSlerp->getMatrix( &frame_matrix, GetTimeAdjustedSlerp( new_slerp, delta_time )); this_dot = mLastDot * 0.9998f; } mLastDot = this_dot; } } else { mpSlerp->getMatrix( &frame_matrix, GetTimeAdjustedSlerp( mVertAirSlerp, delta_time )); // Set the vert air landed timer to the max, since the skater is in vert air. mVertAirLandedTimer = VERT_AIR_LANDED_TIME; } } // Mick: This is a patch to allow the walk camera to override the updating of the camera frame // At this point, frame_matrix is valid to store. mLastActualRight = frame_matrix[X]; mLastActualUp = frame_matrix[Y]; mLastActualAt = frame_matrix[Z]; // Set camera position to be the same as the skater. Mth::Vector cam_pos = GetTripodPos( instantly ); // If in the air doing a trick, we slowly reduce the behind value to 'zoom' in on the skater. float above, behind; if( instantly ) { float temp = mZoomLerp; mZoomLerp = 1.0f; // fully lerp instantly CalculateZoom( &above, &behind ); mZoomLerp = temp; } else { CalculateZoom( &above, &behind ); } // Note we use the camera's at vector, as this will keep the skater in the middle of the frame. //cam_pos -= frame_matrix[Z] * behind; cam_pos += frame_matrix[Z] * behind; // Move camera along the Skater's up vector. cam_pos += skater_up * above; Mth::Vector actual_focus_pos = mpSkater->m_pos + ( skater_up * above ); // Gfx::AddDebugLine( mpSkater->m_pos, actual_focus_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 4 ); // Dbg_Message("Matrix Z (%5.4f,%5.4f,%5.4f)", frame_matrix[Z][X], frame_matrix[Z][Y], frame_matrix[Z][Z]); // Dbg_Message("Matrix UP (%5.4f,%5.4f,%5.4f)", skater_up[X], skater_up[Y], skater_up[Z]); // Dbg_Message("Above %f Behind %f", above, behind); // Dbg_Message("focus pos (%5.1f,%5.1f,%5.1f)", actual_focus_pos[X], actual_focus_pos[Y], actual_focus_pos[Z]); // Dbg_Message("Camera after: pos (%5.1f,%5.1f,%5.1f)", cam_pos[X], cam_pos[Y], cam_pos[Z]); // Reorient the camera to look directly at the skater. We don't want to look at the interpolated position, // since this can lead to nasty jerkiness, especially on rails etc. target[Z].Set( actual_focus_pos.GetX() - cam_pos.GetX(), actual_focus_pos.GetY() - cam_pos.GetY(), actual_focus_pos.GetZ() - cam_pos.GetZ()); target[Z].Normalize(); // Read back the Y from the current matrix. target[Y][0] = frame_matrix[Y][X]; target[Y][1] = frame_matrix[Y][Y]; target[Y][2] = frame_matrix[Y][Z]; // Generate new orthonormal X and Y axes. target[X] = Mth::CrossProduct( target[Y], target[Z] ); target[X].Normalize(); target[Y] = Mth::CrossProduct( target[Z], target[X] ); target[Y].Normalize(); // Here is where lean may safely be applied without moving the focus position on screen. if( mLean != 0.0f ) { target[Y].Rotate( target[Z], mLean ); target[X].Rotate( target[Z], mLean ); } // Write back into camera matrix. // Since camera points in -Z, but player in +Z, we must negate the X and Z axes frame_matrix[X][X] = -target[0][0]; frame_matrix[X][Y] = -target[0][1]; frame_matrix[X][Z] = -target[0][2]; frame_matrix[X][W] = 0.0f; frame_matrix[Y][X] = target[1][0]; frame_matrix[Y][Y] = target[1][1]; frame_matrix[Y][Z] = target[1][2]; frame_matrix[Y][W] = 0.0f; frame_matrix[Z][X] = -target[2][0]; frame_matrix[Z][Y] = -target[2][1]; frame_matrix[Z][Z] = -target[2][2]; frame_matrix[Z][W] = 0.0f; // Update the position if there is a shake active. if( mShakeDuration > 0.0f ) { addShake( cam_pos, frame_matrix ); } // Now do collision detection. bool no_camera_collision; if (mpSkater->IsLocalClient()) { CSkaterCorePhysicsComponent* pSkaterPhysicsComponent = GetSkaterCorePhysicsComponentFromObject(mpSkater); Dbg_Assert(pSkaterPhysicsComponent); no_camera_collision = pSkaterPhysicsComponent->mp_movable_contact_component->GetContact() && state == LIP; } else { no_camera_collision = false; } if (no_camera_collision) { // no camera collision if doing a lip trick on a moving object //printf ("Lip trick on moving object\n"); } else { ApplyCameraCollisionDetection(cam_pos, frame_matrix, cam_pos - ((Mth::Vector)( frame_matrix[Z] )) * behind, actual_focus_pos); } cam_pos[W] = 1.0f; // Gfx::Camera::SetPos( cam_pos ); p_cam_comp->SetPosition( cam_pos ); // reset old position if in instant update if (instantly) { p_cam_comp->StoreOldPosition(); } // Dbg_Message("Camera Final: pos (%5.1f,%5.1f,%5.1f)", cam_pos[X], cam_pos[Y], cam_pos[Z]); // Store the position of the camera in CSkaterCam also (used for proximity nodes). // Dave - there is no m_pos anymore... // m_pos = cam_pos; // Dave - this used to return the value below when it was CSkaterCam::Update() // Mth::Vector to_target = at_pos - cam_pos; // return to_target.Length(); } /******************************************************************/ /* */ /* */ /******************************************************************/ CBaseComponent::EMemberFunctionResult CSkaterCameraComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript ) { switch ( Checksum ) { // @script | SC_ShakeCamera | shake the skater camera // @parm float | duration | duration of the shake (0 clears the shake) // @parm float | vert_amp | maximum vertical amplidtude of the shake // @parm float | horiz_amp | maximum horizontal amplidtude of the shake // @parm float | vert_vel | vertical velocity factor (how many full cycles of movement in 1 second) // @parm float | horiz_vel | horizontal velocity factor (how many full cycles of movement in 1 second) case CRCC(0x75a516d3,"SC_ShakeCamera"): { float duration = 0.0f; float vert_amp = 0.0f; float horiz_amp = 0.0f; float vert_vel = 0.0f; float horiz_vel = 0.0f; pParams->GetFloat( "duration", &duration ); pParams->GetFloat( "vert_amp", &vert_amp ); pParams->GetFloat( "horiz_amp", &horiz_amp ); pParams->GetFloat( "vert_vel", &vert_vel ); pParams->GetFloat( "horiz_vel", &horiz_vel ); SetShake( duration, vert_amp, horiz_amp, vert_vel, horiz_vel ); break; } // @script | SC_SetMode | Set the mode of a skater camera // @parm integer | mode | mode number 1-4, start out at 2 (normal_medium) case CRCC(0xd7867ffe,"SC_SetMode"): { int mode = 0; pParams->GetInteger(CRCD(0x6835b854,"mode"),&mode, true); SetMode((ESkaterCamMode)mode); break; } default: return CBaseComponent::MF_NOT_EXECUTED; } // the "default" case of the switch statement handles // unrecognized functions; if we make it down here, // that means that the component already handled it // somehow return CBaseComponent::MF_TRUE; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::GetDebugInfo(Script::CStruct *p_info) { #ifdef __DEBUG_CODE__ Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterCameraComponent::GetDebugInfo")); // Add any script components to the p_info structure, // and they will be displayed in the script debugger (qdebug.exe) // you will need to add the names to debugger_names.q, if they are not existing checksums /* Example: p_info->AddInteger("m_never_suspend",m_never_suspend); p_info->AddFloat("m_suspend_distance",m_suspend_distance); */ // We call the base component's GetDebugInfo, so we can add info from the common base component. CBaseComponent::GetDebugInfo( p_info ); #endif } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::ResetLookAround( void ) { mp_lookaround_component->mLookaroundTilt = 0.0f; mp_lookaround_component->mLookaroundHeading = 0.0f; SetNoLerpUpdate( true ); Update(); Commit(); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::ResetMode( ) { // Just call SetMode() again with the current mode SetMode(mMode); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::SetMode( ESkaterCamMode mode, float time ) { // We need a CCameraComponent attached in order to set camera details. CCameraComponent *p_cam_comp = GetCameraComponentFromObject(GetObject()); Dbg_MsgAssert( p_cam_comp, ( "CSkaterCameraComponent needs CCameraComponent attached to parent" )); Dbg_MsgAssert( mode < SKATERCAM_NUM_MODES,( "Bad mode" )); // Obtain details for this mode. float h_fov, behind, above, tilt, origin_offset, zoom_lerp; const char* p_name; // Obtain a pointer to the array entry for the desired mode. The array is now dependent // on which game mode is currently being played, 1 player, 2 player vert, or 2 player horiz. Nx::ScreenMode screen_mode = Nx::CViewportManager::sGetScreenMode(); Script::CArray* p_array; switch( screen_mode ) { case Nx::vSPLIT_V: { p_array = Script::GetArray( "Skater_Camera_2P_Vert_Array" ); break; } case Nx::vSPLIT_H: { p_array = Script::GetArray( "Skater_Camera_2P_Horiz_Array" ); break; } default: { p_array = Script::GetArray( "Skater_Camera_Array" ); break; } } // Extra check here, since the array in physics.q may change. Dbg_MsgAssert( mode < (int)p_array->GetSize(), ( "Bad mode" )); uint32 cs = p_array->GetNameChecksum( mode ); Script::CScriptStructure* p_struct = Script::GetStructure( cs ); mMode = mode; mLipTrickTilt = 0.0f; // Read the values from the array. p_struct->GetFloat( CRCD(0x94df1603,"horiz_fov"), &h_fov ); p_struct->GetFloat( CRCD(0x52e6c41b,"behind"), &behind ); p_struct->GetFloat( CRCD(0xb96ae2d,"above"), &above ); p_struct->GetFloat( CRCD(0xe3c07609,"tilt"), &tilt ); p_struct->GetFloat( CRCD(0x12f6992,"origin_offset"), &origin_offset ); p_struct->GetFloat( CRCD(0x7f0032ac,"lip_trick_tilt"), &mLipTrickTilt ); p_struct->GetFloat( CRCD(0xadb6390e,"lip_trick_above"), &mLipTrickAbove ); // Get SLERP values. p_struct->GetFloat( CRCD(0xf54fb9c5,"slerp"), &mSlerp ); p_struct->GetFloat( CRCD(0x2fa11029,"vert_air_slerp"), &mVertAirSlerp ); p_struct->GetFloat( CRCD(0x42fd4a0,"vert_air_landed_slerp"), &mVertAirLandedSlerp ); // Get LERP values. p_struct->GetFloat( CRCD(0xae47899b,"lerp_xz"), &mLerpXZ ); p_struct->GetFloat( CRCD(0xe1e4e104,"lerp_y"), &mLerpY ); p_struct->GetFloat( CRCD(0xf386f4d9,"vert_air_lerp_xz"), &mVertAirLerpXZ ); p_struct->GetFloat( CRCD(0x4882a1fe,"vert_air_lerp_y"), &mVertAirLerpY ); p_struct->GetFloat( CRCD(0xbef0d195,"grind_lerp"), &mGrindLerp ); // Get zoom values. mGrindZoom = 1.0f; mLipTrickZoom = 1.0f; mBigAirTrickZoom = 1.0f; p_struct->GetFloat( CRCD(0x748743a7,"zoom_lerp"), &zoom_lerp ); p_struct->GetFloat( CRCD(0x5a7f5cc5,"grind_zoom"), &mGrindZoom ); p_struct->GetFloat( CRCD(0xd790b83d,"big_air_trick_zoom"), &mBigAirTrickZoom ); p_struct->GetFloat( CRCD(0xd414c22e,"lip_trick_zoom"), &mLipTrickZoom ); mZoomLerp = zoom_lerp; // Get camera style name. p_struct->GetText( CRCD(0xa1dc81f9,"name"), &p_name ); // Send the new camera mode to console line 1. //HUD::PanelMgr* panelMgr = HUD::PanelMgr::Instance(); //panelMgr->SendConsoleMessage( p_name, 1 ); // Need to calculate interpolate timers here. mHorizFOV = h_fov; mTilt = tilt; mOriginOffset = FEET( origin_offset ); // Convert to inches. // Set up interpolators for those values that require it. if( time > 0.0f ) { behind = FEET( behind ); // Convert to inches. above = FEET( above ); // Convert to inches. mBehindInterpolatorTime = time; mBehindInterpolatorDelta = (( behind - mBehind ) * ( SKATERCAMERACOMPONENT_INTERPOLATOR_TIMESTEP / time )); mAboveInterpolatorTime = time; mAboveInterpolatorDelta = (( above - mAbove ) * ( SKATERCAMERACOMPONENT_INTERPOLATOR_TIMESTEP / time )); } else { mBehind = FEET( behind ); mAbove = FEET( above ); mBehindInterpolatorTime = 0.0f; mAboveInterpolatorTime = 0.0f; } // Set focal length immediately. switch( screen_mode ) { case Nx::vSPLIT_V: { # ifdef __PLAT_NGC__ p_cam_comp->SetHFOV( Script::GetFloat( "Skater_Cam_Horiz_FOV" ) / 2.0f ); # else p_cam_comp->SetHFOV( mHorizFOV / 2.0f ); # endif break; } case Nx::vSPLIT_H: { p_cam_comp->SetHFOV( mHorizFOV ); break; } default: { p_cam_comp->SetHFOV( mHorizFOV ); break; } } switch( mode ) { case ( SKATERCAM_MODE_REPLAY_FRONT ): case ( SKATERCAM_MODE_REPLAY_FRONT_ZOOM ): mp_lookaround_component->mLookaroundHeadingStartingPoint = Mth::PI; break; case ( SKATERCAM_MODE_REPLAY_LEFT ): case ( SKATERCAM_MODE_REPLAY_LEFT_ZOOM ): mp_lookaround_component->mLookaroundHeadingStartingPoint = Mth::PI / 2.0f; break; case ( SKATERCAM_MODE_REPLAY_BEHIND ): case ( SKATERCAM_MODE_REPLAY_BEHIND_ZOOM ): mp_lookaround_component->mLookaroundHeadingStartingPoint = 0; break; case ( SKATERCAM_MODE_REPLAY_RIGHT ): case ( SKATERCAM_MODE_REPLAY_RIGHT_ZOOM ): mp_lookaround_component->mLookaroundHeadingStartingPoint = -( Mth::PI / 2.0f ); break; default: mp_lookaround_component->mLookaroundHeadingStartingPoint = 0; break; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::ToggleMode() { if ( mMode >= SKATERCAM_NUM_NORMAL_MODES ) { return; } mMode = (ESkaterCamMode)( mMode + 1); if( mMode >= SKATERCAM_NUM_NORMAL_MODES ) { mMode = SKATERCAM_MODE_FIRST_VALID; } ResetMode(); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::SetShake( float duration, float vert_amp, float horiz_amp, float vert_vel, float horiz_vel ) { mShakeDuration = duration; mShakeInitialDuration = duration; mShakeMaxVertAmp = vert_amp; mShakeMaxHorizAmp = horiz_amp; mShakeVertVel = vert_vel; mShakeHorizVel = horiz_vel; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CSkaterCameraComponent::UseVertCam( void ) { if( mpSkater == NULL ) { return false; } // this logic is used only by cameras observing non-local clients; in all other cases, skating camera logic in suspended when viewing a walking // skater; when observing, skater camera logic is always used; this prevents the "birds-eye-view" issue when observing a skater who transitioned to // walking during vert air if( mpSkaterStateComponent->GetPhysicsState() == WALKING ) { return false; } // Use vert cam when in the lip state. if( mpSkaterStateComponent->GetState() == LIP ) { # ifdef DEBUG_CAMERA printf ("SkaterCam Using Vert Cam as LIP\n"); # endif return true; } if( mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT ) && !mpSkaterStateComponent->GetFlag(SPINE_PHYSICS)) { # ifdef DEBUG_CAMERA printf ("SkaterCam Using Vert Cam as VERT_AIR\n"); # endif return true; } if( mpSkaterStateComponent->GetJumpedOutOfLipTrick()) { # ifdef DEBUG_CAMERA printf ("SkaterCam Using Vert Cam as Jumped out of lip trick\n"); # endif return true; } # ifdef DEBUG_CAMERA printf ("Skatecam NOT using vert cam\n"); # endif return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::addShake( Mth::Vector& cam, Mth::Matrix& frame ) { float damping = mShakeDuration / mShakeInitialDuration; float vert_angle = mShakeDuration * mShakeVertVel * Mth::PI * 2.0f; float horiz_angle = mShakeDuration * mShakeHorizVel * Mth::PI * 2.0f; float vert_offset = mShakeMaxVertAmp * sinf( vert_angle ) * damping; float horiz_offset = mShakeMaxHorizAmp * sinf( horiz_angle ) * damping; cam += frame[X] * horiz_offset; cam += frame[Y] * vert_offset; mShakeDuration -= Tmr::FrameLength(); if( mShakeDuration < 0.0f ) mShakeDuration = 0.0f; } /******************************************************************/ /* */ /* */ /******************************************************************/ Mth::Vector CSkaterCameraComponent::GetTripodPos( bool instantly ) { float lerp_xz; float lerp_y; float delta = Tmr::FrameLength(); if( mpSkater == NULL ) { return Mth::Vector( 0.0f, 0.0f, 0.0f ); } Mth::Vector now = mpSkater->m_pos; // The tripod pos is always *tending* towards the skater position, but the rate at which it does so is // definable, to allow for some lag, which improves the feeling of speed. if( mLerpReductionTimer > 0.0f ) { mLerpReductionTimer -= delta; if( mLerpReductionTimer > 0.0f ) { mLerpReductionMult += ( mLerpReductionDelta * delta ); } else { mLerpReductionMult = 1.0f; mLerpReductionTimer = 0.0f; } } else { mLerpReductionMult = 1.0f; } Dbg_Assert( mpSkaterStateComponent ); if(( mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT ) && !mpSkaterStateComponent->GetFlag( SPINE_PHYSICS ))) { #ifdef DEBUG_CAMERA printf ("Skatercam using mVertAirLerp\n"); #endif lerp_xz = GetTimeAdjustedSlerp( mVertAirLerpXZ * mLerpReductionMult, delta ); lerp_y = GetTimeAdjustedSlerp( mVertAirLerpY * mLerpReductionMult, delta ); } else { lerp_xz = GetTimeAdjustedSlerp( mLerpXZ * mLerpReductionMult, delta ); lerp_y = GetTimeAdjustedSlerp( mLerpY * mLerpReductionMult, delta ); // Added the following check (Dave 7/16/02) to help eliminate the camera rattle when snapping on/off a curb. // The flag is set for one frame only. if( mpSkaterStateComponent->GetFlag( SNAPPED_OVER_CURB )) { lerp_y = 1.0f; } if( mpSkaterStateComponent->GetFlag( SNAPPED )) { instantly = true; } } if( instantly ) { mLastTripodPos = now; } else { mLastTripodPos.Set( mLastTripodPos.GetX() + (( now.GetX() - mLastTripodPos.GetX() ) * lerp_xz ), mLastTripodPos.GetY() + (( now.GetY() - mLastTripodPos.GetY() ) * lerp_y ), mLastTripodPos.GetZ() + (( now.GetZ() - mLastTripodPos.GetZ() ) * lerp_xz )); } return mLastTripodPos; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::CalculateZoom( float* p_above, float* p_behind ) { if( mpSkater == NULL ) { return; } mTargetZoom = 1.0f; // Deal with zoom for Big Air. Set the flag only when a trick is started. if( !mBigAirTrickZoomActive && ( mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT ) && !mpSkaterStateComponent->GetFlag(SPINE_PHYSICS)) && mpSkaterStateComponent->DoingTrick() ) { mBigAirTrickZoomActive = true; } else if( !( mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT ) && !mpSkaterStateComponent->GetFlag( SPINE_PHYSICS ))) { // Must have landed. mBigAirTrickZoomActive = false; } if( mBigAirTrickZoomActive ) { mTargetZoom = mBigAirTrickZoom; } else { EStateType state = mpSkaterStateComponent->GetState(); // If the skater is grinding, zoom also. if( state == RAIL ) { mTargetZoom = mGrindZoom; } else if( state == LIP ) { mTargetZoom = mLipTrickZoom; } else if( state == LIP ) { mTargetZoom = mLipTrickZoom; } } #if 0 // If lookaround override is set, factor in the lookaround override zoom. if( mp_lookaround_component->mLookaroundOverride && ( mp_lookaround_component->mLookaroundZoom != 1.0f )) { mCurrentZoom = mTargetZoom; mCurrentZoom *= mp_lookaround_component->mLookaroundZoom; } else { mCurrentZoom += (( mTargetZoom - mCurrentZoom ) * mZoomLerp ); } #else // If lookaround override is set, factor in the lookaround override zoom. if( mp_lookaround_component->mLookaroundOverride && ( mp_lookaround_component->mLookaroundZoom != 1.0f )) { mTargetZoom *= mp_lookaround_component->mLookaroundZoom; } mCurrentZoom += (( mTargetZoom - mCurrentZoom ) * mZoomLerp ); #endif *p_behind = mBehind * mCurrentZoom; // Behind is also shortened when the lookaround camera is tilting upwards. if( mp_lookaround_component->mLookaroundTilt < 0.0f ) { float max_tilt = 3.14f * 0.2f; *p_behind = *p_behind * ( 0.4f + ( 0.6f * (( max_tilt + mp_lookaround_component->mLookaroundTilt ) / max_tilt ))); } // Use lip_trick_above when doing a lip trick. float above_val = mAbove; if( mpSkaterStateComponent->GetState() == LIP ) { above_val = mLipTrickAbove; } // Figure above tending towards the perfect above, if zoom is < 1.0. if( mCurrentZoom < 1.0f ) { *p_above = SKATERCAMERACOMPONENT_PERFECT_ABOVE + (( above_val - SKATERCAMERACOMPONENT_PERFECT_ABOVE ) * mCurrentZoom ); } else { *p_above = above_val; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::UpdateInterpolators( ) { if( mBehindInterpolatorTime > 0.0f ) { mBehindInterpolatorTime -= SKATERCAMERACOMPONENT_INTERPOLATOR_TIMESTEP; mBehind += mBehindInterpolatorDelta; } else { mBehindInterpolatorTime = 0.0f; } if( mAboveInterpolatorTime > 0.0f ) { mAboveInterpolatorTime -= SKATERCAMERACOMPONENT_INTERPOLATOR_TIMESTEP; mAbove += mAboveInterpolatorDelta; } else { mAboveInterpolatorTime = 0.0f; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::SetLerpReductionTimer( float time ) { if( time > 0.0f ) { mLerpReductionTimer = time; mLerpReductionDelta = 1.0f / time; mLerpReductionMult = 0.0f; } else { mLerpReductionTimer = 0.0f; mLerpReductionDelta = 0.0f; mLerpReductionMult = 1.0f; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::SetSkater( CSkater* p_skater ) { mpSkater = p_skater; if( p_skater == NULL ) { mpSkaterStateComponent = NULL; mpSkaterPhysicsControlComponent = NULL; } else { mpSkaterStateComponent = GetSkaterStateComponentFromObject(mpSkater); Dbg_Assert(mpSkaterStateComponent); // non-local clients will not have a CSkaterPhysicsControlComponent mpSkaterPhysicsControlComponent = GetSkaterPhysicsControlComponentFromObject(mpSkater); SetNoLerpUpdate(true); } } /******************************************************************/ /* */ /* */ /******************************************************************/ CSkater* CSkaterCameraComponent::GetSkater( void ) { return mpSkater; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CSkaterCameraComponent::GetCameraState ( SCameraState& state ) { state.lastActualMatrix[X] = mLastActualRight; state.lastActualMatrix[Y] = mLastActualUp; state.lastActualMatrix[Z] = mLastActualAt; state.lastActualMatrix[W].Set( 0.0f, 0.0f, 0.0f, 1.0f ); state.lastTripodPos = mLastTripodPos; state.lastDot = mLastDot; state.lastZoom = mCurrentZoom; } }