Team Fortress 2 Source Code as on 22/4/2020
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.

931 lines
29 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Client side implementation of the airboat.
  4. //
  5. // - Dampens motion of driver's view to reduce nausea.
  6. // - Autocenters driver's view after a period of inactivity.
  7. // - Controls headlights.
  8. // - Controls curve parameters for pitch/roll blending.
  9. //
  10. //=============================================================================//
  11. #include "cbase.h"
  12. #include "c_prop_vehicle.h"
  13. #include "datacache/imdlcache.h"
  14. #include "flashlighteffect.h"
  15. #include "movevars_shared.h"
  16. #include "ammodef.h"
  17. #include "SpriteTrail.h"
  18. #include "beamdraw.h"
  19. #include "enginesprite.h"
  20. #include "fx_quad.h"
  21. #include "fx.h"
  22. #include "fx_water.h"
  23. #include "engine/ivdebugoverlay.h"
  24. #include "view.h"
  25. #include "clienteffectprecachesystem.h"
  26. #include "c_basehlplayer.h"
  27. #include "vgui_controls/Controls.h"
  28. #include "vgui/ISurface.h"
  29. // memdbgon must be the last include file in a .cpp file!!!
  30. #include "tier0/memdbgon.h"
  31. ConVar r_AirboatViewBlendTo( "r_AirboatViewBlendTo", "1", FCVAR_CHEAT );
  32. ConVar r_AirboatViewBlendToScale( "r_AirboatViewBlendToScale", "0.03", FCVAR_CHEAT );
  33. ConVar r_AirboatViewBlendToTime( "r_AirboatViewBlendToTime", "1.5", FCVAR_CHEAT );
  34. ConVar cl_draw_airboat_wake( "cl_draw_airboat_wake", "1", FCVAR_CHEAT );
  35. // Curve parameters for pitch/roll blending.
  36. // NOTE: Must restart (or create a new airboat) after changing these cvars!
  37. ConVar r_AirboatRollCurveZero( "r_AirboatRollCurveZero", "90.0", FCVAR_CHEAT ); // Roll less than this is clamped to zero.
  38. ConVar r_AirboatRollCurveLinear( "r_AirboatRollCurveLinear", "120.0", FCVAR_CHEAT ); // Roll greater than this is mapped directly.
  39. // Spline in between.
  40. ConVar r_AirboatPitchCurveZero( "r_AirboatPitchCurveZero", "25.0", FCVAR_CHEAT ); // Pitch less than this is clamped to zero.
  41. ConVar r_AirboatPitchCurveLinear( "r_AirboatPitchCurveLinear", "60.0", FCVAR_CHEAT ); // Pitch greater than this is mapped directly.
  42. // Spline in between.
  43. ConVar airboat_joy_response_move( "airboat_joy_response_move", "1" ); // Quadratic steering response
  44. #define AIRBOAT_DELTA_LENGTH_MAX 12.0f // 1 foot
  45. #define AIRBOAT_FRAMETIME_MIN 1e-6
  46. #define HEADLIGHT_DISTANCE 1000
  47. #define MAX_WAKE_POINTS 16
  48. #define WAKE_POINT_MASK (MAX_WAKE_POINTS-1)
  49. #define WAKE_LIFETIME 0.5f
  50. //=============================================================================
  51. //
  52. // Client-side Airboat Class
  53. //
  54. class C_PropAirboat : public C_PropVehicleDriveable
  55. {
  56. DECLARE_CLASS( C_PropAirboat, C_PropVehicleDriveable );
  57. public:
  58. DECLARE_CLIENTCLASS();
  59. DECLARE_INTERPOLATION();
  60. DECLARE_DATADESC();
  61. C_PropAirboat();
  62. ~C_PropAirboat();
  63. public:
  64. // C_BaseEntity
  65. virtual void Simulate();
  66. // IClientVehicle
  67. virtual void UpdateViewAngles( C_BasePlayer *pLocalPlayer, CUserCmd *pCmd );
  68. virtual void OnEnteredVehicle( C_BasePlayer *pPlayer );
  69. virtual int GetPrimaryAmmoType() const;
  70. virtual int GetPrimaryAmmoClip() const;
  71. virtual bool PrimaryAmmoUsesClips() const;
  72. virtual int GetPrimaryAmmoCount() const;
  73. virtual int GetJoystickResponseCurve() const;
  74. int DrawModel( int flags );
  75. // Draws crosshair in the forward direction of the boat
  76. void DrawHudElements( );
  77. private:
  78. void DrawPropWake( Vector origin, float speed );
  79. void DrawPontoonSplash( Vector position, Vector direction, float speed );
  80. void DrawPontoonWake( Vector startPos, Vector wakeDir, float wakeLength, float speed);
  81. void DampenEyePosition( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles );
  82. void DampenForwardMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime );
  83. void DampenUpMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime );
  84. void ComputePDControllerCoefficients( float *pCoefficientsOut, float flFrequency, float flDampening, float flDeltaTime );
  85. void UpdateHeadlight( void );
  86. void UpdateWake( void );
  87. int DrawWake( void );
  88. void DrawSegment( const BeamSeg_t &beamSeg, const Vector &vNormal );
  89. TrailPoint_t *GetTrailPoint( int n )
  90. {
  91. int nIndex = (n + m_nFirstStep) & WAKE_POINT_MASK;
  92. return &m_vecSteps[nIndex];
  93. }
  94. private:
  95. Vector m_vecLastEyePos;
  96. Vector m_vecLastEyeTarget;
  97. Vector m_vecEyeSpeed;
  98. Vector m_vecTargetSpeed;
  99. float m_flViewAngleDeltaTime;
  100. bool m_bHeadlightIsOn;
  101. int m_nAmmoCount;
  102. CHeadlightEffect *m_pHeadlight;
  103. int m_nExactWaterLevel;
  104. TrailPoint_t m_vecSteps[MAX_WAKE_POINTS];
  105. int m_nFirstStep;
  106. int m_nStepCount;
  107. float m_flUpdateTime;
  108. TimedEvent m_SplashTime;
  109. CMeshBuilder m_Mesh;
  110. Vector m_vecPhysVelocity;
  111. };
  112. IMPLEMENT_CLIENTCLASS_DT( C_PropAirboat, DT_PropAirboat, CPropAirboat )
  113. RecvPropBool( RECVINFO( m_bHeadlightIsOn ) ),
  114. RecvPropInt( RECVINFO( m_nAmmoCount ) ),
  115. RecvPropInt( RECVINFO( m_nExactWaterLevel ) ),
  116. RecvPropInt( RECVINFO( m_nWaterLevel ) ),
  117. RecvPropVector( RECVINFO( m_vecPhysVelocity ) ),
  118. END_RECV_TABLE()
  119. BEGIN_DATADESC( C_PropAirboat )
  120. DEFINE_FIELD( m_vecLastEyePos, FIELD_POSITION_VECTOR ),
  121. DEFINE_FIELD( m_vecLastEyeTarget, FIELD_POSITION_VECTOR ),
  122. DEFINE_FIELD( m_vecEyeSpeed, FIELD_VECTOR ),
  123. DEFINE_FIELD( m_vecTargetSpeed, FIELD_VECTOR ),
  124. //DEFINE_FIELD( m_flViewAngleDeltaTime, FIELD_FLOAT ),
  125. END_DATADESC()
  126. //-----------------------------------------------------------------------------
  127. // Purpose: Constructor
  128. //-----------------------------------------------------------------------------
  129. C_PropAirboat::C_PropAirboat()
  130. {
  131. m_vecEyeSpeed.Init();
  132. m_flViewAngleDeltaTime = 0.0f;
  133. m_pHeadlight = NULL;
  134. m_ViewSmoothingData.flPitchCurveZero = r_AirboatPitchCurveZero.GetFloat();
  135. m_ViewSmoothingData.flPitchCurveLinear = r_AirboatPitchCurveLinear.GetFloat();
  136. m_ViewSmoothingData.flRollCurveZero = r_AirboatRollCurveZero.GetFloat();
  137. m_ViewSmoothingData.flRollCurveLinear = r_AirboatRollCurveLinear.GetFloat();
  138. m_ViewSmoothingData.rollLockData.flLockInterval = 1.5;
  139. m_ViewSmoothingData.rollLockData.flUnlockBlendInterval = 1.0;
  140. m_ViewSmoothingData.pitchLockData.flLockInterval = 1.5;
  141. m_ViewSmoothingData.pitchLockData.flUnlockBlendInterval = 1.0;
  142. m_nFirstStep = 0;
  143. m_nStepCount = 0;
  144. m_SplashTime.Init( 60 );
  145. }
  146. //-----------------------------------------------------------------------------
  147. // Purpose: Deconstructor
  148. //-----------------------------------------------------------------------------
  149. C_PropAirboat::~C_PropAirboat()
  150. {
  151. if (m_pHeadlight)
  152. {
  153. delete m_pHeadlight;
  154. }
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Draws the ammo for the airboat
  158. //-----------------------------------------------------------------------------
  159. int C_PropAirboat::GetPrimaryAmmoType() const
  160. {
  161. if ( m_nAmmoCount < 0 )
  162. return -1;
  163. int nAmmoType = GetAmmoDef()->Index( "AirboatGun" );
  164. return nAmmoType;
  165. }
  166. int C_PropAirboat::GetPrimaryAmmoCount() const
  167. {
  168. return m_nAmmoCount;
  169. }
  170. bool C_PropAirboat::PrimaryAmmoUsesClips() const
  171. {
  172. return false;
  173. }
  174. int C_PropAirboat::GetPrimaryAmmoClip() const
  175. {
  176. return -1;
  177. }
  178. //-----------------------------------------------------------------------------
  179. // The airboat prefers a more peppy response curve for joystick control.
  180. //-----------------------------------------------------------------------------
  181. int C_PropAirboat::GetJoystickResponseCurve() const
  182. {
  183. return airboat_joy_response_move.GetInt();
  184. }
  185. //-----------------------------------------------------------------------------
  186. // Draws crosshair in the forward direction of the boat
  187. //-----------------------------------------------------------------------------
  188. void C_PropAirboat::DrawHudElements( )
  189. {
  190. BaseClass::DrawHudElements();
  191. MDLCACHE_CRITICAL_SECTION();
  192. CHudTexture *pIcon = gHUD.GetIcon( IsX360() ? "crosshair_default" : "plushair" );
  193. if ( pIcon != NULL )
  194. {
  195. float x, y;
  196. Vector screen;
  197. int vx, vy, vw, vh;
  198. vgui::surface()->GetFullscreenViewport( vx, vy, vw, vh );
  199. float screenWidth = vw;
  200. float screenHeight = vh;
  201. x = screenWidth/2;
  202. y = screenHeight/2;
  203. int eyeAttachmentIndex = LookupAttachment( "vehicle_driver_eyes" );
  204. Vector vehicleEyeOrigin;
  205. QAngle vehicleEyeAngles;
  206. GetAttachment( eyeAttachmentIndex, vehicleEyeOrigin, vehicleEyeAngles );
  207. // Only worry about yaw.
  208. vehicleEyeAngles.x = vehicleEyeAngles.z = 0.0f;
  209. Vector vecForward;
  210. AngleVectors( vehicleEyeAngles, &vecForward );
  211. VectorMA( vehicleEyeOrigin, 100.0f, vecForward, vehicleEyeOrigin );
  212. ScreenTransform( vehicleEyeOrigin, screen );
  213. x += 0.5 * screen[0] * screenWidth + 0.5;
  214. y -= 0.5 * screen[1] * screenHeight + 0.5;
  215. x -= pIcon->Width() / 2;
  216. y -= pIcon->Height() / 2;
  217. pIcon->DrawSelf( x, y, gHUD.m_clrNormal );
  218. }
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Purpose: Blend view angles.
  222. //-----------------------------------------------------------------------------
  223. void C_PropAirboat::UpdateViewAngles( C_BasePlayer *pLocalPlayer, CUserCmd *pCmd )
  224. {
  225. if ( r_AirboatViewBlendTo.GetInt() )
  226. {
  227. //
  228. // Autocenter the view after a period of no mouse movement while throttling.
  229. //
  230. bool bResetViewAngleTime = false;
  231. if ( ( pCmd->mousedx != 0 || pCmd->mousedy != 0 ) || ( fabsf( m_flThrottle ) < 0.01f ) )
  232. {
  233. if ( IsX360() )
  234. {
  235. // Only reset this if there isn't an autoaim target!
  236. C_BaseHLPlayer *pLocalHLPlayer = (C_BaseHLPlayer *)pLocalPlayer;
  237. if ( pLocalHLPlayer )
  238. {
  239. // Get the autoaim target.
  240. CBaseEntity *pTarget = pLocalHLPlayer->m_HL2Local.m_hAutoAimTarget.Get();
  241. if( !pTarget )
  242. {
  243. bResetViewAngleTime = true;
  244. }
  245. }
  246. }
  247. else
  248. {
  249. bResetViewAngleTime = true;
  250. }
  251. }
  252. if( bResetViewAngleTime )
  253. {
  254. m_flViewAngleDeltaTime = 0.0f;
  255. }
  256. else
  257. {
  258. m_flViewAngleDeltaTime += gpGlobals->frametime;
  259. }
  260. if ( m_flViewAngleDeltaTime > r_AirboatViewBlendToTime.GetFloat() )
  261. {
  262. // Blend the view angles.
  263. int eyeAttachmentIndex = LookupAttachment( "vehicle_driver_eyes" );
  264. Vector vehicleEyeOrigin;
  265. QAngle vehicleEyeAngles;
  266. GetAttachmentLocal( eyeAttachmentIndex, vehicleEyeOrigin, vehicleEyeAngles );
  267. QAngle outAngles;
  268. InterpolateAngles( pCmd->viewangles, vehicleEyeAngles, outAngles, r_AirboatViewBlendToScale.GetFloat() );
  269. pCmd->viewangles = outAngles;
  270. }
  271. }
  272. BaseClass::UpdateViewAngles( pLocalPlayer, pCmd );
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Purpose:
  276. //-----------------------------------------------------------------------------
  277. void C_PropAirboat::DampenEyePosition( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles )
  278. {
  279. // Get the frametime. (Check to see if enough time has passed to warrent dampening).
  280. float flFrameTime = gpGlobals->frametime;
  281. if ( flFrameTime < AIRBOAT_FRAMETIME_MIN )
  282. {
  283. vecVehicleEyePos = m_vecLastEyePos;
  284. DampenUpMotion( vecVehicleEyePos, vecVehicleEyeAngles, 0.0f );
  285. return;
  286. }
  287. // Keep static the sideways motion.
  288. // Dampen forward/backward motion.
  289. DampenForwardMotion( vecVehicleEyePos, vecVehicleEyeAngles, flFrameTime );
  290. // Blend up/down motion.
  291. DampenUpMotion( vecVehicleEyePos, vecVehicleEyeAngles, flFrameTime );
  292. }
  293. //-----------------------------------------------------------------------------
  294. // Use the controller as follows:
  295. // speed += ( pCoefficientsOut[0] * ( targetPos - currentPos ) + pCoefficientsOut[1] * ( targetSpeed - currentSpeed ) ) * flDeltaTime;
  296. //-----------------------------------------------------------------------------
  297. void C_PropAirboat::ComputePDControllerCoefficients( float *pCoefficientsOut,
  298. float flFrequency, float flDampening,
  299. float flDeltaTime )
  300. {
  301. float flKs = 9.0f * flFrequency * flFrequency;
  302. float flKd = 4.5f * flFrequency * flDampening;
  303. float flScale = 1.0f / ( 1.0f + flKd * flDeltaTime + flKs * flDeltaTime * flDeltaTime );
  304. pCoefficientsOut[0] = flKs * flScale;
  305. pCoefficientsOut[1] = ( flKd + flKs * flDeltaTime ) * flScale;
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Purpose:
  309. //-----------------------------------------------------------------------------
  310. void C_PropAirboat::DampenForwardMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime )
  311. {
  312. // vecVehicleEyePos = real eye position this frame
  313. // m_vecLastEyePos = eye position last frame
  314. // m_vecEyeSpeed = eye speed last frame
  315. // vecPredEyePos = predicted eye position this frame (assuming no acceleration - it will get that from the pd controller).
  316. // vecPredEyeSpeed = predicted eye speed
  317. Vector vecPredEyePos = m_vecLastEyePos + m_vecEyeSpeed * flFrameTime;
  318. Vector vecPredEyeSpeed = m_vecEyeSpeed;
  319. // m_vecLastEyeTarget = real eye position last frame (used for speed calculation).
  320. // Calculate the approximate speed based on the current vehicle eye position and the eye position last frame.
  321. Vector vecVehicleEyeSpeed = ( vecVehicleEyePos - m_vecLastEyeTarget ) / flFrameTime;
  322. m_vecLastEyeTarget = vecVehicleEyePos;
  323. if (vecVehicleEyeSpeed.Length() == 0.0)
  324. {
  325. return;
  326. }
  327. // Calculate the delta between the predicted eye position and speed and the current eye position and speed.
  328. Vector vecDeltaSpeed = vecVehicleEyeSpeed - vecPredEyeSpeed;
  329. Vector vecDeltaPos = vecVehicleEyePos - vecPredEyePos;
  330. // Forward vector.
  331. Vector vecForward;
  332. AngleVectors( vecVehicleEyeAngles, &vecForward );
  333. float flDeltaLength = vecDeltaPos.Length();
  334. if ( flDeltaLength > AIRBOAT_DELTA_LENGTH_MAX )
  335. {
  336. // Clamp.
  337. float flDelta = flDeltaLength - AIRBOAT_DELTA_LENGTH_MAX;
  338. if ( flDelta > 40.0f )
  339. {
  340. // This part is a bit of a hack to get rid of large deltas (at level load, etc.).
  341. m_vecLastEyePos = vecVehicleEyePos;
  342. m_vecEyeSpeed = vecVehicleEyeSpeed;
  343. }
  344. else
  345. {
  346. // Position clamp.
  347. float flRatio = AIRBOAT_DELTA_LENGTH_MAX / flDeltaLength;
  348. vecDeltaPos *= flRatio;
  349. Vector vecForwardOffset = vecForward * ( vecForward.Dot( vecDeltaPos ) );
  350. vecVehicleEyePos -= vecForwardOffset;
  351. m_vecLastEyePos = vecVehicleEyePos;
  352. // Speed clamp.
  353. vecDeltaSpeed *= flRatio;
  354. float flCoefficients[2];
  355. ComputePDControllerCoefficients( flCoefficients, r_AirboatViewDampenFreq.GetFloat(), r_AirboatViewDampenDamp.GetFloat(), flFrameTime );
  356. m_vecEyeSpeed += ( ( flCoefficients[0] * vecDeltaPos + flCoefficients[1] * vecDeltaSpeed ) * flFrameTime );
  357. }
  358. }
  359. else
  360. {
  361. // Generate an updated (dampening) speed for use in next frames position prediction.
  362. float flCoefficients[2];
  363. ComputePDControllerCoefficients( flCoefficients, r_AirboatViewDampenFreq.GetFloat(), r_AirboatViewDampenDamp.GetFloat(), flFrameTime );
  364. m_vecEyeSpeed += ( ( flCoefficients[0] * vecDeltaPos + flCoefficients[1] * vecDeltaSpeed ) * flFrameTime );
  365. // Save off data for next frame.
  366. m_vecLastEyePos = vecPredEyePos;
  367. // Move eye forward/backward.
  368. Vector vecForwardOffset = vecForward * ( vecForward.Dot( vecDeltaPos ) );
  369. vecVehicleEyePos -= vecForwardOffset;
  370. }
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose:
  374. //-----------------------------------------------------------------------------
  375. void C_PropAirboat::DampenUpMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime )
  376. {
  377. // Get up vector.
  378. Vector vecUp;
  379. AngleVectors( vecVehicleEyeAngles, NULL, NULL, &vecUp );
  380. vecUp.z = clamp( vecUp.z, 0.0f, vecUp.z );
  381. vecVehicleEyePos.z += r_AirboatViewZHeight.GetFloat() * vecUp.z;
  382. // NOTE: Should probably use some damped equation here.
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Purpose:
  386. //-----------------------------------------------------------------------------
  387. void C_PropAirboat::OnEnteredVehicle( C_BasePlayer *pPlayer )
  388. {
  389. int eyeAttachmentIndex = LookupAttachment( "vehicle_driver_eyes" );
  390. Vector vehicleEyeOrigin;
  391. QAngle vehicleEyeAngles;
  392. GetAttachment( eyeAttachmentIndex, vehicleEyeOrigin, vehicleEyeAngles );
  393. m_vecLastEyeTarget = vehicleEyeOrigin;
  394. m_vecLastEyePos = vehicleEyeOrigin;
  395. m_vecEyeSpeed = vec3_origin;
  396. }
  397. //-----------------------------------------------------------------------------
  398. // Purpose:
  399. //-----------------------------------------------------------------------------
  400. void C_PropAirboat::Simulate()
  401. {
  402. UpdateHeadlight();
  403. UpdateWake();
  404. BaseClass::Simulate();
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Purpose: Creates, destroys, and updates the headlight effect as needed.
  408. //-----------------------------------------------------------------------------
  409. void C_PropAirboat::UpdateHeadlight()
  410. {
  411. if (m_bHeadlightIsOn)
  412. {
  413. if (!m_pHeadlight)
  414. {
  415. // Turned on the headlight; create it.
  416. m_pHeadlight = new CHeadlightEffect();
  417. if (!m_pHeadlight)
  418. return;
  419. m_pHeadlight->TurnOn();
  420. }
  421. // The headlight is emitted from an attachment point so that it can move
  422. // as we turn the handlebars.
  423. int nHeadlightIndex = LookupAttachment( "vehicle_headlight" );
  424. Vector vecLightPos;
  425. QAngle angLightDir;
  426. GetAttachment(nHeadlightIndex, vecLightPos, angLightDir);
  427. Vector vecLightDir, vecLightRight, vecLightUp;
  428. AngleVectors( angLightDir, &vecLightDir, &vecLightRight, &vecLightUp );
  429. // Update the light with the new position and direction.
  430. m_pHeadlight->UpdateLight( vecLightPos, vecLightDir, vecLightRight, vecLightUp, HEADLIGHT_DISTANCE );
  431. }
  432. else if (m_pHeadlight)
  433. {
  434. // Turned off the headlight; delete it.
  435. delete m_pHeadlight;
  436. m_pHeadlight = NULL;
  437. }
  438. }
  439. //-----------------------------------------------------------------------------
  440. // Purpose:
  441. //-----------------------------------------------------------------------------
  442. void C_PropAirboat::UpdateWake( void )
  443. {
  444. if ( gpGlobals->frametime <= 0.0f )
  445. return;
  446. // Can't update too quickly
  447. if ( m_flUpdateTime > gpGlobals->curtime )
  448. return;
  449. Vector screenPos = GetRenderOrigin();
  450. screenPos.z = m_nExactWaterLevel;
  451. TrailPoint_t *pLast = m_nStepCount ? GetTrailPoint( m_nStepCount-1 ) : NULL;
  452. if ( ( pLast == NULL ) || ( pLast->m_vecScreenPos.DistToSqr( screenPos ) > 4.0f ) )
  453. {
  454. // If we're over our limit, steal the last point and put it up front
  455. if ( m_nStepCount >= MAX_WAKE_POINTS )
  456. {
  457. --m_nStepCount;
  458. ++m_nFirstStep;
  459. }
  460. // Save off its screen position, not its world position
  461. TrailPoint_t *pNewPoint = GetTrailPoint( m_nStepCount );
  462. pNewPoint->m_vecScreenPos = screenPos + Vector( 0, 0, 2 );
  463. pNewPoint->m_flDieTime = gpGlobals->curtime + WAKE_LIFETIME;
  464. pNewPoint->m_flWidthVariance = random->RandomFloat( -16, 16 );
  465. if ( pLast )
  466. {
  467. pNewPoint->m_flTexCoord = pLast->m_flTexCoord + pLast->m_vecScreenPos.DistTo( screenPos );
  468. pNewPoint->m_flTexCoord = fmod( pNewPoint->m_flTexCoord, 1 );
  469. }
  470. else
  471. {
  472. pNewPoint->m_flTexCoord = 0.0f;
  473. }
  474. ++m_nStepCount;
  475. }
  476. // Don't update again for a bit
  477. m_flUpdateTime = gpGlobals->curtime + ( 0.5f / (float) MAX_WAKE_POINTS );
  478. }
  479. //-----------------------------------------------------------------------------
  480. // Purpose:
  481. // Input : &beamSeg -
  482. //-----------------------------------------------------------------------------
  483. void C_PropAirboat::DrawSegment( const BeamSeg_t &beamSeg, const Vector &vNormal )
  484. {
  485. // Build the endpoints.
  486. Vector vPoint1, vPoint2;
  487. VectorMA( beamSeg.m_vPos, beamSeg.m_flWidth*0.5f, vNormal, vPoint1 );
  488. VectorMA( beamSeg.m_vPos, -beamSeg.m_flWidth*0.5f, vNormal, vPoint2 );
  489. // Specify the points.
  490. m_Mesh.Position3fv( vPoint1.Base() );
  491. m_Mesh.Color4f( VectorExpand( beamSeg.m_vColor ), beamSeg.m_flAlpha );
  492. m_Mesh.TexCoord2f( 0, 0, beamSeg.m_flTexCoord );
  493. m_Mesh.TexCoord2f( 1, 0, beamSeg.m_flTexCoord );
  494. m_Mesh.AdvanceVertex();
  495. m_Mesh.Position3fv( vPoint2.Base() );
  496. m_Mesh.Color4f( VectorExpand( beamSeg.m_vColor ), beamSeg.m_flAlpha );
  497. m_Mesh.TexCoord2f( 0, 1, beamSeg.m_flTexCoord );
  498. m_Mesh.TexCoord2f( 1, 1, beamSeg.m_flTexCoord );
  499. m_Mesh.AdvanceVertex();
  500. }
  501. //-----------------------------------------------------------------------------
  502. // Purpose:
  503. // Input : position -
  504. //-----------------------------------------------------------------------------
  505. void C_PropAirboat::DrawPontoonSplash( Vector origin, Vector direction, float speed )
  506. {
  507. Vector offset;
  508. CSmartPtr<CSplashParticle> pSimple = CSplashParticle::Create( "splish" );
  509. pSimple->SetSortOrigin( origin );
  510. SimpleParticle *pParticle;
  511. Vector color = Vector( 0.8f, 0.8f, 0.75f );
  512. float colorRamp;
  513. float flScale = RemapVal( speed, 64, 256, 0.75f, 1.0f );
  514. PMaterialHandle hMaterial;
  515. float tempDelta = gpGlobals->frametime;
  516. while( m_SplashTime.NextEvent( tempDelta ) )
  517. {
  518. if ( random->RandomInt( 0, 1 ) )
  519. {
  520. hMaterial = ParticleMgr()->GetPMaterial( "effects/splash1" );
  521. }
  522. else
  523. {
  524. hMaterial = ParticleMgr()->GetPMaterial( "effects/splash2" );
  525. }
  526. offset = RandomVector( -8.0f * flScale, 8.0f * flScale );
  527. offset[2] = 0.0f;
  528. offset += origin;
  529. pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial, offset );
  530. if ( pParticle == NULL )
  531. continue;
  532. pParticle->m_flLifetime = 0.0f;
  533. pParticle->m_flDieTime = 0.25f;
  534. pParticle->m_vecVelocity.Random( -0.4f, 0.4f );
  535. pParticle->m_vecVelocity += (direction*5.0f+Vector(0,0,1));
  536. VectorNormalize( pParticle->m_vecVelocity );
  537. pParticle->m_vecVelocity *= speed + random->RandomFloat( -128.0f, 128.0f );
  538. colorRamp = random->RandomFloat( 0.75f, 1.25f );
  539. pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f;
  540. pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f;
  541. pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f;
  542. pParticle->m_uchStartSize = random->RandomFloat( 8, 16 ) * flScale;
  543. pParticle->m_uchEndSize = pParticle->m_uchStartSize * 2;
  544. pParticle->m_uchStartAlpha = 255;
  545. pParticle->m_uchEndAlpha = 0;
  546. pParticle->m_flRoll = random->RandomInt( 0, 360 );
  547. pParticle->m_flRollDelta = random->RandomFloat( -4.0f, 4.0f );
  548. }
  549. }
  550. //-----------------------------------------------------------------------------
  551. // Purpose:
  552. // Input : Vector startPos -
  553. // wakeDir -
  554. // wakeLength -
  555. //-----------------------------------------------------------------------------
  556. void C_PropAirboat::DrawPontoonWake( Vector startPos, Vector wakeDir, float wakeLength, float speed )
  557. {
  558. #define WAKE_STEPS 6
  559. Vector wakeStep = wakeDir * ( wakeLength / (float) WAKE_STEPS );
  560. Vector origin;
  561. float scale;
  562. IMaterial *pMaterial = materials->FindMaterial( "effects/splashwake1", NULL, false );
  563. CMatRenderContextPtr pRenderContext( materials );
  564. IMesh* pMesh = pRenderContext->GetDynamicMesh( 0, 0, 0, pMaterial );
  565. CMeshBuilder meshBuilder;
  566. meshBuilder.Begin( pMesh, MATERIAL_QUADS, WAKE_STEPS );
  567. for ( int i = 0; i < WAKE_STEPS; i++ )
  568. {
  569. origin = startPos + ( wakeStep * i );
  570. origin[0] += random->RandomFloat( -4.0f, 4.0f );
  571. origin[1] += random->RandomFloat( -4.0f, 4.0f );
  572. origin[2] = m_nExactWaterLevel + 2.0f;
  573. float scaleRange = RemapVal( i, 0, WAKE_STEPS-1, 32, 64 );
  574. scale = scaleRange + ( 8.0f * sin( gpGlobals->curtime * 5 * i ) );
  575. float alpha = RemapValClamped( speed, 128, 600, 0.05f, 0.25f );
  576. float color[4] = { 1.0f, 1.0f, 1.0f, alpha };
  577. // Needs to be time based so it'll freeze when the game is frozen
  578. float yaw = random->RandomFloat( 0, 360 );
  579. Vector rRight = ( Vector(1,0,0) * cos( DEG2RAD( yaw ) ) ) - ( Vector(0,1,0) * sin( DEG2RAD( yaw ) ) );
  580. Vector rUp = ( Vector(1,0,0) * cos( DEG2RAD( yaw+90.0f ) ) ) - ( Vector(0,1,0) * sin( DEG2RAD( yaw+90.0f ) ) );
  581. Vector point;
  582. meshBuilder.Color4fv (color);
  583. meshBuilder.TexCoord2f (0, 0, 1);
  584. VectorMA (origin, -scale, rRight, point);
  585. VectorMA (point, -scale, rUp, point);
  586. meshBuilder.Position3fv (point.Base());
  587. meshBuilder.AdvanceVertex();
  588. meshBuilder.Color4fv (color);
  589. meshBuilder.TexCoord2f (0, 0, 0);
  590. VectorMA (origin, scale, rRight, point);
  591. VectorMA (point, -scale, rUp, point);
  592. meshBuilder.Position3fv (point.Base());
  593. meshBuilder.AdvanceVertex();
  594. meshBuilder.Color4fv (color);
  595. meshBuilder.TexCoord2f (0, 1, 0);
  596. VectorMA (origin, scale, rRight, point);
  597. VectorMA (point, scale, rUp, point);
  598. meshBuilder.Position3fv (point.Base());
  599. meshBuilder.AdvanceVertex();
  600. meshBuilder.Color4fv (color);
  601. meshBuilder.TexCoord2f (0, 1, 1);
  602. VectorMA (origin, -scale, rRight, point);
  603. VectorMA (point, scale, rUp, point);
  604. meshBuilder.Position3fv (point.Base());
  605. meshBuilder.AdvanceVertex();
  606. }
  607. meshBuilder.End();
  608. pMesh->Draw();
  609. }
  610. //-----------------------------------------------------------------------------
  611. // Purpose:
  612. // Output : int
  613. //-----------------------------------------------------------------------------
  614. int C_PropAirboat::DrawWake( void )
  615. {
  616. if ( cl_draw_airboat_wake.GetBool() == false )
  617. return 0;
  618. // Make sure we're in water...
  619. if ( GetWaterLevel() == 0 )
  620. return 0;
  621. //FIXME: For now, we don't draw slime this way
  622. if ( GetWaterLevel() == 2 )
  623. return 0;
  624. bool bDriven = ( GetPassenger( VEHICLE_ROLE_DRIVER ) != NULL );
  625. Vector vehicleDir = m_vecPhysVelocity;
  626. float vehicleSpeed = VectorNormalize( vehicleDir );
  627. Vector vecPontoonFrontLeft;
  628. Vector vecPontoonFrontRight;
  629. Vector vecPontoonRearLeft;
  630. Vector vecPontoonRearRight;
  631. Vector vecSplashPoint;
  632. QAngle fooAngles;
  633. //FIXME: This lookup should be cached off
  634. // Get all attachments
  635. GetAttachment( LookupAttachment( "raytrace_fl" ), vecPontoonFrontLeft, fooAngles );
  636. GetAttachment( LookupAttachment( "raytrace_fr" ), vecPontoonFrontRight, fooAngles );
  637. GetAttachment( LookupAttachment( "raytrace_rl" ), vecPontoonRearLeft, fooAngles );
  638. GetAttachment( LookupAttachment( "raytrace_rr" ), vecPontoonRearRight, fooAngles );
  639. GetAttachment( LookupAttachment( "splash_pt" ), vecSplashPoint, fooAngles );
  640. // Find the direction of the pontoons
  641. Vector vecLeftWakeDir = ( vecPontoonRearLeft - vecPontoonFrontLeft );
  642. Vector vecRightWakeDir = ( vecPontoonRearRight - vecPontoonFrontRight );
  643. // Find the pontoon's size
  644. float flWakeLeftLength = VectorNormalize( vecLeftWakeDir );
  645. float flWakeRightLength = VectorNormalize( vecRightWakeDir );
  646. vecPontoonFrontLeft.z = m_nExactWaterLevel;
  647. vecPontoonFrontRight.z = m_nExactWaterLevel;
  648. if ( bDriven && vehicleSpeed > 128.0f )
  649. {
  650. DrawPontoonWake( vecPontoonFrontLeft, vecLeftWakeDir, flWakeLeftLength, vehicleSpeed );
  651. DrawPontoonWake( vecPontoonFrontRight, vecRightWakeDir, flWakeRightLength, vehicleSpeed );
  652. Vector vecSplashDir;
  653. Vector vForward;
  654. GetVectors( &vForward, NULL, NULL );
  655. if ( m_vecPhysVelocity.x < -64.0f )
  656. {
  657. vecSplashDir = vecLeftWakeDir - vForward;
  658. VectorNormalize( vecSplashDir );
  659. // Don't do this if we're going backwards
  660. if ( m_vecPhysVelocity.y > 0.0f )
  661. {
  662. DrawPontoonSplash( vecPontoonFrontLeft + ( vecLeftWakeDir * 1.0f ), vecSplashDir, m_vecPhysVelocity.y );
  663. }
  664. }
  665. else if ( m_vecPhysVelocity.x > 64.0f )
  666. {
  667. vecSplashDir = vecRightWakeDir + vForward;
  668. VectorNormalize( vecSplashDir );
  669. // Don't do this if we're going backwards
  670. if ( m_vecPhysVelocity.y > 0.0f )
  671. {
  672. DrawPontoonSplash( vecPontoonFrontRight + ( vecRightWakeDir * 1.0f ), vecSplashDir, m_vecPhysVelocity.y );
  673. }
  674. }
  675. }
  676. // Must have at least one point
  677. if ( m_nStepCount <= 1 )
  678. return 1;
  679. IMaterial *pMaterial = materials->FindMaterial( "effects/splashwake4", 0);
  680. //Bind the material
  681. CMatRenderContextPtr pRenderContext( materials );
  682. IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );
  683. m_Mesh.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, (m_nStepCount-1) * 2 );
  684. TrailPoint_t *pLast = GetTrailPoint( m_nStepCount - 1 );
  685. TrailPoint_t currentPoint;
  686. currentPoint.m_flDieTime = gpGlobals->curtime + 0.5f;
  687. currentPoint.m_vecScreenPos = GetAbsOrigin();
  688. currentPoint.m_vecScreenPos[2] = m_nExactWaterLevel + 16;
  689. currentPoint.m_flTexCoord = pLast->m_flTexCoord + currentPoint.m_vecScreenPos.DistTo(pLast->m_vecScreenPos);
  690. currentPoint.m_flTexCoord = fmod( currentPoint.m_flTexCoord, 1 );
  691. currentPoint.m_flWidthVariance = 0.0f;
  692. TrailPoint_t *pPrevPoint = NULL;
  693. Vector segDir, normal;
  694. for ( int i = 0; i <= m_nStepCount; ++i )
  695. {
  696. // This makes it so that we're always drawing to the current location
  697. TrailPoint_t *pPoint = (i != m_nStepCount) ? GetTrailPoint(i) : &currentPoint;
  698. float flLifePerc = RemapValClamped( ( pPoint->m_flDieTime - gpGlobals->curtime ), 0, WAKE_LIFETIME, 0.0f, 1.0f );
  699. BeamSeg_t curSeg;
  700. curSeg.m_vColor.x = curSeg.m_vColor.y = curSeg.m_vColor.z = 1.0f;
  701. float flAlphaFade = flLifePerc;
  702. float alpha = RemapValClamped( fabs( m_vecPhysVelocity.y ), 128, 600, 0.0f, 1.0f );
  703. curSeg.m_flAlpha = 0.25f;
  704. curSeg.m_flAlpha *= flAlphaFade * alpha;
  705. curSeg.m_vPos = pPoint->m_vecScreenPos;
  706. float widthBase = SimpleSplineRemapVal( fabs( m_vecPhysVelocity.y ), 128, 600, 32, 48 );
  707. curSeg.m_flWidth = Lerp( flLifePerc, widthBase*6, widthBase );
  708. curSeg.m_flWidth += pPoint->m_flWidthVariance;
  709. if ( curSeg.m_flWidth < 0.0f )
  710. {
  711. curSeg.m_flWidth = 0.0f;
  712. }
  713. curSeg.m_flTexCoord = pPoint->m_flTexCoord;
  714. if ( pPrevPoint != NULL )
  715. {
  716. segDir = ( pPrevPoint->m_vecScreenPos - pPoint->m_vecScreenPos );
  717. VectorNormalize( segDir );
  718. normal = CrossProduct( segDir, Vector( 0, 0, -1 ) );
  719. DrawSegment( curSeg, normal );
  720. }
  721. pPrevPoint = pPoint;
  722. }
  723. m_Mesh.End();
  724. pMesh->Draw();
  725. return 1;
  726. }
  727. //-----------------------------------------------------------------------------
  728. // Purpose:
  729. // Input : flags -
  730. // Output : int
  731. //-----------------------------------------------------------------------------
  732. int C_PropAirboat::DrawModel( int flags )
  733. {
  734. if ( BaseClass::DrawModel( flags ) == false )
  735. return 0;
  736. if ( !m_bReadyToDraw )
  737. return 0;
  738. return DrawWake();
  739. }