diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /sp/src/game/client/hl2/c_vehicle_airboat.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'sp/src/game/client/hl2/c_vehicle_airboat.cpp')
| -rw-r--r-- | sp/src/game/client/hl2/c_vehicle_airboat.cpp | 931 |
1 files changed, 931 insertions, 0 deletions
diff --git a/sp/src/game/client/hl2/c_vehicle_airboat.cpp b/sp/src/game/client/hl2/c_vehicle_airboat.cpp new file mode 100644 index 00000000..526985d4 --- /dev/null +++ b/sp/src/game/client/hl2/c_vehicle_airboat.cpp @@ -0,0 +1,931 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Client side implementation of the airboat.
+//
+// - Dampens motion of driver's view to reduce nausea.
+// - Autocenters driver's view after a period of inactivity.
+// - Controls headlights.
+// - Controls curve parameters for pitch/roll blending.
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "c_prop_vehicle.h"
+#include "datacache/imdlcache.h"
+#include "flashlighteffect.h"
+#include "movevars_shared.h"
+#include "ammodef.h"
+#include "SpriteTrail.h"
+#include "beamdraw.h"
+#include "enginesprite.h"
+#include "fx_quad.h"
+#include "fx.h"
+#include "fx_water.h"
+#include "engine/ivdebugoverlay.h"
+#include "view.h"
+#include "clienteffectprecachesystem.h"
+#include "c_basehlplayer.h"
+#include "vgui_controls/Controls.h"
+#include "vgui/ISurface.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+ConVar r_AirboatViewBlendTo( "r_AirboatViewBlendTo", "1", FCVAR_CHEAT );
+ConVar r_AirboatViewBlendToScale( "r_AirboatViewBlendToScale", "0.03", FCVAR_CHEAT );
+ConVar r_AirboatViewBlendToTime( "r_AirboatViewBlendToTime", "1.5", FCVAR_CHEAT );
+
+ConVar cl_draw_airboat_wake( "cl_draw_airboat_wake", "1", FCVAR_CHEAT );
+
+// Curve parameters for pitch/roll blending.
+// NOTE: Must restart (or create a new airboat) after changing these cvars!
+ConVar r_AirboatRollCurveZero( "r_AirboatRollCurveZero", "90.0", FCVAR_CHEAT ); // Roll less than this is clamped to zero.
+ConVar r_AirboatRollCurveLinear( "r_AirboatRollCurveLinear", "120.0", FCVAR_CHEAT ); // Roll greater than this is mapped directly.
+ // Spline in between.
+
+ConVar r_AirboatPitchCurveZero( "r_AirboatPitchCurveZero", "25.0", FCVAR_CHEAT ); // Pitch less than this is clamped to zero.
+ConVar r_AirboatPitchCurveLinear( "r_AirboatPitchCurveLinear", "60.0", FCVAR_CHEAT ); // Pitch greater than this is mapped directly.
+ // Spline in between.
+
+ConVar airboat_joy_response_move( "airboat_joy_response_move", "1" ); // Quadratic steering response
+
+
+#define AIRBOAT_DELTA_LENGTH_MAX 12.0f // 1 foot
+#define AIRBOAT_FRAMETIME_MIN 1e-6
+
+#define HEADLIGHT_DISTANCE 1000
+
+#define MAX_WAKE_POINTS 16
+#define WAKE_POINT_MASK (MAX_WAKE_POINTS-1)
+
+#define WAKE_LIFETIME 0.5f
+
+//=============================================================================
+//
+// Client-side Airboat Class
+//
+class C_PropAirboat : public C_PropVehicleDriveable
+{
+ DECLARE_CLASS( C_PropAirboat, C_PropVehicleDriveable );
+
+public:
+
+ DECLARE_CLIENTCLASS();
+ DECLARE_INTERPOLATION();
+ DECLARE_DATADESC();
+
+ C_PropAirboat();
+ ~C_PropAirboat();
+
+public:
+
+ // C_BaseEntity
+ virtual void Simulate();
+
+ // IClientVehicle
+ virtual void UpdateViewAngles( C_BasePlayer *pLocalPlayer, CUserCmd *pCmd );
+ virtual void OnEnteredVehicle( C_BasePlayer *pPlayer );
+ virtual int GetPrimaryAmmoType() const;
+ virtual int GetPrimaryAmmoClip() const;
+ virtual bool PrimaryAmmoUsesClips() const;
+ virtual int GetPrimaryAmmoCount() const;
+ virtual int GetJoystickResponseCurve() const;
+
+ int DrawModel( int flags );
+
+ // Draws crosshair in the forward direction of the boat
+ void DrawHudElements( );
+
+private:
+
+ void DrawPropWake( Vector origin, float speed );
+ void DrawPontoonSplash( Vector position, Vector direction, float speed );
+ void DrawPontoonWake( Vector startPos, Vector wakeDir, float wakeLength, float speed);
+
+ void DampenEyePosition( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles );
+ void DampenForwardMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime );
+ void DampenUpMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime );
+ void ComputePDControllerCoefficients( float *pCoefficientsOut, float flFrequency, float flDampening, float flDeltaTime );
+
+ void UpdateHeadlight( void );
+ void UpdateWake( void );
+ int DrawWake( void );
+ void DrawSegment( const BeamSeg_t &beamSeg, const Vector &vNormal );
+
+ TrailPoint_t *GetTrailPoint( int n )
+ {
+ int nIndex = (n + m_nFirstStep) & WAKE_POINT_MASK;
+ return &m_vecSteps[nIndex];
+ }
+
+private:
+
+ Vector m_vecLastEyePos;
+ Vector m_vecLastEyeTarget;
+ Vector m_vecEyeSpeed;
+ Vector m_vecTargetSpeed;
+
+ float m_flViewAngleDeltaTime;
+
+ bool m_bHeadlightIsOn;
+ int m_nAmmoCount;
+ CHeadlightEffect *m_pHeadlight;
+
+ int m_nExactWaterLevel;
+
+ TrailPoint_t m_vecSteps[MAX_WAKE_POINTS];
+ int m_nFirstStep;
+ int m_nStepCount;
+ float m_flUpdateTime;
+
+ TimedEvent m_SplashTime;
+ CMeshBuilder m_Mesh;
+
+ Vector m_vecPhysVelocity;
+};
+
+IMPLEMENT_CLIENTCLASS_DT( C_PropAirboat, DT_PropAirboat, CPropAirboat )
+ RecvPropBool( RECVINFO( m_bHeadlightIsOn ) ),
+ RecvPropInt( RECVINFO( m_nAmmoCount ) ),
+ RecvPropInt( RECVINFO( m_nExactWaterLevel ) ),
+ RecvPropInt( RECVINFO( m_nWaterLevel ) ),
+ RecvPropVector( RECVINFO( m_vecPhysVelocity ) ),
+END_RECV_TABLE()
+
+
+BEGIN_DATADESC( C_PropAirboat )
+ DEFINE_FIELD( m_vecLastEyePos, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_vecLastEyeTarget, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_vecEyeSpeed, FIELD_VECTOR ),
+ DEFINE_FIELD( m_vecTargetSpeed, FIELD_VECTOR ),
+ //DEFINE_FIELD( m_flViewAngleDeltaTime, FIELD_FLOAT ),
+END_DATADESC()
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+C_PropAirboat::C_PropAirboat()
+{
+ m_vecEyeSpeed.Init();
+ m_flViewAngleDeltaTime = 0.0f;
+ m_pHeadlight = NULL;
+
+ m_ViewSmoothingData.flPitchCurveZero = r_AirboatPitchCurveZero.GetFloat();
+ m_ViewSmoothingData.flPitchCurveLinear = r_AirboatPitchCurveLinear.GetFloat();
+ m_ViewSmoothingData.flRollCurveZero = r_AirboatRollCurveZero.GetFloat();
+ m_ViewSmoothingData.flRollCurveLinear = r_AirboatRollCurveLinear.GetFloat();
+
+ m_ViewSmoothingData.rollLockData.flLockInterval = 1.5;
+ m_ViewSmoothingData.rollLockData.flUnlockBlendInterval = 1.0;
+
+ m_ViewSmoothingData.pitchLockData.flLockInterval = 1.5;
+ m_ViewSmoothingData.pitchLockData.flUnlockBlendInterval = 1.0;
+
+ m_nFirstStep = 0;
+ m_nStepCount = 0;
+ m_SplashTime.Init( 60 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Deconstructor
+//-----------------------------------------------------------------------------
+C_PropAirboat::~C_PropAirboat()
+{
+ if (m_pHeadlight)
+ {
+ delete m_pHeadlight;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the ammo for the airboat
+//-----------------------------------------------------------------------------
+int C_PropAirboat::GetPrimaryAmmoType() const
+{
+ if ( m_nAmmoCount < 0 )
+ return -1;
+
+ int nAmmoType = GetAmmoDef()->Index( "AirboatGun" );
+ return nAmmoType;
+}
+
+int C_PropAirboat::GetPrimaryAmmoCount() const
+{
+ return m_nAmmoCount;
+}
+
+bool C_PropAirboat::PrimaryAmmoUsesClips() const
+{
+ return false;
+}
+
+int C_PropAirboat::GetPrimaryAmmoClip() const
+{
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// The airboat prefers a more peppy response curve for joystick control.
+//-----------------------------------------------------------------------------
+int C_PropAirboat::GetJoystickResponseCurve() const
+{
+ return airboat_joy_response_move.GetInt();
+}
+
+//-----------------------------------------------------------------------------
+// Draws crosshair in the forward direction of the boat
+//-----------------------------------------------------------------------------
+void C_PropAirboat::DrawHudElements( )
+{
+ BaseClass::DrawHudElements();
+
+ MDLCACHE_CRITICAL_SECTION();
+
+ CHudTexture *pIcon = gHUD.GetIcon( IsX360() ? "crosshair_default" : "plushair" );
+ if ( pIcon != NULL )
+ {
+ float x, y;
+ Vector screen;
+
+ int vx, vy, vw, vh;
+ vgui::surface()->GetFullscreenViewport( vx, vy, vw, vh );
+ float screenWidth = vw;
+ float screenHeight = vh;
+
+ x = screenWidth/2;
+ y = screenHeight/2;
+
+ int eyeAttachmentIndex = LookupAttachment( "vehicle_driver_eyes" );
+ Vector vehicleEyeOrigin;
+ QAngle vehicleEyeAngles;
+ GetAttachment( eyeAttachmentIndex, vehicleEyeOrigin, vehicleEyeAngles );
+
+ // Only worry about yaw.
+ vehicleEyeAngles.x = vehicleEyeAngles.z = 0.0f;
+
+ Vector vecForward;
+ AngleVectors( vehicleEyeAngles, &vecForward );
+ VectorMA( vehicleEyeOrigin, 100.0f, vecForward, vehicleEyeOrigin );
+
+ ScreenTransform( vehicleEyeOrigin, screen );
+ x += 0.5 * screen[0] * screenWidth + 0.5;
+ y -= 0.5 * screen[1] * screenHeight + 0.5;
+
+ x -= pIcon->Width() / 2;
+ y -= pIcon->Height() / 2;
+
+ pIcon->DrawSelf( x, y, gHUD.m_clrNormal );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Blend view angles.
+//-----------------------------------------------------------------------------
+void C_PropAirboat::UpdateViewAngles( C_BasePlayer *pLocalPlayer, CUserCmd *pCmd )
+{
+ if ( r_AirboatViewBlendTo.GetInt() )
+ {
+ //
+ // Autocenter the view after a period of no mouse movement while throttling.
+ //
+ bool bResetViewAngleTime = false;
+
+ if ( ( pCmd->mousedx != 0 || pCmd->mousedy != 0 ) || ( fabsf( m_flThrottle ) < 0.01f ) )
+ {
+ if ( IsX360() )
+ {
+ // Only reset this if there isn't an autoaim target!
+ C_BaseHLPlayer *pLocalHLPlayer = (C_BaseHLPlayer *)pLocalPlayer;
+ if ( pLocalHLPlayer )
+ {
+ // Get the autoaim target.
+ CBaseEntity *pTarget = pLocalHLPlayer->m_HL2Local.m_hAutoAimTarget.Get();
+
+ if( !pTarget )
+ {
+ bResetViewAngleTime = true;
+ }
+ }
+ }
+ else
+ {
+ bResetViewAngleTime = true;
+ }
+ }
+
+ if( bResetViewAngleTime )
+ {
+ m_flViewAngleDeltaTime = 0.0f;
+ }
+ else
+ {
+ m_flViewAngleDeltaTime += gpGlobals->frametime;
+ }
+
+ if ( m_flViewAngleDeltaTime > r_AirboatViewBlendToTime.GetFloat() )
+ {
+ // Blend the view angles.
+ int eyeAttachmentIndex = LookupAttachment( "vehicle_driver_eyes" );
+ Vector vehicleEyeOrigin;
+ QAngle vehicleEyeAngles;
+ GetAttachmentLocal( eyeAttachmentIndex, vehicleEyeOrigin, vehicleEyeAngles );
+
+ QAngle outAngles;
+ InterpolateAngles( pCmd->viewangles, vehicleEyeAngles, outAngles, r_AirboatViewBlendToScale.GetFloat() );
+ pCmd->viewangles = outAngles;
+ }
+ }
+
+ BaseClass::UpdateViewAngles( pLocalPlayer, pCmd );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_PropAirboat::DampenEyePosition( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles )
+{
+ // Get the frametime. (Check to see if enough time has passed to warrent dampening).
+ float flFrameTime = gpGlobals->frametime;
+ if ( flFrameTime < AIRBOAT_FRAMETIME_MIN )
+ {
+ vecVehicleEyePos = m_vecLastEyePos;
+ DampenUpMotion( vecVehicleEyePos, vecVehicleEyeAngles, 0.0f );
+ return;
+ }
+
+ // Keep static the sideways motion.
+
+ // Dampen forward/backward motion.
+ DampenForwardMotion( vecVehicleEyePos, vecVehicleEyeAngles, flFrameTime );
+
+ // Blend up/down motion.
+ DampenUpMotion( vecVehicleEyePos, vecVehicleEyeAngles, flFrameTime );
+}
+
+
+//-----------------------------------------------------------------------------
+// Use the controller as follows:
+// speed += ( pCoefficientsOut[0] * ( targetPos - currentPos ) + pCoefficientsOut[1] * ( targetSpeed - currentSpeed ) ) * flDeltaTime;
+//-----------------------------------------------------------------------------
+void C_PropAirboat::ComputePDControllerCoefficients( float *pCoefficientsOut,
+ float flFrequency, float flDampening,
+ float flDeltaTime )
+{
+ float flKs = 9.0f * flFrequency * flFrequency;
+ float flKd = 4.5f * flFrequency * flDampening;
+
+ float flScale = 1.0f / ( 1.0f + flKd * flDeltaTime + flKs * flDeltaTime * flDeltaTime );
+
+ pCoefficientsOut[0] = flKs * flScale;
+ pCoefficientsOut[1] = ( flKd + flKs * flDeltaTime ) * flScale;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_PropAirboat::DampenForwardMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime )
+{
+ // vecVehicleEyePos = real eye position this frame
+
+ // m_vecLastEyePos = eye position last frame
+ // m_vecEyeSpeed = eye speed last frame
+ // vecPredEyePos = predicted eye position this frame (assuming no acceleration - it will get that from the pd controller).
+ // vecPredEyeSpeed = predicted eye speed
+ Vector vecPredEyePos = m_vecLastEyePos + m_vecEyeSpeed * flFrameTime;
+ Vector vecPredEyeSpeed = m_vecEyeSpeed;
+
+ // m_vecLastEyeTarget = real eye position last frame (used for speed calculation).
+ // Calculate the approximate speed based on the current vehicle eye position and the eye position last frame.
+ Vector vecVehicleEyeSpeed = ( vecVehicleEyePos - m_vecLastEyeTarget ) / flFrameTime;
+ m_vecLastEyeTarget = vecVehicleEyePos;
+ if (vecVehicleEyeSpeed.Length() == 0.0)
+ {
+ return;
+ }
+
+ // Calculate the delta between the predicted eye position and speed and the current eye position and speed.
+ Vector vecDeltaSpeed = vecVehicleEyeSpeed - vecPredEyeSpeed;
+ Vector vecDeltaPos = vecVehicleEyePos - vecPredEyePos;
+
+ // Forward vector.
+ Vector vecForward;
+ AngleVectors( vecVehicleEyeAngles, &vecForward );
+
+ float flDeltaLength = vecDeltaPos.Length();
+ if ( flDeltaLength > AIRBOAT_DELTA_LENGTH_MAX )
+ {
+ // Clamp.
+ float flDelta = flDeltaLength - AIRBOAT_DELTA_LENGTH_MAX;
+ if ( flDelta > 40.0f )
+ {
+ // This part is a bit of a hack to get rid of large deltas (at level load, etc.).
+ m_vecLastEyePos = vecVehicleEyePos;
+ m_vecEyeSpeed = vecVehicleEyeSpeed;
+ }
+ else
+ {
+ // Position clamp.
+ float flRatio = AIRBOAT_DELTA_LENGTH_MAX / flDeltaLength;
+ vecDeltaPos *= flRatio;
+ Vector vecForwardOffset = vecForward * ( vecForward.Dot( vecDeltaPos ) );
+ vecVehicleEyePos -= vecForwardOffset;
+ m_vecLastEyePos = vecVehicleEyePos;
+
+ // Speed clamp.
+ vecDeltaSpeed *= flRatio;
+ float flCoefficients[2];
+ ComputePDControllerCoefficients( flCoefficients, r_AirboatViewDampenFreq.GetFloat(), r_AirboatViewDampenDamp.GetFloat(), flFrameTime );
+ m_vecEyeSpeed += ( ( flCoefficients[0] * vecDeltaPos + flCoefficients[1] * vecDeltaSpeed ) * flFrameTime );
+ }
+ }
+ else
+ {
+ // Generate an updated (dampening) speed for use in next frames position prediction.
+ float flCoefficients[2];
+ ComputePDControllerCoefficients( flCoefficients, r_AirboatViewDampenFreq.GetFloat(), r_AirboatViewDampenDamp.GetFloat(), flFrameTime );
+ m_vecEyeSpeed += ( ( flCoefficients[0] * vecDeltaPos + flCoefficients[1] * vecDeltaSpeed ) * flFrameTime );
+
+ // Save off data for next frame.
+ m_vecLastEyePos = vecPredEyePos;
+
+ // Move eye forward/backward.
+ Vector vecForwardOffset = vecForward * ( vecForward.Dot( vecDeltaPos ) );
+ vecVehicleEyePos -= vecForwardOffset;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_PropAirboat::DampenUpMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime )
+{
+ // Get up vector.
+ Vector vecUp;
+ AngleVectors( vecVehicleEyeAngles, NULL, NULL, &vecUp );
+ vecUp.z = clamp( vecUp.z, 0.0f, vecUp.z );
+ vecVehicleEyePos.z += r_AirboatViewZHeight.GetFloat() * vecUp.z;
+
+ // NOTE: Should probably use some damped equation here.
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_PropAirboat::OnEnteredVehicle( C_BasePlayer *pPlayer )
+{
+ int eyeAttachmentIndex = LookupAttachment( "vehicle_driver_eyes" );
+ Vector vehicleEyeOrigin;
+ QAngle vehicleEyeAngles;
+ GetAttachment( eyeAttachmentIndex, vehicleEyeOrigin, vehicleEyeAngles );
+
+ m_vecLastEyeTarget = vehicleEyeOrigin;
+ m_vecLastEyePos = vehicleEyeOrigin;
+ m_vecEyeSpeed = vec3_origin;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_PropAirboat::Simulate()
+{
+ UpdateHeadlight();
+ UpdateWake();
+
+ BaseClass::Simulate();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates, destroys, and updates the headlight effect as needed.
+//-----------------------------------------------------------------------------
+void C_PropAirboat::UpdateHeadlight()
+{
+ if (m_bHeadlightIsOn)
+ {
+ if (!m_pHeadlight)
+ {
+ // Turned on the headlight; create it.
+ m_pHeadlight = new CHeadlightEffect();
+
+ if (!m_pHeadlight)
+ return;
+
+ m_pHeadlight->TurnOn();
+ }
+
+ // The headlight is emitted from an attachment point so that it can move
+ // as we turn the handlebars.
+ int nHeadlightIndex = LookupAttachment( "vehicle_headlight" );
+
+ Vector vecLightPos;
+ QAngle angLightDir;
+ GetAttachment(nHeadlightIndex, vecLightPos, angLightDir);
+
+ Vector vecLightDir, vecLightRight, vecLightUp;
+ AngleVectors( angLightDir, &vecLightDir, &vecLightRight, &vecLightUp );
+
+ // Update the light with the new position and direction.
+ m_pHeadlight->UpdateLight( vecLightPos, vecLightDir, vecLightRight, vecLightUp, HEADLIGHT_DISTANCE );
+ }
+ else if (m_pHeadlight)
+ {
+ // Turned off the headlight; delete it.
+ delete m_pHeadlight;
+ m_pHeadlight = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_PropAirboat::UpdateWake( void )
+{
+ if ( gpGlobals->frametime <= 0.0f )
+ return;
+
+ // Can't update too quickly
+ if ( m_flUpdateTime > gpGlobals->curtime )
+ return;
+
+ Vector screenPos = GetRenderOrigin();
+ screenPos.z = m_nExactWaterLevel;
+
+ TrailPoint_t *pLast = m_nStepCount ? GetTrailPoint( m_nStepCount-1 ) : NULL;
+ if ( ( pLast == NULL ) || ( pLast->m_vecScreenPos.DistToSqr( screenPos ) > 4.0f ) )
+ {
+ // If we're over our limit, steal the last point and put it up front
+ if ( m_nStepCount >= MAX_WAKE_POINTS )
+ {
+ --m_nStepCount;
+ ++m_nFirstStep;
+ }
+
+ // Save off its screen position, not its world position
+ TrailPoint_t *pNewPoint = GetTrailPoint( m_nStepCount );
+ pNewPoint->m_vecScreenPos = screenPos + Vector( 0, 0, 2 );
+ pNewPoint->m_flDieTime = gpGlobals->curtime + WAKE_LIFETIME;
+ pNewPoint->m_flWidthVariance = random->RandomFloat( -16, 16 );
+
+ if ( pLast )
+ {
+ pNewPoint->m_flTexCoord = pLast->m_flTexCoord + pLast->m_vecScreenPos.DistTo( screenPos );
+ pNewPoint->m_flTexCoord = fmod( pNewPoint->m_flTexCoord, 1 );
+ }
+ else
+ {
+ pNewPoint->m_flTexCoord = 0.0f;
+ }
+
+ ++m_nStepCount;
+ }
+
+ // Don't update again for a bit
+ m_flUpdateTime = gpGlobals->curtime + ( 0.5f / (float) MAX_WAKE_POINTS );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &beamSeg -
+//-----------------------------------------------------------------------------
+void C_PropAirboat::DrawSegment( const BeamSeg_t &beamSeg, const Vector &vNormal )
+{
+ // Build the endpoints.
+ Vector vPoint1, vPoint2;
+ VectorMA( beamSeg.m_vPos, beamSeg.m_flWidth*0.5f, vNormal, vPoint1 );
+ VectorMA( beamSeg.m_vPos, -beamSeg.m_flWidth*0.5f, vNormal, vPoint2 );
+
+ // Specify the points.
+ m_Mesh.Position3fv( vPoint1.Base() );
+ m_Mesh.Color4f( VectorExpand( beamSeg.m_vColor ), beamSeg.m_flAlpha );
+ m_Mesh.TexCoord2f( 0, 0, beamSeg.m_flTexCoord );
+ m_Mesh.TexCoord2f( 1, 0, beamSeg.m_flTexCoord );
+ m_Mesh.AdvanceVertex();
+
+ m_Mesh.Position3fv( vPoint2.Base() );
+ m_Mesh.Color4f( VectorExpand( beamSeg.m_vColor ), beamSeg.m_flAlpha );
+ m_Mesh.TexCoord2f( 0, 1, beamSeg.m_flTexCoord );
+ m_Mesh.TexCoord2f( 1, 1, beamSeg.m_flTexCoord );
+ m_Mesh.AdvanceVertex();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : position -
+//-----------------------------------------------------------------------------
+void C_PropAirboat::DrawPontoonSplash( Vector origin, Vector direction, float speed )
+{
+ Vector offset;
+
+ CSmartPtr<CSplashParticle> pSimple = CSplashParticle::Create( "splish" );
+ pSimple->SetSortOrigin( origin );
+
+ SimpleParticle *pParticle;
+
+ Vector color = Vector( 0.8f, 0.8f, 0.75f );
+ float colorRamp;
+
+ float flScale = RemapVal( speed, 64, 256, 0.75f, 1.0f );
+
+ PMaterialHandle hMaterial;
+
+ float tempDelta = gpGlobals->frametime;
+
+ while( m_SplashTime.NextEvent( tempDelta ) )
+ {
+ if ( random->RandomInt( 0, 1 ) )
+ {
+ hMaterial = ParticleMgr()->GetPMaterial( "effects/splash1" );
+ }
+ else
+ {
+ hMaterial = ParticleMgr()->GetPMaterial( "effects/splash2" );
+ }
+
+ offset = RandomVector( -8.0f * flScale, 8.0f * flScale );
+ offset[2] = 0.0f;
+ offset += origin;
+
+ pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial, offset );
+
+ if ( pParticle == NULL )
+ continue;
+
+ pParticle->m_flLifetime = 0.0f;
+ pParticle->m_flDieTime = 0.25f;
+
+ pParticle->m_vecVelocity.Random( -0.4f, 0.4f );
+ pParticle->m_vecVelocity += (direction*5.0f+Vector(0,0,1));
+
+ VectorNormalize( pParticle->m_vecVelocity );
+
+ pParticle->m_vecVelocity *= speed + random->RandomFloat( -128.0f, 128.0f );
+
+ colorRamp = random->RandomFloat( 0.75f, 1.25f );
+
+ pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f;
+ pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f;
+ pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f;
+
+ pParticle->m_uchStartSize = random->RandomFloat( 8, 16 ) * flScale;
+ pParticle->m_uchEndSize = pParticle->m_uchStartSize * 2;
+
+ pParticle->m_uchStartAlpha = 255;
+ pParticle->m_uchEndAlpha = 0;
+
+ pParticle->m_flRoll = random->RandomInt( 0, 360 );
+ pParticle->m_flRollDelta = random->RandomFloat( -4.0f, 4.0f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : Vector startPos -
+// wakeDir -
+// wakeLength -
+//-----------------------------------------------------------------------------
+void C_PropAirboat::DrawPontoonWake( Vector startPos, Vector wakeDir, float wakeLength, float speed )
+{
+#define WAKE_STEPS 6
+
+ Vector wakeStep = wakeDir * ( wakeLength / (float) WAKE_STEPS );
+ Vector origin;
+ float scale;
+
+ IMaterial *pMaterial = materials->FindMaterial( "effects/splashwake1", NULL, false );
+ CMatRenderContextPtr pRenderContext( materials );
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( 0, 0, 0, pMaterial );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, WAKE_STEPS );
+
+ for ( int i = 0; i < WAKE_STEPS; i++ )
+ {
+ origin = startPos + ( wakeStep * i );
+ origin[0] += random->RandomFloat( -4.0f, 4.0f );
+ origin[1] += random->RandomFloat( -4.0f, 4.0f );
+ origin[2] = m_nExactWaterLevel + 2.0f;
+
+ float scaleRange = RemapVal( i, 0, WAKE_STEPS-1, 32, 64 );
+ scale = scaleRange + ( 8.0f * sin( gpGlobals->curtime * 5 * i ) );
+
+ float alpha = RemapValClamped( speed, 128, 600, 0.05f, 0.25f );
+ float color[4] = { 1.0f, 1.0f, 1.0f, alpha };
+
+ // Needs to be time based so it'll freeze when the game is frozen
+ float yaw = random->RandomFloat( 0, 360 );
+
+ Vector rRight = ( Vector(1,0,0) * cos( DEG2RAD( yaw ) ) ) - ( Vector(0,1,0) * sin( DEG2RAD( yaw ) ) );
+ Vector rUp = ( Vector(1,0,0) * cos( DEG2RAD( yaw+90.0f ) ) ) - ( Vector(0,1,0) * sin( DEG2RAD( yaw+90.0f ) ) );
+
+ Vector point;
+ meshBuilder.Color4fv (color);
+ meshBuilder.TexCoord2f (0, 0, 1);
+ VectorMA (origin, -scale, rRight, point);
+ VectorMA (point, -scale, rUp, point);
+ meshBuilder.Position3fv (point.Base());
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4fv (color);
+ meshBuilder.TexCoord2f (0, 0, 0);
+ VectorMA (origin, scale, rRight, point);
+ VectorMA (point, -scale, rUp, point);
+ meshBuilder.Position3fv (point.Base());
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4fv (color);
+ meshBuilder.TexCoord2f (0, 1, 0);
+ VectorMA (origin, scale, rRight, point);
+ VectorMA (point, scale, rUp, point);
+ meshBuilder.Position3fv (point.Base());
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4fv (color);
+ meshBuilder.TexCoord2f (0, 1, 1);
+ VectorMA (origin, -scale, rRight, point);
+ VectorMA (point, scale, rUp, point);
+ meshBuilder.Position3fv (point.Base());
+ meshBuilder.AdvanceVertex();
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int C_PropAirboat::DrawWake( void )
+{
+ if ( cl_draw_airboat_wake.GetBool() == false )
+ return 0;
+
+ // Make sure we're in water...
+ if ( GetWaterLevel() == 0 )
+ return 0;
+
+ //FIXME: For now, we don't draw slime this way
+ if ( GetWaterLevel() == 2 )
+ return 0;
+
+ bool bDriven = ( GetPassenger( VEHICLE_ROLE_DRIVER ) != NULL );
+
+ Vector vehicleDir = m_vecPhysVelocity;
+ float vehicleSpeed = VectorNormalize( vehicleDir );
+
+ Vector vecPontoonFrontLeft;
+ Vector vecPontoonFrontRight;
+ Vector vecPontoonRearLeft;
+ Vector vecPontoonRearRight;
+ Vector vecSplashPoint;
+
+ QAngle fooAngles;
+
+ //FIXME: This lookup should be cached off
+ // Get all attachments
+ GetAttachment( LookupAttachment( "raytrace_fl" ), vecPontoonFrontLeft, fooAngles );
+ GetAttachment( LookupAttachment( "raytrace_fr" ), vecPontoonFrontRight, fooAngles );
+ GetAttachment( LookupAttachment( "raytrace_rl" ), vecPontoonRearLeft, fooAngles );
+ GetAttachment( LookupAttachment( "raytrace_rr" ), vecPontoonRearRight, fooAngles );
+ GetAttachment( LookupAttachment( "splash_pt" ), vecSplashPoint, fooAngles );
+
+ // Find the direction of the pontoons
+ Vector vecLeftWakeDir = ( vecPontoonRearLeft - vecPontoonFrontLeft );
+ Vector vecRightWakeDir = ( vecPontoonRearRight - vecPontoonFrontRight );
+
+ // Find the pontoon's size
+ float flWakeLeftLength = VectorNormalize( vecLeftWakeDir );
+ float flWakeRightLength = VectorNormalize( vecRightWakeDir );
+
+ vecPontoonFrontLeft.z = m_nExactWaterLevel;
+ vecPontoonFrontRight.z = m_nExactWaterLevel;
+
+ if ( bDriven && vehicleSpeed > 128.0f )
+ {
+ DrawPontoonWake( vecPontoonFrontLeft, vecLeftWakeDir, flWakeLeftLength, vehicleSpeed );
+ DrawPontoonWake( vecPontoonFrontRight, vecRightWakeDir, flWakeRightLength, vehicleSpeed );
+
+ Vector vecSplashDir;
+ Vector vForward;
+ GetVectors( &vForward, NULL, NULL );
+
+ if ( m_vecPhysVelocity.x < -64.0f )
+ {
+ vecSplashDir = vecLeftWakeDir - vForward;
+ VectorNormalize( vecSplashDir );
+
+ // Don't do this if we're going backwards
+ if ( m_vecPhysVelocity.y > 0.0f )
+ {
+ DrawPontoonSplash( vecPontoonFrontLeft + ( vecLeftWakeDir * 1.0f ), vecSplashDir, m_vecPhysVelocity.y );
+ }
+ }
+ else if ( m_vecPhysVelocity.x > 64.0f )
+ {
+ vecSplashDir = vecRightWakeDir + vForward;
+ VectorNormalize( vecSplashDir );
+
+ // Don't do this if we're going backwards
+ if ( m_vecPhysVelocity.y > 0.0f )
+ {
+ DrawPontoonSplash( vecPontoonFrontRight + ( vecRightWakeDir * 1.0f ), vecSplashDir, m_vecPhysVelocity.y );
+ }
+ }
+ }
+
+ // Must have at least one point
+ if ( m_nStepCount <= 1 )
+ return 1;
+
+ IMaterial *pMaterial = materials->FindMaterial( "effects/splashwake4", 0);
+
+ //Bind the material
+ CMatRenderContextPtr pRenderContext( materials );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );
+
+ m_Mesh.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, (m_nStepCount-1) * 2 );
+
+ TrailPoint_t *pLast = GetTrailPoint( m_nStepCount - 1 );
+
+ TrailPoint_t currentPoint;
+ currentPoint.m_flDieTime = gpGlobals->curtime + 0.5f;
+ currentPoint.m_vecScreenPos = GetAbsOrigin();
+ currentPoint.m_vecScreenPos[2] = m_nExactWaterLevel + 16;
+ currentPoint.m_flTexCoord = pLast->m_flTexCoord + currentPoint.m_vecScreenPos.DistTo(pLast->m_vecScreenPos);
+ currentPoint.m_flTexCoord = fmod( currentPoint.m_flTexCoord, 1 );
+ currentPoint.m_flWidthVariance = 0.0f;
+
+ TrailPoint_t *pPrevPoint = NULL;
+
+ Vector segDir, normal;
+
+ for ( int i = 0; i <= m_nStepCount; ++i )
+ {
+ // This makes it so that we're always drawing to the current location
+ TrailPoint_t *pPoint = (i != m_nStepCount) ? GetTrailPoint(i) : ¤tPoint;
+
+ float flLifePerc = RemapValClamped( ( pPoint->m_flDieTime - gpGlobals->curtime ), 0, WAKE_LIFETIME, 0.0f, 1.0f );
+
+ BeamSeg_t curSeg;
+ curSeg.m_vColor.x = curSeg.m_vColor.y = curSeg.m_vColor.z = 1.0f;
+
+ float flAlphaFade = flLifePerc;
+ float alpha = RemapValClamped( fabs( m_vecPhysVelocity.y ), 128, 600, 0.0f, 1.0f );
+
+ curSeg.m_flAlpha = 0.25f;
+ curSeg.m_flAlpha *= flAlphaFade * alpha;
+
+ curSeg.m_vPos = pPoint->m_vecScreenPos;
+
+ float widthBase = SimpleSplineRemapVal( fabs( m_vecPhysVelocity.y ), 128, 600, 32, 48 );
+
+ curSeg.m_flWidth = Lerp( flLifePerc, widthBase*6, widthBase );
+ curSeg.m_flWidth += pPoint->m_flWidthVariance;
+
+ if ( curSeg.m_flWidth < 0.0f )
+ {
+ curSeg.m_flWidth = 0.0f;
+ }
+
+ curSeg.m_flTexCoord = pPoint->m_flTexCoord;
+
+ if ( pPrevPoint != NULL )
+ {
+ segDir = ( pPrevPoint->m_vecScreenPos - pPoint->m_vecScreenPos );
+ VectorNormalize( segDir );
+
+ normal = CrossProduct( segDir, Vector( 0, 0, -1 ) );
+
+ DrawSegment( curSeg, normal );
+ }
+
+ pPrevPoint = pPoint;
+ }
+
+ m_Mesh.End();
+ pMesh->Draw();
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : flags -
+// Output : int
+//-----------------------------------------------------------------------------
+int C_PropAirboat::DrawModel( int flags )
+{
+ if ( BaseClass::DrawModel( flags ) == false )
+ return 0;
+
+ if ( !m_bReadyToDraw )
+ return 0;
+
+ return DrawWake();
+}
|