Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

451 lines
14 KiB

  1. //====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose: Used to calculate the player's view in the vehicle
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "vehicle_viewblend_shared.h"
  8. #ifdef CLIENT_DLL
  9. // Client includes
  10. #include "c_prop_vehicle.h"
  11. #include "view.h"
  12. #else
  13. // Server include
  14. #include "vehicle_base.h"
  15. #endif
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. #ifdef CLIENT_DLL
  19. extern ConVar default_fov;
  20. #define CPropVehicleDriveable C_PropVehicleDriveable
  21. #endif // CLIENT_DLL
  22. extern ConVar r_VehicleViewDampen;
  23. BEGIN_SIMPLE_DATADESC( ViewSmoothingData_t )
  24. DEFINE_FIELD( vecAnglesSaved, FIELD_VECTOR ),
  25. DEFINE_FIELD( vecOriginSaved, FIELD_POSITION_VECTOR ),
  26. DEFINE_FIELD( vecAngleDiffSaved, FIELD_VECTOR ),
  27. DEFINE_FIELD( vecAngleDiffMin, FIELD_VECTOR ),
  28. DEFINE_FIELD( bRunningEnterExit, FIELD_BOOLEAN ),
  29. DEFINE_FIELD( bWasRunningAnim, FIELD_BOOLEAN ),
  30. DEFINE_FIELD( flEnterExitStartTime, FIELD_FLOAT ),
  31. DEFINE_FIELD( flEnterExitDuration, FIELD_FLOAT ),
  32. DEFINE_FIELD( flFOV, FIELD_FLOAT ),
  33. // These are filled out in the vehicle's constructor:
  34. //CBaseAnimating *pVehicle;
  35. //bool bClampEyeAngles;
  36. //float flPitchCurveZero;
  37. //float flPitchCurveLinear;
  38. //float flRollCurveZero;
  39. //float flRollCurveLinear;
  40. //ViewLockData_t pitchLockData;
  41. //ViewLockData_t rollLockData;
  42. //bool bDampenEyePosition;
  43. END_DATADESC()
  44. // remaps an angular variable to a 3 band function:
  45. // 0 <= t < start : f(t) = 0
  46. // start <= t <= end : f(t) = end * spline(( t-start) / (end-start) ) // s curve between clamped and linear
  47. // end < t : f(t) = t
  48. float RemapAngleRange( float startInterval, float endInterval, float value, RemapAngleRange_CurvePart_t *peCurvePart )
  49. {
  50. // Fixup the roll
  51. value = AngleNormalize( value );
  52. float absAngle = fabs(value);
  53. // beneath cutoff?
  54. if ( absAngle < startInterval )
  55. {
  56. if ( peCurvePart )
  57. {
  58. *peCurvePart = RemapAngleRange_CurvePart_Zero;
  59. }
  60. value = 0;
  61. }
  62. // in spline range?
  63. else if ( absAngle <= endInterval )
  64. {
  65. float newAngle = SimpleSpline( (absAngle - startInterval) / (endInterval-startInterval) ) * endInterval;
  66. // grab the sign from the initial value
  67. if ( value < 0 )
  68. {
  69. newAngle *= -1;
  70. }
  71. if ( peCurvePart )
  72. {
  73. *peCurvePart = RemapAngleRange_CurvePart_Spline;
  74. }
  75. value = newAngle;
  76. }
  77. // else leave it alone, in linear range
  78. else if ( peCurvePart )
  79. {
  80. *peCurvePart = RemapAngleRange_CurvePart_Linear;
  81. }
  82. return value;
  83. }
  84. //-----------------------------------------------------------------------------
  85. // Purpose: For a given degree of freedom, blends between the raw and clamped
  86. // view depending on this vehicle's preferences. When vehicles wreck
  87. // catastrophically, it's often better to lock the view for a little
  88. // while until things settle down than to keep trying to clamp/flatten
  89. // the view artificially because we can never really catch up with
  90. // the chaotic flipping.
  91. //-----------------------------------------------------------------------------
  92. float ApplyViewLocking( float flAngleRaw, float flAngleClamped, ViewLockData_t &lockData, RemapAngleRange_CurvePart_t eCurvePart )
  93. {
  94. // If we're set up to never lock this degree of freedom, return the clamped value.
  95. if ( lockData.flLockInterval == 0 )
  96. return flAngleClamped;
  97. float flAngleOut = flAngleClamped;
  98. // Lock the view if we're in the linear part of the curve, and keep it locked
  99. // until some duration after we return to the flat (zero) part of the curve.
  100. if ( ( eCurvePart == RemapAngleRange_CurvePart_Linear ) ||
  101. ( lockData.bLocked && ( eCurvePart == RemapAngleRange_CurvePart_Spline ) ) )
  102. {
  103. //Msg( "LOCKED\n" );
  104. lockData.bLocked = true;
  105. lockData.flUnlockTime = gpGlobals->curtime + lockData.flLockInterval;
  106. flAngleOut = flAngleRaw;
  107. }
  108. else
  109. {
  110. if ( ( lockData.bLocked ) && ( gpGlobals->curtime > lockData.flUnlockTime ) )
  111. {
  112. lockData.bLocked = false;
  113. if ( lockData.flUnlockBlendInterval > 0 )
  114. {
  115. lockData.flUnlockTime = gpGlobals->curtime;
  116. }
  117. else
  118. {
  119. lockData.flUnlockTime = 0;
  120. }
  121. }
  122. if ( !lockData.bLocked )
  123. {
  124. if ( lockData.flUnlockTime != 0 )
  125. {
  126. // Blend out from the locked raw view (no remapping) to a remapped view.
  127. float flBlend = RemapValClamped( gpGlobals->curtime - lockData.flUnlockTime, 0, lockData.flUnlockBlendInterval, 0, 1 );
  128. //Msg( "BLEND %f\n", flBlend );
  129. flAngleOut = Lerp( flBlend, flAngleRaw, flAngleClamped );
  130. if ( flBlend >= 1.0f )
  131. {
  132. lockData.flUnlockTime = 0;
  133. }
  134. }
  135. else
  136. {
  137. // Not blending out from a locked view to a remapped view.
  138. //Msg( "CLAMPED\n" );
  139. flAngleOut = flAngleClamped;
  140. }
  141. }
  142. else
  143. {
  144. //Msg( "STILL LOCKED\n" );
  145. flAngleOut = flAngleRaw;
  146. }
  147. }
  148. return flAngleOut;
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Purpose:
  152. // Input : pData -
  153. // vehicleEyeAngles -
  154. //-----------------------------------------------------------------------------
  155. void RemapViewAngles( ViewSmoothingData_t *pData, QAngle &vehicleEyeAngles )
  156. {
  157. QAngle vecEyeAnglesRemapped;
  158. // Clamp pitch.
  159. RemapAngleRange_CurvePart_t ePitchCurvePart;
  160. vecEyeAnglesRemapped.x = RemapAngleRange( pData->flPitchCurveZero, pData->flPitchCurveLinear, vehicleEyeAngles.x, &ePitchCurvePart );
  161. vehicleEyeAngles.z = vecEyeAnglesRemapped.z = AngleNormalize( vehicleEyeAngles.z );
  162. // Blend out the roll dampening as our pitch approaches 90 degrees, to avoid gimbal lock problems.
  163. float flBlendRoll = 1.0;
  164. if ( fabs( vehicleEyeAngles.x ) > 60 )
  165. {
  166. flBlendRoll = RemapValClamped( fabs( vecEyeAnglesRemapped.x ), 60, 80, 1, 0);
  167. }
  168. RemapAngleRange_CurvePart_t eRollCurvePart;
  169. float flRollDamped = RemapAngleRange( pData->flRollCurveZero, pData->flRollCurveLinear, vecEyeAnglesRemapped.z, &eRollCurvePart );
  170. vecEyeAnglesRemapped.z = Lerp( flBlendRoll, vecEyeAnglesRemapped.z, flRollDamped );
  171. //Msg("PITCH ");
  172. vehicleEyeAngles.x = ApplyViewLocking( vehicleEyeAngles.x, vecEyeAnglesRemapped.x, pData->pitchLockData, ePitchCurvePart );
  173. //Msg("ROLL ");
  174. vehicleEyeAngles.z = ApplyViewLocking( vehicleEyeAngles.z, vecEyeAnglesRemapped.z, pData->rollLockData, eRollCurvePart );
  175. }
  176. //-----------------------------------------------------------------------------
  177. // Purpose: Vehicle dampening shared between server and client
  178. //-----------------------------------------------------------------------------
  179. void SharedVehicleViewSmoothing(CBasePlayer *pPlayer,
  180. Vector *pAbsOrigin, QAngle *pAbsAngles,
  181. bool bEnterAnimOn, bool bExitAnimOn,
  182. const Vector &vecEyeExitEndpoint,
  183. ViewSmoothingData_t *pData,
  184. float *pFOV, bool bForceViewToAttachment /*= false*/ )
  185. {
  186. int eyeAttachmentIndex = pData->pVehicle->LookupAttachment( "vehicle_driver_eyes" );
  187. matrix3x4_t vehicleEyePosToWorld;
  188. Vector vehicleEyeOrigin;
  189. QAngle vehicleEyeAngles;
  190. // 79061: When this gets called from CreateMove the attachment point can be invalid.
  191. // Forcing it to recalculate on 360 (the only platform the bug shows) for this entity and
  192. // it's hierarchy.
  193. #if defined ( CLIENT_DLL )
  194. if ( IsX360() )
  195. {
  196. C_BaseAnimating* pParent = (C_BaseAnimating*)pData->pVehicle->GetMoveParent();
  197. while ( pParent )
  198. {
  199. pParent->InvalidateBoneCache();
  200. pParent = (C_BaseAnimating*)pParent->GetMoveParent();
  201. }
  202. pData->pVehicle->InvalidateBoneCache();
  203. }
  204. #endif
  205. pData->pVehicle->GetAttachment( eyeAttachmentIndex, vehicleEyeOrigin, vehicleEyeAngles );
  206. AngleMatrix( vehicleEyeAngles, vehicleEyePosToWorld );
  207. if ( bForceViewToAttachment )
  208. {
  209. *pAbsOrigin = vehicleEyeOrigin;
  210. *pAbsAngles = vehicleEyeAngles;
  211. if ( pFOV != NULL )
  212. {
  213. *pFOV = pData->flFOV;
  214. }
  215. return;
  216. }
  217. // Dampen the eye positional change as we drive around.
  218. *pAbsAngles = pPlayer->EyeAngles();
  219. if ( r_VehicleViewDampen.GetInt() && pData->bDampenEyePosition )
  220. {
  221. CPropVehicleDriveable *pDriveable = assert_cast<CPropVehicleDriveable*>(pData->pVehicle);
  222. pDriveable->DampenEyePosition( vehicleEyeOrigin, vehicleEyeAngles );
  223. }
  224. // Started running an entry or exit anim?
  225. bool bRunningAnim = ( bEnterAnimOn || bExitAnimOn );
  226. if ( bRunningAnim && !pData->bWasRunningAnim )
  227. {
  228. pData->bRunningEnterExit = true;
  229. pData->flEnterExitStartTime = gpGlobals->curtime;
  230. pData->flEnterExitDuration = pData->pVehicle->SequenceDuration( pData->pVehicle->GetSequence() );
  231. #ifdef CLIENT_DLL
  232. pData->vecOriginSaved = PrevMainViewOrigin( pPlayer->GetSplitScreenPlayerSlot() );
  233. pData->vecAnglesSaved = PrevMainViewAngles( pPlayer->GetSplitScreenPlayerSlot() );
  234. #endif
  235. // Save our initial angular error, which we will blend out over the length of the animation.
  236. pData->vecAngleDiffSaved.x = AngleDiff( vehicleEyeAngles.x, pData->vecAnglesSaved.x );
  237. pData->vecAngleDiffSaved.y = AngleDiff( vehicleEyeAngles.y, pData->vecAnglesSaved.y );
  238. pData->vecAngleDiffSaved.z = AngleDiff( vehicleEyeAngles.z, pData->vecAnglesSaved.z );
  239. pData->vecAngleDiffMin = pData->vecAngleDiffSaved;
  240. }
  241. pData->bWasRunningAnim = bRunningAnim;
  242. float frac = 0;
  243. float flFracFOV = 0;
  244. // If we're in an enter/exit animation, blend the player's eye angles to the attachment's
  245. if ( bRunningAnim || pData->bRunningEnterExit )
  246. {
  247. *pAbsAngles = vehicleEyeAngles;
  248. // Forward integrate to determine the elapsed time in this entry/exit anim.
  249. frac = ( gpGlobals->curtime - pData->flEnterExitStartTime ) / pData->flEnterExitDuration;
  250. frac = clamp( frac, 0.0f, 1.0f );
  251. flFracFOV = ( gpGlobals->curtime - pData->flEnterExitStartTime ) / ( pData->flEnterExitDuration * 0.85f );
  252. flFracFOV = clamp( flFracFOV, 0.0f, 1.0f );
  253. //Msg("Frac: %f\n", frac );
  254. if ( frac < 1.0 )
  255. {
  256. // Blend to the desired vehicle eye origin
  257. //Vector vecToView = (vehicleEyeOrigin - PrevMainViewOrigin(pPlayer->GetSplitScreenPlayerSlot() ));
  258. //vehicleEyeOrigin = PrevMainViewOrigin(pPlayer->GetSplitScreenPlayerSlot() ) + (vecToView * SimpleSpline(frac));
  259. //debugoverlay->AddBoxOverlay( vehicleEyeOrigin, -Vector(1,1,1), Vector(1,1,1), vec3_angle, 0,255,255, 64, 10 );
  260. }
  261. else
  262. {
  263. pData->bRunningEnterExit = false;
  264. // Enter animation has finished, align view with the eye attachment point
  265. // so they can start mouselooking around.
  266. if ( !bExitAnimOn )
  267. {
  268. Vector localEyeOrigin;
  269. QAngle localEyeAngles;
  270. pData->pVehicle->GetAttachmentLocal( eyeAttachmentIndex, localEyeOrigin, localEyeAngles );
  271. #ifdef CLIENT_DLL
  272. engine->SetViewAngles( localEyeAngles );
  273. #endif
  274. }
  275. }
  276. }
  277. // Compute the relative rotation between the unperturbed eye attachment + the eye angles
  278. matrix3x4_t cameraToWorld;
  279. AngleMatrix( *pAbsAngles, cameraToWorld );
  280. matrix3x4_t worldToEyePos;
  281. MatrixInvert( vehicleEyePosToWorld, worldToEyePos );
  282. matrix3x4_t vehicleCameraToEyePos;
  283. ConcatTransforms( worldToEyePos, cameraToWorld, vehicleCameraToEyePos );
  284. // Damp out some of the vehicle motion (neck/head would do this)
  285. if ( pData->bClampEyeAngles )
  286. {
  287. RemapViewAngles( pData, vehicleEyeAngles );
  288. }
  289. AngleMatrix( vehicleEyeAngles, vehicleEyeOrigin, vehicleEyePosToWorld );
  290. // Now treat the relative eye angles as being relative to this new, perturbed view position...
  291. matrix3x4_t newCameraToWorld;
  292. ConcatTransforms( vehicleEyePosToWorld, vehicleCameraToEyePos, newCameraToWorld );
  293. // output new view abs angles
  294. MatrixAngles( newCameraToWorld, *pAbsAngles );
  295. // UNDONE: *pOrigin would already be correct in single player if the HandleView() on the server ran after vphysics
  296. MatrixGetColumn( newCameraToWorld, 3, *pAbsOrigin );
  297. float flDefaultFOV;
  298. #ifdef CLIENT_DLL
  299. flDefaultFOV = default_fov.GetFloat();
  300. #else
  301. flDefaultFOV = pPlayer->GetDefaultFOV();
  302. #endif
  303. // If we're playing an entry or exit animation...
  304. if ( bRunningAnim || pData->bRunningEnterExit )
  305. {
  306. float flSplineFrac = clamp( SimpleSpline( frac ), 0, 1 );
  307. // Blend out the error between the player's initial eye angles and the animation's initial
  308. // eye angles over the duration of the animation.
  309. QAngle vecAngleDiffBlend = ( ( 1 - flSplineFrac ) * pData->vecAngleDiffSaved );
  310. // If our current error is less than the error amount that we're blending
  311. // out, use that. This lets the angles converge as quickly as possible.
  312. QAngle vecAngleDiffCur;
  313. vecAngleDiffCur.x = AngleDiff( vehicleEyeAngles.x, pData->vecAnglesSaved.x );
  314. vecAngleDiffCur.y = AngleDiff( vehicleEyeAngles.y, pData->vecAnglesSaved.y );
  315. vecAngleDiffCur.z = AngleDiff( vehicleEyeAngles.z, pData->vecAnglesSaved.z );
  316. // In either case, never increase the error, so track the minimum error and clamp to that.
  317. for (int i = 0; i < 3; i++)
  318. {
  319. if ( fabs(vecAngleDiffCur[i] ) < fabs( pData->vecAngleDiffMin[i] ) )
  320. {
  321. pData->vecAngleDiffMin[i] = vecAngleDiffCur[i];
  322. }
  323. if ( fabs(vecAngleDiffBlend[i] ) < fabs( pData->vecAngleDiffMin[i] ) )
  324. {
  325. pData->vecAngleDiffMin[i] = vecAngleDiffBlend[i];
  326. }
  327. }
  328. // Add the error to the animation's eye angles.
  329. *pAbsAngles -= pData->vecAngleDiffMin;
  330. // Use this as the basis for the next error calculation.
  331. pData->vecAnglesSaved = *pAbsAngles;
  332. //if ( gpGlobals->frametime )
  333. //{
  334. // Msg("Angle : %.2f %.2f %.2f\n", target.x, target.y, target.z );
  335. //}
  336. //Msg("Prev: %.2f %.2f %.2f\n", pData->vecAnglesSaved.x, pData->vecAnglesSaved.y, pData->vecAnglesSaved.z );
  337. Vector vecAbsOrigin = *pAbsOrigin;
  338. // If we're exiting, our desired position is the server-sent exit position
  339. if ( bExitAnimOn )
  340. {
  341. //debugoverlay->AddBoxOverlay( vecEyeExitEndpoint, -Vector(1,1,1), Vector(1,1,1), vec3_angle, 255,255,255, 64, 10 );
  342. // Blend to the exit position
  343. *pAbsOrigin = Lerp( flSplineFrac, vecAbsOrigin, vecEyeExitEndpoint );
  344. if ( pFOV != NULL )
  345. {
  346. if ( pData->flFOV > flDefaultFOV )
  347. {
  348. *pFOV = Lerp( flFracFOV, pData->flFOV, flDefaultFOV );
  349. }
  350. }
  351. }
  352. else
  353. {
  354. // Blend from our starting position to the desired origin
  355. *pAbsOrigin = Lerp( flSplineFrac, pData->vecOriginSaved, vecAbsOrigin );
  356. if ( pFOV != NULL )
  357. {
  358. #if defined ( PORTAL2 )
  359. *pFOV = Lerp( flFracFOV, flDefaultFOV, pData->flFOV );
  360. #else
  361. if ( pData->flFOV > flDefaultFOV )
  362. {
  363. *pFOV = Lerp( flFracFOV, flDefaultFOV, pData->flFOV );
  364. }
  365. #endif
  366. }
  367. }
  368. }
  369. else if ( pFOV != NULL )
  370. {
  371. #if defined ( PORTAL2 )
  372. *pFOV = pData->flFOV;
  373. #else
  374. if ( pData->flFOV > flDefaultFOV )
  375. {
  376. // Not running an entry/exit anim. Just use the vehicle's FOV.
  377. *pFOV = pData->flFOV;
  378. }
  379. #endif
  380. }
  381. }