// Copyright (C) 2007 Id Software, Inc. // #include "../precompiled.h" #pragma hdrstop #if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE ) #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #include "VehicleView.h" #include "VehicleWeapon.h" #include "Transport.h" #include "../ContentMask.h" /* =============================================================================== sdVehicleView =============================================================================== */ sdVehicleViewFactory sdVehicleView::viewFactory; /* ================ sdVehicleView::AllocView ================ */ sdVehicleView* sdVehicleView::AllocView( const char* name ) { return viewFactory.CreateType( name ); } /* ================ sdVehicleView::Startup ================ */ void sdVehicleView::Startup( void ) { viewFactory.RegisterType( sdDampedVehicleView::TypeName(), sdVehicleViewFactory::Allocator< sdDampedVehicleView > ); viewFactory.RegisterType( "", sdVehicleViewFactory::Allocator< sdDampedVehicleView > ); viewFactory.RegisterType( sdDampedVehicleView_Pivot::TypeName(), sdVehicleViewFactory::Allocator< sdDampedVehicleView_Pivot > ); viewFactory.RegisterType( sdDampedVehicleView_FreePivot::TypeName(), sdVehicleViewFactory::Allocator< sdDampedVehicleView_FreePivot > ); viewFactory.RegisterType( sdDampedVehicleView_Player::TypeName(), sdVehicleViewFactory::Allocator< sdDampedVehicleView_Player > ); viewFactory.RegisterType( sdSmoothVehicleView::TypeName(), sdVehicleViewFactory::Allocator< sdSmoothVehicleView > ); viewFactory.RegisterType( sdSmoothVehicleView_Free::TypeName(), sdVehicleViewFactory::Allocator< sdSmoothVehicleView_Free > ); viewFactory.RegisterType( sdSmoothVehicleView_Locked::TypeName(), sdVehicleViewFactory::Allocator< sdSmoothVehicleView_Locked > ); viewFactory.RegisterType( sdIcarusVehicleView::TypeName(), sdVehicleViewFactory::Allocator< sdIcarusVehicleView > ); } /* ================ sdVehicleView::Shutdown ================ */ void sdVehicleView::Shutdown( void ) { viewFactory.Shutdown(); } /* ================ sdVehicleView::Init ================ */ void sdVehicleView::Init( sdVehiclePosition* _position, const positionViewMode_t& _viewMode ) { position = _position; viewMode = _viewMode; SetupEyes( position->GetTransport() ); zoomTable = gameLocal.declTableType[ viewMode.zoomTable ]; sensitivityYaw = cvarSystem->Find( viewMode.sensitivityYaw ); sensitivityPitch = cvarSystem->Find( viewMode.sensitivityPitch ); sensitivityYawScale = cvarSystem->Find( viewMode.sensitivityYawScale ); sensitivityPitchScale = cvarSystem->Find( viewMode.sensitivityPitchScale ); thirdPersonViewOrigin.Zero(); thirdPersonViewAxes.Identity(); } /* ================ sdVehicleView::GetSensitivity ================ */ bool sdVehicleView::GetSensitivity( float& x, float& y ) { bool changed = false; if ( sensitivityPitch != NULL ) { y = sensitivityPitch->GetFloat(); changed = true; } if ( sensitivityYaw != NULL ) { x = sensitivityYaw->GetFloat(); changed = true; } if ( sensitivityPitchScale != NULL ) { y *= sensitivityPitchScale->GetFloat(); changed = true; } if ( sensitivityYawScale != NULL ) { x *= sensitivityYawScale->GetFloat(); changed = true; } return changed; } /* ================ sdVehicleView::GetFov ================ */ float sdVehicleView::GetFov( void ) const { if ( !zoomTable ) { return 90.0f; } idPlayer* player = position->GetPlayer(); if ( player->vehicleViewCurrentZoom < 0 || player->vehicleViewCurrentZoom >= zoomTable->NumValues() ) { return 90.0f; } return zoomTable->GetValue( player->vehicleViewCurrentZoom ); } /* ================ sdVehicleView::ZoomCycle ================ */ void sdVehicleView::ZoomCycle( void ) const { if ( !zoomTable ) { return; } idPlayer* player = position->GetPlayer(); player->vehicleViewCurrentZoom++; player->vehicleViewCurrentZoom %= zoomTable->NumValues() - 1; if ( player->vehicleViewCurrentZoom == 0 ) { gameSoundWorld->PlayShaderDirectly( viewMode.zoomOutSound, SND_VEHICLE_ZOOM ); } else { gameSoundWorld->PlayShaderDirectly( viewMode.zoomInSound, SND_VEHICLE_ZOOM ); } } /* ================ sdVehicleView::ClampViewAngles ================ */ void sdVehicleView::ClampViewAngles( idAngles& viewAngles, const idAngles& oldViewAngles ) const { idAngles oldAngles = oldViewAngles; DoFreshEntryAngles( viewAngles, oldAngles ); sdVehiclePosition::ClampAngle( viewAngles, oldAngles, viewMode.clampPitch, 0 ); sdVehiclePosition::ClampAngle( viewAngles, oldAngles, viewMode.clampYaw, 1 ); } /* ================ sdVehicleView::DoFreshEntryAngles ================ */ void sdVehicleView::DoFreshEntryAngles( idAngles& viewAngles, idAngles& oldAngles ) const { if ( freshEntry ) { idAngles oldViewAngles = oldAngles; // calculate the angles needed to point in the same direction as pointed // when the player entered the vehicle freshEntry = false; CalcNewViewAngles( oldAngles ); idAngles deltaAngles = viewAngles - oldViewAngles; viewAngles = oldAngles + deltaAngles; viewAngles.Normalize180(); } } /* ================ sdVehicleView::ClipView ================ */ void sdVehicleView::ClipView( idVec3& viewOrigin, const idVec3& pivotPoint ) { idEntity* owner = position->GetTransport(); owner->DisableClip( false ); // HACK: Disable clip of everything bound to it too for ( idEntity* ent = owner; ent != NULL; ent = ent->GetNextTeamEntity() ) { ent->DisableClip( false ); } const idClipModel* thirdPersonModel = gameLocal.clip.GetThirdPersonOffsetModel(); // trace a ray from the origin to the viewpoint to make sure the view isn't // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything trace_t trace; gameLocal.clip.Translation( CLIP_DEBUG_PARMS trace, pivotPoint, viewOrigin, thirdPersonModel, mat3_identity, MASK_SHOT_RENDERMODEL & ~CONTENTS_FORCEFIELD, owner ); if ( trace.fraction != 1.0 ) { viewOrigin = trace.endpos; viewOrigin.z += ( 1.0f - trace.fraction ) * 32.0f; // try another trace to this position, because a tunnel may have the ceiling // close enough that this is poking out gameLocal.clip.Translation( CLIP_DEBUG_PARMS trace, pivotPoint, viewOrigin, thirdPersonModel, mat3_identity, MASK_SHOT_RENDERMODEL & ~CONTENTS_FORCEFIELD, owner ); viewOrigin = trace.endpos; } owner->EnableClip(); for ( idEntity* ent = owner; ent != NULL; ent = ent->GetNextTeamEntity() ) { ent->EnableClip(); } } /* ================ sdVehicleView::Update ================ */ void sdVehicleView::Update( sdVehicleWeapon* weapon ) { } /* ================ sdVehicleView::GetRequiredViewAngles ================ */ const idAngles sdVehicleView::GetRequiredViewAngles( const idVec3& aimPosition ) const { return idAngles( vec3_origin ); } /* ================ sdVehicleView::OnPlayerEntered ================ */ void sdVehicleView::OnPlayerEntered( idPlayer* player ) { GetInitialViewAxis( player->firstPersonViewAxis ); OnPlayerSwitched( player, true ); } /* ================ sdVehicleView::OnPlayerSwitched ================ */ void sdVehicleView::OnPlayerSwitched( idPlayer* player, bool newPosition ) { freshEntry = true; if ( GetViewParms().matchPrevious || !newPosition ) { // aim in the same direction as we were previously aiming entryAim = position->GetPlayer()->firstPersonViewAxis; } else { GetInitialViewAxis( entryAim ); } } /* ================ sdVehicleView::GetInitialViewAxis ================ */ void sdVehicleView::GetInitialViewAxis( idMat3& aim ) const { aim = position->GetTransport()->GetAxis(); } /* ================ sdVehicleView::OnTeleport ================ */ void sdVehicleView::OnTeleport( void ) { idPlayer* player = position->GetPlayer(); OnPlayerEntered( player ); } /* ================ sdVehicleView::ClampFinalAxis ================ */ void sdVehicleView::ClampFinalAxis( idMat3& axis, const idMat3& baseAxis, idMat3& dampedAxis ) const { // clamp this final axis to the damped clamp values // need the fovs of the final rendering screen to do it const renderView_t& view = gameLocal.playerView.GetCurrentView(); // calculate the clamps necessary to prevent the edge of the view exceeding the maximum angleClamp_t pitchClamp = viewMode.clampDampedPitch; angleClamp_t yawClamp = viewMode.clampDampedYaw; pitchClamp.extents[ 0 ] += view.fov_y * 0.5f; pitchClamp.extents[ 1 ] -= view.fov_y * 0.5f; yawClamp.extents[ 0 ] += view.fov_x * 0.5f; yawClamp.extents[ 1 ] -= view.fov_x * 0.5f; idMat3 dampedAxisTransform = axis * dampedAxis.Transpose(); idMat3 localAxis = axis * baseAxis.Transpose(); idAngles localAngles = localAxis.ToAngles(); idAngles tempAngles = localAngles; if ( !sdVehiclePosition::ClampAngle( localAngles, tempAngles, pitchClamp, 0, 0.00001f ) || !sdVehiclePosition::ClampAngle( localAngles, tempAngles, yawClamp, 1, 0.00001f ) ) { axis = localAngles.ToMat3() * baseAxis; // now we have the final clamped axis, we can reverse engineer what the damped axis // should have been to achieve that... hopefully.. dampedAxis = dampedAxisTransform.TransposeMultiply( axis ); } } /* =============================================================================== sdDampedVehicleView =============================================================================== */ /* ================ sdDampedVehicleView::SetupEyes ================ */ void sdDampedVehicleView::SetupEyes( idAnimatedEntity* other ) { idAnimator* animator = other->GetAnimator(); eyeJoint = animator->GetJointHandle( viewMode.eyes ); eyeBaseAxis.Identity(); if ( eyeJoint == INVALID_JOINT ) { gameLocal.Warning( "sdVehicleView::Init can't find eye joint '%s'", viewMode.eyes.c_str() ); eyeBaseJoint = INVALID_JOINT; } else { eyeBaseJoint = animator->GetJointHandle( viewMode.eyePivot ); if ( eyeBaseJoint == INVALID_JOINT ) { eyeBaseJoint = animator->GetJointParent( eyeJoint ); } if ( eyeBaseJoint != INVALID_JOINT ) { idVec3 eyePos; idMat3 eyeAxis; other->GetJointTransformForAnim( eyeJoint, 1, gameLocal.time, eyePos, eyeAxis ); other->GetJointTransformForAnim( eyeBaseJoint, 1, gameLocal.time, eyeBaseOffset, eyeBaseAxis ); idMat3 eyeBaseAxisT = eyeBaseAxis.Transpose(); eyeOffset = ( eyePos - eyeBaseOffset ) * eyeBaseAxisT; eyeAxisOffset = eyeBaseAxisT * eyeAxis; } } lastReturnedAxis = eyeBaseAxis; } /* ================ sdDampedVehicleView::GetDampingPos ================ */ void sdDampedVehicleView::GetDampingPos( idVec3& pos ) const { position->GetTransport()->GetWorldOrigin( eyeJoint, pos ); } /* ================ sdDampedVehicleView::GetDampingAxis ================ */ void sdDampedVehicleView::GetDampingAxis( idMat3& axis ) const { position->GetTransport()->GetWorldAxis( eyeJoint, axis ); } /* ================ sdDampedVehicleView::GetInitialViewAxis ================ */ void sdDampedVehicleView::GetInitialViewAxis( idMat3& axis ) const { position->GetTransport()->GetWorldAxis( eyeJoint, axis ); } /* ================ sdDampedVehicleView::DampEyeAxes ================ */ void sdDampedVehicleView::DampEyeAxes( const idMat3& oldAxes, idMat3& outAxes ) { idMat3 vAxes; GetDampingAxis( vAxes ); if ( !GetViewParms().allowDamping ) { outAxes = vAxes; return; } for ( int i = 0; i < 3; i++ ) { outAxes[ i ] = Lerp( oldAxes[ i ], vAxes[ i ], viewMode.dampCopyFactor[ i ] ); } idQuat a = outAxes.ToQuat(); idQuat b = vAxes.ToQuat(); idQuat c; c.Slerp( a, b, MS2SEC( gameLocal.msec ) / viewMode.dampSpeed ); c.Normalize(); outAxes = c.ToMat3(); outAxes.FixDenormals(); } /* ================ sdDampedVehicleView::CalcViewOrigin ================ */ void sdDampedVehicleView::CalcViewOrigin( const idMat3& dampedAxis, const idVec3& posIn, idVec3& posOut, const idAngles& angles ) { posOut = posIn; } /* ================ sdDampedVehicleView::CalcViewAxes ================ */ void sdDampedVehicleView::CalcViewAxes( const idMat3& axisIn, idMat3& axisOut, const idAngles& angles ) { axisOut = angles.ToMat3() * axisIn; if ( !viewMode.thirdPerson ) { sdTransport* transport = position->GetTransport(); idRotation rotation; rotation.SetVec( transport->GetPhysics()->GetAxis()[ 2 ] ); rotation.SetAngle( position->GetCurrentViewOffset() ); axisOut *= rotation.ToMat3(); } } /* =============== sdDampedVehicleView::CalcThirdPersonView =============== */ void sdDampedVehicleView::CalcThirdPersonView( const idVec3& fpOrigin, const idMat3& fpAxis ) { idEntity* owner = position->GetTransport(); idVec3 origin = fpOrigin; thirdPersonViewAxes = fpAxis; idVec3 focusPoint = origin + thirdPersonViewAxes[ 0 ] * viewMode.cameraFocus + owner->GetAxis()[ 2 ] * viewMode.cameraFocusHeight; idVec3 view = origin; view.z += 8 + viewMode.cameraHeight; idAngles angles = thirdPersonViewAxes.ToAngles(); view -= viewMode.cameraDistance * thirdPersonViewAxes[ 0 ]; ClipView( view, origin ); // select pitch to look at focus point from vieworg focusPoint -= view; float focusDist = idMath::Sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] ); if ( focusDist < 1 ) { focusDist = 1; // should never happen } angles.pitch = - RAD2DEG( atan2( focusPoint.z, focusDist ) ); thirdPersonViewOrigin = view; thirdPersonViewAxes = angles.ToMat3(); } /* ================ sdDampedVehicleView::CalculateViewPos ================ */ void sdDampedVehicleView::CalculateViewPos( idPlayer* player, idVec3& origin, idMat3& axis, bool fullUpdate ) { if ( fullUpdate ) { if ( freshEntry ) { // ensure that the angles have been clamped to the valid range idAngles tempAngles = player->clientViewAngles; ClampViewAngles( player->clientViewAngles, tempAngles ); freshEntry = true; } // damping DampEyeAxes( player->vehicleEyeAxis, player->vehicleEyeAxis ); GetDampingPos( player->vehicleEyeOrigin ); } // axis CalcViewAxes( player->vehicleEyeAxis, axis, player->clientViewAngles ); if ( fullUpdate ) { // clamp the final result idMat3 baseAxis; GetDampingAxis( baseAxis ); ClampFinalAxis( axis, baseAxis, player->vehicleEyeAxis ); } // origin CalcViewOrigin( player->vehicleEyeAxis, player->vehicleEyeOrigin, origin, player->clientViewAngles ); if ( viewMode.thirdPerson ) { CalcThirdPersonView( origin, axis ); } lastReturnedAxis = axis; } /* ================ sdDampedVehicleView::OnPlayerSwitched ================ */ void sdDampedVehicleView::OnPlayerSwitched( idPlayer* player, bool newPosition ) { GetDampingAxis( player->vehicleEyeAxis ); sdVehicleView::OnPlayerSwitched( player, newPosition ); lastReturnedAxis = player->firstPersonViewAxis; } /* ================ sdDampedVehicleView::GetRequiredViewAngles ================ */ const idAngles sdDampedVehicleView::GetRequiredViewAngles( const idVec3& aimPosition ) const { idVec3 eyePos; GetDampingPos( eyePos ); idVec3 deltaPos = aimPosition - eyePos; const idMat3& ownerAxis = position->GetTransport()->GetAxis(); // find the delta in car space idVec3 deltaDirection = deltaPos; deltaDirection.Normalize(); deltaDirection = deltaDirection * ownerAxis.Transpose(); idAngles result = deltaDirection.ToAngles(); result.Normalize180(); return result; } /* ================ sdDampedVehicleView::CalcNewViewAngles ================ */ void sdDampedVehicleView::CalcNewViewAngles( idAngles& angles ) const { idMat3 dampAxis; GetDampingAxis( dampAxis ); idMat3 angleMat = entryAim * dampAxis.Transpose(); angles = angleMat[ 0 ].ToAngles(); } /* =============================================================================== sdDampedVehicleView_Pivot =============================================================================== */ /* ================ sdDampedVehicleView_Pivot::GetDampingAxis ================ */ void sdDampedVehicleView_Pivot::GetDampingAxis( idMat3& axis ) const { axis = position->GetTransport()->GetRenderEntity()->axis; } /* ================ sdDampedVehicleView_Pivot::GetDampingPos ================ */ void sdDampedVehicleView_Pivot::GetDampingPos( idVec3& pos ) const { position->GetTransport()->GetWorldOrigin( eyeBaseJoint, pos ); } /* ================ sdDampedVehicleView_Pivot::CalcViewOrigin ================ */ void sdDampedVehicleView_Pivot::CalcViewOrigin( const idMat3& dampedAxis, const idVec3& posIn, idVec3& posOut, const idAngles& angles ) { idMat3 gunRotation = mat3_identity; idMat3 temp; if ( viewMode.followPitch ) { idAngles::PitchToMat3( angles.pitch, temp ); gunRotation *= temp; } if ( viewMode.followYaw ) { idAngles::YawToMat3( angles.yaw, temp ); gunRotation *= temp; } posOut = posIn + ( eyeOffset * eyeBaseAxis ) * gunRotation * dampedAxis; } /* ================ sdDampedVehicleView_Pivot::GetRequiredViewAngles ================ */ const idAngles sdDampedVehicleView_Pivot::GetRequiredViewAngles( const idVec3& aimPosition ) const { // FIXME: This isn't quite correct, it doesn't take the arm into account properly. return sdDampedVehicleView::GetRequiredViewAngles( aimPosition ); } /* ================ sdDampedVehicleView_Pivot::CalcNewViewAngles ================ */ void sdDampedVehicleView_Pivot::CalcNewViewAngles( idAngles& angles ) const { sdDampedVehicleView::CalcNewViewAngles( angles ); } /* =============================================================================== sdDampedVehicleView_FreePivot =============================================================================== */ /* ================ sdDampedVehicleView_FreePivot::GetDampingAxis ================ */ void sdDampedVehicleView_FreePivot::GetDampingAxis( idMat3& axis ) const { axis = mat3_identity; } /* ================ sdDampedVehicleView_FreePivot::ClampViewAngles ================ */ void sdDampedVehicleView_FreePivot::ClampViewAngles( idAngles& viewAngles, const idAngles& oldViewAngles ) const { if ( !freshEntry ) { // convert angle delta into one in screen space idAngles oldFinalViewAngles = lastReturnedAxis.ToAngles(); idMat2 rotMat; rotMat.Rotation( DEG2RAD( oldFinalViewAngles.roll ) ); idAngles deltaAngles = ( viewAngles - oldViewAngles ).Normalize180(); idVec2 deltaVec( deltaAngles.yaw, deltaAngles.pitch ); deltaVec *= rotMat; deltaAngles.yaw = deltaVec[ 0 ]; deltaAngles.pitch = deltaVec[ 1 ]; viewAngles = oldViewAngles + deltaAngles; } idAngles oldAngles = oldViewAngles; DoFreshEntryAngles( viewAngles, oldAngles ); // do a clamp to prevent it going crazy when the player looks all the way up or all the way down angleClamp_t flipClamp; flipClamp.extents.x = -80.0f; flipClamp.extents.y = 80.0f; flipClamp.flags.enabled = true; flipClamp.flags.limitRate = false; sdVehiclePosition::ClampAngle( viewAngles, oldAngles, flipClamp, 0 ); // move angles back into "clamping space" using the damping axis to find a transform idMat3 ownerAxis = position->GetTransport()->GetRenderEntity()->axis; idMat3 dampingAxis; GetDampingAxis( dampingAxis ); idMat3 dampingSpaceToClampSpace = ownerAxis.TransposeMultiply( dampingAxis ); // transform forward vectors into clamping space & convert back to angles idAngles ownerSpaceOldAngles = ( oldAngles.ToForward() * dampingSpaceToClampSpace ).ToAngles(); idAngles ownerSpaceAngles = ( viewAngles.ToForward() * dampingSpaceToClampSpace ).ToAngles(); ownerSpaceAngles.Normalize180(); ownerSpaceOldAngles.Normalize180(); sdVehiclePosition::ClampAngle( ownerSpaceAngles, ownerSpaceOldAngles, viewMode.clampPitch, 0 ); sdVehiclePosition::ClampAngle( ownerSpaceAngles, ownerSpaceOldAngles, viewMode.clampYaw, 1 ); // now transform the clamped angle back into clamping space viewAngles = ( dampingSpaceToClampSpace.TransposeMultiply( ownerSpaceAngles.ToForward() ) ).ToAngles(); viewAngles.Normalize180(); } /* ================ sdDampedVehicleView_FreePivot::CalcViewAxes ================ */ void sdDampedVehicleView_FreePivot::CalcViewAxes( const idMat3& axisIn, idMat3& axisOut, const idAngles& angles ) { axisOut = angles.ToMat3() * axisIn; idMat3 ownerAxes = position->GetTransport()->GetRenderEntity()->axis; // stick the up axis of the vehicle in as the z axis and renormalize axisOut[ 2 ] = ownerAxes[ 2 ]; axisOut[ 1 ].Cross( axisOut[ 2 ], axisOut[ 0 ] ); axisOut[ 1 ].Normalize(); axisOut[ 2 ].Cross( axisOut[ 0 ], axisOut[ 1 ] ); axisOut[ 2 ].Normalize(); } /* ================ sdDampedVehicleView_FreePivot::CalculateViewPos ================ */ void sdDampedVehicleView_FreePivot::CalculateViewPos( idPlayer* player, idVec3& origin, idMat3& axis, bool fullUpdate ) { if ( fullUpdate ) { if ( freshEntry ) { // ensure that the angles have been clamped to the valid range idAngles tempAngles = player->clientViewAngles; ClampViewAngles( player->clientViewAngles, tempAngles ); freshEntry = true; } // damping DampEyeAxes( player->vehicleEyeAxis, player->vehicleEyeAxis ); GetDampingPos( player->vehicleEyeOrigin ); } // axis CalcViewAxes( player->vehicleEyeAxis, axis, player->clientViewAngles ); if ( fullUpdate ) { ClampFinalAxis( axis, mat3_identity, player->vehicleEyeAxis ); } // origin CalcViewOrigin( axis, player->vehicleEyeOrigin, origin, ang_zero ); if ( viewMode.thirdPerson ) { CalcThirdPersonView( origin, axis ); } lastReturnedAxis = axis; } /* ================ sdDampedVehicleView_FreePivot::GetRequiredViewAngles ================ */ const idAngles sdDampedVehicleView_FreePivot::GetRequiredViewAngles( const idVec3& aimPosition ) const { // FIXME: This isn't quite correct, it doesn't take the arm into account properly. idVec3 eyePos; // position->GetTransport()->GetWorldOrigin( eyeJoint, eyePos ); GetDampingPos( eyePos ); idVec3 deltaDirection = aimPosition - eyePos; deltaDirection.Normalize(); deltaDirection *= eyeAxisOffset.Transpose(); idAngles result = deltaDirection.ToAngles(); result.Normalize180(); return result; } /* ================ sdDampedVehicleView_FreePivot::CalcNewViewAngles ================ */ void sdDampedVehicleView_FreePivot::CalcNewViewAngles( idAngles& angles ) const { angles = entryAim[ 0 ].ToAngles(); } /* =============================================================================== sdDampedVehicleView_Player =============================================================================== */ /* ================ sdDampedVehicleView_Player::Init ================ */ void sdDampedVehicleView_Player::Init( sdVehiclePosition* _position, const positionViewMode_t& _viewMode ) { position = _position; viewMode = _viewMode; zoomTable = NULL; sensitivityYaw = NULL; sensitivityPitch = NULL; sensitivityYawScale = NULL; sensitivityPitchScale = NULL; } /* ================ sdDampedVehicleView_Player::CalcViewAxes ================ */ void sdDampedVehicleView_Player::CalcViewAxes( const idMat3& axisIn, idMat3& axisOut, const idAngles& angles ) { axisOut = position->GetTransport()->GetAxis() * axisIn; } /* ================ sdDampedVehicleView_Player::CalcViewOrigin ================ */ void sdDampedVehicleView_Player::CalcViewOrigin( const idMat3& dampedAxis, const idVec3& posIn, idVec3& posOut, const idAngles& angles ) { posOut = posIn; } /* ================ sdDampedVehicleView_Player::GetDampingPos ================ */ void sdDampedVehicleView_Player::GetDampingPos( idVec3& pos ) const { renderEntity_t* renderEnt = position->GetTransport()->GetRenderEntity(); pos = renderEnt->origin + idVec3( 0.f, 0.f, 72.f ); } /* ================ sdDampedVehicleView_Player::GetDampingAxis ================ */ void sdDampedVehicleView_Player::GetDampingAxis( idMat3& axis ) const { axis = mat3_identity; } /* ================ sdDampedVehicleView_Player::GetRequiredViewAngles ================ */ const idAngles sdDampedVehicleView_Player::GetRequiredViewAngles( const idVec3& aimPosition ) const { return sdDampedVehicleView::GetRequiredViewAngles( aimPosition ); } /* ================ sdDampedVehicleView_Player::CalcNewViewAngles ================ */ void sdDampedVehicleView_Player::CalcNewViewAngles( idAngles& angles ) const { } /* =============================================================================== sdSmoothVehicleView =============================================================================== */ /* ================ sdSmoothVehicleView::Init ================ */ void sdSmoothVehicleView::Init( sdVehiclePosition* _position, const positionViewMode_t& _viewMode ) { previousAimMatrix.Identity(); previousRawAimMatrix.Identity(); previousCameraDistance = _viewMode.cameraDistance; previousOwnerOrigin.Zero(); previousCameraHeightDelta = 0.0f; sdVehicleView::Init( _position, _viewMode ); } /* ================ sdSmoothVehicleView::ClampViewAngles ================ */ void sdSmoothVehicleView::ClampViewAngles( idAngles& viewAngles, const idAngles& oldViewAngles ) const { idAngles oldAngles = oldViewAngles; DoFreshEntryAngles( viewAngles, oldAngles ); idPhysics* ownerPhysics = position->GetTransport()->GetPhysics(); const idMat3& ownerAxes = ownerPhysics->GetAxis(); // transform the angles from world space into local space const idMat3 ownerAxesT = ownerAxes.Transpose(); // pitch is not relative to the vehicle but yaw is! idAngles ownerAngles = ownerAxes.ToAngles(); viewAngles.yaw += ownerAngles.yaw; oldAngles.yaw += ownerAngles.yaw; idAngles localSpaceViewAngles = ( viewAngles.ToMat3() * ownerAxesT ).ToAngles(); idAngles localSpaceOldAngles = ( oldAngles.ToMat3() * ownerAxesT ).ToAngles(); sdVehiclePosition::ClampAngle( localSpaceViewAngles, localSpaceOldAngles, viewMode.clampPitch, 0 ); sdVehiclePosition::ClampAngle( localSpaceViewAngles, localSpaceOldAngles, viewMode.clampYaw, 1 ); // transform the clamped angles back into world space viewAngles = ( localSpaceViewAngles.ToMat3() * ownerAxes )[ 0 ].ToAngles(); viewAngles.yaw -= ownerAngles.yaw; // clean out the roll, it has a habit of accumulating little values viewAngles.roll = 0.0f; } /* ================ sdSmoothVehicleView::CalcNewViewAngles ================ */ void sdSmoothVehicleView::CalcNewViewAngles( idAngles& angles ) const { // yaw relative to body const idMat3& ownerAxes = position->GetTransport()->GetPhysics()->GetAxis(); idAngles ownerAngles = ownerAxes[ 0 ].ToAngles(); angles = entryAim[ 0 ].ToAngles(); angles.yaw -= ownerAngles.yaw; angles.Normalize180(); } /* ================ sdSmoothVehicleView::CalculateAimMatrix ================ */ void sdSmoothVehicleView::CalculateAimMatrix( const viewEvalProperties_t& state ) { // in this mode yaw is based off the owner, but pitch is free to smooth out bumps :) idMat3 yawMat = ( idRotation( vec3_origin, idVec3( 0.0f, 0.0f, 1.0f ), -state.viewAngles.yaw - state.ownerAngles.yaw ) ).ToMat3(); idMat3 pitchMat = ( idRotation( vec3_origin, idVec3( 0.0f, 1.0f, 0.0f ), -state.viewAngles.pitch ) ).ToMat3(); evalState.aimMatrix = pitchMat * yawMat; } /* ================ sdSmoothVehicleView::ClampToViewConstraints ================ */ void sdSmoothVehicleView::ClampToViewConstraints( const viewEvalProperties_t& state ) { // clamp the resultant angles idAngles localAxisAngles = ( evalState.cameraAxis * state.ownerAxesT ).ToAngles(); idAngles clampedAxisAngles = localAxisAngles; bool changed = false; changed = !sdVehiclePosition::ClampAngle( clampedAxisAngles, localAxisAngles, viewMode.clampPitch, 0 ); changed |= !sdVehiclePosition::ClampAngle( clampedAxisAngles, localAxisAngles, viewMode.clampYaw, 1 ); // clampedAxisAngles.roll = 0.0f; idMat3 ownerSpaceAxis = clampedAxisAngles.ToMat3(); if ( changed ) { evalState.cameraAxis = ownerSpaceAxis * state.ownerAxes; } } /* ================ sdSmoothVehicleView::ClampToWorld ================ */ void sdSmoothVehicleView::ClampToWorld( const viewEvalProperties_t& state ) { ClipView( evalState.cameraOrigin, state.ownerCenter ); evalState.newCameraDistance = ( state.ownerCenter - evalState.cameraOrigin ).Length(); } /* ================ sdSmoothVehicleView::CalculateCameraDelta ================ */ idVec3 sdSmoothVehicleView::CalculateCameraDelta( const viewEvalProperties_t& state ) { float cameraDist = viewMode.cameraDistance; float cameraHeight = viewMode.cameraHeight; idVec3 cameraDelta( -cameraDist, 0.0f, cameraHeight ); cameraDelta = cameraDelta * evalState.aimMatrix; return cameraDelta; } /* ================ sdSmoothVehicleView::DampenMotion ================ */ void sdSmoothVehicleView::DampenMotion( const viewEvalProperties_t& state ) { if ( !firstFrame ) { // find the difference in aiming matrices idMat3 aimingDiff = previousAimMatrix.Transpose() * ( evalState.ownerYawAxis.Transpose() * evalState.aimMatrix ); // find the difference in result matrices idMat3 resultDiff = state.oldAxis.Transpose() * evalState.cameraAxis; // remove the result contribution by the view angle changes resultDiff = resultDiff * aimingDiff.Transpose(); // now! this result is the change in viewing axis caused by car movement only! // damp out the yaw & pitch caused by the car movement idAngles angleDiff = resultDiff.ToAngles(); angleDiff.yaw = DampenYaw( angleDiff.yaw ); angleDiff.pitch = DampenPitch( angleDiff.pitch ); resultDiff = angleDiff.ToMat3(); evalState.cameraAxis = state.oldAxis * resultDiff * aimingDiff; // ok, this is kinda dodgy isn't it? but it smooths out the tiny // fluctuations that creep in at really low level idAngles tempAngles = evalState.cameraAxis.ToAngles(); tempAngles.yaw = idMath::Floor( tempAngles.yaw * 100000.0f ) / 100000.0f; tempAngles.pitch = idMath::Floor( tempAngles.pitch * 100000.0f ) / 100000.0f; tempAngles.roll = idMath::Floor( tempAngles.roll * 100000.0f ) / 100000.0f; evalState.cameraAxis = tempAngles.ToMat3(); // FINALLY - eliminate any roll component idVec3 newUp( 0.0f, 0.0f, 1.0f ); idVec3 newRight = newUp.Cross( evalState.cameraAxis[ 0 ] ); newRight.Normalize(); newUp = evalState.cameraAxis[ 0 ].Cross( newRight ); newUp.Normalize(); evalState.cameraAxis[ 1 ] = newRight; evalState.cameraAxis[ 2 ] = newUp; } } /* ================ sdSmoothVehicleView::DoTeleporting ================ */ void sdSmoothVehicleView::DoTeleporting( viewEvalProperties_t& state ) const { state.viewAngles.Zero(); } /* ================ sdSmoothVehicleView::CalculateViewPos ================ */ void sdSmoothVehicleView::CalculateViewPos( idPlayer* player, idVec3& origin, idMat3& axis, bool fullUpdate ) { if ( !fullUpdate ) { origin = thirdPersonViewOrigin; axis = thirdPersonViewAxes; return; } if ( freshEntry ) { // ensure that the angles have been clamped to the valid range idAngles tempAngles = player->clientViewAngles; ClampViewAngles( player->clientViewAngles, tempAngles ); freshEntry = true; } // Harvest data viewEvalProperties_t state; state.driver = player; state.oldOrigin = origin; state.oldAxis = axis; state.oldAxisAngles = axis.ToAngles(); state.viewAngles = player->clientViewAngles; state.owner = position->GetTransport(); state.ownerPhysics = state.owner->GetPhysics(); state.ownerOrigin = state.owner->GetLastPushedOrigin(); state.ownerAxes = state.owner->GetLastPushedAxis(); state.ownerAxesT = state.ownerAxes.Transpose(); state.ownerAngles = state.ownerAxes.ToAngles(); idAngles::YawToMat3( state.ownerAngles.yaw, evalState.ownerYawAxis ); state.ownerVelocity = state.ownerPhysics->GetLinearVelocity(); state.ownerDirection = state.ownerVelocity; state.ownerSpeed = state.ownerDirection.NormalizeFast(); if ( position->GetTransport()->IsTeleporting() ) { DoTeleporting( state ); } state.timeStep = MS2SEC( gameLocal.msec ); evalState.cameraAxis = axis; // TODO: store this joint handle jointHandle_t eyeJoint = position->GetTransport()->GetAnimator()->GetJointHandle( viewMode.eyes ); if ( eyeJoint != INVALID_JOINT ) { idMat3 tempAxes; state.owner->GetWorldOriginAxisNoUpdate( eyeJoint, state.ownerOrigin, tempAxes ); } state.ownerCenter = state.ownerPhysics->GetBounds().GetCenter() * state.ownerAxes + state.ownerOrigin; previousOwnerOrigin = state.ownerOrigin; // // Aiming // CalculateAimMatrix( state ); idVec3 cameraDelta = CalculateCameraDelta( state ); evalState.cameraOrigin = cameraDelta + state.ownerOrigin; previousRawAimMatrix = evalState.aimMatrix; // // Focusing on the target // // calculate the axes so its looking at the focus point float focusPointHeight = viewMode.cameraHeight; evalState.focusPoint = state.ownerOrigin + idVec3( 0.0f, 0.0f, focusPointHeight ); idVec3 deltaToCar = evalState.focusPoint - evalState.cameraOrigin; deltaToCar.Normalize(); evalState.cameraAxis = deltaToCar.ToMat3(); // // Clamping to the view constraints // ClampToViewConstraints( state ); // remove roll evalState.cameraAxis = ( evalState.cameraAxis[ 0 ].ToAngles() ).ToMat3(); // // Prevent it clipping through the vehicle // float focusLength = ( evalState.focusPoint - evalState.cameraOrigin ).Length(); // // Damping // // do some dampening of the camera movement based on the vehicle movement DampenMotion( state ); previousAimMatrix = evalState.ownerYawAxis.Transpose() * evalState.aimMatrix; // fix up the camera origin (as the axis may have changed) evalState.cameraOrigin = evalState.focusPoint - evalState.cameraAxis[ 0 ] * focusLength; // // Prevent it clipping through the world // idVec3 distanceClampDirection = state.ownerCenter - evalState.cameraOrigin; evalState.newCameraDistance = distanceClampDirection.Normalize(); evalState.newCameraHeightDelta = 0.0f; ClampToWorld( state ); // // Prevent it clipping through the vehicle // idClipModel* combatModel = state.owner->GetCombatModel(); if ( combatModel != NULL ) { idBounds bounds = combatModel->GetBounds(); idBounds absBounds = combatModel->GetAbsBounds(); idVec3 traceDownStart = evalState.cameraOrigin; idVec3 traceDownEnd = evalState.cameraOrigin; float traceLength = absBounds.GetSize().z; traceDownStart.z += traceLength; if ( absBounds.LineIntersection( traceDownStart, traceDownEnd ) ) { // it intersects the abs bounds so theres a chance that it'll collide with the combat model float traceRadius = 32.0f; traceDownStart.z -= traceRadius; traceDownEnd.z -= traceRadius; // check against the combat model trace_t trace; trace.fraction = 1.0f; gameLocal.clip.TraceRenderModel( trace, traceDownStart, traceDownEnd, traceRadius, mat3_identity, -1, combatModel ); float tracedDistance = trace.fraction * traceLength; if ( trace.fraction < 1.0f ) { evalState.newCameraHeightDelta = trace.endpos.z + 8.0f - evalState.cameraOrigin.z; } } } // // Damp out the camera height when lowering rapidly // if ( evalState.newCameraHeightDelta < previousCameraHeightDelta ) { evalState.newCameraHeightDelta -= ( evalState.newCameraHeightDelta - previousCameraHeightDelta ) * ( 1.0f - viewMode.dampSpeed ); if ( evalState.newCameraHeightDelta < 0.001f ) { evalState.newCameraHeightDelta = 0.001f; } } if ( evalState.newCameraHeightDelta > 0.0f ) { evalState.cameraOrigin.z += evalState.newCameraHeightDelta; // Re-clamp to the world to make sure it doesn't go through anything as a result ClampToWorld( state ); } // // Damp out the camera distance when going out rapidly // distanceClampDirection = state.ownerCenter - evalState.cameraOrigin; evalState.newCameraDistance = distanceClampDirection.Normalize(); if ( evalState.newCameraDistance > previousCameraDistance ) { evalState.newCameraDistance -= ( evalState.newCameraDistance - previousCameraDistance ) * ( 1.0f - viewMode.dampSpeed ); } evalState.cameraOrigin = state.ownerCenter - distanceClampDirection * evalState.newCameraDistance; previousCameraDistance = evalState.newCameraDistance; previousCameraHeightDelta = evalState.newCameraHeightDelta; origin = evalState.cameraOrigin; axis = evalState.cameraAxis; // clamp the final result idMat3 temp = mat3_identity; ClampFinalAxis( axis, mat3_identity, temp ); thirdPersonViewOrigin = origin; thirdPersonViewAxes = axis; firstFrame = false; } /* ================ sdSmoothVehicleView::SetupEyes ================ */ void sdSmoothVehicleView::SetupEyes( idAnimatedEntity* other ) { } /* ================ sdSmoothVehicleView::GetRequiredViewAngles ================ */ const idAngles sdSmoothVehicleView::GetRequiredViewAngles( const idVec3& aimPosition ) const { return idAngles( vec3_origin ); } /* ================ sdSmoothVehicleView::OnPlayerSwitched ================ */ void sdSmoothVehicleView::OnPlayerSwitched( idPlayer* player, bool newPosition ) { sdVehicleView::OnPlayerSwitched( player, newPosition ); firstFrame = true; previousAimMatrix.Identity(); previousRawAimMatrix.Identity(); previousCameraDistance = viewMode.cameraDistance; previousOwnerOrigin.Zero(); previousCameraHeightDelta = 0.0f; } /* =============================================================================== sdSmoothVehicleView_Free =============================================================================== */ /* ================ sdSmoothVehicleView_Free::ClampViewAngles ================ */ void sdSmoothVehicleView_Free::ClampViewAngles( idAngles& viewAngles, const idAngles& oldViewAngles ) const { idAngles oldAngles = oldViewAngles; DoFreshEntryAngles( viewAngles, oldAngles ); idPhysics* ownerPhysics = position->GetTransport()->GetPhysics(); const idMat3& ownerAxes = ownerPhysics->GetAxis(); // transform the angles from world space into local space const idMat3 ownerAxesT = ownerAxes.Transpose(); idAngles localSpaceViewAngles = ( viewAngles.ToMat3() * ownerAxesT ).ToAngles(); idAngles localSpaceOldAngles = ( oldAngles.ToMat3() * ownerAxesT ).ToAngles(); sdVehiclePosition::ClampAngle( localSpaceViewAngles, localSpaceOldAngles, viewMode.clampPitch, 0 ); sdVehiclePosition::ClampAngle( localSpaceViewAngles, localSpaceOldAngles, viewMode.clampYaw, 1 ); // transform the clamped angles back into world space viewAngles = ( localSpaceViewAngles.ToMat3() * ownerAxes ).ToAngles(); // clean out the roll, it has a habit of accumulating little values viewAngles.roll = 0.0f; } /* ================ sdSmoothVehicleView_Free::CalcNewViewAngles ================ */ void sdSmoothVehicleView_Free::CalcNewViewAngles( idAngles& angles ) const { // angles relative to world angles = entryAim[ 0 ].ToAngles(); } /* ================ sdSmoothVehicleView_Free::DoTeleporting ================ */ void sdSmoothVehicleView_Free::DoTeleporting( viewEvalProperties_t& state ) const { state.viewAngles = state.ownerAngles; } /* ================ sdSmoothVehicleView_Free::CalculateAimMatrix ================ */ void sdSmoothVehicleView_Free::CalculateAimMatrix( const viewEvalProperties_t& state ) { evalState.aimMatrix = state.viewAngles.ToMat3(); } /* =============================================================================== sdSmoothVehicleView_Locked =============================================================================== */ /* ================ sdSmoothVehicleView_Locked::InTophat ================ */ bool sdSmoothVehicleView_Locked::InTophat( const idPlayer* player ) const { if ( player == NULL ) { return false; } if ( player->GetUserInfo().drivingCameraFreelook ) { return !player->usercmd.buttons.btn.tophat; } else { return player->usercmd.buttons.btn.tophat; } } /* ================ sdSmoothVehicleView_Locked::ClampViewAngles ================ */ void sdSmoothVehicleView_Locked::ClampViewAngles( idAngles& viewAngles, const idAngles& oldViewAngles ) const { idPlayer* player = position->GetPlayer(); assert( player != NULL ); if ( !InTophat( player ) ) { const idMat3& ownerAxes = position->GetTransport()->GetPhysics()->GetAxis(); idAngles ownerAngles = ownerAxes[ 0 ].ToAngles(); viewAngles = player->firstPersonViewAxis[ 0 ].ToAngles(); viewAngles.yaw -= ownerAngles.yaw; viewAngles.roll = 0.0f; viewAngles.Normalize180(); } else { idAngles oldAngles = oldViewAngles; DoFreshEntryAngles( viewAngles, oldAngles ); viewAngles.Normalize180(); viewAngles.pitch = idMath::ClampFloat( -40.0f, 40.0f, viewAngles.pitch ); } } /* ================ sdSmoothVehicleView_Locked::CalculateViewPos ================ */ void sdSmoothVehicleView_Locked::CalculateViewPos( idPlayer* player, idVec3& origin, idMat3& axis, bool fullUpdate ) { if ( !fullUpdate ) { origin = thirdPersonViewOrigin; axis = thirdPersonViewAxes; return; } // update transitioning in and out of tophat if ( InTophat( player )) { topHatTransition += MS2SEC( gameLocal.msec )*2.0f; } else { topHatTransition -= MS2SEC( gameLocal.msec )*2.0f; } topHatTransition = idMath::ClampFloat( 0.0f, 1.0f, topHatTransition ); sdSmoothVehicleView::CalculateViewPos( player, origin, axis, fullUpdate ); } /* ================ sdSmoothVehicleView_Locked::OnPlayerSwitched ================ */ void sdSmoothVehicleView_Locked::OnPlayerSwitched( idPlayer* player, bool newPosition ) { sdSmoothVehicleView::OnPlayerSwitched( player, newPosition ); topHatTransition = 0.0f; previousViewAngles = player->clientViewAngles; } /* ================ sdSmoothVehicleView_Locked::CalcNewViewAngles ================ */ void sdSmoothVehicleView_Locked::CalcNewViewAngles( idAngles& angles ) const { // yaw relative to body const idMat3& ownerAxes = position->GetTransport()->GetPhysics()->GetAxis(); idAngles ownerAngles = ownerAxes[ 0 ].ToAngles(); angles = entryAim[ 0 ].ToAngles(); angles.yaw -= ownerAngles.yaw; angles.Normalize180(); } /* ================ sdSmoothVehicleView_Locked::DoTeleporting ================ */ void sdSmoothVehicleView_Locked::DoTeleporting( viewEvalProperties_t& state ) const { state.viewAngles.Zero(); } /* ================ sdSmoothVehicleView_Locked::CalculateAimMatrix ================ */ void sdSmoothVehicleView_Locked::CalculateAimMatrix( const viewEvalProperties_t& state ) { // aggressively dampen the pitch idAngles idealAngles( state.ownerAngles.pitch * 0.5f, state.ownerAngles.yaw, 0.0f ); // don't let it tilt up too far, it looks silly if ( idealAngles.pitch < -25.0f ) { idealAngles.pitch = -25.0f; } if ( !firstFrame ) { idAngles fromAngles = ( previousRawAimMatrix ).ToAngles(); float pitchDiff = idMath::AngleDelta( idealAngles.pitch, fromAngles.pitch ); float pitchDampSpeed = viewMode.dampSpeed * viewMode.dampSpeed; float ownerSpeedSqr = InchesToMetres( state.ownerSpeed*state.ownerSpeed ); // hooray for magic numbers! // TODO: make this configurable, or at least a #define if ( ownerSpeedSqr < 7500.0f ) { float targetPitchDampSpeed = viewMode.dampSpeed; float distToGo = targetPitchDampSpeed - pitchDampSpeed; pitchDampSpeed += distToGo * ( 1.0f - ( ownerSpeedSqr / 7500.0f ) ); } idealAngles.pitch = idMath::AngleNormalize180( fromAngles.pitch + pitchDiff * pitchDampSpeed ); } evalState.aimMatrix = idealAngles.ToMat3(); } /* ================ sdSmoothVehicleView_Locked::CalculateCameraDelta ================ */ idVec3 sdSmoothVehicleView_Locked::CalculateCameraDelta( const viewEvalProperties_t& state ) { float cameraDist = viewMode.cameraDistance; float cameraHeight = viewMode.cameraHeight; idVec3 cameraDelta( -cameraDist, 0.0f, cameraHeight ); cameraDelta = cameraDelta * evalState.aimMatrix; idVec3 noTopHatFutureDelta = cameraDelta; idVec3 currentDelta = state.oldOrigin - state.ownerOrigin; if ( !firstFrame && topHatTransition < 1.0f ) { // world space idVec3 futureDelta = cameraDelta; idVec3 futurePredictedPosition = state.ownerOrigin + /*state.ownerVelocity * 0.5f +*/ state.ownerAxes[ 0 ] * 128.0f; idVec3 turningPredict = 15.0f * ( state.ownerPhysics->GetAngularVelocity() * state.ownerAxes[ 2 ] ) * state.ownerAxes[ 1 ]; if ( state.ownerAxes[ 0 ] * state.ownerVelocity < 0.0f ) { turningPredict = -turningPredict; } futurePredictedPosition += turningPredict; idVec3 directionToFuture = futurePredictedPosition - state.ownerOrigin; float distanceFromHere = directionToFuture.Normalize(); if ( distanceFromHere < 32.0f ) { futureDelta = currentDelta; } else { directionToFuture.z = cameraHeight; directionToFuture.x *= -cameraDist; directionToFuture.y *= -cameraDist; futureDelta = directionToFuture; } // soften the blow - don't let the delta rotate too fast float currentYawAngle = RAD2DEG( idMath::ATan( currentDelta.y, currentDelta.x ) ); float newYawAngle = RAD2DEG( idMath::ATan( futureDelta.y, futureDelta.x ) ); float yawDiff = idMath::AngleDelta( newYawAngle, currentYawAngle ); float maxRotateSpeedMin = 360.0f * MS2SEC( gameLocal.msec ); float maxRotateSpeedMax = 2000.0f * MS2SEC( gameLocal.msec ); float maxRotateSpeed = Lerp( maxRotateSpeedMin, maxRotateSpeedMax, idMath::Fabs( yawDiff ) / 180.0f ); yawDiff = idMath::ClampFloat( -maxRotateSpeed, maxRotateSpeed, yawDiff ); newYawAngle = currentYawAngle + yawDiff; futureDelta.x = cameraDist * idMath::Cos( DEG2RAD( newYawAngle ) ); futureDelta.y = cameraDist * idMath::Sin( DEG2RAD( newYawAngle ) ); // modify the aim matrix to fit what all this has done noTopHatFutureDelta = cameraDelta = futureDelta; } if ( topHatTransition > 0.0f ) { idAngles topHatViewAngles = state.viewAngles; topHatViewAngles.yaw += state.ownerAngles.yaw; // dampen the vehicle's motion a little if ( topHatTransition >= 1.0f ) { float aimingDiff = idMath::AngleDelta( state.viewAngles.yaw, previousViewAngles.yaw ); float resultDiff = idMath::AngleDelta( topHatViewAngles.yaw, state.oldAxisAngles.yaw ); resultDiff = idMath::AngleDelta( resultDiff, aimingDiff ); topHatViewAngles.yaw -= resultDiff * 0.8f; } idVec3 topHatDelta = topHatViewAngles.ToMat3()[ 0 ] * -cameraDist; topHatDelta.z += cameraHeight; // blend in the delta it'd have without top hat pressed if ( topHatTransition < 1.0f ) { cameraDelta = Lerp( noTopHatFutureDelta, topHatDelta, topHatTransition ); idVec3 deltaDirection = cameraDelta; float deltaLength = deltaDirection.Normalize(); if ( deltaLength > idMath::FLT_EPSILON ) { float idealLength = topHatDelta.Length(); cameraDelta = deltaDirection * idealLength; } else { cameraDelta = currentDelta; } } else { cameraDelta = topHatDelta; } } previousViewAngles = state.viewAngles; return cameraDelta; } /* ================ sdSmoothVehicleView_Locked::ClampToViewConstraints ================ */ void sdSmoothVehicleView_Locked::ClampToViewConstraints( const viewEvalProperties_t& state ) { if ( InTophat( state.driver ) ) { // clamp the resultant angles idAngles localAxisAngles = ( evalState.cameraAxis * state.ownerAxesT ).ToAngles(); localAxisAngles.roll = 0.0f; idAngles clampedAxisAngles = localAxisAngles; clampedAxisAngles.pitch = idMath::ClampFloat( -40.0f, 40.0f, clampedAxisAngles.pitch ); if ( localAxisAngles != clampedAxisAngles ) { idMat3 ownerSpaceAxis = clampedAxisAngles.ToMat3(); evalState.cameraAxis = ownerSpaceAxis * state.ownerAxes; } } } /* ================ sdSmoothVehicleView_Locked::DampenMotion ================ */ void sdSmoothVehicleView_Locked::DampenMotion( const viewEvalProperties_t& state ) { bool topHatOverride = false; if ( topHatTransition == 1.0f ) { topHatOverride = true; } if ( !topHatOverride ) { sdSmoothVehicleView::DampenMotion( state ); } } /* ================ sdSmoothVehicleView_Locked::DampenYaw ================ */ float sdSmoothVehicleView_Locked::DampenYaw( float input ) const { float dampSpeed = Lerp( viewMode.dampSpeed, 1.0f, topHatTransition ); return input * dampSpeed; } /* ================ sdSmoothVehicleView_Locked::DampenPitch ================ */ float sdSmoothVehicleView_Locked::DampenPitch( float input ) const { float dampSpeed = Lerp( viewMode.dampSpeed, 1.0f, topHatTransition ); return input * dampSpeed; } /* =============================================================================== sdIcarusVehicleView =============================================================================== */ /* ================ sdIcarusVehicleView::CalculateViewPos ================ */ void sdIcarusVehicleView::CalculateViewPos( idPlayer* player, idVec3& origin, idMat3& axis, bool fullUpdate ) { if ( freshEntry && fullUpdate ) { // ensure that the angles have been clamped to the valid range idAngles tempAngles = player->clientViewAngles; ClampViewAngles( player->clientViewAngles, tempAngles ); freshEntry = true; } origin = position->GetTransport()->GetPhysics()->GetOrigin(); axis = player->clientViewAngles.ToMat3(); sdTeleporter* teleportEnt = position->GetTransport()->GetTeleportEntity(); if ( teleportEnt != NULL ) { idPlayer* player = gameLocal.GetLocalViewPlayer(); if ( player != NULL && player->GetProxyEntity() == position->GetTransport() ) { idEntity* viewer = teleportEnt->GetViewEntity(); if ( viewer != NULL ) { origin = viewer->GetPhysics()->GetOrigin(); axis = viewer->GetPhysics()->GetAxis(); } } } origin += player->GetEyeOffset( idPlayer::EP_NORMAL ); thirdPersonViewOrigin = origin; thirdPersonViewAxes = axis; } /* ================ sdIcarusVehicleView::GetRequiredViewAngles ================ */ const idAngles sdIcarusVehicleView::GetRequiredViewAngles( const idVec3& aimPosition ) const { idVec3 deltaToTarget = aimPosition - position->GetTransport()->GetPhysics()->GetOrigin(); deltaToTarget.Normalize(); return deltaToTarget.ToAngles(); } /* ================ sdIcarusVehicleView::SetupEyes ================ */ void sdIcarusVehicleView::SetupEyes( idAnimatedEntity* other ) { } /* ================ sdIcarusVehicleView::CalcNewViewAngles ================ */ void sdIcarusVehicleView::CalcNewViewAngles( idAngles& angles ) const { }