From 39ed87570bdb2f86969d4be821c94b722dc71179 Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Wed, 26 Jun 2013 15:22:04 -0700 Subject: First version of the SOurce SDK 2013 --- mp/src/game/shared/vehicle_viewblend_shared.cpp | 416 ++++++++++++++++++++++++ 1 file changed, 416 insertions(+) create mode 100644 mp/src/game/shared/vehicle_viewblend_shared.cpp (limited to 'mp/src/game/shared/vehicle_viewblend_shared.cpp') diff --git a/mp/src/game/shared/vehicle_viewblend_shared.cpp b/mp/src/game/shared/vehicle_viewblend_shared.cpp new file mode 100644 index 00000000..af71fba6 --- /dev/null +++ b/mp/src/game/shared/vehicle_viewblend_shared.cpp @@ -0,0 +1,416 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Used to calculate the player's view in the vehicle +// +//============================================================================= + +#include "cbase.h" +#include "vehicle_viewblend_shared.h" + +#ifdef CLIENT_DLL + +// Client includes +#include "c_prop_vehicle.h" +#include "view.h" + +#else +// Server include +#include "vehicle_base.h" + +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifdef CLIENT_DLL + +extern ConVar default_fov; + +#define CPropVehicleDriveable C_PropVehicleDriveable + +#endif // CLIENT_DLL + +extern ConVar r_VehicleViewDampen; + +BEGIN_SIMPLE_DATADESC( ViewSmoothingData_t ) + DEFINE_FIELD( vecAnglesSaved, FIELD_VECTOR ), + DEFINE_FIELD( vecOriginSaved, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( vecAngleDiffSaved, FIELD_VECTOR ), + DEFINE_FIELD( vecAngleDiffMin, FIELD_VECTOR ), + DEFINE_FIELD( bRunningEnterExit, FIELD_BOOLEAN ), + DEFINE_FIELD( bWasRunningAnim, FIELD_BOOLEAN ), + DEFINE_FIELD( flEnterExitStartTime, FIELD_FLOAT ), + DEFINE_FIELD( flEnterExitDuration, FIELD_FLOAT ), + DEFINE_FIELD( flFOV, FIELD_FLOAT ), + + // These are filled out in the vehicle's constructor: + //CBaseAnimating *pVehicle; + //bool bClampEyeAngles; + //float flPitchCurveZero; + //float flPitchCurveLinear; + //float flRollCurveZero; + //float flRollCurveLinear; + //ViewLockData_t pitchLockData; + //ViewLockData_t rollLockData; + //bool bDampenEyePosition; +END_DATADESC() + +// remaps an angular variable to a 3 band function: +// 0 <= t < start : f(t) = 0 +// start <= t <= end : f(t) = end * spline(( t-start) / (end-start) ) // s curve between clamped and linear +// end < t : f(t) = t +float RemapAngleRange( float startInterval, float endInterval, float value, RemapAngleRange_CurvePart_t *peCurvePart ) +{ + // Fixup the roll + value = AngleNormalize( value ); + float absAngle = fabs(value); + + // beneath cutoff? + if ( absAngle < startInterval ) + { + if ( peCurvePart ) + { + *peCurvePart = RemapAngleRange_CurvePart_Zero; + } + value = 0; + } + // in spline range? + else if ( absAngle <= endInterval ) + { + float newAngle = SimpleSpline( (absAngle - startInterval) / (endInterval-startInterval) ) * endInterval; + + // grab the sign from the initial value + if ( value < 0 ) + { + newAngle *= -1; + } + + if ( peCurvePart ) + { + *peCurvePart = RemapAngleRange_CurvePart_Spline; + } + value = newAngle; + } + // else leave it alone, in linear range + else if ( peCurvePart ) + { + *peCurvePart = RemapAngleRange_CurvePart_Linear; + } + + return value; +} + +//----------------------------------------------------------------------------- +// Purpose: For a given degree of freedom, blends between the raw and clamped +// view depending on this vehicle's preferences. When vehicles wreck +// catastrophically, it's often better to lock the view for a little +// while until things settle down than to keep trying to clamp/flatten +// the view artificially because we can never really catch up with +// the chaotic flipping. +//----------------------------------------------------------------------------- +float ApplyViewLocking( float flAngleRaw, float flAngleClamped, ViewLockData_t &lockData, RemapAngleRange_CurvePart_t eCurvePart ) +{ + // If we're set up to never lock this degree of freedom, return the clamped value. + if ( lockData.flLockInterval == 0 ) + return flAngleClamped; + + float flAngleOut = flAngleClamped; + + // Lock the view if we're in the linear part of the curve, and keep it locked + // until some duration after we return to the flat (zero) part of the curve. + if ( ( eCurvePart == RemapAngleRange_CurvePart_Linear ) || + ( lockData.bLocked && ( eCurvePart == RemapAngleRange_CurvePart_Spline ) ) ) + { + //Msg( "LOCKED\n" ); + lockData.bLocked = true; + lockData.flUnlockTime = gpGlobals->curtime + lockData.flLockInterval; + flAngleOut = flAngleRaw; + } + else + { + if ( ( lockData.bLocked ) && ( gpGlobals->curtime > lockData.flUnlockTime ) ) + { + lockData.bLocked = false; + if ( lockData.flUnlockBlendInterval > 0 ) + { + lockData.flUnlockTime = gpGlobals->curtime; + } + else + { + lockData.flUnlockTime = 0; + } + } + + if ( !lockData.bLocked ) + { + if ( lockData.flUnlockTime != 0 ) + { + // Blend out from the locked raw view (no remapping) to a remapped view. + float flBlend = RemapValClamped( gpGlobals->curtime - lockData.flUnlockTime, 0, lockData.flUnlockBlendInterval, 0, 1 ); + //Msg( "BLEND %f\n", flBlend ); + + flAngleOut = Lerp( flBlend, flAngleRaw, flAngleClamped ); + if ( flBlend >= 1.0f ) + { + lockData.flUnlockTime = 0; + } + } + else + { + // Not blending out from a locked view to a remapped view. + //Msg( "CLAMPED\n" ); + flAngleOut = flAngleClamped; + } + } + else + { + //Msg( "STILL LOCKED\n" ); + flAngleOut = flAngleRaw; + } + } + + return flAngleOut; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pData - +// vehicleEyeAngles - +//----------------------------------------------------------------------------- +void RemapViewAngles( ViewSmoothingData_t *pData, QAngle &vehicleEyeAngles ) +{ + QAngle vecEyeAnglesRemapped; + + // Clamp pitch. + RemapAngleRange_CurvePart_t ePitchCurvePart; + vecEyeAnglesRemapped.x = RemapAngleRange( pData->flPitchCurveZero, pData->flPitchCurveLinear, vehicleEyeAngles.x, &ePitchCurvePart ); + + vehicleEyeAngles.z = vecEyeAnglesRemapped.z = AngleNormalize( vehicleEyeAngles.z ); + + // Blend out the roll dampening as our pitch approaches 90 degrees, to avoid gimbal lock problems. + float flBlendRoll = 1.0; + if ( fabs( vehicleEyeAngles.x ) > 60 ) + { + flBlendRoll = RemapValClamped( fabs( vecEyeAnglesRemapped.x ), 60, 80, 1, 0); + } + + RemapAngleRange_CurvePart_t eRollCurvePart; + float flRollDamped = RemapAngleRange( pData->flRollCurveZero, pData->flRollCurveLinear, vecEyeAnglesRemapped.z, &eRollCurvePart ); + vecEyeAnglesRemapped.z = Lerp( flBlendRoll, vecEyeAnglesRemapped.z, flRollDamped ); + + //Msg("PITCH "); + vehicleEyeAngles.x = ApplyViewLocking( vehicleEyeAngles.x, vecEyeAnglesRemapped.x, pData->pitchLockData, ePitchCurvePart ); + + //Msg("ROLL "); + vehicleEyeAngles.z = ApplyViewLocking( vehicleEyeAngles.z, vecEyeAnglesRemapped.z, pData->rollLockData, eRollCurvePart ); +} + +//----------------------------------------------------------------------------- +// Purpose: Vehicle dampening shared between server and client +//----------------------------------------------------------------------------- +void SharedVehicleViewSmoothing(CBasePlayer *pPlayer, + Vector *pAbsOrigin, QAngle *pAbsAngles, + bool bEnterAnimOn, bool bExitAnimOn, + const Vector &vecEyeExitEndpoint, + ViewSmoothingData_t *pData, + float *pFOV ) +{ + int eyeAttachmentIndex = pData->pVehicle->LookupAttachment( "vehicle_driver_eyes" ); + matrix3x4_t vehicleEyePosToWorld; + Vector vehicleEyeOrigin; + QAngle vehicleEyeAngles; + pData->pVehicle->GetAttachment( eyeAttachmentIndex, vehicleEyeOrigin, vehicleEyeAngles ); + AngleMatrix( vehicleEyeAngles, vehicleEyePosToWorld ); + + // Dampen the eye positional change as we drive around. + *pAbsAngles = pPlayer->EyeAngles(); + if ( r_VehicleViewDampen.GetInt() && pData->bDampenEyePosition ) + { + CPropVehicleDriveable *pDriveable = assert_cast(pData->pVehicle); + pDriveable->DampenEyePosition( vehicleEyeOrigin, vehicleEyeAngles ); + } + + // Started running an entry or exit anim? + bool bRunningAnim = ( bEnterAnimOn || bExitAnimOn ); + if ( bRunningAnim && !pData->bWasRunningAnim ) + { + pData->bRunningEnterExit = true; + pData->flEnterExitStartTime = gpGlobals->curtime; + pData->flEnterExitDuration = pData->pVehicle->SequenceDuration( pData->pVehicle->GetSequence() ); + +#ifdef CLIENT_DLL + pData->vecOriginSaved = PrevMainViewOrigin(); + pData->vecAnglesSaved = PrevMainViewAngles(); +#endif + + // Save our initial angular error, which we will blend out over the length of the animation. + pData->vecAngleDiffSaved.x = AngleDiff( vehicleEyeAngles.x, pData->vecAnglesSaved.x ); + pData->vecAngleDiffSaved.y = AngleDiff( vehicleEyeAngles.y, pData->vecAnglesSaved.y ); + pData->vecAngleDiffSaved.z = AngleDiff( vehicleEyeAngles.z, pData->vecAnglesSaved.z ); + + pData->vecAngleDiffMin = pData->vecAngleDiffSaved; + } + + pData->bWasRunningAnim = bRunningAnim; + + float frac = 0; + float flFracFOV = 0; + + // If we're in an enter/exit animation, blend the player's eye angles to the attachment's + if ( bRunningAnim || pData->bRunningEnterExit ) + { + *pAbsAngles = vehicleEyeAngles; + + // Forward integrate to determine the elapsed time in this entry/exit anim. + frac = ( gpGlobals->curtime - pData->flEnterExitStartTime ) / pData->flEnterExitDuration; + frac = clamp( frac, 0.0f, 1.0f ); + + flFracFOV = ( gpGlobals->curtime - pData->flEnterExitStartTime ) / ( pData->flEnterExitDuration * 0.85f ); + flFracFOV = clamp( flFracFOV, 0.0f, 1.0f ); + + //Msg("Frac: %f\n", frac ); + + if ( frac < 1.0 ) + { + // Blend to the desired vehicle eye origin + //Vector vecToView = (vehicleEyeOrigin - PrevMainViewOrigin()); + //vehicleEyeOrigin = PrevMainViewOrigin() + (vecToView * SimpleSpline(frac)); + //debugoverlay->AddBoxOverlay( vehicleEyeOrigin, -Vector(1,1,1), Vector(1,1,1), vec3_angle, 0,255,255, 64, 10 ); + } + else + { + pData->bRunningEnterExit = false; + + // Enter animation has finished, align view with the eye attachment point + // so they can start mouselooking around. + if ( !bExitAnimOn ) + { + Vector localEyeOrigin; + QAngle localEyeAngles; + + pData->pVehicle->GetAttachmentLocal( eyeAttachmentIndex, localEyeOrigin, localEyeAngles ); +#ifdef CLIENT_DLL + engine->SetViewAngles( localEyeAngles ); +#endif + } + } + } + + // Compute the relative rotation between the unperturbed eye attachment + the eye angles + matrix3x4_t cameraToWorld; + AngleMatrix( *pAbsAngles, cameraToWorld ); + + matrix3x4_t worldToEyePos; + MatrixInvert( vehicleEyePosToWorld, worldToEyePos ); + + matrix3x4_t vehicleCameraToEyePos; + ConcatTransforms( worldToEyePos, cameraToWorld, vehicleCameraToEyePos ); + + // Damp out some of the vehicle motion (neck/head would do this) + if ( pData->bClampEyeAngles ) + { + RemapViewAngles( pData, vehicleEyeAngles ); + } + + AngleMatrix( vehicleEyeAngles, vehicleEyeOrigin, vehicleEyePosToWorld ); + + // Now treat the relative eye angles as being relative to this new, perturbed view position... + matrix3x4_t newCameraToWorld; + ConcatTransforms( vehicleEyePosToWorld, vehicleCameraToEyePos, newCameraToWorld ); + + // output new view abs angles + MatrixAngles( newCameraToWorld, *pAbsAngles ); + + // UNDONE: *pOrigin would already be correct in single player if the HandleView() on the server ran after vphysics + MatrixGetColumn( newCameraToWorld, 3, *pAbsOrigin ); + + float flDefaultFOV; +#ifdef CLIENT_DLL + flDefaultFOV = default_fov.GetFloat(); +#else + flDefaultFOV = pPlayer->GetDefaultFOV(); +#endif + + // If we're playing an entry or exit animation... + if ( bRunningAnim || pData->bRunningEnterExit ) + { + float flSplineFrac = clamp( SimpleSpline( frac ), 0.f, 1.f ); + + // Blend out the error between the player's initial eye angles and the animation's initial + // eye angles over the duration of the animation. + QAngle vecAngleDiffBlend = ( ( 1 - flSplineFrac ) * pData->vecAngleDiffSaved ); + + // If our current error is less than the error amount that we're blending + // out, use that. This lets the angles converge as quickly as possible. + QAngle vecAngleDiffCur; + vecAngleDiffCur.x = AngleDiff( vehicleEyeAngles.x, pData->vecAnglesSaved.x ); + vecAngleDiffCur.y = AngleDiff( vehicleEyeAngles.y, pData->vecAnglesSaved.y ); + vecAngleDiffCur.z = AngleDiff( vehicleEyeAngles.z, pData->vecAnglesSaved.z ); + + // In either case, never increase the error, so track the minimum error and clamp to that. + for (int i = 0; i < 3; i++) + { + if ( fabs(vecAngleDiffCur[i] ) < fabs( pData->vecAngleDiffMin[i] ) ) + { + pData->vecAngleDiffMin[i] = vecAngleDiffCur[i]; + } + + if ( fabs(vecAngleDiffBlend[i] ) < fabs( pData->vecAngleDiffMin[i] ) ) + { + pData->vecAngleDiffMin[i] = vecAngleDiffBlend[i]; + } + } + + // Add the error to the animation's eye angles. + *pAbsAngles -= pData->vecAngleDiffMin; + + // Use this as the basis for the next error calculation. + pData->vecAnglesSaved = *pAbsAngles; + + //if ( gpGlobals->frametime ) + //{ + // Msg("Angle : %.2f %.2f %.2f\n", target.x, target.y, target.z ); + //} + //Msg("Prev: %.2f %.2f %.2f\n", pData->vecAnglesSaved.x, pData->vecAnglesSaved.y, pData->vecAnglesSaved.z ); + + Vector vecAbsOrigin = *pAbsOrigin; + + // If we're exiting, our desired position is the server-sent exit position + if ( bExitAnimOn ) + { + //debugoverlay->AddBoxOverlay( vecEyeExitEndpoint, -Vector(1,1,1), Vector(1,1,1), vec3_angle, 255,255,255, 64, 10 ); + + // Blend to the exit position + *pAbsOrigin = Lerp( flSplineFrac, vecAbsOrigin, vecEyeExitEndpoint ); + + if ( pFOV != NULL ) + { + if ( pData->flFOV > flDefaultFOV ) + { + *pFOV = Lerp( flFracFOV, pData->flFOV, flDefaultFOV ); + } + } + } + else + { + // Blend from our starting position to the desired origin + *pAbsOrigin = Lerp( flSplineFrac, pData->vecOriginSaved, vecAbsOrigin ); + + if ( pFOV != NULL ) + { + if ( pData->flFOV > flDefaultFOV ) + { + *pFOV = Lerp( flFracFOV, flDefaultFOV, pData->flFOV ); + } + } + } + } + else if ( pFOV != NULL ) + { + if ( pData->flFOV > flDefaultFOV ) + { + // Not running an entry/exit anim. Just use the vehicle's FOV. + *pFOV = pData->flFOV; + } + } +} -- cgit v1.2.3