mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-22 06:06:50 +00:00
1797 lines
63 KiB
C++
1797 lines
63 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: The airboat, a sporty nimble water craft.
|
|
//
|
|
//=============================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include "physics_airboat.h"
|
|
#include "cmodel.h"
|
|
#include <ivp_ray_solver.hxx>
|
|
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
#ifdef _X360
|
|
#define AIRBOAT_STEERING_RATE_MIN 0.000225f
|
|
#define AIRBOAT_STEERING_RATE_MAX (10.0f * AIRBOAT_STEERING_RATE_MIN)
|
|
#define AIRBOAT_STEERING_INTERVAL 1.5f
|
|
#else
|
|
#define AIRBOAT_STEERING_RATE_MIN 0.00045f
|
|
#define AIRBOAT_STEERING_RATE_MAX (5.0f * AIRBOAT_STEERING_RATE_MIN)
|
|
#define AIRBOAT_STEERING_INTERVAL 0.5f
|
|
#endif //_X360
|
|
|
|
#define AIRBOAT_ROT_DRAG 0.00004f
|
|
#define AIRBOAT_ROT_DAMPING 0.001f
|
|
|
|
// Mass-independent thrust values
|
|
#define AIRBOAT_THRUST_MAX 11.0f // N / kg
|
|
#define AIRBOAT_THRUST_MAX_REVERSE 7.5f // N / kg
|
|
|
|
// Mass-independent drag values
|
|
#define AIRBOAT_WATER_DRAG_LEFT_RIGHT 0.6f
|
|
#define AIRBOAT_WATER_DRAG_FORWARD_BACK 0.005f
|
|
#define AIRBOAT_WATER_DRAG_UP_DOWN 0.0025f
|
|
|
|
#define AIRBOAT_GROUND_DRAG_LEFT_RIGHT 2.0
|
|
#define AIRBOAT_GROUND_DRAG_FORWARD_BACK 1.0
|
|
#define AIRBOAT_GROUND_DRAG_UP_DOWN 0.8
|
|
|
|
#define AIRBOAT_DRY_FRICTION_SCALE 0.6f // unitless, reduces our friction on all surfaces other than water
|
|
|
|
#define AIRBOAT_RAYCAST_DIST 0.35f // m (~14in)
|
|
#define AIRBOAT_RAYCAST_DIST_WATER_LOW 0.1f // m (~4in)
|
|
#define AIRBOAT_RAYCAST_DIST_WATER_HIGH 0.35f // m (~16in)
|
|
|
|
// Amplitude of wave noise. Blend from max to min as speed increases.
|
|
#define AIRBOAT_WATER_NOISE_MIN 0.01 // m (~0.4in)
|
|
#define AIRBOAT_WATER_NOISE_MAX 0.03 // m (~1.2in)
|
|
|
|
// Frequency of wave noise. Blend from min to max as speed increases.
|
|
#define AIRBOAT_WATER_FREQ_MIN 1.5
|
|
#define AIRBOAT_WATER_FREQ_MAX 1.5
|
|
|
|
// Phase difference in wave noise between left and right pontoons
|
|
// Blend from max to min as speed increases.
|
|
#define AIRBOAT_WATER_PHASE_MIN 0.0 // s
|
|
#define AIRBOAT_WATER_PHASE_MAX 1.5 // s
|
|
|
|
|
|
#define AIRBOAT_GRAVITY 9.81f // m/s2
|
|
|
|
// Pontoon indices
|
|
enum
|
|
{
|
|
AIRBOAT_PONTOON_FRONT_LEFT = 0,
|
|
AIRBOAT_PONTOON_FRONT_RIGHT,
|
|
AIRBOAT_PONTOON_REAR_LEFT,
|
|
AIRBOAT_PONTOON_REAR_RIGHT,
|
|
};
|
|
|
|
|
|
class IVP_Ray_Solver_Template;
|
|
class IVP_Ray_Hit;
|
|
class IVP_Event_Sim;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Constructor
|
|
//-----------------------------------------------------------------------------
|
|
class CAirboatFrictionData : public IPhysicsCollisionData
|
|
{
|
|
public:
|
|
CAirboatFrictionData()
|
|
{
|
|
m_vecPoint.Init( 0, 0, 0 );
|
|
m_vecNormal.Init( 0, 0, 0 );
|
|
m_vecVelocity.Init( 0, 0, 0 );
|
|
}
|
|
|
|
virtual void GetSurfaceNormal( Vector &out )
|
|
{
|
|
out = m_vecPoint;
|
|
}
|
|
|
|
virtual void GetContactPoint( Vector &out )
|
|
{
|
|
out = m_vecNormal;
|
|
}
|
|
|
|
virtual void GetContactSpeed( Vector &out )
|
|
{
|
|
out = m_vecVelocity;
|
|
}
|
|
|
|
public:
|
|
Vector m_vecPoint;
|
|
Vector m_vecNormal;
|
|
Vector m_vecVelocity;
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Constructor
|
|
//-----------------------------------------------------------------------------
|
|
CPhysics_Airboat::CPhysics_Airboat( IVP_Environment *pEnv, const IVP_Template_Car_System *pCarSystem,
|
|
IPhysicsGameTrace *pGameTrace )
|
|
{
|
|
InitRaycastCarBody( pCarSystem );
|
|
InitRaycastCarEnvironment( pEnv, pCarSystem );
|
|
InitRaycastCarWheels( pCarSystem );
|
|
InitRaycastCarAxes( pCarSystem );
|
|
|
|
InitAirboat( pCarSystem );
|
|
m_pGameTrace = pGameTrace;
|
|
|
|
m_SteeringAngle = 0;
|
|
m_bSteeringReversed = false;
|
|
|
|
m_flThrust = 0;
|
|
|
|
m_bAirborne = false;
|
|
m_flAirTime = 0;
|
|
m_bWeakJump = false;
|
|
|
|
m_flPitchErrorPrev = 0;
|
|
m_flRollErrorPrev = 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Deconstructor
|
|
//-----------------------------------------------------------------------------
|
|
CPhysics_Airboat::~CPhysics_Airboat()
|
|
{
|
|
m_pAirboatBody->get_environment()->get_controller_manager()->remove_controller_from_environment( this, IVP_TRUE );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Setup the car system wheels.
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::InitAirboat( const IVP_Template_Car_System *pCarSystem )
|
|
{
|
|
for ( int iWheel = 0; iWheel < pCarSystem->n_wheels; ++iWheel )
|
|
{
|
|
m_pWheels[iWheel] = pCarSystem->car_wheel[iWheel];
|
|
m_pWheels[iWheel]->enable_collision_detection( IVP_FALSE );
|
|
}
|
|
|
|
CPhysicsObject* pBodyObject = static_cast<CPhysicsObject*>(pCarSystem->car_body->client_data);
|
|
|
|
pBodyObject->EnableGravity( false );
|
|
|
|
// We do our own buoyancy simulation.
|
|
pBodyObject->SetCallbackFlags( pBodyObject->GetCallbackFlags() & ~CALLBACK_DO_FLUID_SIMULATION );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Get the raycast wheel.
|
|
//-----------------------------------------------------------------------------
|
|
IPhysicsObject *CPhysics_Airboat::GetWheel( int index )
|
|
{
|
|
Assert( index >= 0 );
|
|
Assert( index < n_wheels );
|
|
|
|
return ( IPhysicsObject* )m_pWheels[index]->client_data;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::SetWheelFriction( int iWheel, float flFriction )
|
|
{
|
|
change_friction_of_wheel( IVP_POS_WHEEL( iWheel ), flFriction );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns an amount to add to the front pontoon raycasts to simulate wave noise.
|
|
// Input : nPontoonIndex - Which pontoon we're dealing with (0 or 1).
|
|
// flSpeedRatio - Speed as a ratio of max speed [0..1]
|
|
//-----------------------------------------------------------------------------
|
|
float CPhysics_Airboat::ComputeFrontPontoonWaveNoise( int nPontoonIndex, float flSpeedRatio )
|
|
{
|
|
// Add in sinusoidal noise cause by undulating water. Reduce the amplitude of the noise at higher speeds.
|
|
IVP_FLOAT flNoiseScale = RemapValClamped( 1.0 - flSpeedRatio, 0, 1, AIRBOAT_WATER_NOISE_MIN, AIRBOAT_WATER_NOISE_MAX );
|
|
|
|
// Apply a phase shift between left and right pontoons to simulate waves passing under the boat.
|
|
IVP_FLOAT flPhaseShift = 0;
|
|
if ( flSpeedRatio < 0.3 )
|
|
{
|
|
// BUG: this allows a discontinuity in the waveform - use two superimposed sine waves instead?
|
|
flPhaseShift = nPontoonIndex * AIRBOAT_WATER_PHASE_MAX;
|
|
}
|
|
|
|
// Increase the wave frequency as speed increases.
|
|
IVP_FLOAT flFrequency = RemapValClamped( flSpeedRatio, 0, 1, AIRBOAT_WATER_FREQ_MIN, AIRBOAT_WATER_FREQ_MAX );
|
|
|
|
//Msg( "Wave amp=%f, freq=%f, phase=%f\n", flNoiseScale, flFrequency, flPhaseShift );
|
|
return flNoiseScale * sin( flFrequency * ( m_pCore->environment->get_current_time().get_seconds() + flPhaseShift ) );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:: Convert data to HL2 measurements, and test direction of raycast.
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::pre_raycasts_gameside( int nRaycastCount, IVP_Ray_Solver_Template *pRays,
|
|
Ray_t *pGameRays, IVP_Raycast_Airboat_Impact *pImpacts )
|
|
{
|
|
IVP_FLOAT flForwardSpeedRatio = clamp( m_vecLocalVelocity.k[2] / 10.0f, 0.f, 1.0f );
|
|
//Msg( "flForwardSpeedRatio = %f\n", flForwardSpeedRatio );
|
|
|
|
IVP_FLOAT flSpeed = ( IVP_FLOAT )m_pCore->speed.real_length();
|
|
IVP_FLOAT flSpeedRatio = clamp( flSpeed / 15.0f, 0.f, 1.0f );
|
|
if ( !m_flThrust )
|
|
{
|
|
flForwardSpeedRatio *= 0.5;
|
|
}
|
|
|
|
// This is a little weird. We adjust the front pontoon ray lengths based on forward velocity,
|
|
// but ONLY if both pontoons are in the water, which we won't know until we do the raycast.
|
|
// So we do most of the work here, and cache some of the results to use them later.
|
|
Vector vecStart[4];
|
|
Vector vecDirection[4];
|
|
Vector vecZero( 0.0f, 0.0f, 0.0f );
|
|
|
|
int nFrontPontoonsInWater = 0;
|
|
|
|
int iRaycast;
|
|
for ( iRaycast = 0; iRaycast < nRaycastCount; ++iRaycast )
|
|
{
|
|
// Setup the ray.
|
|
ConvertPositionToHL( pRays[iRaycast].ray_start_point, vecStart[iRaycast] );
|
|
ConvertDirectionToHL( pRays[iRaycast].ray_normized_direction, vecDirection[iRaycast] );
|
|
|
|
float flRayLength = IVP2HL( pRays[iRaycast].ray_length );
|
|
|
|
// Check to see if that point is in water.
|
|
pImpacts[iRaycast].bInWater = IVP_FALSE;
|
|
if ( m_pGameTrace->VehiclePointInWater( vecStart[iRaycast] ) )
|
|
{
|
|
vecDirection[iRaycast].Negate();
|
|
pImpacts[iRaycast].bInWater = IVP_TRUE;
|
|
}
|
|
|
|
Vector vecEnd = vecStart[iRaycast] + ( vecDirection[iRaycast] * flRayLength );
|
|
|
|
// Adjust the trace if the pontoon is in the water.
|
|
if ( m_pGameTrace->VehiclePointInWater( vecEnd ) )
|
|
{
|
|
// Reduce the ray length in the water.
|
|
pRays[iRaycast].ray_length = AIRBOAT_RAYCAST_DIST_WATER_LOW;
|
|
|
|
if ( iRaycast < 2 )
|
|
{
|
|
nFrontPontoonsInWater++;
|
|
|
|
// Front pontoons.
|
|
// Add a little sinusoidal noise to simulate waves.
|
|
IVP_FLOAT flNoise = ComputeFrontPontoonWaveNoise( iRaycast, flSpeedRatio );
|
|
pRays[iRaycast].ray_length += flNoise;
|
|
}
|
|
else
|
|
{
|
|
// Recalculate the end position in HL coordinates.
|
|
flRayLength = IVP2HL( pRays[iRaycast].ray_length );
|
|
vecEnd = vecStart[iRaycast] + ( vecDirection[iRaycast] * flRayLength );
|
|
}
|
|
}
|
|
|
|
pGameRays[iRaycast].Init( vecStart[iRaycast], vecEnd, vecZero, vecZero );
|
|
}
|
|
|
|
// If both front pontoons are in the water, add in a bit of lift proportional to our
|
|
// forward speed. We can't do this to only one of the front pontoons because it causes
|
|
// some twist if we do.
|
|
// FIXME: this does some redundant work (computes the wave noise again)
|
|
if ( nFrontPontoonsInWater == 2 )
|
|
{
|
|
for ( int i = 0; i < 2; i++ )
|
|
{
|
|
// Front pontoons.
|
|
// Raise it higher out of the water as we go faster forward.
|
|
pRays[i].ray_length = RemapValClamped( flForwardSpeedRatio, 0, 1, AIRBOAT_RAYCAST_DIST_WATER_LOW, AIRBOAT_RAYCAST_DIST_WATER_HIGH );
|
|
|
|
// Add a little sinusoidal noise to simulate waves.
|
|
IVP_FLOAT flNoise = ComputeFrontPontoonWaveNoise( i, flSpeedRatio );
|
|
pRays[i].ray_length += flNoise;
|
|
|
|
// Recalculate the end position in HL coordinates.
|
|
float flRayLength = IVP2HL( pRays[i].ray_length );
|
|
Vector vecEnd = vecStart[i] + ( vecDirection[i] * flRayLength );
|
|
|
|
pGameRays[i].Init( vecStart[i], vecEnd, vecZero, vecZero );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
float CPhysics_Airboat::GetWaterDepth( Ray_t *pGameRay, IPhysicsObject *pPhysAirboat )
|
|
{
|
|
float flDepth = 0.0f;
|
|
|
|
trace_t trace;
|
|
|
|
Ray_t waterRay;
|
|
Vector vecStart = pGameRay->m_Start;
|
|
Vector vecEnd( vecStart.x, vecStart.y, vecStart.z + 1000.0f );
|
|
Vector vecZero( 0.0f, 0.0f, 0.0f );
|
|
waterRay.Init( vecStart, vecEnd, vecZero, vecZero );
|
|
m_pGameTrace->VehicleTraceRayWithWater( waterRay, pPhysAirboat->GetGameData(), &trace );
|
|
|
|
flDepth = 1000.0f * trace.fractionleftsolid;
|
|
|
|
return flDepth;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Performs traces to figure out what is at each of the raycast points
|
|
// and fills out the pImpacts array with that information.
|
|
// Input : nRaycastCount - Number of elements in the arrays pointed to by pRays
|
|
// and pImpacts.
|
|
// pRays - Holds the rays to trace with.
|
|
// pImpacts - Receives the trace results.
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::do_raycasts_gameside( int nRaycastCount, IVP_Ray_Solver_Template *pRays,
|
|
IVP_Raycast_Airboat_Impact *pImpacts )
|
|
{
|
|
Assert( nRaycastCount >= 0 );
|
|
Assert( nRaycastCount <= IVP_RAYCAST_AIRBOAT_MAX_WHEELS );
|
|
|
|
Ray_t gameRays[IVP_RAYCAST_AIRBOAT_MAX_WHEELS];
|
|
pre_raycasts_gameside( nRaycastCount, pRays, gameRays, pImpacts );
|
|
|
|
// Do the raycasts and set impact data.
|
|
trace_t trace;
|
|
for ( int iRaycast = 0; iRaycast < nRaycastCount; ++iRaycast )
|
|
{
|
|
// Trace.
|
|
if ( pImpacts[iRaycast].bInWater )
|
|
{
|
|
// The start position is underwater. Trace up to find the water surface.
|
|
IPhysicsObject *pPhysAirboat = static_cast<IPhysicsObject*>( m_pAirboatBody->client_data );
|
|
m_pGameTrace->VehicleTraceRay( gameRays[iRaycast], pPhysAirboat->GetGameData(), &trace );
|
|
pImpacts[iRaycast].flDepth = GetWaterDepth( &gameRays[iRaycast], pPhysAirboat );
|
|
}
|
|
else
|
|
{
|
|
// Trace down to find the ground or water.
|
|
IPhysicsObject *pPhysAirboat = static_cast<IPhysicsObject*>( m_pAirboatBody->client_data );
|
|
m_pGameTrace->VehicleTraceRayWithWater( gameRays[iRaycast], pPhysAirboat->GetGameData(), &trace );
|
|
}
|
|
|
|
ConvertPositionToIVP( gameRays[iRaycast].m_Start + gameRays[iRaycast].m_StartOffset, m_CarSystemDebugData.wheelRaycasts[iRaycast][0] );
|
|
ConvertPositionToIVP( gameRays[iRaycast].m_Start + gameRays[iRaycast].m_StartOffset + gameRays[iRaycast].m_Delta, m_CarSystemDebugData.wheelRaycasts[iRaycast][1] );
|
|
m_CarSystemDebugData.wheelRaycastImpacts[iRaycast] = trace.fraction * gameRays[iRaycast].m_Delta.Length();
|
|
|
|
// Set impact data.
|
|
pImpacts[iRaycast].bImpactWater = IVP_FALSE;
|
|
pImpacts[iRaycast].bImpact = IVP_FALSE;
|
|
if ( trace.fraction != 1.0f )
|
|
{
|
|
pImpacts[iRaycast].bImpact = IVP_TRUE;
|
|
|
|
// Set water surface flag.
|
|
pImpacts[iRaycast].flDepth = 0.0f;
|
|
if ( trace.contents & MASK_WATER )
|
|
{
|
|
pImpacts[iRaycast].bImpactWater = IVP_TRUE;
|
|
}
|
|
|
|
// Save impact surface data.
|
|
ConvertPositionToIVP( trace.endpos, pImpacts[iRaycast].vecImpactPointWS );
|
|
ConvertDirectionToIVP( trace.plane.normal, pImpacts[iRaycast].vecImpactNormalWS );
|
|
|
|
// Save surface properties.
|
|
const surfacedata_t *pSurfaceData = physprops->GetSurfaceData( trace.surface.surfaceProps );
|
|
|
|
pImpacts[iRaycast].nSurfaceProps = trace.surface.surfaceProps;
|
|
|
|
if (pImpacts[iRaycast].vecImpactNormalWS.k[1] < -0.707)
|
|
{
|
|
// dampening is 1/t, where t is how long it takes to come to a complete stop
|
|
pImpacts[iRaycast].flDampening = pSurfaceData->physics.dampening;
|
|
pImpacts[iRaycast].flFriction = pSurfaceData->physics.friction;
|
|
}
|
|
else
|
|
{
|
|
// This surface is too vertical -- no friction or damping from it.
|
|
pImpacts[iRaycast].flDampening = pSurfaceData->physics.dampening;
|
|
pImpacts[iRaycast].flFriction = pSurfaceData->physics.friction;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Entry point for airboat simulation.
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::do_simulation_controller( IVP_Event_Sim *pEventSim, IVP_U_Vector<IVP_Core> * )
|
|
{
|
|
IVP_Ray_Solver_Template raySolverTemplates[IVP_RAYCAST_AIRBOAT_MAX_WHEELS];
|
|
IVP_Raycast_Airboat_Impact impacts[IVP_RAYCAST_AIRBOAT_MAX_WHEELS];
|
|
|
|
// Cache some data into members here so we only do the work once.
|
|
m_pCore = m_pAirboatBody->get_core();
|
|
const IVP_U_Matrix *matWorldFromCore = m_pCore->get_m_world_f_core_PSI();
|
|
|
|
// Cache the speed.
|
|
m_flSpeed = ( IVP_FLOAT )m_pCore->speed.real_length();
|
|
|
|
// Cache the local velocity vector.
|
|
matWorldFromCore->vimult3(&m_pCore->speed, &m_vecLocalVelocity);
|
|
|
|
// Raycasts.
|
|
PreRaycasts( raySolverTemplates, matWorldFromCore, impacts );
|
|
do_raycasts_gameside( n_wheels, raySolverTemplates, impacts );
|
|
if ( !PostRaycasts( raySolverTemplates, matWorldFromCore, impacts ) )
|
|
return;
|
|
|
|
UpdateAirborneState( impacts, pEventSim );
|
|
|
|
// Enumerate the controllers attached to us.
|
|
//for (int i = m_pCore->controllers_of_core.len() - 1; i >= 0; i--)
|
|
//{
|
|
// IVP_Controller *pController = m_pCore->controllers_of_core.element_at(i);
|
|
//}
|
|
|
|
// Pontoons. Buoyancy or ground impacts.
|
|
DoSimulationPontoons( impacts, pEventSim );
|
|
|
|
// Drag due to water and ground friction.
|
|
DoSimulationDrag( impacts, pEventSim );
|
|
|
|
// Turbine (fan).
|
|
DoSimulationTurbine( pEventSim );
|
|
|
|
// Steering.
|
|
DoSimulationSteering( pEventSim );
|
|
|
|
// Anti-pitch.
|
|
DoSimulationKeepUprightPitch( impacts, pEventSim );
|
|
|
|
// Anti-roll.
|
|
DoSimulationKeepUprightRoll( impacts, pEventSim );
|
|
|
|
// Additional gravity based on speed.
|
|
DoSimulationGravity( pEventSim );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Initialize the rays to be cast from the vehicle wheel positions to
|
|
// the "ground."
|
|
// Input : pRaySolverTemplates -
|
|
// matWorldFromCore -
|
|
// pImpacts -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::PreRaycasts( IVP_Ray_Solver_Template *pRaySolverTemplates,
|
|
const IVP_U_Matrix *matWorldFromCore,
|
|
IVP_Raycast_Airboat_Impact *pImpacts )
|
|
{
|
|
int nPontoonPoints = n_wheels;
|
|
for ( int iPoint = 0; iPoint < nPontoonPoints; ++iPoint )
|
|
{
|
|
IVP_Raycast_Airboat_Wheel *pPontoonPoint = get_wheel( IVP_POS_WHEEL( iPoint ) );
|
|
if ( pPontoonPoint )
|
|
{
|
|
// Fill the in the ray solver template for the current wheel.
|
|
IVP_Ray_Solver_Template &raySolverTemplate = pRaySolverTemplates[iPoint];
|
|
|
|
// Transform the wheel "start" position from vehicle core-space to world-space. This is
|
|
// the raycast starting position.
|
|
matWorldFromCore->vmult4( &pPontoonPoint->raycast_start_cs, &raySolverTemplate.ray_start_point );
|
|
|
|
// Transform the shock (spring) direction from vehicle core-space to world-space. This is
|
|
// the raycast direction.
|
|
matWorldFromCore->vmult3( &pPontoonPoint->raycast_dir_cs, &pImpacts[iPoint].raycast_dir_ws );
|
|
raySolverTemplate.ray_normized_direction.set( &pImpacts[iPoint].raycast_dir_ws );
|
|
|
|
// Set the length of the ray cast.
|
|
raySolverTemplate.ray_length = AIRBOAT_RAYCAST_DIST;
|
|
|
|
// Set the ray solver template flags. This defines which objects you wish to
|
|
// collide against in the physics environment.
|
|
raySolverTemplate.ray_flags = IVP_RAY_SOLVER_ALL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Determines whether we are airborne and whether we just performed a
|
|
// weak or strong jump. Weak jumps are jumps at below a threshold speed,
|
|
// and disable the turbine and pitch controller.
|
|
// Input : pImpacts -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::UpdateAirborneState( IVP_Raycast_Airboat_Impact *pImpacts, IVP_Event_Sim *pEventSim )
|
|
{
|
|
int nCount = CountSurfaceContactPoints(pImpacts);
|
|
if (!nCount)
|
|
{
|
|
if (!m_bAirborne)
|
|
{
|
|
m_bAirborne = true;
|
|
m_flAirTime = 0;
|
|
|
|
IVP_FLOAT flSpeed = ( IVP_FLOAT )m_pCore->speed.real_length();
|
|
if (flSpeed < 11.0f)
|
|
{
|
|
//Msg("*** WEAK JUMP at %f!!!\n", flSpeed);
|
|
m_bWeakJump = true;
|
|
}
|
|
else
|
|
{
|
|
//Msg("Strong JUMP at %f\n", flSpeed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_flAirTime += pEventSim->delta_time;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_bAirborne = false;
|
|
m_bWeakJump = false;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CPhysics_Airboat::PostRaycasts( IVP_Ray_Solver_Template *pRaySolverTemplates, const IVP_U_Matrix *matWorldFromCore,
|
|
IVP_Raycast_Airboat_Impact *pImpacts )
|
|
{
|
|
bool bReturn = true;
|
|
|
|
int nPontoonPoints = n_wheels;
|
|
for( int iPoint = 0; iPoint < nPontoonPoints; ++iPoint )
|
|
{
|
|
// Get data at raycast position.
|
|
IVP_Raycast_Airboat_Wheel *pPontoonPoint = get_wheel( IVP_POS_WHEEL( iPoint ) );
|
|
IVP_Raycast_Airboat_Impact *pImpact = &pImpacts[iPoint];
|
|
IVP_Ray_Solver_Template *pRaySolver = &pRaySolverTemplates[iPoint];
|
|
if ( !pPontoonPoint || !pImpact || !pRaySolver )
|
|
continue;
|
|
|
|
// Copy the ray length back, it may have changed.
|
|
pPontoonPoint->raycast_length = pRaySolver->ray_length;
|
|
|
|
// Test for inverted raycast direction.
|
|
if ( pImpact->bInWater )
|
|
{
|
|
pImpact->raycast_dir_ws.set_multiple( &pImpact->raycast_dir_ws, -1 );
|
|
}
|
|
|
|
// Impact.
|
|
if ( pImpact->bImpact )
|
|
{
|
|
// Save impact distance.
|
|
IVP_U_Point vecDelta;
|
|
vecDelta.subtract( &pImpact->vecImpactPointWS, &pRaySolver->ray_start_point );
|
|
pPontoonPoint->raycast_dist = vecDelta.real_length();
|
|
|
|
// Get the inverse portion of the surface normal in the direction of the ray cast (shock - used in the shock simulation code for the sign
|
|
// and percentage of force applied to the shock).
|
|
pImpact->inv_normal_dot_dir = 1.1f / ( IVP_Inline_Math::fabsd( pImpact->raycast_dir_ws.dot_product( &pImpact->vecImpactNormalWS ) ) + 0.1f );
|
|
|
|
// Set the wheel friction - ground friction (if any) + wheel friction.
|
|
pImpact->friction_value = pImpact->flFriction * pPontoonPoint->friction_of_wheel;
|
|
}
|
|
// No impact.
|
|
else
|
|
{
|
|
pPontoonPoint->raycast_dist = pPontoonPoint->raycast_length;
|
|
|
|
pImpact->inv_normal_dot_dir = 1.0f;
|
|
pImpact->moveable_object_hit_by_ray = NULL;
|
|
pImpact->vecImpactNormalWS.set_multiple( &pImpact->raycast_dir_ws, -1 );
|
|
pImpact->friction_value = 1.0f;
|
|
}
|
|
|
|
// Set the new wheel position (the impact point or the full ray distance). Make this from the wheel not the ray trace position.
|
|
pImpact->vecImpactPointWS.add_multiple( &pRaySolver->ray_start_point, &pImpact->raycast_dir_ws, pPontoonPoint->raycast_dist );
|
|
|
|
// Get the speed (velocity) at the impact point.
|
|
m_pCore->get_surface_speed_ws( &pImpact->vecImpactPointWS, &pImpact->surface_speed_wheel_ws );
|
|
pImpact->projected_surface_speed_wheel_ws.set_orthogonal_part( &pImpact->surface_speed_wheel_ws, &pImpact->vecImpactNormalWS );
|
|
|
|
matWorldFromCore->vmult3( &pPontoonPoint->axis_direction_cs, &pImpact->axis_direction_ws );
|
|
pImpact->projected_axis_direction_ws.set_orthogonal_part( &pImpact->axis_direction_ws, &pImpact->vecImpactNormalWS );
|
|
if ( pImpact->projected_axis_direction_ws.normize() == IVP_FAULT )
|
|
{
|
|
DevMsg( "CPhysics_Airboat::do_simulation_controller projected_axis_direction_ws.normize failed\n" );
|
|
bReturn = false;
|
|
}
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::DoSimulationPontoons( IVP_Raycast_Airboat_Impact *pImpacts, IVP_Event_Sim *pEventSim )
|
|
{
|
|
int nPontoonPoints = n_wheels;
|
|
for ( int iPoint = 0; iPoint < nPontoonPoints; ++iPoint )
|
|
{
|
|
IVP_Raycast_Airboat_Wheel *pPontoonPoint = get_wheel( IVP_POS_WHEEL( iPoint ) );
|
|
if ( !pPontoonPoint )
|
|
continue;
|
|
|
|
if ( pImpacts[iPoint].bImpact )
|
|
{
|
|
DoSimulationPontoonsGround( pPontoonPoint, &pImpacts[iPoint], pEventSim );
|
|
}
|
|
else if ( pImpacts[iPoint].bInWater )
|
|
{
|
|
DoSimulationPontoonsWater( pPontoonPoint, &pImpacts[iPoint], pEventSim );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Handle pontoons on ground.
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::DoSimulationPontoonsGround( IVP_Raycast_Airboat_Wheel *pPontoonPoint,
|
|
IVP_Raycast_Airboat_Impact *pImpact, IVP_Event_Sim *pEventSim )
|
|
{
|
|
// Check to see if we hit anything, otherwise the no force on this point.
|
|
IVP_DOUBLE flDiff = pPontoonPoint->raycast_dist - pPontoonPoint->raycast_length;
|
|
if ( flDiff >= 0 )
|
|
return;
|
|
|
|
IVP_FLOAT flSpringConstant, flSpringRelax, flSpringCompress;
|
|
flSpringConstant = pPontoonPoint->spring_constant;
|
|
flSpringRelax = pPontoonPoint->spring_damp_relax;
|
|
flSpringCompress = pPontoonPoint->spring_damp_compress;
|
|
|
|
IVP_DOUBLE flForce = -flDiff * flSpringConstant;
|
|
IVP_FLOAT flInvNormalDotDir = clamp(pImpact->inv_normal_dot_dir, 0.0f, 3.0f);
|
|
flForce *= flInvNormalDotDir;
|
|
|
|
IVP_U_Float_Point vecSpeedDelta;
|
|
vecSpeedDelta.subtract( &pImpact->projected_surface_speed_wheel_ws, &pImpact->surface_speed_wheel_ws );
|
|
|
|
IVP_DOUBLE flSpeed = vecSpeedDelta.dot_product( &pImpact->raycast_dir_ws );
|
|
if ( flSpeed > 0 )
|
|
{
|
|
flForce -= flSpringRelax * flSpeed;
|
|
}
|
|
else
|
|
{
|
|
flForce -= flSpringCompress * flSpeed;
|
|
}
|
|
|
|
if ( flForce < 0 )
|
|
{
|
|
flForce = 0.0f;
|
|
}
|
|
|
|
// NOTE: Spring constants are all mass-independent, so no need to multiply by mass here.
|
|
IVP_DOUBLE flImpulse = flForce * pEventSim->delta_time;
|
|
|
|
IVP_U_Float_Point vecImpulseWS;
|
|
vecImpulseWS.set_multiple( &pImpact->vecImpactNormalWS, flImpulse );
|
|
m_pCore->push_core_ws( &pImpact->vecImpactPointWS, &vecImpulseWS );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Handle pontoons on water.
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::DoSimulationPontoonsWater( IVP_Raycast_Airboat_Wheel *pPontoonPoint,
|
|
IVP_Raycast_Airboat_Impact *pImpact, IVP_Event_Sim *pEventSim )
|
|
{
|
|
#define AIRBOAT_BUOYANCY_SCALAR 1.6f
|
|
#define PONTOON_AREA_2D 2.8f // 2 pontoons x 16 in x 136 in = 4352 sq inches = 2.8 sq meters
|
|
#define PONTOON_HEIGHT 0.41f // 16 inches high = 0.41 meters
|
|
|
|
float flDepth = clamp( pImpact->flDepth, 0.f, PONTOON_HEIGHT );
|
|
//Msg("depth: %f\n", pImpact->flDepth);
|
|
|
|
// Depth is in inches, so multiply by 0.0254 meters/inch
|
|
IVP_FLOAT flSubmergedVolume = PONTOON_AREA_2D * flDepth * 0.0254;
|
|
|
|
// Buoyancy forces are equal to the mass of the water displaced, which is 1000 kg/m^3
|
|
// There are 4 pontoon points, so each one can exert 1/4th of the total buoyancy force.
|
|
IVP_FLOAT flForce = AIRBOAT_BUOYANCY_SCALAR * 0.25f * m_pCore->get_mass() * flSubmergedVolume * 1000.0f;
|
|
IVP_DOUBLE flImpulse = flForce * pEventSim->delta_time;
|
|
|
|
IVP_U_Float_Point vecImpulseWS;
|
|
vecImpulseWS.set( 0, -1, 0 );
|
|
vecImpulseWS.mult( flImpulse );
|
|
m_pCore->push_core_ws( &pImpact->vecImpactPointWS, &vecImpulseWS );
|
|
|
|
// Vector vecPoint;
|
|
// Vector vecDir(0, 0, 1);
|
|
//
|
|
// ConvertPositionToHL( pImpact->vecImpactPointWS, vecPoint );
|
|
// CPhysicsEnvironment *pEnv = (CPhysicsEnvironment *)m_pAirboatBody->get_core()->environment->client_data;
|
|
// IVPhysicsDebugOverlay *debugoverlay = pEnv->GetDebugOverlay();
|
|
// debugoverlay->AddLineOverlay(vecPoint, vecPoint + vecDir * 128, 255, 0, 255, false, 10.0 );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::PerformFrictionNotification( float flEliminatedEnergy, float dt, int nSurfaceProp, IPhysicsCollisionData *pCollisionData )
|
|
{
|
|
CPhysicsObject *pPhysAirboat = static_cast<CPhysicsObject*>( m_pAirboatBody->client_data );
|
|
if ( ( pPhysAirboat->CallbackFlags() & CALLBACK_GLOBAL_FRICTION ) == 0 )
|
|
return;
|
|
|
|
IPhysicsCollisionEvent *pEventHandler = pPhysAirboat->GetVPhysicsEnvironment()->GetCollisionEventHandler();
|
|
if ( !pEventHandler )
|
|
return;
|
|
|
|
// scrape with an estimate for the energy per unit mass
|
|
// This assumes that the game is interested in some measure of vibration
|
|
// for sound effects. This also assumes that more massive objects require
|
|
// more energy to vibrate.
|
|
flEliminatedEnergy *= dt / pPhysAirboat->GetMass();
|
|
if ( flEliminatedEnergy > 0.05f )
|
|
{
|
|
pEventHandler->Friction( pPhysAirboat, flEliminatedEnergy, pPhysAirboat->GetMaterialIndexInternal(), nSurfaceProp, pCollisionData );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Drag due to water and ground friction.
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::DoSimulationDrag( IVP_Raycast_Airboat_Impact *pImpacts,
|
|
IVP_Event_Sim *pEventSim )
|
|
{
|
|
const IVP_U_Matrix *matWorldFromCore = m_pCore->get_m_world_f_core_PSI();
|
|
IVP_FLOAT flSpeed = ( IVP_FLOAT )m_pCore->speed.real_length();
|
|
|
|
// Used to make airboat sliding sounds
|
|
CAirboatFrictionData frictionData;
|
|
ConvertDirectionToHL( m_pCore->speed, frictionData.m_vecVelocity );
|
|
|
|
// Count the pontoons in the water.
|
|
int nPontoonPoints = n_wheels;
|
|
int nPointsInWater = 0;
|
|
int nPointsOnGround = 0;
|
|
float flGroundFriction = 0;
|
|
float flAverageDampening = 0.0f;
|
|
int *pSurfacePropCount = (int *)stackalloc( n_wheels * sizeof(int) );
|
|
int *pSurfaceProp = (int *)stackalloc( n_wheels * sizeof(int) );
|
|
memset( pSurfacePropCount, 0, n_wheels * sizeof(int) );
|
|
memset( pSurfaceProp, 0xFF, n_wheels * sizeof(int) );
|
|
int nSurfacePropCount = 0;
|
|
int nMaxSurfacePropIdx = 0;
|
|
for( int iPoint = 0; iPoint < nPontoonPoints; ++iPoint )
|
|
{
|
|
// Get data at raycast position.
|
|
IVP_Raycast_Airboat_Impact *pImpact = &pImpacts[iPoint];
|
|
if ( !pImpact || !pImpact->bImpact )
|
|
continue;
|
|
|
|
if ( pImpact->bImpactWater )
|
|
{
|
|
flAverageDampening += pImpact->flDampening;
|
|
nPointsInWater++;
|
|
}
|
|
else
|
|
{
|
|
flGroundFriction += pImpact->flFriction;
|
|
nPointsOnGround++;
|
|
|
|
// This logic is used to determine which surface prop we hit the most.
|
|
int i;
|
|
for ( i = 0; i < nSurfacePropCount; ++i )
|
|
{
|
|
if ( pSurfaceProp[i] == pImpact->nSurfaceProps )
|
|
break;
|
|
}
|
|
|
|
if ( i == nSurfacePropCount )
|
|
{
|
|
++nSurfacePropCount;
|
|
}
|
|
pSurfaceProp[i] = pImpact->nSurfaceProps;
|
|
if ( ++pSurfacePropCount[i] > pSurfacePropCount[nMaxSurfacePropIdx] )
|
|
{
|
|
nMaxSurfacePropIdx = i;
|
|
}
|
|
|
|
Vector frictionPoint, frictionNormal;
|
|
ConvertPositionToHL( pImpact->vecImpactPointWS, frictionPoint );
|
|
ConvertDirectionToHL( pImpact->vecImpactNormalWS, frictionNormal );
|
|
frictionData.m_vecPoint += frictionPoint;
|
|
frictionData.m_vecNormal += frictionNormal;
|
|
}
|
|
}
|
|
|
|
int nSurfaceProp = pSurfaceProp[nMaxSurfacePropIdx];
|
|
if ( nPointsOnGround > 0 )
|
|
{
|
|
frictionData.m_vecPoint /= nPointsOnGround;
|
|
frictionData.m_vecNormal /= nPointsOnGround;
|
|
VectorNormalize( frictionData.m_vecNormal );
|
|
}
|
|
|
|
if ( nPointsInWater > 0 )
|
|
{
|
|
flAverageDampening /= nPointsInWater;
|
|
}
|
|
|
|
//IVP_FLOAT flDebugSpeed = ( IVP_FLOAT )m_pCore->speed.real_length();
|
|
//Msg("(water=%d/land=%d) speed=%f (%f %f %f)\n", nPointsInWater, nPointsOnGround, flDebugSpeed, vecAirboatDirLS.k[0], vecAirboatDirLS.k[1], vecAirboatDirLS.k[2]);
|
|
|
|
if ( nPointsInWater )
|
|
{
|
|
// Apply the drag force opposite to the direction of motion in local space.
|
|
IVP_U_Float_Point vecAirboatNegDirLS;
|
|
vecAirboatNegDirLS.set_negative( &m_vecLocalVelocity );
|
|
|
|
// Water drag is directional -- the pontoons resist left/right motion much more than forward/back.
|
|
IVP_U_Float_Point vecDragLS;
|
|
vecDragLS.set( AIRBOAT_WATER_DRAG_LEFT_RIGHT * vecAirboatNegDirLS.k[0],
|
|
AIRBOAT_WATER_DRAG_UP_DOWN * vecAirboatNegDirLS.k[1],
|
|
AIRBOAT_WATER_DRAG_FORWARD_BACK * vecAirboatNegDirLS.k[2] );
|
|
|
|
vecDragLS.mult( flSpeed * m_pCore->get_mass() * pEventSim->delta_time );
|
|
// dvs TODO: apply flAverageDampening here
|
|
|
|
// Convert the drag force to world space and apply the drag.
|
|
IVP_U_Float_Point vecDragWS;
|
|
matWorldFromCore->vmult3(&vecDragLS, &vecDragWS);
|
|
m_pCore->center_push_core_multiple_ws( &vecDragWS );
|
|
}
|
|
|
|
//
|
|
// Calculate ground friction drag:
|
|
//
|
|
if ( nPointsOnGround && ( flSpeed > 0 ))
|
|
{
|
|
// Calculate the average friction across all contact points.
|
|
flGroundFriction /= (float)nPointsOnGround;
|
|
|
|
// Apply the drag force opposite to the direction of motion.
|
|
IVP_U_Float_Point vecAirboatNegDir;
|
|
vecAirboatNegDir.set_negative( &m_pCore->speed );
|
|
|
|
IVP_FLOAT flFrictionDrag = m_pCore->get_mass() * AIRBOAT_GRAVITY * AIRBOAT_DRY_FRICTION_SCALE * flGroundFriction;
|
|
flFrictionDrag /= flSpeed;
|
|
|
|
IPhysicsObject *pPhysAirboat = static_cast<IPhysicsObject*>( m_pAirboatBody->client_data );
|
|
float flEliminatedEnergy = pPhysAirboat->GetEnergy();
|
|
|
|
// Apply the drag force opposite to the direction of motion in local space.
|
|
IVP_U_Float_Point vecAirboatNegDirLS;
|
|
vecAirboatNegDirLS.set_negative( &m_vecLocalVelocity );
|
|
|
|
// Ground drag is directional -- the pontoons resist left/right motion much more than forward/back.
|
|
IVP_U_Float_Point vecDragLS;
|
|
vecDragLS.set( AIRBOAT_GROUND_DRAG_LEFT_RIGHT * vecAirboatNegDirLS.k[0],
|
|
AIRBOAT_GROUND_DRAG_UP_DOWN * vecAirboatNegDirLS.k[1],
|
|
AIRBOAT_GROUND_DRAG_FORWARD_BACK * vecAirboatNegDirLS.k[2] );
|
|
|
|
vecDragLS.mult( flFrictionDrag * pEventSim->delta_time );
|
|
// dvs TODO: apply flAverageDampening here
|
|
|
|
// Convert the drag force to world space and apply the drag.
|
|
IVP_U_Float_Point vecDragWS;
|
|
matWorldFromCore->vmult3(&vecDragLS, &vecDragWS);
|
|
m_pCore->center_push_core_multiple_ws( &vecDragWS );
|
|
|
|
// Figure out how much energy was eliminated by friction.
|
|
flEliminatedEnergy -= pPhysAirboat->GetEnergy();
|
|
PerformFrictionNotification( flEliminatedEnergy, pEventSim->delta_time, nSurfaceProp, &frictionData );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::DoSimulationTurbine( IVP_Event_Sim *pEventSim )
|
|
{
|
|
// Reduce the turbine power during weak jumps to avoid unrealistic air control.
|
|
// Also, reduce reverse thrust while airborne.
|
|
float flThrust = m_flThrust;
|
|
if ((m_bWeakJump) || (m_bAirborne && (flThrust < 0)))
|
|
{
|
|
flThrust *= 0.5;
|
|
}
|
|
|
|
// Get the forward vector in world-space.
|
|
IVP_U_Float_Point vecForwardWS;
|
|
const IVP_U_Matrix *matWorldFromCore = m_pCore->get_m_world_f_core_PSI();
|
|
matWorldFromCore->get_col( IVP_COORDINATE_INDEX( index_z ), &vecForwardWS );
|
|
|
|
//Msg("thrust: %f\n", m_flThrust);
|
|
if ( ( vecForwardWS.k[1] < -0.5 ) && ( flThrust > 0 ) )
|
|
{
|
|
// Driving up a slope. Reduce upward thrust to prevent ludicrous climbing of steep surfaces.
|
|
float flFactor = 1 + vecForwardWS.k[1];
|
|
//Msg("FWD: y=%f, factor=%f\n", vecForwardWS.k[1], flFactor);
|
|
flThrust *= flFactor;
|
|
}
|
|
else if ( ( vecForwardWS.k[1] > 0.5 ) && ( flThrust < 0 ) )
|
|
{
|
|
// Reversing up a slope. Reduce upward thrust to prevent ludicrous climbing of steep surfaces.
|
|
float flFactor = 1 - vecForwardWS.k[1];
|
|
//Msg("REV: y=%f, factor=%f\n", vecForwardWS.k[1], flFactor);
|
|
flThrust *= flFactor;
|
|
}
|
|
|
|
// Forward (Front/Back) force
|
|
IVP_U_Float_Point vecImpulse;
|
|
vecImpulse.set_multiple( &vecForwardWS, flThrust * m_pCore->get_mass() * pEventSim->delta_time );
|
|
|
|
m_pCore->center_push_core_multiple_ws( &vecImpulse );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::DoSimulationSteering( IVP_Event_Sim *pEventSim )
|
|
{
|
|
// Calculate the steering direction: forward or reverse.
|
|
// Don't mess with the steering direction while we're steering, unless thrust is applied.
|
|
// This prevents the steering from reversing because we started drifting backwards.
|
|
if ( ( m_SteeringAngle == 0 ) || ( m_flThrust != 0 ) )
|
|
{
|
|
if ( !m_bAnalogSteering )
|
|
{
|
|
// If we're applying reverse thrust, steering is always reversed.
|
|
if ( m_flThrust < 0 )
|
|
{
|
|
m_bSteeringReversed = true;
|
|
}
|
|
// Else if we are applying forward thrust or moving forward, use forward steering.
|
|
else if ( ( m_flThrust > 0 ) || ( m_vecLocalVelocity.k[2] > 0 ) )
|
|
{
|
|
m_bSteeringReversed = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Create a dead zone through the middle of the joystick where we don't reverse thrust.
|
|
// If we're applying reverse thrust, steering is always reversed.
|
|
if ( m_flThrust < -2.0f )
|
|
{
|
|
m_bSteeringReversed = true;
|
|
}
|
|
// Else if we are applying forward thrust or moving forward, use forward steering.
|
|
else if ( ( m_flThrust > 2.0f ) || ( m_vecLocalVelocity.k[2] > 0 ) )
|
|
{
|
|
m_bSteeringReversed = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate the steering force.
|
|
IVP_FLOAT flForceSteering = 0.0f;
|
|
if ( fabsf( m_SteeringAngle ) > 0.01 )
|
|
{
|
|
// Get the sign of the steering force.
|
|
IVP_FLOAT flSteeringSign = m_SteeringAngle < 0.0f ? -1.0f : 1.0f;
|
|
if ( m_bSteeringReversed )
|
|
{
|
|
flSteeringSign *= -1.0f;
|
|
}
|
|
|
|
// If we changed steering sign or went from not steering to steering, reset the steer time
|
|
// to blend the new steering force in over time.
|
|
IVP_FLOAT flPrevSteeringSign = m_flPrevSteeringAngle < 0.0f ? -1.0f : 1.0f;
|
|
if ( ( fabs( m_flPrevSteeringAngle ) < 0.01 ) || ( flSteeringSign != flPrevSteeringSign ) )
|
|
{
|
|
m_flSteerTime = 0;
|
|
}
|
|
|
|
float flSteerScale = 0.f;
|
|
if ( !m_bAnalogSteering )
|
|
{
|
|
// Ramp the steering force up over two seconds.
|
|
flSteerScale = RemapValClamped( m_flSteerTime, 0, AIRBOAT_STEERING_INTERVAL, AIRBOAT_STEERING_RATE_MIN, AIRBOAT_STEERING_RATE_MAX );
|
|
}
|
|
else // consoles
|
|
{
|
|
// Analog steering
|
|
flSteerScale = RemapValClamped( fabs(m_SteeringAngle), 0, AIRBOAT_STEERING_INTERVAL, AIRBOAT_STEERING_RATE_MIN, AIRBOAT_STEERING_RATE_MAX );
|
|
}
|
|
|
|
flForceSteering = flSteerScale * m_pCore->get_mass() * pEventSim->i_delta_time;
|
|
flForceSteering *= -flSteeringSign;
|
|
|
|
m_flSteerTime += pEventSim->delta_time;
|
|
}
|
|
|
|
//Msg("steer force=%f\n", flForceSteering);
|
|
|
|
m_flPrevSteeringAngle = m_SteeringAngle * ( m_bSteeringReversed ? -1.0 : 1.0 );
|
|
|
|
// Get the sign of the drag forces.
|
|
IVP_FLOAT flRotSpeedSign = m_pCore->rot_speed.k[1] < 0.0f ? -1.0f : 1.0f;
|
|
|
|
// Apply drag proportional to the square of the angular velocity.
|
|
IVP_FLOAT flRotationalDrag = AIRBOAT_ROT_DRAG * m_pCore->rot_speed.k[1] * m_pCore->rot_speed.k[1] * m_pCore->get_mass() * pEventSim->i_delta_time;
|
|
flRotationalDrag *= flRotSpeedSign;
|
|
|
|
// Apply dampening proportional to angular velocity.
|
|
IVP_FLOAT flRotationalDamping = AIRBOAT_ROT_DAMPING * fabs(m_pCore->rot_speed.k[1]) * m_pCore->get_mass() * pEventSim->i_delta_time;
|
|
flRotationalDamping *= flRotSpeedSign;
|
|
|
|
// Calculate the net rotational force.
|
|
IVP_FLOAT flForceRotational = flForceSteering + flRotationalDrag + flRotationalDamping;
|
|
|
|
// Apply it.
|
|
IVP_U_Float_Point vecRotImpulse;
|
|
vecRotImpulse.set( 0, -1, 0 );
|
|
vecRotImpulse.mult( flForceRotational );
|
|
m_pCore->rot_push_core_cs( &vecRotImpulse );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Adds extra gravity unless we are performing a strong jump.
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::DoSimulationGravity( IVP_Event_Sim *pEventSim )
|
|
{
|
|
return;
|
|
|
|
if ( !m_bAirborne || m_bWeakJump )
|
|
{
|
|
IVP_U_Float_Point vecGravity;
|
|
vecGravity.set( 0, AIRBOAT_GRAVITY / 2.0f, 0 );
|
|
vecGravity.mult( m_pCore->get_mass() * pEventSim->delta_time );
|
|
m_pCore->center_push_core_multiple_ws( &vecGravity );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns the number of pontoon raycast points that were found to contact
|
|
// the ground or water.
|
|
//-----------------------------------------------------------------------------
|
|
int CPhysics_Airboat::CountSurfaceContactPoints( IVP_Raycast_Airboat_Impact *pImpacts )
|
|
{
|
|
int nContacts = 0;
|
|
int nPontoonPoints = n_wheels;
|
|
for ( int iPoint = 0; iPoint < nPontoonPoints; iPoint++ )
|
|
{
|
|
// Get data at raycast position.
|
|
IVP_Raycast_Airboat_Impact *pImpact = &pImpacts[iPoint];
|
|
if ( !pImpact )
|
|
continue;
|
|
|
|
if ( pImpact->bImpact )
|
|
{
|
|
nContacts++;
|
|
}
|
|
}
|
|
|
|
return nContacts;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Prevents us from nosing down dramatically during jumps, which
|
|
// increases our maximum jump distance.
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::DoSimulationKeepUprightPitch( IVP_Raycast_Airboat_Impact *pImpacts, IVP_Event_Sim *pEventSim )
|
|
{
|
|
// Disable pitch control during weak jumps. This reduces the unreal 'floaty' sensation.
|
|
if (m_bWeakJump)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Reference vector in core space.
|
|
// Pitch back by 10 degrees while airborne.
|
|
IVP_U_Float_Point vecUpCS;
|
|
vecUpCS.set( 0, -cos(DEG2RAD(10)), sin(DEG2RAD(10)));
|
|
|
|
// Calculate the goal vector in core space. We will try to align the reference
|
|
// vector with the goal vector.
|
|
IVP_U_Float_Point vecGoalAxisWS;
|
|
vecGoalAxisWS.set( 0, -1, 0 );
|
|
const IVP_U_Matrix *matWorldFromCore = m_pCore->get_m_world_f_core_PSI();
|
|
IVP_U_Float_Point vecGoalAxisCS;
|
|
matWorldFromCore->vimult3( &vecGoalAxisWS, &vecGoalAxisCS );
|
|
|
|
// Eliminate roll control
|
|
vecGoalAxisCS.k[0] = vecUpCS.k[0];
|
|
vecGoalAxisCS.normize();
|
|
|
|
// Get an axis to rotate around.
|
|
IVP_U_Float_Point vecRotAxisCS;
|
|
vecRotAxisCS.calc_cross_product( &vecUpCS, &vecGoalAxisCS );
|
|
|
|
// Get the amount that we need to rotate.
|
|
// atan2() is well defined, so do a Dot & Cross instead of asin(Cross)
|
|
IVP_FLOAT cosine = vecUpCS.dot_product( &vecGoalAxisCS );
|
|
IVP_FLOAT sine = vecRotAxisCS.real_length_plus_normize();
|
|
IVP_FLOAT angle = atan2( sine, cosine );
|
|
|
|
//Msg("angle: %.2f, axis: (%.2f %.2f %.2f)\n", RAD2DEG(angle), vecRotAxisCS.k[0], vecRotAxisCS.k[1], vecRotAxisCS.k[2]);
|
|
|
|
// Don't keep upright if any pontoons are contacting a surface.
|
|
if ( CountSurfaceContactPoints( pImpacts ) > 0 )
|
|
{
|
|
m_flPitchErrorPrev = angle;
|
|
return;
|
|
}
|
|
|
|
// Don't do any correction if we're within 15 degrees of the goal orientation.
|
|
//if ( fabs( angle ) < DEG2RAD( 15 ) )
|
|
//{
|
|
// m_flPitchErrorPrev = angle;
|
|
// return;
|
|
//}
|
|
|
|
//Msg("CORRECTING\n");
|
|
|
|
// Generate an angular impulse describing the rotation.
|
|
IVP_U_Float_Point vecAngularImpulse;
|
|
vecAngularImpulse.set_multiple( &vecRotAxisCS, m_pCore->get_mass() * ( 0.1f * angle + 0.04f * pEventSim->i_delta_time * ( angle - m_flPitchErrorPrev ) ) );
|
|
|
|
// Save the last error value for calculating the derivative.
|
|
m_flPitchErrorPrev = angle;
|
|
|
|
// Clamp the impulse at a maximum length.
|
|
IVP_FLOAT len = vecAngularImpulse.real_length_plus_normize();
|
|
if ( len > ( DEG2RAD( 1.5 ) * m_pCore->get_mass() ) )
|
|
{
|
|
len = DEG2RAD( 1.5 ) * m_pCore->get_mass();
|
|
}
|
|
vecAngularImpulse.mult( len );
|
|
|
|
// Apply the rotation.
|
|
m_pCore->rot_push_core_cs( &vecAngularImpulse );
|
|
|
|
#if DRAW_AIRBOAT_KEEP_UPRIGHT_PITCH_VECTORS
|
|
CPhysicsEnvironment *pEnv = (CPhysicsEnvironment *)m_pAirboatBody->get_core()->environment->client_data;
|
|
IVPhysicsDebugOverlay *debugoverlay = pEnv->GetDebugOverlay();
|
|
|
|
IVP_U_Float_Point vecPosIVP = m_pCore->get_position_PSI();
|
|
Vector vecPosHL;
|
|
ConvertPositionToHL(vecPosIVP, vecPosHL);
|
|
|
|
Vector vecGoalAxisHL;
|
|
ConvertDirectionToHL(vecGoalAxisWS, vecGoalAxisHL);
|
|
|
|
IVP_U_Float_Point vecUpWS;
|
|
matWorldFromCore->vmult3( &vecUpCS, &vecUpWS );
|
|
Vector vecCurHL;
|
|
ConvertDirectionToHL(vecUpWS, vecCurHL);
|
|
|
|
static IVP_FLOAT flLastLen = 0;
|
|
IVP_FLOAT flDebugLen = vecAngularImpulse.real_length();
|
|
if ( flLastLen && ( fabs( flDebugLen - flLastLen ) > DEG2RAD( 1 ) * m_pCore->get_mass() ) )
|
|
{
|
|
debugoverlay->AddLineOverlay(vecPosHL, vecPosHL + Vector(0, 0, 10) * flDebugLen, 255, 0, 255, false, 100.0 );
|
|
}
|
|
else
|
|
{
|
|
debugoverlay->AddLineOverlay(vecPosHL, vecPosHL + Vector(0, 0, 10) * flDebugLen, 255, 255, 255, false, 100.0 );
|
|
}
|
|
debugoverlay->AddLineOverlay(vecPosHL + Vector(0, 0, 10) * flDebugLen, vecPosHL + Vector(0, 0, 10) * flDebugLen + vecGoalAxisHL * 10, 0, 255, 0, false, 100.0 );
|
|
debugoverlay->AddLineOverlay(vecPosHL + Vector(0, 0, 10) * flDebugLen, vecPosHL + Vector(0, 0, 10) * flDebugLen + vecCurHL * 10, 255, 0, 0, false, 100.0 );
|
|
flLastLen = flDebugLen;
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Roll stabilizer when airborne.
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::DoSimulationKeepUprightRoll( IVP_Raycast_Airboat_Impact *pImpacts, IVP_Event_Sim *pEventSim )
|
|
{
|
|
// Reference vector in core space.
|
|
// Pitch back by 10 degrees while airborne.
|
|
IVP_U_Float_Point vecUpCS;
|
|
vecUpCS.set( 0, -cos(DEG2RAD(10)), sin(DEG2RAD(10)));
|
|
|
|
// Calculate the goal vector in core space. We will try to align the reference
|
|
// vector with the goal vector.
|
|
IVP_U_Float_Point vecGoalAxisWS;
|
|
vecGoalAxisWS.set( 0, -1, 0 );
|
|
const IVP_U_Matrix *matWorldFromCore = m_pCore->get_m_world_f_core_PSI();
|
|
IVP_U_Float_Point vecGoalAxisCS;
|
|
matWorldFromCore->vimult3( &vecGoalAxisWS, &vecGoalAxisCS );
|
|
|
|
// Eliminate pitch control
|
|
vecGoalAxisCS.k[1] = vecUpCS.k[1];
|
|
vecGoalAxisCS.normize();
|
|
|
|
// Get an axis to rotate around.
|
|
IVP_U_Float_Point vecRotAxisCS;
|
|
vecRotAxisCS.calc_cross_product( &vecUpCS, &vecGoalAxisCS );
|
|
|
|
// Get the amount that we need to rotate.
|
|
// atan2() is well defined, so do a Dot & Cross instead of asin(Cross)
|
|
IVP_FLOAT cosine = vecUpCS.dot_product( &vecGoalAxisCS );
|
|
IVP_FLOAT sine = vecRotAxisCS.real_length_plus_normize();
|
|
IVP_FLOAT angle = atan2( sine, cosine );
|
|
|
|
//Msg("angle: %.2f, axis: (%.2f %.2f %.2f)\n", RAD2DEG(angle), vecRotAxisCS.k[0], vecRotAxisCS.k[1], vecRotAxisCS.k[2]);
|
|
|
|
// Don't keep upright if any pontoons are contacting a surface.
|
|
if ( CountSurfaceContactPoints( pImpacts ) > 0 )
|
|
{
|
|
m_flRollErrorPrev = angle;
|
|
return;
|
|
}
|
|
|
|
// Don't do any correction if we're within 10 degrees of the goal orientation.
|
|
if ( fabs( angle ) < DEG2RAD( 10 ) )
|
|
{
|
|
m_flRollErrorPrev = angle;
|
|
return;
|
|
}
|
|
|
|
//Msg("CORRECTING\n");
|
|
|
|
// Generate an angular impulse describing the rotation.
|
|
IVP_U_Float_Point vecAngularImpulse;
|
|
vecAngularImpulse.set_multiple( &vecRotAxisCS, m_pCore->get_mass() * ( 0.2f * angle + 0.3f * pEventSim->i_delta_time * ( angle - m_flRollErrorPrev ) ) );
|
|
|
|
// Save the last error value for calculating the derivative.
|
|
m_flRollErrorPrev = angle;
|
|
|
|
// Clamp the impulse at a maximum length.
|
|
IVP_FLOAT len = vecAngularImpulse.real_length_plus_normize();
|
|
if ( len > ( DEG2RAD( 2 ) * m_pCore->get_mass() ) )
|
|
{
|
|
len = DEG2RAD( 2 ) * m_pCore->get_mass();
|
|
}
|
|
vecAngularImpulse.mult( len );
|
|
m_pCore->rot_push_core_cs( &vecAngularImpulse );
|
|
|
|
// Debugging visualization.
|
|
#if DRAW_AIRBOAT_KEEP_UPRIGHT_ROLL_VECTORS
|
|
CPhysicsEnvironment *pEnv = (CPhysicsEnvironment *)m_pAirboatBody->get_core()->environment->client_data;
|
|
IVPhysicsDebugOverlay *debugoverlay = pEnv->GetDebugOverlay();
|
|
|
|
IVP_U_Float_Point vecPosIVP = m_pCore->get_position_PSI();
|
|
Vector vecPosHL;
|
|
ConvertPositionToHL(vecPosIVP, vecPosHL);
|
|
|
|
Vector vecGoalAxisHL;
|
|
ConvertDirectionToHL(vecGoalAxisWS, vecGoalAxisHL);
|
|
|
|
IVP_U_Float_Point vecUpWS;
|
|
matWorldFromCore->vmult3( &vecUpCS, &vecUpWS );
|
|
Vector vecCurHL;
|
|
ConvertDirectionToHL(vecUpWS, vecCurHL);
|
|
|
|
static IVP_FLOAT flLastLen = 0;
|
|
IVP_FLOAT flDebugLen = vecAngularImpulse.real_length();
|
|
if ( flLastLen && ( fabs( flDebugLen - flLastLen ) > ( DEG2RAD( 0.25 ) * m_pCore->get_mass() ) )
|
|
{
|
|
debugoverlay->AddLineOverlay(vecPosHL, vecPosHL + Vector(0, 0, 10) * flDebugLen, 255, 0, 255, false, 100.0 );
|
|
}
|
|
else
|
|
{
|
|
debugoverlay->AddLineOverlay(vecPosHL, vecPosHL + Vector(0, 0, 10) * flDebugLen, 255, 255, 255, false, 100.0 );
|
|
}
|
|
debugoverlay->AddLineOverlay(vecPosHL + Vector(0, 0, 10) * flDebugLen, vecPosHL + Vector(0, 0, 10) * flDebugLen + vecGoalAxisHL * 10, 0, 255, 0, false, 100.0 );
|
|
debugoverlay->AddLineOverlay(vecPosHL + Vector(0, 0, 10) * flDebugLen, vecPosHL + Vector(0, 0, 10) * flDebugLen + vecCurHL * 10, 255, 0, 0, false, 100.0 );
|
|
flLastLen = flDebugLen;
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : wheel_nr -
|
|
// s_angle -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::do_steering_wheel(IVP_POS_WHEEL wheel_nr, IVP_FLOAT s_angle)
|
|
{
|
|
IVP_Raycast_Airboat_Wheel *wheel = get_wheel(wheel_nr);
|
|
|
|
wheel->axis_direction_cs.set_to_zero();
|
|
wheel->axis_direction_cs.k[ index_x ] = 1.0f;
|
|
wheel->axis_direction_cs.rotate( IVP_COORDINATE_INDEX(index_y), s_angle);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : pos -
|
|
// spring_constant -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::change_spring_constant(IVP_POS_WHEEL pos, IVP_FLOAT spring_constant)
|
|
{
|
|
IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
|
|
wheel->spring_constant = spring_constant;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : pos -
|
|
// spring_dampening -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::change_spring_dampening(IVP_POS_WHEEL pos, IVP_FLOAT spring_dampening)
|
|
{
|
|
IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
|
|
wheel->spring_damp_relax = spring_dampening;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : pos -
|
|
// spring_dampening -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::change_spring_dampening_compression(IVP_POS_WHEEL pos, IVP_FLOAT spring_dampening)
|
|
{
|
|
IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
|
|
wheel->spring_damp_compress = spring_dampening;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : pos -
|
|
// pre_tension_length -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::change_spring_pre_tension(IVP_POS_WHEEL pos, IVP_FLOAT pre_tension_length)
|
|
{
|
|
IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
|
|
wheel->spring_len = gravity_y_direction * (wheel->distance_orig_hp_to_hp - pre_tension_length);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : pos -
|
|
// spring_length -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::change_spring_length(IVP_POS_WHEEL pos, IVP_FLOAT spring_length)
|
|
{
|
|
IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
|
|
wheel->spring_len = spring_length;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : pos -
|
|
// torque -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::change_wheel_torque(IVP_POS_WHEEL pos, IVP_FLOAT torque)
|
|
{
|
|
IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
|
|
wheel->torque = torque;
|
|
|
|
// Wake the physics object if need be!
|
|
m_pAirboatBody->get_environment()->get_controller_manager()->ensure_controller_in_simulation( this );
|
|
}
|
|
|
|
IVP_FLOAT CPhysics_Airboat::get_wheel_torque(IVP_POS_WHEEL pos)
|
|
{
|
|
return get_wheel(pos)->torque;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Throttle input is -1 to 1.
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::update_throttle( IVP_FLOAT flThrottle )
|
|
{
|
|
// Forward
|
|
if ( fabs( flThrottle ) < 0.01f )
|
|
{
|
|
m_flThrust = 0.0f;
|
|
}
|
|
else if ( flThrottle > 0.0f )
|
|
{
|
|
m_flThrust = AIRBOAT_THRUST_MAX * flThrottle;
|
|
}
|
|
else if ( flThrottle < 0.0f )
|
|
{
|
|
m_flThrust = AIRBOAT_THRUST_MAX_REVERSE * flThrottle;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : pos -
|
|
// stop_wheel -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::fix_wheel(IVP_POS_WHEEL pos, IVP_BOOL stop_wheel)
|
|
{
|
|
IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
|
|
wheel->wheel_is_fixed = stop_wheel;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : pos -
|
|
// friction -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::change_friction_of_wheel( IVP_POS_WHEEL pos, IVP_FLOAT friction )
|
|
{
|
|
IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
|
|
wheel->friction_of_wheel = friction;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : pos -
|
|
// stabi_constant -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::change_stabilizer_constant(IVP_POS_AXIS pos, IVP_FLOAT stabi_constant)
|
|
{
|
|
IVP_Raycast_Airboat_Axle *pAxle = get_axle( pos );
|
|
pAxle->stabilizer_constant = stabi_constant;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : fast_turn_factor_ -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::change_fast_turn_factor( IVP_FLOAT fast_turn_factor_ )
|
|
{
|
|
//fast_turn_factor = fast_turn_factor_;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : force -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::change_body_downforce(IVP_FLOAT force)
|
|
{
|
|
down_force = force;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : IVP_CONTROLLER_PRIORITY
|
|
//-----------------------------------------------------------------------------
|
|
IVP_CONTROLLER_PRIORITY CPhysics_Airboat::get_controller_priority()
|
|
{
|
|
return IVP_CP_CONSTRAINTS_MAX;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : steering_angle_in -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::do_steering( IVP_FLOAT steering_angle_in, bool bAnalog )
|
|
{
|
|
// Check for a change.
|
|
if ( m_SteeringAngle == steering_angle_in)
|
|
return;
|
|
|
|
MEM_ALLOC_CREDIT();
|
|
|
|
// Set the new steering angle.
|
|
m_bAnalogSteering = bAnalog;
|
|
m_SteeringAngle = steering_angle_in;
|
|
|
|
// Make sure the simulation is awake - we just go input.
|
|
m_pAirboatBody->get_environment()->get_controller_manager()->ensure_controller_in_simulation( this );
|
|
|
|
// Steer each wheel.
|
|
for ( int iWheel = 0; iWheel < wheels_per_axis; ++iWheel )
|
|
{
|
|
do_steering_wheel( IVP_POS_WHEEL( iWheel ), m_SteeringAngle );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : pos -
|
|
// Output : IVP_DOUBLE
|
|
//-----------------------------------------------------------------------------
|
|
IVP_DOUBLE CPhysics_Airboat::get_wheel_angular_velocity(IVP_POS_WHEEL pos)
|
|
{
|
|
IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
|
|
return wheel->wheel_angular_velocity;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : index -
|
|
// Output : IVP_DOUBLE
|
|
//-----------------------------------------------------------------------------
|
|
IVP_DOUBLE CPhysics_Airboat::get_body_speed(IVP_COORDINATE_INDEX index)
|
|
{
|
|
// return (IVP_FLOAT)car_body->get_geom_center_speed();
|
|
IVP_U_Float_Point *vec_ws = &m_pAirboatBody->get_core()->speed;
|
|
// works well as we do not use merged cores
|
|
const IVP_U_Matrix *mat_ws = m_pAirboatBody->get_core()->get_m_world_f_core_PSI();
|
|
IVP_U_Point orientation;
|
|
mat_ws->get_col(index, &orientation);
|
|
|
|
return orientation.dot_product(vec_ws);
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
IVP_DOUBLE CPhysics_Airboat::get_orig_front_wheel_distance()
|
|
{
|
|
IVP_U_Float_Point *left_wheel_cs = &this->get_wheel(IVP_FRONT_LEFT)->hp_cs;
|
|
IVP_U_Float_Point *right_wheel_cs = &this->get_wheel(IVP_FRONT_RIGHT)->hp_cs;
|
|
|
|
IVP_DOUBLE dist = left_wheel_cs->k[this->index_x] - right_wheel_cs->k[this->index_x];
|
|
|
|
return IVP_Inline_Math::fabsd(dist); // was fabs, which was a sml call
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
IVP_DOUBLE CPhysics_Airboat::get_orig_axles_distance()
|
|
{
|
|
IVP_U_Float_Point *front_wheel_cs = &this->get_wheel(IVP_FRONT_LEFT)->hp_cs;
|
|
IVP_U_Float_Point *rear_wheel_cs = &this->get_wheel(IVP_REAR_LEFT)->hp_cs;
|
|
|
|
IVP_DOUBLE dist = front_wheel_cs->k[this->index_z] - rear_wheel_cs->k[this->index_z];
|
|
|
|
return IVP_Inline_Math::fabsd(dist); // was fabs, which was a sml call
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *array_of_skid_info_out -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::get_skid_info( IVP_Wheel_Skid_Info *array_of_skid_info_out)
|
|
{
|
|
for ( int w = 0; w < n_wheels; w++)
|
|
{
|
|
IVP_Wheel_Skid_Info &info = array_of_skid_info_out[w];
|
|
//IVP_Constraint_Car_Object *wheel = car_constraint_solver->wheel_objects.element_at(w);
|
|
info.last_contact_position_ws.set_to_zero(); // = wheel->last_contact_position_ws;
|
|
info.last_skid_value = 0.0f; // wheel->last_skid_value;
|
|
info.last_skid_time = 0.0f; //wheel->last_skid_time;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::InitRaycastCarEnvironment( IVP_Environment *pEnvironment,
|
|
const IVP_Template_Car_System *pCarSystemTemplate )
|
|
{
|
|
// Copies of the car system template component indices and handedness.
|
|
index_x = pCarSystemTemplate->index_x;
|
|
index_y = pCarSystemTemplate->index_y;
|
|
index_z = pCarSystemTemplate->index_z;
|
|
is_left_handed = pCarSystemTemplate->is_left_handed;
|
|
|
|
IVP_Standard_Gravity_Controller *pGravityController = new IVP_Standard_Gravity_Controller();
|
|
IVP_U_Point vecGravity( 0.0f, AIRBOAT_GRAVITY, 0.0f );
|
|
pGravityController->grav_vec.set( &vecGravity );
|
|
|
|
BEGIN_IVP_ALLOCATION();
|
|
|
|
m_pAirboatBody->get_core()->add_core_controller( pGravityController );
|
|
|
|
// Add this controller to the physics environment and setup the objects gravity.
|
|
pEnvironment->get_controller_manager()->announce_controller_to_environment( this );
|
|
|
|
END_IVP_ALLOCATION();
|
|
|
|
extra_gravity = pCarSystemTemplate->extra_gravity_force_value;
|
|
|
|
// This works because gravity is still int the same direction, just smaller.
|
|
if ( pEnvironment->get_gravity()->k[index_y] > 0 )
|
|
{
|
|
gravity_y_direction = 1.0f;
|
|
}
|
|
else
|
|
{
|
|
gravity_y_direction = -1.0f;
|
|
}
|
|
normized_gravity_ws.set( pEnvironment->get_gravity() );
|
|
normized_gravity_ws.normize();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::InitRaycastCarBody( const IVP_Template_Car_System *pCarSystemTemplate )
|
|
{
|
|
// Car body attributes.
|
|
n_wheels = pCarSystemTemplate->n_wheels;
|
|
n_axis = pCarSystemTemplate->n_axis;
|
|
wheels_per_axis = n_wheels / n_axis;
|
|
|
|
// Add the car body "core" to the list of raycast car controller "cores."
|
|
m_pAirboatBody = pCarSystemTemplate->car_body;
|
|
this->vector_of_cores.add( m_pAirboatBody->get_core() );
|
|
|
|
// Init extra downward force applied to car.
|
|
down_force_vertical_offset = pCarSystemTemplate->body_down_force_vertical_offset;
|
|
down_force = 0.0f;
|
|
|
|
// Initialize.
|
|
for ( int iAxis = 0; iAxis < 3; ++iAxis )
|
|
{
|
|
m_pAirboatBody->get_core()->rot_speed.k[iAxis] = 0.0f;
|
|
m_pAirboatBody->get_core()->speed.k[iAxis] = 0.0f;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::InitRaycastCarWheels( const IVP_Template_Car_System *pCarSystemTemplate )
|
|
{
|
|
IVP_U_Matrix m_core_f_object;
|
|
m_pAirboatBody->calc_m_core_f_object( &m_core_f_object );
|
|
|
|
// Initialize the car wheel system.
|
|
for ( int iWheel = 0; iWheel < n_wheels; iWheel++ )
|
|
{
|
|
// Get and clear out memory for the current raycast wheel.
|
|
IVP_Raycast_Airboat_Wheel *pRaycastWheel = get_wheel( IVP_POS_WHEEL( iWheel ) );
|
|
P_MEM_CLEAR( pRaycastWheel );
|
|
|
|
// Put the wheel in car space.
|
|
m_core_f_object.vmult4( &pCarSystemTemplate->wheel_pos_Bos[iWheel], &pRaycastWheel->hp_cs );
|
|
m_core_f_object.vmult4( &pCarSystemTemplate->trace_pos_Bos[iWheel], &pRaycastWheel->raycast_start_cs );
|
|
|
|
// Add in the raycast start offset.
|
|
pRaycastWheel->raycast_length = AIRBOAT_RAYCAST_DIST;
|
|
pRaycastWheel->raycast_dir_cs.set_to_zero();
|
|
pRaycastWheel->raycast_dir_cs.k[index_y] = gravity_y_direction;
|
|
|
|
// Spring (Shocks) data.
|
|
pRaycastWheel->spring_len = -pCarSystemTemplate->spring_pre_tension[iWheel];
|
|
|
|
pRaycastWheel->spring_direction_cs.set_to_zero();
|
|
pRaycastWheel->spring_direction_cs.k[index_y] = gravity_y_direction;
|
|
|
|
pRaycastWheel->spring_constant = pCarSystemTemplate->spring_constant[iWheel];
|
|
pRaycastWheel->spring_damp_relax = pCarSystemTemplate->spring_dampening[iWheel];
|
|
pRaycastWheel->spring_damp_compress = pCarSystemTemplate->spring_dampening_compression[iWheel];
|
|
|
|
// Wheel data.
|
|
pRaycastWheel->friction_of_wheel = 1.0f;//pCarSystemTemplate->friction_of_wheel[iWheel];
|
|
pRaycastWheel->wheel_radius = pCarSystemTemplate->wheel_radius[iWheel];
|
|
pRaycastWheel->inv_wheel_radius = 1.0f / pCarSystemTemplate->wheel_radius[iWheel];
|
|
|
|
do_steering_wheel( IVP_POS_WHEEL( iWheel ), 0.0f );
|
|
|
|
pRaycastWheel->wheel_is_fixed = IVP_FALSE;
|
|
pRaycastWheel->max_rotation_speed = pCarSystemTemplate->wheel_max_rotation_speed[iWheel>>1];
|
|
|
|
pRaycastWheel->wheel_is_fixed = IVP_TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::InitRaycastCarAxes( const IVP_Template_Car_System *pCarSystemTemplate )
|
|
{
|
|
m_SteeringAngle = -1.0f; // make sure next call is not optimized
|
|
this->do_steering( 0.0f, false ); // make sure next call gets through
|
|
|
|
for ( int iAxis = 0; iAxis < n_axis; iAxis++ )
|
|
{
|
|
IVP_Raycast_Airboat_Axle *pAxle = get_axle( IVP_POS_AXIS( iAxis ) );
|
|
pAxle->stabilizer_constant = pCarSystemTemplate->stabilizer_constant[iAxis];
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Debug data for use in vphysics and the engine to visualize car data.
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::SetCarSystemDebugData( const IVP_CarSystemDebugData_t &carSystemDebugData )
|
|
{
|
|
// Wheels (raycast data only!)
|
|
for ( int iWheel = 0; iWheel < IVP_RAYCAST_AIRBOAT_MAX_WHEELS; ++iWheel )
|
|
{
|
|
m_CarSystemDebugData.wheelRaycasts[iWheel][0] = carSystemDebugData.wheelRaycasts[iWheel][0];
|
|
m_CarSystemDebugData.wheelRaycasts[iWheel][1] = carSystemDebugData.wheelRaycasts[iWheel][1];
|
|
m_CarSystemDebugData.wheelRaycastImpacts[iWheel] = carSystemDebugData.wheelRaycastImpacts[iWheel];
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Debug data for use in vphysics and the engine to visualize car data.
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::GetCarSystemDebugData( IVP_CarSystemDebugData_t &carSystemDebugData )
|
|
{
|
|
// Wheels (raycast data only!)
|
|
for ( int iWheel = 0; iWheel < IVP_RAYCAST_AIRBOAT_MAX_WHEELS; ++iWheel )
|
|
{
|
|
carSystemDebugData.wheelRaycasts[iWheel][0] = m_CarSystemDebugData.wheelRaycasts[iWheel][0];
|
|
carSystemDebugData.wheelRaycasts[iWheel][1] = m_CarSystemDebugData.wheelRaycasts[iWheel][1];
|
|
carSystemDebugData.wheelRaycastImpacts[iWheel] = m_CarSystemDebugData.wheelRaycastImpacts[iWheel];
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : IVP_U_Vector<IVP_Core>
|
|
//-----------------------------------------------------------------------------
|
|
IVP_U_Vector<IVP_Core> *CPhysics_Airboat::get_associated_controlled_cores( void )
|
|
{
|
|
return &vector_of_cores;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *core -
|
|
//-----------------------------------------------------------------------------
|
|
void CPhysics_Airboat::core_is_going_to_be_deleted_event( IVP_Core *core )
|
|
{
|
|
P_DELETE_THIS(this);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : i -
|
|
// Output : IVP_Raycast_Airboat_Axle
|
|
//-----------------------------------------------------------------------------
|
|
IVP_Raycast_Airboat_Axle *CPhysics_Airboat::get_axle( IVP_POS_AXIS i )
|
|
{
|
|
return &m_aAirboatAxles[i];
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : i -
|
|
// Output : IVP_Raycast_Airboat_Wheel
|
|
//-----------------------------------------------------------------------------
|
|
IVP_Raycast_Airboat_Wheel *CPhysics_Airboat::get_wheel( IVP_POS_WHEEL i )
|
|
{
|
|
return &m_aAirboatWheels[i];
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
IVP_Controller_Raycast_Airboat_Vector_of_Cores_1::IVP_Controller_Raycast_Airboat_Vector_of_Cores_1():
|
|
IVP_U_Vector<IVP_Core>( &elem_buffer[0],1 )
|
|
{
|
|
}
|
|
|