Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1796 lines
63 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: The airboat, a sporty nimble water craft.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "physics_airboat.h"
  8. #include "cmodel.h"
  9. #include <ivp_ray_solver.hxx>
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include "tier0/memdbgon.h"
  12. #ifdef _X360
  13. #define AIRBOAT_STEERING_RATE_MIN 0.000225f
  14. #define AIRBOAT_STEERING_RATE_MAX (10.0f * AIRBOAT_STEERING_RATE_MIN)
  15. #define AIRBOAT_STEERING_INTERVAL 1.5f
  16. #else
  17. #define AIRBOAT_STEERING_RATE_MIN 0.00045f
  18. #define AIRBOAT_STEERING_RATE_MAX (5.0f * AIRBOAT_STEERING_RATE_MIN)
  19. #define AIRBOAT_STEERING_INTERVAL 0.5f
  20. #endif //_X360
  21. #define AIRBOAT_ROT_DRAG 0.00004f
  22. #define AIRBOAT_ROT_DAMPING 0.001f
  23. // Mass-independent thrust values
  24. #define AIRBOAT_THRUST_MAX 11.0f // N / kg
  25. #define AIRBOAT_THRUST_MAX_REVERSE 7.5f // N / kg
  26. // Mass-independent drag values
  27. #define AIRBOAT_WATER_DRAG_LEFT_RIGHT 0.6f
  28. #define AIRBOAT_WATER_DRAG_FORWARD_BACK 0.005f
  29. #define AIRBOAT_WATER_DRAG_UP_DOWN 0.0025f
  30. #define AIRBOAT_GROUND_DRAG_LEFT_RIGHT 2.0
  31. #define AIRBOAT_GROUND_DRAG_FORWARD_BACK 1.0
  32. #define AIRBOAT_GROUND_DRAG_UP_DOWN 0.8
  33. #define AIRBOAT_DRY_FRICTION_SCALE 0.6f // unitless, reduces our friction on all surfaces other than water
  34. #define AIRBOAT_RAYCAST_DIST 0.35f // m (~14in)
  35. #define AIRBOAT_RAYCAST_DIST_WATER_LOW 0.1f // m (~4in)
  36. #define AIRBOAT_RAYCAST_DIST_WATER_HIGH 0.35f // m (~16in)
  37. // Amplitude of wave noise. Blend from max to min as speed increases.
  38. #define AIRBOAT_WATER_NOISE_MIN 0.01 // m (~0.4in)
  39. #define AIRBOAT_WATER_NOISE_MAX 0.03 // m (~1.2in)
  40. // Frequency of wave noise. Blend from min to max as speed increases.
  41. #define AIRBOAT_WATER_FREQ_MIN 1.5
  42. #define AIRBOAT_WATER_FREQ_MAX 1.5
  43. // Phase difference in wave noise between left and right pontoons
  44. // Blend from max to min as speed increases.
  45. #define AIRBOAT_WATER_PHASE_MIN 0.0 // s
  46. #define AIRBOAT_WATER_PHASE_MAX 1.5 // s
  47. #define AIRBOAT_GRAVITY 9.81f // m/s2
  48. // Pontoon indices
  49. enum
  50. {
  51. AIRBOAT_PONTOON_FRONT_LEFT = 0,
  52. AIRBOAT_PONTOON_FRONT_RIGHT,
  53. AIRBOAT_PONTOON_REAR_LEFT,
  54. AIRBOAT_PONTOON_REAR_RIGHT,
  55. };
  56. class IVP_Ray_Solver_Template;
  57. class IVP_Ray_Hit;
  58. class IVP_Event_Sim;
  59. //-----------------------------------------------------------------------------
  60. // Purpose: Constructor
  61. //-----------------------------------------------------------------------------
  62. class CAirboatFrictionData : public IPhysicsCollisionData
  63. {
  64. public:
  65. CAirboatFrictionData()
  66. {
  67. m_vecPoint.Init( 0, 0, 0 );
  68. m_vecNormal.Init( 0, 0, 0 );
  69. m_vecVelocity.Init( 0, 0, 0 );
  70. }
  71. virtual void GetSurfaceNormal( Vector &out )
  72. {
  73. out = m_vecPoint;
  74. }
  75. virtual void GetContactPoint( Vector &out )
  76. {
  77. out = m_vecNormal;
  78. }
  79. virtual void GetContactSpeed( Vector &out )
  80. {
  81. out = m_vecVelocity;
  82. }
  83. public:
  84. Vector m_vecPoint;
  85. Vector m_vecNormal;
  86. Vector m_vecVelocity;
  87. };
  88. //-----------------------------------------------------------------------------
  89. // Purpose: Constructor
  90. //-----------------------------------------------------------------------------
  91. CPhysics_Airboat::CPhysics_Airboat( IVP_Environment *pEnv, const IVP_Template_Car_System *pCarSystem,
  92. IPhysicsGameTrace *pGameTrace )
  93. {
  94. InitRaycastCarBody( pCarSystem );
  95. InitRaycastCarEnvironment( pEnv, pCarSystem );
  96. InitRaycastCarWheels( pCarSystem );
  97. InitRaycastCarAxes( pCarSystem );
  98. InitAirboat( pCarSystem );
  99. m_pGameTrace = pGameTrace;
  100. m_SteeringAngle = 0;
  101. m_bSteeringReversed = false;
  102. m_flThrust = 0;
  103. m_bAirborne = false;
  104. m_flAirTime = 0;
  105. m_bWeakJump = false;
  106. m_flPitchErrorPrev = 0;
  107. m_flRollErrorPrev = 0;
  108. }
  109. //-----------------------------------------------------------------------------
  110. // Purpose: Deconstructor
  111. //-----------------------------------------------------------------------------
  112. CPhysics_Airboat::~CPhysics_Airboat()
  113. {
  114. m_pAirboatBody->get_environment()->get_controller_manager()->remove_controller_from_environment( this, IVP_TRUE );
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Purpose: Setup the car system wheels.
  118. //-----------------------------------------------------------------------------
  119. void CPhysics_Airboat::InitAirboat( const IVP_Template_Car_System *pCarSystem )
  120. {
  121. for ( int iWheel = 0; iWheel < pCarSystem->n_wheels; ++iWheel )
  122. {
  123. m_pWheels[iWheel] = pCarSystem->car_wheel[iWheel];
  124. m_pWheels[iWheel]->enable_collision_detection( IVP_FALSE );
  125. }
  126. CPhysicsObject* pBodyObject = static_cast<CPhysicsObject*>(pCarSystem->car_body->client_data);
  127. pBodyObject->EnableGravity( false );
  128. // We do our own buoyancy simulation.
  129. pBodyObject->SetCallbackFlags( pBodyObject->GetCallbackFlags() & ~CALLBACK_DO_FLUID_SIMULATION );
  130. }
  131. //-----------------------------------------------------------------------------
  132. // Purpose: Get the raycast wheel.
  133. //-----------------------------------------------------------------------------
  134. IPhysicsObject *CPhysics_Airboat::GetWheel( int index )
  135. {
  136. Assert( index >= 0 );
  137. Assert( index < n_wheels );
  138. return ( IPhysicsObject* )m_pWheels[index]->client_data;
  139. }
  140. //-----------------------------------------------------------------------------
  141. // Purpose:
  142. //-----------------------------------------------------------------------------
  143. void CPhysics_Airboat::SetWheelFriction( int iWheel, float flFriction )
  144. {
  145. change_friction_of_wheel( IVP_POS_WHEEL( iWheel ), flFriction );
  146. }
  147. //-----------------------------------------------------------------------------
  148. // Purpose: Returns an amount to add to the front pontoon raycasts to simulate wave noise.
  149. // Input : nPontoonIndex - Which pontoon we're dealing with (0 or 1).
  150. // flSpeedRatio - Speed as a ratio of max speed [0..1]
  151. //-----------------------------------------------------------------------------
  152. float CPhysics_Airboat::ComputeFrontPontoonWaveNoise( int nPontoonIndex, float flSpeedRatio )
  153. {
  154. // Add in sinusoidal noise cause by undulating water. Reduce the amplitude of the noise at higher speeds.
  155. IVP_FLOAT flNoiseScale = RemapValClamped( 1.0 - flSpeedRatio, 0, 1, AIRBOAT_WATER_NOISE_MIN, AIRBOAT_WATER_NOISE_MAX );
  156. // Apply a phase shift between left and right pontoons to simulate waves passing under the boat.
  157. IVP_FLOAT flPhaseShift = 0;
  158. if ( flSpeedRatio < 0.3 )
  159. {
  160. // BUG: this allows a discontinuity in the waveform - use two superimposed sine waves instead?
  161. flPhaseShift = nPontoonIndex * AIRBOAT_WATER_PHASE_MAX;
  162. }
  163. // Increase the wave frequency as speed increases.
  164. IVP_FLOAT flFrequency = RemapValClamped( flSpeedRatio, 0, 1, AIRBOAT_WATER_FREQ_MIN, AIRBOAT_WATER_FREQ_MAX );
  165. //Msg( "Wave amp=%f, freq=%f, phase=%f\n", flNoiseScale, flFrequency, flPhaseShift );
  166. return flNoiseScale * sin( flFrequency * ( m_pCore->environment->get_current_time().get_seconds() + flPhaseShift ) );
  167. }
  168. //-----------------------------------------------------------------------------
  169. // Purpose:: Convert data to HL2 measurements, and test direction of raycast.
  170. //-----------------------------------------------------------------------------
  171. void CPhysics_Airboat::pre_raycasts_gameside( int nRaycastCount, IVP_Ray_Solver_Template *pRays,
  172. Ray_t *pGameRays, IVP_Raycast_Airboat_Impact *pImpacts )
  173. {
  174. IVP_FLOAT flForwardSpeedRatio = clamp( m_vecLocalVelocity.k[2] / 10.0f, 0.f, 1.0f );
  175. //Msg( "flForwardSpeedRatio = %f\n", flForwardSpeedRatio );
  176. IVP_FLOAT flSpeed = ( IVP_FLOAT )m_pCore->speed.real_length();
  177. IVP_FLOAT flSpeedRatio = clamp( flSpeed / 15.0f, 0.f, 1.0f );
  178. if ( !m_flThrust )
  179. {
  180. flForwardSpeedRatio *= 0.5;
  181. }
  182. // This is a little weird. We adjust the front pontoon ray lengths based on forward velocity,
  183. // but ONLY if both pontoons are in the water, which we won't know until we do the raycast.
  184. // So we do most of the work here, and cache some of the results to use them later.
  185. Vector vecStart[4];
  186. Vector vecDirection[4];
  187. Vector vecZero( 0.0f, 0.0f, 0.0f );
  188. int nFrontPontoonsInWater = 0;
  189. int iRaycast;
  190. for ( iRaycast = 0; iRaycast < nRaycastCount; ++iRaycast )
  191. {
  192. // Setup the ray.
  193. ConvertPositionToHL( pRays[iRaycast].ray_start_point, vecStart[iRaycast] );
  194. ConvertDirectionToHL( pRays[iRaycast].ray_normized_direction, vecDirection[iRaycast] );
  195. float flRayLength = IVP2HL( pRays[iRaycast].ray_length );
  196. // Check to see if that point is in water.
  197. pImpacts[iRaycast].bInWater = IVP_FALSE;
  198. if ( m_pGameTrace->VehiclePointInWater( vecStart[iRaycast] ) )
  199. {
  200. vecDirection[iRaycast].Negate();
  201. pImpacts[iRaycast].bInWater = IVP_TRUE;
  202. }
  203. Vector vecEnd = vecStart[iRaycast] + ( vecDirection[iRaycast] * flRayLength );
  204. // Adjust the trace if the pontoon is in the water.
  205. if ( m_pGameTrace->VehiclePointInWater( vecEnd ) )
  206. {
  207. // Reduce the ray length in the water.
  208. pRays[iRaycast].ray_length = AIRBOAT_RAYCAST_DIST_WATER_LOW;
  209. if ( iRaycast < 2 )
  210. {
  211. nFrontPontoonsInWater++;
  212. // Front pontoons.
  213. // Add a little sinusoidal noise to simulate waves.
  214. IVP_FLOAT flNoise = ComputeFrontPontoonWaveNoise( iRaycast, flSpeedRatio );
  215. pRays[iRaycast].ray_length += flNoise;
  216. }
  217. else
  218. {
  219. // Recalculate the end position in HL coordinates.
  220. flRayLength = IVP2HL( pRays[iRaycast].ray_length );
  221. vecEnd = vecStart[iRaycast] + ( vecDirection[iRaycast] * flRayLength );
  222. }
  223. }
  224. pGameRays[iRaycast].Init( vecStart[iRaycast], vecEnd, vecZero, vecZero );
  225. }
  226. // If both front pontoons are in the water, add in a bit of lift proportional to our
  227. // forward speed. We can't do this to only one of the front pontoons because it causes
  228. // some twist if we do.
  229. // FIXME: this does some redundant work (computes the wave noise again)
  230. if ( nFrontPontoonsInWater == 2 )
  231. {
  232. for ( int i = 0; i < 2; i++ )
  233. {
  234. // Front pontoons.
  235. // Raise it higher out of the water as we go faster forward.
  236. pRays[i].ray_length = RemapValClamped( flForwardSpeedRatio, 0, 1, AIRBOAT_RAYCAST_DIST_WATER_LOW, AIRBOAT_RAYCAST_DIST_WATER_HIGH );
  237. // Add a little sinusoidal noise to simulate waves.
  238. IVP_FLOAT flNoise = ComputeFrontPontoonWaveNoise( i, flSpeedRatio );
  239. pRays[i].ray_length += flNoise;
  240. // Recalculate the end position in HL coordinates.
  241. float flRayLength = IVP2HL( pRays[i].ray_length );
  242. Vector vecEnd = vecStart[i] + ( vecDirection[i] * flRayLength );
  243. pGameRays[i].Init( vecStart[i], vecEnd, vecZero, vecZero );
  244. }
  245. }
  246. }
  247. //-----------------------------------------------------------------------------
  248. // Purpose:
  249. //-----------------------------------------------------------------------------
  250. float CPhysics_Airboat::GetWaterDepth( Ray_t *pGameRay, IPhysicsObject *pPhysAirboat )
  251. {
  252. float flDepth = 0.0f;
  253. trace_t trace;
  254. Ray_t waterRay;
  255. Vector vecStart = pGameRay->m_Start;
  256. Vector vecEnd( vecStart.x, vecStart.y, vecStart.z + 1000.0f );
  257. Vector vecZero( 0.0f, 0.0f, 0.0f );
  258. waterRay.Init( vecStart, vecEnd, vecZero, vecZero );
  259. m_pGameTrace->VehicleTraceRayWithWater( waterRay, pPhysAirboat->GetGameData(), &trace );
  260. flDepth = 1000.0f * trace.fractionleftsolid;
  261. return flDepth;
  262. }
  263. //-----------------------------------------------------------------------------
  264. // Purpose: Performs traces to figure out what is at each of the raycast points
  265. // and fills out the pImpacts array with that information.
  266. // Input : nRaycastCount - Number of elements in the arrays pointed to by pRays
  267. // and pImpacts.
  268. // pRays - Holds the rays to trace with.
  269. // pImpacts - Receives the trace results.
  270. //-----------------------------------------------------------------------------
  271. void CPhysics_Airboat::do_raycasts_gameside( int nRaycastCount, IVP_Ray_Solver_Template *pRays,
  272. IVP_Raycast_Airboat_Impact *pImpacts )
  273. {
  274. Assert( nRaycastCount >= 0 );
  275. Assert( nRaycastCount <= IVP_RAYCAST_AIRBOAT_MAX_WHEELS );
  276. Ray_t gameRays[IVP_RAYCAST_AIRBOAT_MAX_WHEELS];
  277. pre_raycasts_gameside( nRaycastCount, pRays, gameRays, pImpacts );
  278. // Do the raycasts and set impact data.
  279. trace_t trace;
  280. for ( int iRaycast = 0; iRaycast < nRaycastCount; ++iRaycast )
  281. {
  282. // Trace.
  283. if ( pImpacts[iRaycast].bInWater )
  284. {
  285. // The start position is underwater. Trace up to find the water surface.
  286. IPhysicsObject *pPhysAirboat = static_cast<IPhysicsObject*>( m_pAirboatBody->client_data );
  287. m_pGameTrace->VehicleTraceRay( gameRays[iRaycast], pPhysAirboat->GetGameData(), &trace );
  288. pImpacts[iRaycast].flDepth = GetWaterDepth( &gameRays[iRaycast], pPhysAirboat );
  289. }
  290. else
  291. {
  292. // Trace down to find the ground or water.
  293. IPhysicsObject *pPhysAirboat = static_cast<IPhysicsObject*>( m_pAirboatBody->client_data );
  294. m_pGameTrace->VehicleTraceRayWithWater( gameRays[iRaycast], pPhysAirboat->GetGameData(), &trace );
  295. }
  296. ConvertPositionToIVP( gameRays[iRaycast].m_Start + gameRays[iRaycast].m_StartOffset, m_CarSystemDebugData.wheelRaycasts[iRaycast][0] );
  297. ConvertPositionToIVP( gameRays[iRaycast].m_Start + gameRays[iRaycast].m_StartOffset + gameRays[iRaycast].m_Delta, m_CarSystemDebugData.wheelRaycasts[iRaycast][1] );
  298. m_CarSystemDebugData.wheelRaycastImpacts[iRaycast] = trace.fraction * gameRays[iRaycast].m_Delta.Length();
  299. // Set impact data.
  300. pImpacts[iRaycast].bImpactWater = IVP_FALSE;
  301. pImpacts[iRaycast].bImpact = IVP_FALSE;
  302. if ( trace.fraction != 1.0f )
  303. {
  304. pImpacts[iRaycast].bImpact = IVP_TRUE;
  305. // Set water surface flag.
  306. pImpacts[iRaycast].flDepth = 0.0f;
  307. if ( trace.contents & MASK_WATER )
  308. {
  309. pImpacts[iRaycast].bImpactWater = IVP_TRUE;
  310. }
  311. // Save impact surface data.
  312. ConvertPositionToIVP( trace.endpos, pImpacts[iRaycast].vecImpactPointWS );
  313. ConvertDirectionToIVP( trace.plane.normal, pImpacts[iRaycast].vecImpactNormalWS );
  314. // Save surface properties.
  315. const surfacedata_t *pSurfaceData = physprops->GetSurfaceData( trace.surface.surfaceProps );
  316. pImpacts[iRaycast].nSurfaceProps = trace.surface.surfaceProps;
  317. if (pImpacts[iRaycast].vecImpactNormalWS.k[1] < -0.707)
  318. {
  319. // dampening is 1/t, where t is how long it takes to come to a complete stop
  320. pImpacts[iRaycast].flDampening = pSurfaceData->physics.dampening;
  321. pImpacts[iRaycast].flFriction = pSurfaceData->physics.friction;
  322. }
  323. else
  324. {
  325. // This surface is too vertical -- no friction or damping from it.
  326. pImpacts[iRaycast].flDampening = pSurfaceData->physics.dampening;
  327. pImpacts[iRaycast].flFriction = pSurfaceData->physics.friction;
  328. }
  329. }
  330. }
  331. }
  332. //-----------------------------------------------------------------------------
  333. // Purpose: Entry point for airboat simulation.
  334. //-----------------------------------------------------------------------------
  335. void CPhysics_Airboat::do_simulation_controller( IVP_Event_Sim *pEventSim, IVP_U_Vector<IVP_Core> * )
  336. {
  337. IVP_Ray_Solver_Template raySolverTemplates[IVP_RAYCAST_AIRBOAT_MAX_WHEELS];
  338. IVP_Raycast_Airboat_Impact impacts[IVP_RAYCAST_AIRBOAT_MAX_WHEELS];
  339. // Cache some data into members here so we only do the work once.
  340. m_pCore = m_pAirboatBody->get_core();
  341. const IVP_U_Matrix *matWorldFromCore = m_pCore->get_m_world_f_core_PSI();
  342. // Cache the speed.
  343. m_flSpeed = ( IVP_FLOAT )m_pCore->speed.real_length();
  344. // Cache the local velocity vector.
  345. matWorldFromCore->vimult3(&m_pCore->speed, &m_vecLocalVelocity);
  346. // Raycasts.
  347. PreRaycasts( raySolverTemplates, matWorldFromCore, impacts );
  348. do_raycasts_gameside( n_wheels, raySolverTemplates, impacts );
  349. if ( !PostRaycasts( raySolverTemplates, matWorldFromCore, impacts ) )
  350. return;
  351. UpdateAirborneState( impacts, pEventSim );
  352. // Enumerate the controllers attached to us.
  353. //for (int i = m_pCore->controllers_of_core.len() - 1; i >= 0; i--)
  354. //{
  355. // IVP_Controller *pController = m_pCore->controllers_of_core.element_at(i);
  356. //}
  357. // Pontoons. Buoyancy or ground impacts.
  358. DoSimulationPontoons( impacts, pEventSim );
  359. // Drag due to water and ground friction.
  360. DoSimulationDrag( impacts, pEventSim );
  361. // Turbine (fan).
  362. DoSimulationTurbine( pEventSim );
  363. // Steering.
  364. DoSimulationSteering( pEventSim );
  365. // Anti-pitch.
  366. DoSimulationKeepUprightPitch( impacts, pEventSim );
  367. // Anti-roll.
  368. DoSimulationKeepUprightRoll( impacts, pEventSim );
  369. // Additional gravity based on speed.
  370. DoSimulationGravity( pEventSim );
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose: Initialize the rays to be cast from the vehicle wheel positions to
  374. // the "ground."
  375. // Input : pRaySolverTemplates -
  376. // matWorldFromCore -
  377. // pImpacts -
  378. //-----------------------------------------------------------------------------
  379. void CPhysics_Airboat::PreRaycasts( IVP_Ray_Solver_Template *pRaySolverTemplates,
  380. const IVP_U_Matrix *matWorldFromCore,
  381. IVP_Raycast_Airboat_Impact *pImpacts )
  382. {
  383. int nPontoonPoints = n_wheels;
  384. for ( int iPoint = 0; iPoint < nPontoonPoints; ++iPoint )
  385. {
  386. IVP_Raycast_Airboat_Wheel *pPontoonPoint = get_wheel( IVP_POS_WHEEL( iPoint ) );
  387. if ( pPontoonPoint )
  388. {
  389. // Fill the in the ray solver template for the current wheel.
  390. IVP_Ray_Solver_Template &raySolverTemplate = pRaySolverTemplates[iPoint];
  391. // Transform the wheel "start" position from vehicle core-space to world-space. This is
  392. // the raycast starting position.
  393. matWorldFromCore->vmult4( &pPontoonPoint->raycast_start_cs, &raySolverTemplate.ray_start_point );
  394. // Transform the shock (spring) direction from vehicle core-space to world-space. This is
  395. // the raycast direction.
  396. matWorldFromCore->vmult3( &pPontoonPoint->raycast_dir_cs, &pImpacts[iPoint].raycast_dir_ws );
  397. raySolverTemplate.ray_normized_direction.set( &pImpacts[iPoint].raycast_dir_ws );
  398. // Set the length of the ray cast.
  399. raySolverTemplate.ray_length = AIRBOAT_RAYCAST_DIST;
  400. // Set the ray solver template flags. This defines which objects you wish to
  401. // collide against in the physics environment.
  402. raySolverTemplate.ray_flags = IVP_RAY_SOLVER_ALL;
  403. }
  404. }
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Purpose: Determines whether we are airborne and whether we just performed a
  408. // weak or strong jump. Weak jumps are jumps at below a threshold speed,
  409. // and disable the turbine and pitch controller.
  410. // Input : pImpacts -
  411. // Output : Returns true on success, false on failure.
  412. //-----------------------------------------------------------------------------
  413. void CPhysics_Airboat::UpdateAirborneState( IVP_Raycast_Airboat_Impact *pImpacts, IVP_Event_Sim *pEventSim )
  414. {
  415. int nCount = CountSurfaceContactPoints(pImpacts);
  416. if (!nCount)
  417. {
  418. if (!m_bAirborne)
  419. {
  420. m_bAirborne = true;
  421. m_flAirTime = 0;
  422. IVP_FLOAT flSpeed = ( IVP_FLOAT )m_pCore->speed.real_length();
  423. if (flSpeed < 11.0f)
  424. {
  425. //Msg("*** WEAK JUMP at %f!!!\n", flSpeed);
  426. m_bWeakJump = true;
  427. }
  428. else
  429. {
  430. //Msg("Strong JUMP at %f\n", flSpeed);
  431. }
  432. }
  433. else
  434. {
  435. m_flAirTime += pEventSim->delta_time;
  436. }
  437. }
  438. else
  439. {
  440. m_bAirborne = false;
  441. m_bWeakJump = false;
  442. }
  443. }
  444. //-----------------------------------------------------------------------------
  445. // Purpose:
  446. //-----------------------------------------------------------------------------
  447. bool CPhysics_Airboat::PostRaycasts( IVP_Ray_Solver_Template *pRaySolverTemplates, const IVP_U_Matrix *matWorldFromCore,
  448. IVP_Raycast_Airboat_Impact *pImpacts )
  449. {
  450. bool bReturn = true;
  451. int nPontoonPoints = n_wheels;
  452. for( int iPoint = 0; iPoint < nPontoonPoints; ++iPoint )
  453. {
  454. // Get data at raycast position.
  455. IVP_Raycast_Airboat_Wheel *pPontoonPoint = get_wheel( IVP_POS_WHEEL( iPoint ) );
  456. IVP_Raycast_Airboat_Impact *pImpact = &pImpacts[iPoint];
  457. IVP_Ray_Solver_Template *pRaySolver = &pRaySolverTemplates[iPoint];
  458. if ( !pPontoonPoint || !pImpact || !pRaySolver )
  459. continue;
  460. // Copy the ray length back, it may have changed.
  461. pPontoonPoint->raycast_length = pRaySolver->ray_length;
  462. // Test for inverted raycast direction.
  463. if ( pImpact->bInWater )
  464. {
  465. pImpact->raycast_dir_ws.set_multiple( &pImpact->raycast_dir_ws, -1 );
  466. }
  467. // Impact.
  468. if ( pImpact->bImpact )
  469. {
  470. // Save impact distance.
  471. IVP_U_Point vecDelta;
  472. vecDelta.subtract( &pImpact->vecImpactPointWS, &pRaySolver->ray_start_point );
  473. pPontoonPoint->raycast_dist = vecDelta.real_length();
  474. // 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
  475. // and percentage of force applied to the shock).
  476. pImpact->inv_normal_dot_dir = 1.1f / ( IVP_Inline_Math::fabsd( pImpact->raycast_dir_ws.dot_product( &pImpact->vecImpactNormalWS ) ) + 0.1f );
  477. // Set the wheel friction - ground friction (if any) + wheel friction.
  478. pImpact->friction_value = pImpact->flFriction * pPontoonPoint->friction_of_wheel;
  479. }
  480. // No impact.
  481. else
  482. {
  483. pPontoonPoint->raycast_dist = pPontoonPoint->raycast_length;
  484. pImpact->inv_normal_dot_dir = 1.0f;
  485. pImpact->moveable_object_hit_by_ray = NULL;
  486. pImpact->vecImpactNormalWS.set_multiple( &pImpact->raycast_dir_ws, -1 );
  487. pImpact->friction_value = 1.0f;
  488. }
  489. // Set the new wheel position (the impact point or the full ray distance). Make this from the wheel not the ray trace position.
  490. pImpact->vecImpactPointWS.add_multiple( &pRaySolver->ray_start_point, &pImpact->raycast_dir_ws, pPontoonPoint->raycast_dist );
  491. // Get the speed (velocity) at the impact point.
  492. m_pCore->get_surface_speed_ws( &pImpact->vecImpactPointWS, &pImpact->surface_speed_wheel_ws );
  493. pImpact->projected_surface_speed_wheel_ws.set_orthogonal_part( &pImpact->surface_speed_wheel_ws, &pImpact->vecImpactNormalWS );
  494. matWorldFromCore->vmult3( &pPontoonPoint->axis_direction_cs, &pImpact->axis_direction_ws );
  495. pImpact->projected_axis_direction_ws.set_orthogonal_part( &pImpact->axis_direction_ws, &pImpact->vecImpactNormalWS );
  496. if ( pImpact->projected_axis_direction_ws.normize() == IVP_FAULT )
  497. {
  498. DevMsg( "CPhysics_Airboat::do_simulation_controller projected_axis_direction_ws.normize failed\n" );
  499. bReturn = false;
  500. }
  501. }
  502. return bReturn;
  503. }
  504. //-----------------------------------------------------------------------------
  505. // Purpose:
  506. //-----------------------------------------------------------------------------
  507. void CPhysics_Airboat::DoSimulationPontoons( IVP_Raycast_Airboat_Impact *pImpacts, IVP_Event_Sim *pEventSim )
  508. {
  509. int nPontoonPoints = n_wheels;
  510. for ( int iPoint = 0; iPoint < nPontoonPoints; ++iPoint )
  511. {
  512. IVP_Raycast_Airboat_Wheel *pPontoonPoint = get_wheel( IVP_POS_WHEEL( iPoint ) );
  513. if ( !pPontoonPoint )
  514. continue;
  515. if ( pImpacts[iPoint].bImpact )
  516. {
  517. DoSimulationPontoonsGround( pPontoonPoint, &pImpacts[iPoint], pEventSim );
  518. }
  519. else if ( pImpacts[iPoint].bInWater )
  520. {
  521. DoSimulationPontoonsWater( pPontoonPoint, &pImpacts[iPoint], pEventSim );
  522. }
  523. }
  524. }
  525. //-----------------------------------------------------------------------------
  526. // Purpose: Handle pontoons on ground.
  527. //-----------------------------------------------------------------------------
  528. void CPhysics_Airboat::DoSimulationPontoonsGround( IVP_Raycast_Airboat_Wheel *pPontoonPoint,
  529. IVP_Raycast_Airboat_Impact *pImpact, IVP_Event_Sim *pEventSim )
  530. {
  531. // Check to see if we hit anything, otherwise the no force on this point.
  532. IVP_DOUBLE flDiff = pPontoonPoint->raycast_dist - pPontoonPoint->raycast_length;
  533. if ( flDiff >= 0 )
  534. return;
  535. IVP_FLOAT flSpringConstant, flSpringRelax, flSpringCompress;
  536. flSpringConstant = pPontoonPoint->spring_constant;
  537. flSpringRelax = pPontoonPoint->spring_damp_relax;
  538. flSpringCompress = pPontoonPoint->spring_damp_compress;
  539. IVP_DOUBLE flForce = -flDiff * flSpringConstant;
  540. IVP_FLOAT flInvNormalDotDir = clamp(pImpact->inv_normal_dot_dir, 0.0f, 3.0f);
  541. flForce *= flInvNormalDotDir;
  542. IVP_U_Float_Point vecSpeedDelta;
  543. vecSpeedDelta.subtract( &pImpact->projected_surface_speed_wheel_ws, &pImpact->surface_speed_wheel_ws );
  544. IVP_DOUBLE flSpeed = vecSpeedDelta.dot_product( &pImpact->raycast_dir_ws );
  545. if ( flSpeed > 0 )
  546. {
  547. flForce -= flSpringRelax * flSpeed;
  548. }
  549. else
  550. {
  551. flForce -= flSpringCompress * flSpeed;
  552. }
  553. if ( flForce < 0 )
  554. {
  555. flForce = 0.0f;
  556. }
  557. // NOTE: Spring constants are all mass-independent, so no need to multiply by mass here.
  558. IVP_DOUBLE flImpulse = flForce * pEventSim->delta_time;
  559. IVP_U_Float_Point vecImpulseWS;
  560. vecImpulseWS.set_multiple( &pImpact->vecImpactNormalWS, flImpulse );
  561. m_pCore->push_core_ws( &pImpact->vecImpactPointWS, &vecImpulseWS );
  562. }
  563. //-----------------------------------------------------------------------------
  564. // Purpose: Handle pontoons on water.
  565. //-----------------------------------------------------------------------------
  566. void CPhysics_Airboat::DoSimulationPontoonsWater( IVP_Raycast_Airboat_Wheel *pPontoonPoint,
  567. IVP_Raycast_Airboat_Impact *pImpact, IVP_Event_Sim *pEventSim )
  568. {
  569. #define AIRBOAT_BUOYANCY_SCALAR 1.6f
  570. #define PONTOON_AREA_2D 2.8f // 2 pontoons x 16 in x 136 in = 4352 sq inches = 2.8 sq meters
  571. #define PONTOON_HEIGHT 0.41f // 16 inches high = 0.41 meters
  572. float flDepth = clamp( pImpact->flDepth, 0.f, PONTOON_HEIGHT );
  573. //Msg("depth: %f\n", pImpact->flDepth);
  574. // Depth is in inches, so multiply by 0.0254 meters/inch
  575. IVP_FLOAT flSubmergedVolume = PONTOON_AREA_2D * flDepth * 0.0254;
  576. // Buoyancy forces are equal to the mass of the water displaced, which is 1000 kg/m^3
  577. // There are 4 pontoon points, so each one can exert 1/4th of the total buoyancy force.
  578. IVP_FLOAT flForce = AIRBOAT_BUOYANCY_SCALAR * 0.25f * m_pCore->get_mass() * flSubmergedVolume * 1000.0f;
  579. IVP_DOUBLE flImpulse = flForce * pEventSim->delta_time;
  580. IVP_U_Float_Point vecImpulseWS;
  581. vecImpulseWS.set( 0, -1, 0 );
  582. vecImpulseWS.mult( flImpulse );
  583. m_pCore->push_core_ws( &pImpact->vecImpactPointWS, &vecImpulseWS );
  584. // Vector vecPoint;
  585. // Vector vecDir(0, 0, 1);
  586. //
  587. // ConvertPositionToHL( pImpact->vecImpactPointWS, vecPoint );
  588. // CPhysicsEnvironment *pEnv = (CPhysicsEnvironment *)m_pAirboatBody->get_core()->environment->client_data;
  589. // IVPhysicsDebugOverlay *debugoverlay = pEnv->GetDebugOverlay();
  590. // debugoverlay->AddLineOverlay(vecPoint, vecPoint + vecDir * 128, 255, 0, 255, false, 10.0 );
  591. }
  592. //-----------------------------------------------------------------------------
  593. // Purpose:
  594. //-----------------------------------------------------------------------------
  595. void CPhysics_Airboat::PerformFrictionNotification( float flEliminatedEnergy, float dt, int nSurfaceProp, IPhysicsCollisionData *pCollisionData )
  596. {
  597. CPhysicsObject *pPhysAirboat = static_cast<CPhysicsObject*>( m_pAirboatBody->client_data );
  598. if ( ( pPhysAirboat->CallbackFlags() & CALLBACK_GLOBAL_FRICTION ) == 0 )
  599. return;
  600. IPhysicsCollisionEvent *pEventHandler = pPhysAirboat->GetVPhysicsEnvironment()->GetCollisionEventHandler();
  601. if ( !pEventHandler )
  602. return;
  603. // scrape with an estimate for the energy per unit mass
  604. // This assumes that the game is interested in some measure of vibration
  605. // for sound effects. This also assumes that more massive objects require
  606. // more energy to vibrate.
  607. flEliminatedEnergy *= dt / pPhysAirboat->GetMass();
  608. if ( flEliminatedEnergy > 0.05f )
  609. {
  610. pEventHandler->Friction( pPhysAirboat, flEliminatedEnergy, pPhysAirboat->GetMaterialIndexInternal(), nSurfaceProp, pCollisionData );
  611. }
  612. }
  613. //-----------------------------------------------------------------------------
  614. // Purpose: Drag due to water and ground friction.
  615. //-----------------------------------------------------------------------------
  616. void CPhysics_Airboat::DoSimulationDrag( IVP_Raycast_Airboat_Impact *pImpacts,
  617. IVP_Event_Sim *pEventSim )
  618. {
  619. const IVP_U_Matrix *matWorldFromCore = m_pCore->get_m_world_f_core_PSI();
  620. IVP_FLOAT flSpeed = ( IVP_FLOAT )m_pCore->speed.real_length();
  621. // Used to make airboat sliding sounds
  622. CAirboatFrictionData frictionData;
  623. ConvertDirectionToHL( m_pCore->speed, frictionData.m_vecVelocity );
  624. // Count the pontoons in the water.
  625. int nPontoonPoints = n_wheels;
  626. int nPointsInWater = 0;
  627. int nPointsOnGround = 0;
  628. float flGroundFriction = 0;
  629. float flAverageDampening = 0.0f;
  630. int *pSurfacePropCount = (int *)stackalloc( n_wheels * sizeof(int) );
  631. int *pSurfaceProp = (int *)stackalloc( n_wheels * sizeof(int) );
  632. memset( pSurfacePropCount, 0, n_wheels * sizeof(int) );
  633. memset( pSurfaceProp, 0xFF, n_wheels * sizeof(int) );
  634. int nSurfacePropCount = 0;
  635. int nMaxSurfacePropIdx = 0;
  636. for( int iPoint = 0; iPoint < nPontoonPoints; ++iPoint )
  637. {
  638. // Get data at raycast position.
  639. IVP_Raycast_Airboat_Impact *pImpact = &pImpacts[iPoint];
  640. if ( !pImpact || !pImpact->bImpact )
  641. continue;
  642. if ( pImpact->bImpactWater )
  643. {
  644. flAverageDampening += pImpact->flDampening;
  645. nPointsInWater++;
  646. }
  647. else
  648. {
  649. flGroundFriction += pImpact->flFriction;
  650. nPointsOnGround++;
  651. // This logic is used to determine which surface prop we hit the most.
  652. int i;
  653. for ( i = 0; i < nSurfacePropCount; ++i )
  654. {
  655. if ( pSurfaceProp[i] == pImpact->nSurfaceProps )
  656. break;
  657. }
  658. if ( i == nSurfacePropCount )
  659. {
  660. ++nSurfacePropCount;
  661. }
  662. pSurfaceProp[i] = pImpact->nSurfaceProps;
  663. if ( ++pSurfacePropCount[i] > pSurfacePropCount[nMaxSurfacePropIdx] )
  664. {
  665. nMaxSurfacePropIdx = i;
  666. }
  667. Vector frictionPoint, frictionNormal;
  668. ConvertPositionToHL( pImpact->vecImpactPointWS, frictionPoint );
  669. ConvertDirectionToHL( pImpact->vecImpactNormalWS, frictionNormal );
  670. frictionData.m_vecPoint += frictionPoint;
  671. frictionData.m_vecNormal += frictionNormal;
  672. }
  673. }
  674. int nSurfaceProp = pSurfaceProp[nMaxSurfacePropIdx];
  675. if ( nPointsOnGround > 0 )
  676. {
  677. frictionData.m_vecPoint /= nPointsOnGround;
  678. frictionData.m_vecNormal /= nPointsOnGround;
  679. VectorNormalize( frictionData.m_vecNormal );
  680. }
  681. if ( nPointsInWater > 0 )
  682. {
  683. flAverageDampening /= nPointsInWater;
  684. }
  685. //IVP_FLOAT flDebugSpeed = ( IVP_FLOAT )m_pCore->speed.real_length();
  686. //Msg("(water=%d/land=%d) speed=%f (%f %f %f)\n", nPointsInWater, nPointsOnGround, flDebugSpeed, vecAirboatDirLS.k[0], vecAirboatDirLS.k[1], vecAirboatDirLS.k[2]);
  687. if ( nPointsInWater )
  688. {
  689. // Apply the drag force opposite to the direction of motion in local space.
  690. IVP_U_Float_Point vecAirboatNegDirLS;
  691. vecAirboatNegDirLS.set_negative( &m_vecLocalVelocity );
  692. // Water drag is directional -- the pontoons resist left/right motion much more than forward/back.
  693. IVP_U_Float_Point vecDragLS;
  694. vecDragLS.set( AIRBOAT_WATER_DRAG_LEFT_RIGHT * vecAirboatNegDirLS.k[0],
  695. AIRBOAT_WATER_DRAG_UP_DOWN * vecAirboatNegDirLS.k[1],
  696. AIRBOAT_WATER_DRAG_FORWARD_BACK * vecAirboatNegDirLS.k[2] );
  697. vecDragLS.mult( flSpeed * m_pCore->get_mass() * pEventSim->delta_time );
  698. // dvs TODO: apply flAverageDampening here
  699. // Convert the drag force to world space and apply the drag.
  700. IVP_U_Float_Point vecDragWS;
  701. matWorldFromCore->vmult3(&vecDragLS, &vecDragWS);
  702. m_pCore->center_push_core_multiple_ws( &vecDragWS );
  703. }
  704. //
  705. // Calculate ground friction drag:
  706. //
  707. if ( nPointsOnGround && ( flSpeed > 0 ))
  708. {
  709. // Calculate the average friction across all contact points.
  710. flGroundFriction /= (float)nPointsOnGround;
  711. // Apply the drag force opposite to the direction of motion.
  712. IVP_U_Float_Point vecAirboatNegDir;
  713. vecAirboatNegDir.set_negative( &m_pCore->speed );
  714. IVP_FLOAT flFrictionDrag = m_pCore->get_mass() * AIRBOAT_GRAVITY * AIRBOAT_DRY_FRICTION_SCALE * flGroundFriction;
  715. flFrictionDrag /= flSpeed;
  716. IPhysicsObject *pPhysAirboat = static_cast<IPhysicsObject*>( m_pAirboatBody->client_data );
  717. float flEliminatedEnergy = pPhysAirboat->GetEnergy();
  718. // Apply the drag force opposite to the direction of motion in local space.
  719. IVP_U_Float_Point vecAirboatNegDirLS;
  720. vecAirboatNegDirLS.set_negative( &m_vecLocalVelocity );
  721. // Ground drag is directional -- the pontoons resist left/right motion much more than forward/back.
  722. IVP_U_Float_Point vecDragLS;
  723. vecDragLS.set( AIRBOAT_GROUND_DRAG_LEFT_RIGHT * vecAirboatNegDirLS.k[0],
  724. AIRBOAT_GROUND_DRAG_UP_DOWN * vecAirboatNegDirLS.k[1],
  725. AIRBOAT_GROUND_DRAG_FORWARD_BACK * vecAirboatNegDirLS.k[2] );
  726. vecDragLS.mult( flFrictionDrag * pEventSim->delta_time );
  727. // dvs TODO: apply flAverageDampening here
  728. // Convert the drag force to world space and apply the drag.
  729. IVP_U_Float_Point vecDragWS;
  730. matWorldFromCore->vmult3(&vecDragLS, &vecDragWS);
  731. m_pCore->center_push_core_multiple_ws( &vecDragWS );
  732. // Figure out how much energy was eliminated by friction.
  733. flEliminatedEnergy -= pPhysAirboat->GetEnergy();
  734. PerformFrictionNotification( flEliminatedEnergy, pEventSim->delta_time, nSurfaceProp, &frictionData );
  735. }
  736. }
  737. //-----------------------------------------------------------------------------
  738. // Purpose:
  739. //-----------------------------------------------------------------------------
  740. void CPhysics_Airboat::DoSimulationTurbine( IVP_Event_Sim *pEventSim )
  741. {
  742. // Reduce the turbine power during weak jumps to avoid unrealistic air control.
  743. // Also, reduce reverse thrust while airborne.
  744. float flThrust = m_flThrust;
  745. if ((m_bWeakJump) || (m_bAirborne && (flThrust < 0)))
  746. {
  747. flThrust *= 0.5;
  748. }
  749. // Get the forward vector in world-space.
  750. IVP_U_Float_Point vecForwardWS;
  751. const IVP_U_Matrix *matWorldFromCore = m_pCore->get_m_world_f_core_PSI();
  752. matWorldFromCore->get_col( IVP_COORDINATE_INDEX( index_z ), &vecForwardWS );
  753. //Msg("thrust: %f\n", m_flThrust);
  754. if ( ( vecForwardWS.k[1] < -0.5 ) && ( flThrust > 0 ) )
  755. {
  756. // Driving up a slope. Reduce upward thrust to prevent ludicrous climbing of steep surfaces.
  757. float flFactor = 1 + vecForwardWS.k[1];
  758. //Msg("FWD: y=%f, factor=%f\n", vecForwardWS.k[1], flFactor);
  759. flThrust *= flFactor;
  760. }
  761. else if ( ( vecForwardWS.k[1] > 0.5 ) && ( flThrust < 0 ) )
  762. {
  763. // Reversing up a slope. Reduce upward thrust to prevent ludicrous climbing of steep surfaces.
  764. float flFactor = 1 - vecForwardWS.k[1];
  765. //Msg("REV: y=%f, factor=%f\n", vecForwardWS.k[1], flFactor);
  766. flThrust *= flFactor;
  767. }
  768. // Forward (Front/Back) force
  769. IVP_U_Float_Point vecImpulse;
  770. vecImpulse.set_multiple( &vecForwardWS, flThrust * m_pCore->get_mass() * pEventSim->delta_time );
  771. m_pCore->center_push_core_multiple_ws( &vecImpulse );
  772. }
  773. //-----------------------------------------------------------------------------
  774. // Purpose:
  775. //-----------------------------------------------------------------------------
  776. void CPhysics_Airboat::DoSimulationSteering( IVP_Event_Sim *pEventSim )
  777. {
  778. // Calculate the steering direction: forward or reverse.
  779. // Don't mess with the steering direction while we're steering, unless thrust is applied.
  780. // This prevents the steering from reversing because we started drifting backwards.
  781. if ( ( m_SteeringAngle == 0 ) || ( m_flThrust != 0 ) )
  782. {
  783. if ( !m_bAnalogSteering )
  784. {
  785. // If we're applying reverse thrust, steering is always reversed.
  786. if ( m_flThrust < 0 )
  787. {
  788. m_bSteeringReversed = true;
  789. }
  790. // Else if we are applying forward thrust or moving forward, use forward steering.
  791. else if ( ( m_flThrust > 0 ) || ( m_vecLocalVelocity.k[2] > 0 ) )
  792. {
  793. m_bSteeringReversed = false;
  794. }
  795. }
  796. else
  797. {
  798. // Create a dead zone through the middle of the joystick where we don't reverse thrust.
  799. // If we're applying reverse thrust, steering is always reversed.
  800. if ( m_flThrust < -2.0f )
  801. {
  802. m_bSteeringReversed = true;
  803. }
  804. // Else if we are applying forward thrust or moving forward, use forward steering.
  805. else if ( ( m_flThrust > 2.0f ) || ( m_vecLocalVelocity.k[2] > 0 ) )
  806. {
  807. m_bSteeringReversed = false;
  808. }
  809. }
  810. }
  811. // Calculate the steering force.
  812. IVP_FLOAT flForceSteering = 0.0f;
  813. if ( fabsf( m_SteeringAngle ) > 0.01 )
  814. {
  815. // Get the sign of the steering force.
  816. IVP_FLOAT flSteeringSign = m_SteeringAngle < 0.0f ? -1.0f : 1.0f;
  817. if ( m_bSteeringReversed )
  818. {
  819. flSteeringSign *= -1.0f;
  820. }
  821. // If we changed steering sign or went from not steering to steering, reset the steer time
  822. // to blend the new steering force in over time.
  823. IVP_FLOAT flPrevSteeringSign = m_flPrevSteeringAngle < 0.0f ? -1.0f : 1.0f;
  824. if ( ( fabs( m_flPrevSteeringAngle ) < 0.01 ) || ( flSteeringSign != flPrevSteeringSign ) )
  825. {
  826. m_flSteerTime = 0;
  827. }
  828. float flSteerScale = 0.f;
  829. if ( !m_bAnalogSteering )
  830. {
  831. // Ramp the steering force up over two seconds.
  832. flSteerScale = RemapValClamped( m_flSteerTime, 0, AIRBOAT_STEERING_INTERVAL, AIRBOAT_STEERING_RATE_MIN, AIRBOAT_STEERING_RATE_MAX );
  833. }
  834. else // consoles
  835. {
  836. // Analog steering
  837. flSteerScale = RemapValClamped( fabs(m_SteeringAngle), 0, AIRBOAT_STEERING_INTERVAL, AIRBOAT_STEERING_RATE_MIN, AIRBOAT_STEERING_RATE_MAX );
  838. }
  839. flForceSteering = flSteerScale * m_pCore->get_mass() * pEventSim->i_delta_time;
  840. flForceSteering *= -flSteeringSign;
  841. m_flSteerTime += pEventSim->delta_time;
  842. }
  843. //Msg("steer force=%f\n", flForceSteering);
  844. m_flPrevSteeringAngle = m_SteeringAngle * ( m_bSteeringReversed ? -1.0 : 1.0 );
  845. // Get the sign of the drag forces.
  846. IVP_FLOAT flRotSpeedSign = m_pCore->rot_speed.k[1] < 0.0f ? -1.0f : 1.0f;
  847. // Apply drag proportional to the square of the angular velocity.
  848. 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;
  849. flRotationalDrag *= flRotSpeedSign;
  850. // Apply dampening proportional to angular velocity.
  851. IVP_FLOAT flRotationalDamping = AIRBOAT_ROT_DAMPING * fabs(m_pCore->rot_speed.k[1]) * m_pCore->get_mass() * pEventSim->i_delta_time;
  852. flRotationalDamping *= flRotSpeedSign;
  853. // Calculate the net rotational force.
  854. IVP_FLOAT flForceRotational = flForceSteering + flRotationalDrag + flRotationalDamping;
  855. // Apply it.
  856. IVP_U_Float_Point vecRotImpulse;
  857. vecRotImpulse.set( 0, -1, 0 );
  858. vecRotImpulse.mult( flForceRotational );
  859. m_pCore->rot_push_core_cs( &vecRotImpulse );
  860. }
  861. //-----------------------------------------------------------------------------
  862. // Purpose: Adds extra gravity unless we are performing a strong jump.
  863. //-----------------------------------------------------------------------------
  864. void CPhysics_Airboat::DoSimulationGravity( IVP_Event_Sim *pEventSim )
  865. {
  866. return;
  867. if ( !m_bAirborne || m_bWeakJump )
  868. {
  869. IVP_U_Float_Point vecGravity;
  870. vecGravity.set( 0, AIRBOAT_GRAVITY / 2.0f, 0 );
  871. vecGravity.mult( m_pCore->get_mass() * pEventSim->delta_time );
  872. m_pCore->center_push_core_multiple_ws( &vecGravity );
  873. }
  874. }
  875. //-----------------------------------------------------------------------------
  876. // Purpose: Returns the number of pontoon raycast points that were found to contact
  877. // the ground or water.
  878. //-----------------------------------------------------------------------------
  879. int CPhysics_Airboat::CountSurfaceContactPoints( IVP_Raycast_Airboat_Impact *pImpacts )
  880. {
  881. int nContacts = 0;
  882. int nPontoonPoints = n_wheels;
  883. for ( int iPoint = 0; iPoint < nPontoonPoints; iPoint++ )
  884. {
  885. // Get data at raycast position.
  886. IVP_Raycast_Airboat_Impact *pImpact = &pImpacts[iPoint];
  887. if ( !pImpact )
  888. continue;
  889. if ( pImpact->bImpact )
  890. {
  891. nContacts++;
  892. }
  893. }
  894. return nContacts;
  895. }
  896. //-----------------------------------------------------------------------------
  897. // Purpose: Prevents us from nosing down dramatically during jumps, which
  898. // increases our maximum jump distance.
  899. //-----------------------------------------------------------------------------
  900. void CPhysics_Airboat::DoSimulationKeepUprightPitch( IVP_Raycast_Airboat_Impact *pImpacts, IVP_Event_Sim *pEventSim )
  901. {
  902. // Disable pitch control during weak jumps. This reduces the unreal 'floaty' sensation.
  903. if (m_bWeakJump)
  904. {
  905. return;
  906. }
  907. // Reference vector in core space.
  908. // Pitch back by 10 degrees while airborne.
  909. IVP_U_Float_Point vecUpCS;
  910. vecUpCS.set( 0, -cos(DEG2RAD(10)), sin(DEG2RAD(10)));
  911. // Calculate the goal vector in core space. We will try to align the reference
  912. // vector with the goal vector.
  913. IVP_U_Float_Point vecGoalAxisWS;
  914. vecGoalAxisWS.set( 0, -1, 0 );
  915. const IVP_U_Matrix *matWorldFromCore = m_pCore->get_m_world_f_core_PSI();
  916. IVP_U_Float_Point vecGoalAxisCS;
  917. matWorldFromCore->vimult3( &vecGoalAxisWS, &vecGoalAxisCS );
  918. // Eliminate roll control
  919. vecGoalAxisCS.k[0] = vecUpCS.k[0];
  920. vecGoalAxisCS.normize();
  921. // Get an axis to rotate around.
  922. IVP_U_Float_Point vecRotAxisCS;
  923. vecRotAxisCS.calc_cross_product( &vecUpCS, &vecGoalAxisCS );
  924. // Get the amount that we need to rotate.
  925. // atan2() is well defined, so do a Dot & Cross instead of asin(Cross)
  926. IVP_FLOAT cosine = vecUpCS.dot_product( &vecGoalAxisCS );
  927. IVP_FLOAT sine = vecRotAxisCS.real_length_plus_normize();
  928. IVP_FLOAT angle = atan2( sine, cosine );
  929. //Msg("angle: %.2f, axis: (%.2f %.2f %.2f)\n", RAD2DEG(angle), vecRotAxisCS.k[0], vecRotAxisCS.k[1], vecRotAxisCS.k[2]);
  930. // Don't keep upright if any pontoons are contacting a surface.
  931. if ( CountSurfaceContactPoints( pImpacts ) > 0 )
  932. {
  933. m_flPitchErrorPrev = angle;
  934. return;
  935. }
  936. // Don't do any correction if we're within 15 degrees of the goal orientation.
  937. //if ( fabs( angle ) < DEG2RAD( 15 ) )
  938. //{
  939. // m_flPitchErrorPrev = angle;
  940. // return;
  941. //}
  942. //Msg("CORRECTING\n");
  943. // Generate an angular impulse describing the rotation.
  944. IVP_U_Float_Point vecAngularImpulse;
  945. vecAngularImpulse.set_multiple( &vecRotAxisCS, m_pCore->get_mass() * ( 0.1f * angle + 0.04f * pEventSim->i_delta_time * ( angle - m_flPitchErrorPrev ) ) );
  946. // Save the last error value for calculating the derivative.
  947. m_flPitchErrorPrev = angle;
  948. // Clamp the impulse at a maximum length.
  949. IVP_FLOAT len = vecAngularImpulse.real_length_plus_normize();
  950. if ( len > ( DEG2RAD( 1.5 ) * m_pCore->get_mass() ) )
  951. {
  952. len = DEG2RAD( 1.5 ) * m_pCore->get_mass();
  953. }
  954. vecAngularImpulse.mult( len );
  955. // Apply the rotation.
  956. m_pCore->rot_push_core_cs( &vecAngularImpulse );
  957. #if DRAW_AIRBOAT_KEEP_UPRIGHT_PITCH_VECTORS
  958. CPhysicsEnvironment *pEnv = (CPhysicsEnvironment *)m_pAirboatBody->get_core()->environment->client_data;
  959. IVPhysicsDebugOverlay *debugoverlay = pEnv->GetDebugOverlay();
  960. IVP_U_Float_Point vecPosIVP = m_pCore->get_position_PSI();
  961. Vector vecPosHL;
  962. ConvertPositionToHL(vecPosIVP, vecPosHL);
  963. Vector vecGoalAxisHL;
  964. ConvertDirectionToHL(vecGoalAxisWS, vecGoalAxisHL);
  965. IVP_U_Float_Point vecUpWS;
  966. matWorldFromCore->vmult3( &vecUpCS, &vecUpWS );
  967. Vector vecCurHL;
  968. ConvertDirectionToHL(vecUpWS, vecCurHL);
  969. static IVP_FLOAT flLastLen = 0;
  970. IVP_FLOAT flDebugLen = vecAngularImpulse.real_length();
  971. if ( flLastLen && ( fabs( flDebugLen - flLastLen ) > DEG2RAD( 1 ) * m_pCore->get_mass() ) )
  972. {
  973. debugoverlay->AddLineOverlay(vecPosHL, vecPosHL + Vector(0, 0, 10) * flDebugLen, 255, 0, 255, false, 100.0 );
  974. }
  975. else
  976. {
  977. debugoverlay->AddLineOverlay(vecPosHL, vecPosHL + Vector(0, 0, 10) * flDebugLen, 255, 255, 255, false, 100.0 );
  978. }
  979. debugoverlay->AddLineOverlay(vecPosHL + Vector(0, 0, 10) * flDebugLen, vecPosHL + Vector(0, 0, 10) * flDebugLen + vecGoalAxisHL * 10, 0, 255, 0, false, 100.0 );
  980. debugoverlay->AddLineOverlay(vecPosHL + Vector(0, 0, 10) * flDebugLen, vecPosHL + Vector(0, 0, 10) * flDebugLen + vecCurHL * 10, 255, 0, 0, false, 100.0 );
  981. flLastLen = flDebugLen;
  982. #endif
  983. }
  984. //-----------------------------------------------------------------------------
  985. // Purpose: Roll stabilizer when airborne.
  986. //-----------------------------------------------------------------------------
  987. void CPhysics_Airboat::DoSimulationKeepUprightRoll( IVP_Raycast_Airboat_Impact *pImpacts, IVP_Event_Sim *pEventSim )
  988. {
  989. // Reference vector in core space.
  990. // Pitch back by 10 degrees while airborne.
  991. IVP_U_Float_Point vecUpCS;
  992. vecUpCS.set( 0, -cos(DEG2RAD(10)), sin(DEG2RAD(10)));
  993. // Calculate the goal vector in core space. We will try to align the reference
  994. // vector with the goal vector.
  995. IVP_U_Float_Point vecGoalAxisWS;
  996. vecGoalAxisWS.set( 0, -1, 0 );
  997. const IVP_U_Matrix *matWorldFromCore = m_pCore->get_m_world_f_core_PSI();
  998. IVP_U_Float_Point vecGoalAxisCS;
  999. matWorldFromCore->vimult3( &vecGoalAxisWS, &vecGoalAxisCS );
  1000. // Eliminate pitch control
  1001. vecGoalAxisCS.k[1] = vecUpCS.k[1];
  1002. vecGoalAxisCS.normize();
  1003. // Get an axis to rotate around.
  1004. IVP_U_Float_Point vecRotAxisCS;
  1005. vecRotAxisCS.calc_cross_product( &vecUpCS, &vecGoalAxisCS );
  1006. // Get the amount that we need to rotate.
  1007. // atan2() is well defined, so do a Dot & Cross instead of asin(Cross)
  1008. IVP_FLOAT cosine = vecUpCS.dot_product( &vecGoalAxisCS );
  1009. IVP_FLOAT sine = vecRotAxisCS.real_length_plus_normize();
  1010. IVP_FLOAT angle = atan2( sine, cosine );
  1011. //Msg("angle: %.2f, axis: (%.2f %.2f %.2f)\n", RAD2DEG(angle), vecRotAxisCS.k[0], vecRotAxisCS.k[1], vecRotAxisCS.k[2]);
  1012. // Don't keep upright if any pontoons are contacting a surface.
  1013. if ( CountSurfaceContactPoints( pImpacts ) > 0 )
  1014. {
  1015. m_flRollErrorPrev = angle;
  1016. return;
  1017. }
  1018. // Don't do any correction if we're within 10 degrees of the goal orientation.
  1019. if ( fabs( angle ) < DEG2RAD( 10 ) )
  1020. {
  1021. m_flRollErrorPrev = angle;
  1022. return;
  1023. }
  1024. //Msg("CORRECTING\n");
  1025. // Generate an angular impulse describing the rotation.
  1026. IVP_U_Float_Point vecAngularImpulse;
  1027. vecAngularImpulse.set_multiple( &vecRotAxisCS, m_pCore->get_mass() * ( 0.2f * angle + 0.3f * pEventSim->i_delta_time * ( angle - m_flRollErrorPrev ) ) );
  1028. // Save the last error value for calculating the derivative.
  1029. m_flRollErrorPrev = angle;
  1030. // Clamp the impulse at a maximum length.
  1031. IVP_FLOAT len = vecAngularImpulse.real_length_plus_normize();
  1032. if ( len > ( DEG2RAD( 2 ) * m_pCore->get_mass() ) )
  1033. {
  1034. len = DEG2RAD( 2 ) * m_pCore->get_mass();
  1035. }
  1036. vecAngularImpulse.mult( len );
  1037. m_pCore->rot_push_core_cs( &vecAngularImpulse );
  1038. // Debugging visualization.
  1039. #if DRAW_AIRBOAT_KEEP_UPRIGHT_ROLL_VECTORS
  1040. CPhysicsEnvironment *pEnv = (CPhysicsEnvironment *)m_pAirboatBody->get_core()->environment->client_data;
  1041. IVPhysicsDebugOverlay *debugoverlay = pEnv->GetDebugOverlay();
  1042. IVP_U_Float_Point vecPosIVP = m_pCore->get_position_PSI();
  1043. Vector vecPosHL;
  1044. ConvertPositionToHL(vecPosIVP, vecPosHL);
  1045. Vector vecGoalAxisHL;
  1046. ConvertDirectionToHL(vecGoalAxisWS, vecGoalAxisHL);
  1047. IVP_U_Float_Point vecUpWS;
  1048. matWorldFromCore->vmult3( &vecUpCS, &vecUpWS );
  1049. Vector vecCurHL;
  1050. ConvertDirectionToHL(vecUpWS, vecCurHL);
  1051. static IVP_FLOAT flLastLen = 0;
  1052. IVP_FLOAT flDebugLen = vecAngularImpulse.real_length();
  1053. if ( flLastLen && ( fabs( flDebugLen - flLastLen ) > ( DEG2RAD( 0.25 ) * m_pCore->get_mass() ) )
  1054. {
  1055. debugoverlay->AddLineOverlay(vecPosHL, vecPosHL + Vector(0, 0, 10) * flDebugLen, 255, 0, 255, false, 100.0 );
  1056. }
  1057. else
  1058. {
  1059. debugoverlay->AddLineOverlay(vecPosHL, vecPosHL + Vector(0, 0, 10) * flDebugLen, 255, 255, 255, false, 100.0 );
  1060. }
  1061. debugoverlay->AddLineOverlay(vecPosHL + Vector(0, 0, 10) * flDebugLen, vecPosHL + Vector(0, 0, 10) * flDebugLen + vecGoalAxisHL * 10, 0, 255, 0, false, 100.0 );
  1062. debugoverlay->AddLineOverlay(vecPosHL + Vector(0, 0, 10) * flDebugLen, vecPosHL + Vector(0, 0, 10) * flDebugLen + vecCurHL * 10, 255, 0, 0, false, 100.0 );
  1063. flLastLen = flDebugLen;
  1064. #endif
  1065. }
  1066. //-----------------------------------------------------------------------------
  1067. // Purpose:
  1068. // Input : wheel_nr -
  1069. // s_angle -
  1070. //-----------------------------------------------------------------------------
  1071. void CPhysics_Airboat::do_steering_wheel(IVP_POS_WHEEL wheel_nr, IVP_FLOAT s_angle)
  1072. {
  1073. IVP_Raycast_Airboat_Wheel *wheel = get_wheel(wheel_nr);
  1074. wheel->axis_direction_cs.set_to_zero();
  1075. wheel->axis_direction_cs.k[ index_x ] = 1.0f;
  1076. wheel->axis_direction_cs.rotate( IVP_COORDINATE_INDEX(index_y), s_angle);
  1077. }
  1078. //-----------------------------------------------------------------------------
  1079. // Purpose:
  1080. // Input : pos -
  1081. // spring_constant -
  1082. //-----------------------------------------------------------------------------
  1083. void CPhysics_Airboat::change_spring_constant(IVP_POS_WHEEL pos, IVP_FLOAT spring_constant)
  1084. {
  1085. IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
  1086. wheel->spring_constant = spring_constant;
  1087. }
  1088. //-----------------------------------------------------------------------------
  1089. // Purpose:
  1090. // Input : pos -
  1091. // spring_dampening -
  1092. //-----------------------------------------------------------------------------
  1093. void CPhysics_Airboat::change_spring_dampening(IVP_POS_WHEEL pos, IVP_FLOAT spring_dampening)
  1094. {
  1095. IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
  1096. wheel->spring_damp_relax = spring_dampening;
  1097. }
  1098. //-----------------------------------------------------------------------------
  1099. // Purpose:
  1100. // Input : pos -
  1101. // spring_dampening -
  1102. //-----------------------------------------------------------------------------
  1103. void CPhysics_Airboat::change_spring_dampening_compression(IVP_POS_WHEEL pos, IVP_FLOAT spring_dampening)
  1104. {
  1105. IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
  1106. wheel->spring_damp_compress = spring_dampening;
  1107. }
  1108. //-----------------------------------------------------------------------------
  1109. // Purpose:
  1110. // Input : pos -
  1111. // pre_tension_length -
  1112. //-----------------------------------------------------------------------------
  1113. void CPhysics_Airboat::change_spring_pre_tension(IVP_POS_WHEEL pos, IVP_FLOAT pre_tension_length)
  1114. {
  1115. IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
  1116. wheel->spring_len = gravity_y_direction * (wheel->distance_orig_hp_to_hp - pre_tension_length);
  1117. }
  1118. //-----------------------------------------------------------------------------
  1119. // Purpose:
  1120. // Input : pos -
  1121. // spring_length -
  1122. //-----------------------------------------------------------------------------
  1123. void CPhysics_Airboat::change_spring_length(IVP_POS_WHEEL pos, IVP_FLOAT spring_length)
  1124. {
  1125. IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
  1126. wheel->spring_len = spring_length;
  1127. }
  1128. //-----------------------------------------------------------------------------
  1129. // Purpose:
  1130. // Input : pos -
  1131. // torque -
  1132. //-----------------------------------------------------------------------------
  1133. void CPhysics_Airboat::change_wheel_torque(IVP_POS_WHEEL pos, IVP_FLOAT torque)
  1134. {
  1135. IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
  1136. wheel->torque = torque;
  1137. // Wake the physics object if need be!
  1138. m_pAirboatBody->get_environment()->get_controller_manager()->ensure_controller_in_simulation( this );
  1139. }
  1140. IVP_FLOAT CPhysics_Airboat::get_wheel_torque(IVP_POS_WHEEL pos)
  1141. {
  1142. return get_wheel(pos)->torque;
  1143. }
  1144. //-----------------------------------------------------------------------------
  1145. // Purpose:
  1146. // Throttle input is -1 to 1.
  1147. //-----------------------------------------------------------------------------
  1148. void CPhysics_Airboat::update_throttle( IVP_FLOAT flThrottle )
  1149. {
  1150. // Forward
  1151. if ( fabs( flThrottle ) < 0.01f )
  1152. {
  1153. m_flThrust = 0.0f;
  1154. }
  1155. else if ( flThrottle > 0.0f )
  1156. {
  1157. m_flThrust = AIRBOAT_THRUST_MAX * flThrottle;
  1158. }
  1159. else if ( flThrottle < 0.0f )
  1160. {
  1161. m_flThrust = AIRBOAT_THRUST_MAX_REVERSE * flThrottle;
  1162. }
  1163. }
  1164. //-----------------------------------------------------------------------------
  1165. // Purpose:
  1166. // Input : pos -
  1167. // stop_wheel -
  1168. //-----------------------------------------------------------------------------
  1169. void CPhysics_Airboat::fix_wheel(IVP_POS_WHEEL pos, IVP_BOOL stop_wheel)
  1170. {
  1171. IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
  1172. wheel->wheel_is_fixed = stop_wheel;
  1173. }
  1174. //-----------------------------------------------------------------------------
  1175. // Purpose:
  1176. // Input : pos -
  1177. // friction -
  1178. //-----------------------------------------------------------------------------
  1179. void CPhysics_Airboat::change_friction_of_wheel( IVP_POS_WHEEL pos, IVP_FLOAT friction )
  1180. {
  1181. IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
  1182. wheel->friction_of_wheel = friction;
  1183. }
  1184. //-----------------------------------------------------------------------------
  1185. // Purpose:
  1186. // Input : pos -
  1187. // stabi_constant -
  1188. //-----------------------------------------------------------------------------
  1189. void CPhysics_Airboat::change_stabilizer_constant(IVP_POS_AXIS pos, IVP_FLOAT stabi_constant)
  1190. {
  1191. IVP_Raycast_Airboat_Axle *pAxle = get_axle( pos );
  1192. pAxle->stabilizer_constant = stabi_constant;
  1193. }
  1194. //-----------------------------------------------------------------------------
  1195. // Purpose:
  1196. // Input : fast_turn_factor_ -
  1197. //-----------------------------------------------------------------------------
  1198. void CPhysics_Airboat::change_fast_turn_factor( IVP_FLOAT fast_turn_factor_ )
  1199. {
  1200. //fast_turn_factor = fast_turn_factor_;
  1201. }
  1202. //-----------------------------------------------------------------------------
  1203. // Purpose:
  1204. // Input : force -
  1205. //-----------------------------------------------------------------------------
  1206. void CPhysics_Airboat::change_body_downforce(IVP_FLOAT force)
  1207. {
  1208. down_force = force;
  1209. }
  1210. //-----------------------------------------------------------------------------
  1211. // Purpose:
  1212. // Output : IVP_CONTROLLER_PRIORITY
  1213. //-----------------------------------------------------------------------------
  1214. IVP_CONTROLLER_PRIORITY CPhysics_Airboat::get_controller_priority()
  1215. {
  1216. return IVP_CP_CONSTRAINTS_MAX;
  1217. }
  1218. //-----------------------------------------------------------------------------
  1219. // Purpose:
  1220. // Input : steering_angle_in -
  1221. //-----------------------------------------------------------------------------
  1222. void CPhysics_Airboat::do_steering( IVP_FLOAT steering_angle_in, bool bAnalog )
  1223. {
  1224. // Check for a change.
  1225. if ( m_SteeringAngle == steering_angle_in)
  1226. return;
  1227. MEM_ALLOC_CREDIT();
  1228. // Set the new steering angle.
  1229. m_bAnalogSteering = bAnalog;
  1230. m_SteeringAngle = steering_angle_in;
  1231. // Make sure the simulation is awake - we just go input.
  1232. m_pAirboatBody->get_environment()->get_controller_manager()->ensure_controller_in_simulation( this );
  1233. // Steer each wheel.
  1234. for ( int iWheel = 0; iWheel < wheels_per_axis; ++iWheel )
  1235. {
  1236. do_steering_wheel( IVP_POS_WHEEL( iWheel ), m_SteeringAngle );
  1237. }
  1238. }
  1239. //-----------------------------------------------------------------------------
  1240. // Purpose:
  1241. // Input : pos -
  1242. // Output : IVP_DOUBLE
  1243. //-----------------------------------------------------------------------------
  1244. IVP_DOUBLE CPhysics_Airboat::get_wheel_angular_velocity(IVP_POS_WHEEL pos)
  1245. {
  1246. IVP_Raycast_Airboat_Wheel *wheel = get_wheel(pos);
  1247. return wheel->wheel_angular_velocity;
  1248. }
  1249. //-----------------------------------------------------------------------------
  1250. // Purpose:
  1251. // Input : index -
  1252. // Output : IVP_DOUBLE
  1253. //-----------------------------------------------------------------------------
  1254. IVP_DOUBLE CPhysics_Airboat::get_body_speed(IVP_COORDINATE_INDEX index)
  1255. {
  1256. // return (IVP_FLOAT)car_body->get_geom_center_speed();
  1257. IVP_U_Float_Point *vec_ws = &m_pAirboatBody->get_core()->speed;
  1258. // works well as we do not use merged cores
  1259. const IVP_U_Matrix *mat_ws = m_pAirboatBody->get_core()->get_m_world_f_core_PSI();
  1260. IVP_U_Point orientation;
  1261. mat_ws->get_col(index, &orientation);
  1262. return orientation.dot_product(vec_ws);
  1263. };
  1264. //-----------------------------------------------------------------------------
  1265. // Purpose:
  1266. //-----------------------------------------------------------------------------
  1267. IVP_DOUBLE CPhysics_Airboat::get_orig_front_wheel_distance()
  1268. {
  1269. IVP_U_Float_Point *left_wheel_cs = &this->get_wheel(IVP_FRONT_LEFT)->hp_cs;
  1270. IVP_U_Float_Point *right_wheel_cs = &this->get_wheel(IVP_FRONT_RIGHT)->hp_cs;
  1271. IVP_DOUBLE dist = left_wheel_cs->k[this->index_x] - right_wheel_cs->k[this->index_x];
  1272. return IVP_Inline_Math::fabsd(dist); // was fabs, which was a sml call
  1273. }
  1274. //-----------------------------------------------------------------------------
  1275. // Purpose:
  1276. //-----------------------------------------------------------------------------
  1277. IVP_DOUBLE CPhysics_Airboat::get_orig_axles_distance()
  1278. {
  1279. IVP_U_Float_Point *front_wheel_cs = &this->get_wheel(IVP_FRONT_LEFT)->hp_cs;
  1280. IVP_U_Float_Point *rear_wheel_cs = &this->get_wheel(IVP_REAR_LEFT)->hp_cs;
  1281. IVP_DOUBLE dist = front_wheel_cs->k[this->index_z] - rear_wheel_cs->k[this->index_z];
  1282. return IVP_Inline_Math::fabsd(dist); // was fabs, which was a sml call
  1283. }
  1284. //-----------------------------------------------------------------------------
  1285. // Purpose:
  1286. // Input : *array_of_skid_info_out -
  1287. //-----------------------------------------------------------------------------
  1288. void CPhysics_Airboat::get_skid_info( IVP_Wheel_Skid_Info *array_of_skid_info_out)
  1289. {
  1290. for ( int w = 0; w < n_wheels; w++)
  1291. {
  1292. IVP_Wheel_Skid_Info &info = array_of_skid_info_out[w];
  1293. //IVP_Constraint_Car_Object *wheel = car_constraint_solver->wheel_objects.element_at(w);
  1294. info.last_contact_position_ws.set_to_zero(); // = wheel->last_contact_position_ws;
  1295. info.last_skid_value = 0.0f; // wheel->last_skid_value;
  1296. info.last_skid_time = 0.0f; //wheel->last_skid_time;
  1297. }
  1298. }
  1299. //-----------------------------------------------------------------------------
  1300. // Purpose:
  1301. //-----------------------------------------------------------------------------
  1302. void CPhysics_Airboat::InitRaycastCarEnvironment( IVP_Environment *pEnvironment,
  1303. const IVP_Template_Car_System *pCarSystemTemplate )
  1304. {
  1305. // Copies of the car system template component indices and handedness.
  1306. index_x = pCarSystemTemplate->index_x;
  1307. index_y = pCarSystemTemplate->index_y;
  1308. index_z = pCarSystemTemplate->index_z;
  1309. is_left_handed = pCarSystemTemplate->is_left_handed;
  1310. IVP_Standard_Gravity_Controller *pGravityController = new IVP_Standard_Gravity_Controller();
  1311. IVP_U_Point vecGravity( 0.0f, AIRBOAT_GRAVITY, 0.0f );
  1312. pGravityController->grav_vec.set( &vecGravity );
  1313. BEGIN_IVP_ALLOCATION();
  1314. m_pAirboatBody->get_core()->add_core_controller( pGravityController );
  1315. // Add this controller to the physics environment and setup the objects gravity.
  1316. pEnvironment->get_controller_manager()->announce_controller_to_environment( this );
  1317. END_IVP_ALLOCATION();
  1318. extra_gravity = pCarSystemTemplate->extra_gravity_force_value;
  1319. // This works because gravity is still int the same direction, just smaller.
  1320. if ( pEnvironment->get_gravity()->k[index_y] > 0 )
  1321. {
  1322. gravity_y_direction = 1.0f;
  1323. }
  1324. else
  1325. {
  1326. gravity_y_direction = -1.0f;
  1327. }
  1328. normized_gravity_ws.set( pEnvironment->get_gravity() );
  1329. normized_gravity_ws.normize();
  1330. }
  1331. //-----------------------------------------------------------------------------
  1332. // Purpose:
  1333. //-----------------------------------------------------------------------------
  1334. void CPhysics_Airboat::InitRaycastCarBody( const IVP_Template_Car_System *pCarSystemTemplate )
  1335. {
  1336. // Car body attributes.
  1337. n_wheels = pCarSystemTemplate->n_wheels;
  1338. n_axis = pCarSystemTemplate->n_axis;
  1339. wheels_per_axis = n_wheels / n_axis;
  1340. // Add the car body "core" to the list of raycast car controller "cores."
  1341. m_pAirboatBody = pCarSystemTemplate->car_body;
  1342. this->vector_of_cores.add( m_pAirboatBody->get_core() );
  1343. // Init extra downward force applied to car.
  1344. down_force_vertical_offset = pCarSystemTemplate->body_down_force_vertical_offset;
  1345. down_force = 0.0f;
  1346. // Initialize.
  1347. for ( int iAxis = 0; iAxis < 3; ++iAxis )
  1348. {
  1349. m_pAirboatBody->get_core()->rot_speed.k[iAxis] = 0.0f;
  1350. m_pAirboatBody->get_core()->speed.k[iAxis] = 0.0f;
  1351. }
  1352. }
  1353. //-----------------------------------------------------------------------------
  1354. // Purpose:
  1355. //-----------------------------------------------------------------------------
  1356. void CPhysics_Airboat::InitRaycastCarWheels( const IVP_Template_Car_System *pCarSystemTemplate )
  1357. {
  1358. IVP_U_Matrix m_core_f_object;
  1359. m_pAirboatBody->calc_m_core_f_object( &m_core_f_object );
  1360. // Initialize the car wheel system.
  1361. for ( int iWheel = 0; iWheel < n_wheels; iWheel++ )
  1362. {
  1363. // Get and clear out memory for the current raycast wheel.
  1364. IVP_Raycast_Airboat_Wheel *pRaycastWheel = get_wheel( IVP_POS_WHEEL( iWheel ) );
  1365. P_MEM_CLEAR( pRaycastWheel );
  1366. // Put the wheel in car space.
  1367. m_core_f_object.vmult4( &pCarSystemTemplate->wheel_pos_Bos[iWheel], &pRaycastWheel->hp_cs );
  1368. m_core_f_object.vmult4( &pCarSystemTemplate->trace_pos_Bos[iWheel], &pRaycastWheel->raycast_start_cs );
  1369. // Add in the raycast start offset.
  1370. pRaycastWheel->raycast_length = AIRBOAT_RAYCAST_DIST;
  1371. pRaycastWheel->raycast_dir_cs.set_to_zero();
  1372. pRaycastWheel->raycast_dir_cs.k[index_y] = gravity_y_direction;
  1373. // Spring (Shocks) data.
  1374. pRaycastWheel->spring_len = -pCarSystemTemplate->spring_pre_tension[iWheel];
  1375. pRaycastWheel->spring_direction_cs.set_to_zero();
  1376. pRaycastWheel->spring_direction_cs.k[index_y] = gravity_y_direction;
  1377. pRaycastWheel->spring_constant = pCarSystemTemplate->spring_constant[iWheel];
  1378. pRaycastWheel->spring_damp_relax = pCarSystemTemplate->spring_dampening[iWheel];
  1379. pRaycastWheel->spring_damp_compress = pCarSystemTemplate->spring_dampening_compression[iWheel];
  1380. // Wheel data.
  1381. pRaycastWheel->friction_of_wheel = 1.0f;//pCarSystemTemplate->friction_of_wheel[iWheel];
  1382. pRaycastWheel->wheel_radius = pCarSystemTemplate->wheel_radius[iWheel];
  1383. pRaycastWheel->inv_wheel_radius = 1.0f / pCarSystemTemplate->wheel_radius[iWheel];
  1384. do_steering_wheel( IVP_POS_WHEEL( iWheel ), 0.0f );
  1385. pRaycastWheel->wheel_is_fixed = IVP_FALSE;
  1386. pRaycastWheel->max_rotation_speed = pCarSystemTemplate->wheel_max_rotation_speed[iWheel>>1];
  1387. pRaycastWheel->wheel_is_fixed = IVP_TRUE;
  1388. }
  1389. }
  1390. //-----------------------------------------------------------------------------
  1391. // Purpose:
  1392. //-----------------------------------------------------------------------------
  1393. void CPhysics_Airboat::InitRaycastCarAxes( const IVP_Template_Car_System *pCarSystemTemplate )
  1394. {
  1395. m_SteeringAngle = -1.0f; // make sure next call is not optimized
  1396. this->do_steering( 0.0f, false ); // make sure next call gets through
  1397. for ( int iAxis = 0; iAxis < n_axis; iAxis++ )
  1398. {
  1399. IVP_Raycast_Airboat_Axle *pAxle = get_axle( IVP_POS_AXIS( iAxis ) );
  1400. pAxle->stabilizer_constant = pCarSystemTemplate->stabilizer_constant[iAxis];
  1401. }
  1402. }
  1403. //-----------------------------------------------------------------------------
  1404. // Purpose: Debug data for use in vphysics and the engine to visualize car data.
  1405. //-----------------------------------------------------------------------------
  1406. void CPhysics_Airboat::SetCarSystemDebugData( const IVP_CarSystemDebugData_t &carSystemDebugData )
  1407. {
  1408. // Wheels (raycast data only!)
  1409. for ( int iWheel = 0; iWheel < IVP_RAYCAST_AIRBOAT_MAX_WHEELS; ++iWheel )
  1410. {
  1411. m_CarSystemDebugData.wheelRaycasts[iWheel][0] = carSystemDebugData.wheelRaycasts[iWheel][0];
  1412. m_CarSystemDebugData.wheelRaycasts[iWheel][1] = carSystemDebugData.wheelRaycasts[iWheel][1];
  1413. m_CarSystemDebugData.wheelRaycastImpacts[iWheel] = carSystemDebugData.wheelRaycastImpacts[iWheel];
  1414. }
  1415. }
  1416. //-----------------------------------------------------------------------------
  1417. // Purpose: Debug data for use in vphysics and the engine to visualize car data.
  1418. //-----------------------------------------------------------------------------
  1419. void CPhysics_Airboat::GetCarSystemDebugData( IVP_CarSystemDebugData_t &carSystemDebugData )
  1420. {
  1421. // Wheels (raycast data only!)
  1422. for ( int iWheel = 0; iWheel < IVP_RAYCAST_AIRBOAT_MAX_WHEELS; ++iWheel )
  1423. {
  1424. carSystemDebugData.wheelRaycasts[iWheel][0] = m_CarSystemDebugData.wheelRaycasts[iWheel][0];
  1425. carSystemDebugData.wheelRaycasts[iWheel][1] = m_CarSystemDebugData.wheelRaycasts[iWheel][1];
  1426. carSystemDebugData.wheelRaycastImpacts[iWheel] = m_CarSystemDebugData.wheelRaycastImpacts[iWheel];
  1427. }
  1428. }
  1429. //-----------------------------------------------------------------------------
  1430. // Purpose:
  1431. // Output : IVP_U_Vector<IVP_Core>
  1432. //-----------------------------------------------------------------------------
  1433. IVP_U_Vector<IVP_Core> *CPhysics_Airboat::get_associated_controlled_cores( void )
  1434. {
  1435. return &vector_of_cores;
  1436. }
  1437. //-----------------------------------------------------------------------------
  1438. // Purpose:
  1439. // Input : *core -
  1440. //-----------------------------------------------------------------------------
  1441. void CPhysics_Airboat::core_is_going_to_be_deleted_event( IVP_Core *core )
  1442. {
  1443. P_DELETE_THIS(this);
  1444. }
  1445. //-----------------------------------------------------------------------------
  1446. // Purpose:
  1447. // Input : i -
  1448. // Output : IVP_Raycast_Airboat_Axle
  1449. //-----------------------------------------------------------------------------
  1450. IVP_Raycast_Airboat_Axle *CPhysics_Airboat::get_axle( IVP_POS_AXIS i )
  1451. {
  1452. return &m_aAirboatAxles[i];
  1453. }
  1454. //-----------------------------------------------------------------------------
  1455. // Purpose:
  1456. // Input : i -
  1457. // Output : IVP_Raycast_Airboat_Wheel
  1458. //-----------------------------------------------------------------------------
  1459. IVP_Raycast_Airboat_Wheel *CPhysics_Airboat::get_wheel( IVP_POS_WHEEL i )
  1460. {
  1461. return &m_aAirboatWheels[i];
  1462. }
  1463. //-----------------------------------------------------------------------------
  1464. // Purpose:
  1465. //-----------------------------------------------------------------------------
  1466. IVP_Controller_Raycast_Airboat_Vector_of_Cores_1::IVP_Controller_Raycast_Airboat_Vector_of_Cores_1():
  1467. IVP_U_Vector<IVP_Core>( &elem_buffer[0],1 )
  1468. {
  1469. }