etqw-sdk/source/game/vehicles/VehicleSuspension.cpp

550 lines
16 KiB
C++
Raw Permalink Normal View History

2008-05-29 00:00:00 +00:00
// Copyright (C) 2007 Id Software, Inc.
//
#include "../precompiled.h"
#pragma hdrstop
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#include "VehicleSuspension.h"
#include "Transport.h"
#include "TransportComponents.h"
sdVehicleSuspension::factoryType_t sdVehicleSuspension::suspensionFactory;
// These should be tweaked so stationary vehicles don't cause any reskinning
// These are in degrees and world units
#define SUSPENSION_PIVOT_ANGLE_THRESHOLD 0.5f
#define SUSPENSION_DOUBLEWISHBONE_ANGLE_THRESHOLD 0.5f
#define SUSPENSION_VERTICAL_OFFSET_THRESHOLD 0.25f
/*
===============================================================================
sdVehicleSuspension
===============================================================================
*/
/*
================
sdVehicleSuspension::Startup
================
*/
void sdVehicleSuspension::Startup( void ) {
suspensionFactory.RegisterType( sdVehicleSuspension_Pivot::TypeName(), factoryType_t::Allocator< sdVehicleSuspension_Pivot > );
suspensionFactory.RegisterType( sdVehicleSuspension_DoubleWishbone::TypeName(), factoryType_t::Allocator< sdVehicleSuspension_DoubleWishbone > );
suspensionFactory.RegisterType( sdVehicleSuspension_Vertical::TypeName(), factoryType_t::Allocator< sdVehicleSuspension_Vertical > );
suspensionFactory.RegisterType( sdVehicleSuspension_2JointLeg::TypeName(), factoryType_t::Allocator< sdVehicleSuspension_2JointLeg > );
}
/*
================
sdVehicleView::Shutdown
================
*/
void sdVehicleSuspension::Shutdown( void ) {
suspensionFactory.Shutdown();
}
/*
================
sdVehicleSuspension::GetSuspension
================
*/
sdVehicleSuspension* sdVehicleSuspension::GetSuspension( const char* name ) {
return suspensionFactory.CreateType( name );
}
/*
===============================================================================
sdVehicleSuspension_Pivot
===============================================================================
*/
/*
================
sdVehicleSuspension_Pivot::sdVehicleSuspension_Pivot
================
*/
sdVehicleSuspension_Pivot::sdVehicleSuspension_Pivot( void ) {
_object = NULL;
_suspensionJoint = INVALID_JOINT;
_suspensionRadius = 0;
_suspensionAngle = 0;
_initialOffset = 0;
_lastOffset = 0;
_idealSuspensionAngle = 0;
_oldSuspensionAngle = 0;
}
/*
================
sdVehicleSuspension_Pivot::~sdVehicleSuspension_Pivot
================
*/
sdVehicleSuspension_Pivot::~sdVehicleSuspension_Pivot( void ) {
}
/*
================
sdVehicleSuspension_Pivot::Init
================
*/
bool sdVehicleSuspension_Pivot::Init( sdVehicleSuspensionInterface* object, const idDict& info ) {
idVec3 wheelOrg, suspensionOrg;
_object = object;
sdTransport* transport = _object->GetParent();
idAnimator* animator = transport->GetAnimator();
_suspensionJoint = animator->GetJointHandle( info.GetString( "joint" ) );
if ( _suspensionJoint == INVALID_JOINT ) {
gameLocal.Error( "sdVehicleSuspension_Pivot::Init Invalid Suspension Joint '%s'", info.GetString( "joint" ) );
}
transport->GetJointTransformForAnim( _suspensionJoint, 1, gameLocal.time, suspensionOrg, _suspensionAxis );
transport->GetJointTransformForAnim( _object->GetJoint(), 1, gameLocal.time, wheelOrg );
idVec3 dir = wheelOrg - suspensionOrg;
dir[ 1 ] = 0.f;
_suspensionRadius = dir.Normalize();
float v = -dir[ 2 ];
float h = dir[ 0 ];
_suspensionAngle = RAD2DEG( atan2( v, h ) );
_initialOffset = v * _suspensionRadius;
_lastOffset = -idMath::INFINITY;
_reverse = info.GetBool( "reverse" );
return true;
}
/*
================
sdVehicleSuspension_Pivot::ClearIKJoints
================
*/
void sdVehicleSuspension_Pivot::ClearIKJoints( idAnimator* animator ) {
animator->SetJointAxis( _suspensionJoint, JOINTMOD_NONE, mat3_identity );
}
/*
================
sdVehicleSuspension_Pivot::UpdateIKJoints
================
*/
bool sdVehicleSuspension_Pivot::UpdateIKJoints( idAnimator* animator ) {
float offset = _object->GetOffset();
if ( _lastOffset != -idMath::INFINITY ) {
if ( offset < _lastOffset ) {
offset = Lerp( _lastOffset, offset, 0.5f );
}
}
_lastOffset = offset;
float offsetAngle = RAD2DEG( asin( ( _initialOffset - offset ) / _suspensionRadius ) );
if ( !_reverse ) {
_idealSuspensionAngle = offsetAngle - _suspensionAngle;
} else {
_idealSuspensionAngle = -( offsetAngle - ( 180 - _suspensionAngle ) );
}
if ( fabs( _oldSuspensionAngle - _idealSuspensionAngle ) > SUSPENSION_PIVOT_ANGLE_THRESHOLD ) {
_oldSuspensionAngle = _idealSuspensionAngle;
idMat3 temp;
idAngles::PitchToMat3( _idealSuspensionAngle, temp );
animator->SetJointAxis( _suspensionJoint, JOINTMOD_WORLD_OVERRIDE, _suspensionAxis * temp );
return true;
}
return false;
}
/*
===============================================================================
sdVehicleSuspension_DoubleWishbone
===============================================================================
*/
/*
================
sdVehicleSuspension_DoubleWishbone::sdVehicleSuspension_DoubleWishbone
================
*/
sdVehicleSuspension_DoubleWishbone::sdVehicleSuspension_DoubleWishbone( void ) {
_object = NULL;
_upperSuspensionJoint = INVALID_JOINT;
_lowerSuspensionJoint = INVALID_JOINT;
_suspensionRadius = 0;
_suspensionAngle = 0;
_initialOffset = 0;
_lastOffset = 0;
_currentSuspensionAngle = 0;
_oldSuspensionAngle = 0;
_upperSuspensionAxes.Identity();
_lowerSuspensionAxes.Identity();
}
/*
================
sdVehicleSuspension_DoubleWishbone::~sdVehicleSuspension_DoubleWishbone
================
*/
sdVehicleSuspension_DoubleWishbone::~sdVehicleSuspension_DoubleWishbone( void ) {
}
/*
================
sdVehicleSuspension_DoubleWishbone::Init
================
*/
bool sdVehicleSuspension_DoubleWishbone::Init( sdVehicleSuspensionInterface* object, const idDict& info ) {
idVec3 wheelOrg, suspensionOrg;
idMat3 suspensionAxes;
_object = object;
sdTransport* transport = _object->GetParent();
idAnimator* animator = transport->GetAnimator();
_upperSuspensionJoint = animator->GetJointHandle( info.GetString( "joint_upper" ) );
if ( _upperSuspensionJoint == INVALID_JOINT ) {
gameLocal.Error( "sdVehicleSuspension_DoubleWishbone::Init Invalid Suspension Joint '%s'", info.GetString( "joint_upper" ) );
}
_lowerSuspensionJoint = animator->GetJointHandle( info.GetString( "joint_lower" ) );
if ( _lowerSuspensionJoint == INVALID_JOINT ) {
gameLocal.Error( "sdVehicleSuspension_DoubleWishbone::Init Invalid Suspension Joint '%s'", info.GetString( "joint_lower" ) );
}
_lerpScale = info.GetFloat( "lerp_scale", "0.5" );
transport->GetJointTransformForAnim( _lowerSuspensionJoint, 1, gameLocal.time, _lowerSuspensionAxes );
transport->GetJointTransformForAnim( _upperSuspensionJoint, 1, gameLocal.time, _upperSuspensionAxes );
transport->GetJointTransformForAnim( _upperSuspensionJoint, 1, gameLocal.time, suspensionOrg );
transport->GetJointTransformForAnim( _object->GetJoint(), 1, gameLocal.time, wheelOrg );
_rightWheel = suspensionOrg[ 1 ] < wheelOrg[ 1 ];
idVec3 dir = wheelOrg - suspensionOrg;
dir[ 0 ] = 0.f;
_suspensionRadius = dir.Normalize();
_suspensionAngle = RAD2DEG( atan2( -dir[ 2 ], dir[ 1 ] ) );
_initialOffset = -dir[ 2 ] * _suspensionRadius;
_lastOffset = -idMath::INFINITY;
return true;
}
/*
================
sdVehicleSuspension_DoubleWishbone::ClearIKJoints
================
*/
void sdVehicleSuspension_DoubleWishbone::ClearIKJoints( idAnimator* animator ) {
animator->SetJointAxis( _upperSuspensionJoint, JOINTMOD_NONE, mat3_identity );
animator->SetJointAxis( _lowerSuspensionJoint, JOINTMOD_NONE, mat3_identity );
}
/*
================
sdVehicleSuspension_DoubleWishbone::UpdateIKJoints
================
*/
bool sdVehicleSuspension_DoubleWishbone::UpdateIKJoints( idAnimator* animator ) {
float offset = _object->GetOffset();
if ( _lastOffset != -idMath::INFINITY ) {
if ( offset < _lastOffset ) {
offset = Lerp( _lastOffset, offset, _lerpScale );
}
}
_lastOffset = offset;
float offsetAngle = RAD2DEG( asin( ( _initialOffset - offset ) / _suspensionRadius ) );
if ( _rightWheel ) {
_currentSuspensionAngle = offsetAngle;
} else {
_currentSuspensionAngle = 180 - offsetAngle;
}
_currentSuspensionAngle -= _suspensionAngle;
if ( fabs( _oldSuspensionAngle - _currentSuspensionAngle ) > SUSPENSION_DOUBLEWISHBONE_ANGLE_THRESHOLD ) {
_oldSuspensionAngle = _currentSuspensionAngle;
idMat3 temp;
idAngles::RollToMat3( -_currentSuspensionAngle, temp );
animator->SetJointAxis( _upperSuspensionJoint, JOINTMOD_WORLD_OVERRIDE, _upperSuspensionAxes * temp );
animator->SetJointAxis( _lowerSuspensionJoint, JOINTMOD_WORLD_OVERRIDE, _lowerSuspensionAxes * temp );
return true;
}
return false;
}
/*
===============================================================================
sdVehicleSuspension_Vertical
===============================================================================
*/
/*
================
sdVehicleSuspension_Vertical::sdVehicleSuspension_Vertical
================
*/
sdVehicleSuspension_Vertical::sdVehicleSuspension_Vertical( void ) {
_oldOffset = 0.0f;
_offset = -idMath::INFINITY;
}
/*
================
sdVehicleSuspension_Vertical::~sdVehicleSuspension_Vertical
================
*/
sdVehicleSuspension_Vertical::~sdVehicleSuspension_Vertical( void ) {
}
/*
================
sdVehicleSuspension_Vertical::Init
================
*/
bool sdVehicleSuspension_Vertical::Init( sdVehicleSuspensionInterface* object, const idDict& info ) {
return Init( object, info.GetString( "joint" ) );
}
/*
================
sdVehicleSuspension_Vertical::Init
================
*/
bool sdVehicleSuspension_Vertical::Init( sdVehicleSuspensionInterface* object, const char* jointName ) {
_object = object;
sdTransport* transport = _object->GetParent();
idAnimator* animator = transport->GetAnimator();
_suspensionJoint = animator->GetJointHandle( jointName );
if ( _suspensionJoint == INVALID_JOINT ) {
gameLocal.Error( "sdVehicleSuspension_Vertical::Init Invalid Suspension Joint '%s'", jointName );
}
return true;
}
/*
================
sdVehicleSuspension_Vertical::ClearIKJoints
================
*/
void sdVehicleSuspension_Vertical::ClearIKJoints( idAnimator* animator ) {
animator->SetJointPos( _suspensionJoint, JOINTMOD_NONE, vec3_origin );
}
/*
================
sdVehicleSuspension_Vertical::UpdateIKJoints
================
*/
bool sdVehicleSuspension_Vertical::UpdateIKJoints( idAnimator* animator ) {
float offset = _object->GetOffset();
if ( _offset != -idMath::INFINITY && offset < _offset ) {
_offset = Lerp( _offset, offset, 0.3f );
} else {
_offset = offset;
}
if ( idMath::Fabs( _oldOffset - _offset ) < SUSPENSION_VERTICAL_OFFSET_THRESHOLD ) {
return false;
}
animator->SetJointPos( _suspensionJoint, JOINTMOD_WORLD, idVec3( 0.f, 0.f, _offset ) );
_oldOffset = _offset;
return true;
}
/*
===============================================================================
sdVehicleSuspension_2JointLeg
===============================================================================
*/
/*
================
sdVehicleSuspension_2JointLeg::sdVehicleSuspension_2JointLeg
================
*/
sdVehicleSuspension_2JointLeg::sdVehicleSuspension_2JointLeg( void ) {
_lastOffset = -idMath::INFINITY;
}
/*
================
sdVehicleSuspension_2JointLeg::Init
================
*/
bool sdVehicleSuspension_2JointLeg::Init( sdVehicleSuspensionInterface* object, const idDict& info ) {
const char* jointName;
_object = object;
idAnimator* animator = object->GetParent()->GetAnimator();
jointHandle_t _ankleJoint = object->GetJoint();
if ( _ankleJoint == INVALID_JOINT ) {
gameLocal.Error( "sdVehicleSuspension_2JointLeg::Init: Invalid ankle joint" );
}
jointName = info.GetString( "joint_knee" );
_kneeJoint = animator->GetJointHandle( jointName );
if ( _kneeJoint == INVALID_JOINT ) {
gameLocal.Error( "sdVehicleSuspension_2JointLeg::Init: invalid knee joint '%s'", jointName );
}
jointName = info.GetString( "joint_hip" );
_hipJoint = animator->GetJointHandle( jointName );
if ( _hipJoint == INVALID_JOINT ) {
gameLocal.Error( "sdVehicleSuspension_2JointLeg::Init: invalid hip joint '%s'", jointName );
}
idVec3 ankleOrigin;
idMat3 ankleAxis;
animator->GetJointTransform( _ankleJoint, gameLocal.time, ankleOrigin, ankleAxis );
idVec3 kneeOrigin;
idMat3 kneeAxis;
animator->GetJointTransform( _kneeJoint, gameLocal.time, kneeOrigin, kneeAxis );
idVec3 hipOrigin;
idMat3 hipAxis;
animator->GetJointTransform( _hipJoint, gameLocal.time, hipOrigin, hipAxis );
animator->GetJointTransform( _object->GetJoint(), gameLocal.time, _offset );
idVec3 dir = info.GetVector( "dir_hip", "0 0 1" );
dir.Normalize();
_hipForward = dir * hipAxis.Transpose();
_kneeForward = dir * kneeAxis.Transpose();
idMat3 axis;
// conversion from upper leg bone axis to hip joint axis
_upperLegLength = idIK::GetBoneAxis( hipOrigin, kneeOrigin, dir, axis );
_upperLegToHipJoint = hipAxis * axis.Transpose();
// conversion from lower leg bone axis to knee joint axis
_lowerLegLength = idIK::GetBoneAxis( kneeOrigin, ankleOrigin, dir, axis );
_lowerLegToKneeJoint = kneeAxis * axis.Transpose();
_lerpScale = info.GetFloat( "lerp_scale", "0.3" );
_reverse = info.GetBool( "reverse", "0" );
return true;
}
/*
================
sdVehicleSuspension_2JointLeg::UpdateIKJoints
================
*/
bool sdVehicleSuspension_2JointLeg::UpdateIKJoints( idAnimator* animator ) {
float offset = _object->GetOffset();
if ( _lastOffset != idMath::INFINITY ) {
if ( offset < _lastOffset ) {
offset = Lerp( _lastOffset, offset, _lerpScale );
}
}
if ( idMath::Fabs( _lastOffset - offset ) < 0.1f ) {
return false;
}
_lastOffset = offset;
idMat3 axis;
const idVec3& modelOrigin = _object->GetParent()->GetRenderEntity()->origin;
const idMat3& modelAxis = _object->GetParent()->GetRenderEntity()->axis;
idVec3 newOffset = _offset;
if ( !_reverse ) {
newOffset[ 2 ] -= _lastOffset;
} else {
newOffset[ 2 ] += _lastOffset;
}
idVec3 target = modelOrigin + ( newOffset * modelAxis );
// get the position of the hip in world space
idVec3 hipOrigin;
animator->GetJointTransform( _hipJoint, gameLocal.time, hipOrigin, axis );
hipOrigin = modelOrigin + hipOrigin * modelAxis;
idVec3 hipDir = _hipForward * axis * modelAxis;
// get the IK bend direction
idVec3 kneeOrigin;
animator->GetJointTransform( _kneeJoint, gameLocal.time, kneeOrigin, axis );
idVec3 kneeDir = _kneeForward * axis * modelAxis;
// solve IK and calculate knee position
idIK::SolveTwoBones( hipOrigin, target, kneeDir, _upperLegLength, _lowerLegLength, kneeOrigin );
// get the axis for the hip joint
idIK::GetBoneAxis( hipOrigin, kneeOrigin, hipDir, axis );
idMat3 hipAxis = _upperLegToHipJoint * ( axis * modelAxis.Transpose() );
// get the axis for the knee joint
idIK::GetBoneAxis( kneeOrigin, target, kneeDir, axis );
idMat3 kneeAxis = _lowerLegToKneeJoint * ( axis * modelAxis.Transpose() );
animator->SetJointAxis( _hipJoint, JOINTMOD_WORLD_OVERRIDE, hipAxis );
animator->SetJointAxis( _kneeJoint, JOINTMOD_WORLD_OVERRIDE, kneeAxis );
return true;
}
/*
================
sdVehicleSuspension_2JointLeg::ClearIKJoints
================
*/
void sdVehicleSuspension_2JointLeg::ClearIKJoints( idAnimator* animator ) {
animator->SetJointAxis( _hipJoint, JOINTMOD_NONE, mat3_identity );
animator->SetJointAxis( _kneeJoint, JOINTMOD_NONE, mat3_identity );
}