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

1292 lines
35 KiB
C++
Raw 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 "VehicleIK.h"
#include "TransportComponents.h"
#include "../Entity.h"
#include "../anim/Anim.h"
#include "../physics/Physics_RigidBodyMultiple.h"
#include "Transport.h"
#include "../Player.h"
#include "../ContentMask.h"
#include "VehicleSuspension.h"
#include "VehicleWeapon.h"
/*
===============================================================================
sdIK_WheeledVehicle
===============================================================================
*/
CLASS_DECLARATION( idIK, sdIK_WheeledVehicle )
END_CLASS
/*
================
sdIK_WheeledVehicle::sdIK_WheeledVehicle
================
*/
sdIK_WheeledVehicle::sdIK_WheeledVehicle( void ) {
}
/*
================
sdIK_WheeledVehicle::~sdIK_WheeledVehicle
================
*/
sdIK_WheeledVehicle::~sdIK_WheeledVehicle( void ) {
}
/*
================
sdIK_WheeledVehicle::AddWheel
================
*/
void sdIK_WheeledVehicle::AddWheel( sdVehicleRigidBodyWheel& _wheel ) {
wheels.Alloc() = &_wheel;
}
/*
================
sdIK_WheeledVehicle::ClearWheels
================
*/
void sdIK_WheeledVehicle::ClearWheels( void ) {
wheels.Clear();
}
/*
================
sdIK_WheeledVehicle::ClearJointMods
================
*/
void sdIK_WheeledVehicle::ClearJointMods( void ) {
if ( !self ) {
return;
}
vehicleDriveObjectList_t& list = rbParent->GetDriveObjects();
for ( int i = 0; i < list.Num(); i++ ) {
sdVehicleDriveObject* object = list[ i ];
object->ClearSuspensionIK();
}
}
/*
================
sdIK_WheeledVehicle::Evaluate
================
*/
bool sdIK_WheeledVehicle::Evaluate( void ) {
idAnimator* animator = self->GetAnimator();
bool changed = false;
vehicleDriveObjectList_t& list = rbParent->GetDriveObjects();
for ( int i = 0; i < list.Num(); i++ ) {
sdVehicleDriveObject* object = list[ i ];
changed |= object->UpdateSuspensionIK();
}
for ( int i = 0; i < wheels.Num(); i++ ) {
sdVehicleRigidBodyWheel* wheel = wheels[ i ];
if ( !wheel->HasVisualStateChanged() ) {
continue;
}
wheel->ResetVisualState();
const idMat3& frictionAxes = wheel->GetFrictionAxes();
const idMat3& baseAxes = wheel->GetBaseAxes();
idRotation rotation;
rotation.SetVec( wheel->GetRotationAxis() );
rotation.SetAngle( wheel->GetWheelAngle() );
animator->SetJointAxis( wheel->GetWheelJoint(), JOINTMOD_WORLD_OVERRIDE, baseAxes * rotation.ToMat3() * frictionAxes );
changed = true;
}
return changed;
}
/*
================
sdIK_WheeledVehicle::Init
================
*/
bool sdIK_WheeledVehicle::Init( sdTransport_RB* self, const char *anim, const idVec3 &modelOffset ) {
rbParent = self;
if( !idIK::Init( self, anim, modelOffset ) ) {
return false;
}
initialized = true;
return true;
}
/*
===============================================================================
sdIK_Walker
===============================================================================
*/
CLASS_DECLARATION( idIK, sdIK_Walker )
END_CLASS
/*
================
sdIK_Walker::sdIK_Walker
================
*/
sdIK_Walker::sdIK_Walker( void ) {
}
/*
================
sdIK_Walker::~sdIK_Walker
================
*/
sdIK_Walker::~sdIK_Walker( void ) {
gameLocal.clip.DeleteClipModel( footModel );
}
/*
================
sdIK_Walker::Init
================
*/
bool sdIK_Walker::Init( idEntity *self, const char *anim, const idVec3 &modelOffset ) {
float footSize;
idVec3 verts[ 4 ];
idTraceModel trm;
static idVec3 footWinding[ 4 ] = {
idVec3( 1.0f, 1.0f, 0.0f ),
idVec3( -1.0f, 1.0f, 0.0f ),
idVec3( -1.0f, -1.0f, 0.0f ),
idVec3( 1.0f, -1.0f, 0.0f )
};
waistOffset.Zero();
oldWaistHeight = 0;
oldHeightsValid = false;
isStable = true;
assert( self );
const idDict& spawnArgs = self->spawnArgs;
int numLegs = spawnArgs.GetInt( "ik_numLegs" );
if ( numLegs == 0 ) {
return true;
}
if ( !idIK::Init( self, anim, modelOffset ) ) {
return false;
}
float tweakFactor = spawnArgs.GetFloat( "ik_tweakFactor", "0.5" );
compressionInterpolate.Init( MS2SEC( gameLocal.time ), 1.f, tweakFactor, tweakFactor );
// get all the joints
for ( int i = 0; i < numLegs; i++ ) {
leg_t& leg = legs.Alloc();
const char* jointName;
jointName = spawnArgs.GetString( va( "ik_foot%d", i + 1 ) );
leg.footJoint = animator->GetJointHandle( jointName );
if ( leg.footJoint == INVALID_JOINT ) {
gameLocal.Error( "idIK_Walk::Init: invalid foot joint '%s'", jointName );
}
jointName = spawnArgs.GetString( va( "ik_ankle%d", i + 1 ) );
leg.ankleJoint = animator->GetJointHandle( jointName );
if ( leg.ankleJoint == INVALID_JOINT ) {
gameLocal.Error( "idIK_Walk::Init: invalid ankle joint '%s'", jointName );
}
jointName = spawnArgs.GetString( va( "ik_knee%d", i + 1 ) );
leg.kneeJoint = animator->GetJointHandle( jointName );
if ( leg.kneeJoint == INVALID_JOINT ) {
gameLocal.Error( "idIK_Walk::Init: invalid knee joint '%s'", jointName );
}
jointName = spawnArgs.GetString( va( "ik_mid%d", i + 1 ) );
leg.midJoint = animator->GetJointHandle( jointName );
if ( leg.midJoint == INVALID_JOINT ) {
gameLocal.Error( "idIK_Walk::Init: invalid mid joint '%s'", jointName );
}
jointName = spawnArgs.GetString( va( "ik_hip%d", i + 1 ) );
leg.hipJoint = animator->GetJointHandle( jointName );
if ( leg.hipJoint == INVALID_JOINT ) {
gameLocal.Error( "idIK_Walk::Init: invalid hip joint '%s'", jointName );
}
}
const char* jointName = spawnArgs.GetString( "ik_waist" );
waistJoint = animator->GetJointHandle( jointName );
if ( waistJoint == INVALID_JOINT ) {
gameLocal.Error( "idIK_Walk::Init: invalid waist joint '%s'", jointName );
}
// get the leg bone lengths and rotation matrices
for ( int i = 0; i < legs.Num(); i++ ) {
leg_t& leg = legs[ i ];
idVec3 dir, ankleOrigin, kneeOrigin, midOrigin, hipOrigin, footOrigin, dirOrigin;
idVec3 dir2;
idMat3 axis, ankleAxis, kneeAxis, midAxis, hipAxis, footAxis;
leg.oldAnkleHeight = 0.0f;
animator->GetJointTransform( leg.ankleJoint, gameLocal.time, ankleOrigin, ankleAxis );
animator->GetJointTransform( leg.kneeJoint, gameLocal.time, kneeOrigin, kneeAxis );
animator->GetJointTransform( leg.midJoint, gameLocal.time, midOrigin, midAxis );
animator->GetJointTransform( leg.hipJoint, gameLocal.time, hipOrigin, hipAxis );
animator->GetJointTransform( leg.footJoint, gameLocal.time, footOrigin, footAxis );
// get the IK direction
dir = spawnArgs.GetVector( "ik_direction", "1 0 0" );
dir.Normalize();
dir2 = spawnArgs.GetVector( "ik_direction2", "0 1 0" );
dir2.Normalize();
leg.hipForward = dir * hipAxis.Transpose();
leg.midForward = dir * midAxis.Transpose();
leg.kneeForward = dir * kneeAxis.Transpose();
leg.midSide = dir2 * midAxis.Transpose();
// conversion from upper leg bone axis to hip joint axis
leg.upperLegLength = GetBoneAxis( hipOrigin, midOrigin, dir, axis );
leg.upperLegToHipJoint = hipAxis * axis.Transpose();
// conversion from upper leg bone axis to hip joint axis
leg.midLegLength = GetBoneAxis( midOrigin, kneeOrigin, dir2, axis );
leg.midToUpperLegJoint = midAxis * axis.Transpose();
// conversion from lower leg bone axis to knee joint axis
leg.lowerLegLength = GetBoneAxis( kneeOrigin, ankleOrigin, dir, axis );
leg.lowerLegToKneeJoint = kneeAxis * axis.Transpose();
leg.lastAnkleAxes.Identity();
}
smoothing = spawnArgs.GetFloat( "ik_smoothing", "0.75" );
downsmoothing = spawnArgs.GetFloat( "ik_downsmoothing", "0.25" );
waistSmoothing = spawnArgs.GetFloat( "ik_waistSmoothing", "0.75" );
footShift = spawnArgs.GetFloat( "ik_footShift", "0" );
waistShift = spawnArgs.GetFloat( "ik_waistShift", "0" );
minWaistFloorDist = spawnArgs.GetFloat( "ik_minWaistFloorDist", "0" );
minWaistAnkleDist = spawnArgs.GetFloat( "ik_minWaistAnkleDist", "0" );
footUpTrace = spawnArgs.GetFloat( "ik_footUpTrace", "32" );
footDownTrace = spawnArgs.GetFloat( "ik_footDownTrace", "32" );
tiltWaist = spawnArgs.GetBool( "ik_tiltWaist", "0" );
usePivot = spawnArgs.GetBool( "ik_usePivot", "0" );
invertedLegDirOffset= spawnArgs.GetFloat( "ik_invertlegoffset", "-2" ); // Gordon: Hack to tweak the inverted dir for the back section of the legs
// setup a clip model for the feet
footSize = spawnArgs.GetFloat( "ik_footSize", "4" ) * 0.5f;
if ( footSize > 0.0f ) {
for ( int i = 0; i < 4; i++ ) {
verts[ i ] = footWinding[ i ] * footSize;
}
trm.SetupPolygon( verts, 4 );
footModel = new idClipModel( trm, false );
}
initialized = true;
return true;
}
/*
================
sdIK_Walker::SetCompressionScale
================
*/
void sdIK_Walker::SetCompressionScale( float scale, float length ) {
float current = compressionInterpolate.GetCurrentValue( MS2SEC( gameLocal.time ) );
compressionInterpolate.Init( MS2SEC( gameLocal.time ), length, current, scale );
}
/*
================
sdIK_Walker::FindSmallest
================
*/
float sdIK_Walker::FindSmallest( float min1, float max1, float min2, float max2, float& sizeMin1, float& sizeMax1, float& sizeMin2, float& sizeMax2 ) {
if ( max1 < min2 ) {
sizeMin1 = sizeMax1 = max1;
sizeMin2 = sizeMax2 = min2;
return min2 - max1;
}
if ( max2 < min1 ) {
sizeMin2 = sizeMin2 = max2;
sizeMin1 = sizeMax1 = min1;
return min1 - max2;
}
if ( max2 > max1 ) {
sizeMax1 = sizeMax2 = max1;
} else {
sizeMax1 = sizeMax2 = max2;
}
if ( min2 < min1 ) {
sizeMin1 = sizeMin2 = min1;
} else {
sizeMin1 = sizeMin2 = min2;
}
return 0;
}
/*
================
sdIK_Walker::FindSmallest
================
*/
float sdIK_Walker::FindSmallest( float size1, float min2, float max2, float& sizeMin2, float& sizeMax2 ) {
if ( size1 < min2 ) {
sizeMin2 = sizeMax2 = min2;
return min2 - size1;
}
if ( max2 < size1 ) {
sizeMin2 = sizeMin2 = max2;
return size1 - max2;
}
if ( max2 > size1 ) {
sizeMax2 = size1;
} else {
sizeMax2 = max2;
}
if ( min2 < size1 ) {
sizeMin2 = size1;
} else {
sizeMin2 = min2;
}
return 0;
}
/*
================
sdIK_Walker::Evaluate
================
*/
bool sdIK_Walker::Evaluate( void ) {
trace_t results;
// clear joint mods
ClearJointMods();
animator->CreateFrame( gameLocal.time, true );
const renderEntity_t* renderEnt = self->GetRenderEntity();
idVec3 normal = -self->GetPhysics()->GetGravityNormal();
const idVec3& modelOrigin = renderEnt->origin;
const idMat3& modelAxis = renderEnt->axis;
float modelHeight = modelOrigin * normal;
isStable = true;
// get the joint positions for the feet
for ( int i = 0; i < legs.Num(); i++ ) {
leg_t& leg = legs[ i ];
idVec3 footOrigin;
animator->GetJointTransform( leg.footJoint, gameLocal.time, footOrigin );
leg.current.jointWorldOrigin = modelOrigin + footOrigin * modelAxis;
idVec3 start = leg.current.jointWorldOrigin + ( normal * footUpTrace );
idVec3 end = leg.current.jointWorldOrigin - ( normal * footDownTrace );
// gameLocal.clip.Translation( results, start, end, footModel, modelAxis, CONTENTS_SOLID, self );
if ( !gameLocal.clip.TracePoint( CLIP_DEBUG_PARMS results, start, end, MASK_VEHICLESOLID | CONTENTS_MONSTER, self ) ) {
isStable = false;
}
leg.current.floorHeight = results.endpos * normal;
idMat3 newAxes;
if ( results.fraction != 1.f ) {
idVec3 normal = results.c.normal * modelAxis.Transpose();
idVec3 vec3_forward( 1.f, 0.f, 0.f );
newAxes[ 0 ] = vec3_forward - ( normal * ( vec3_forward * normal ) );
newAxes[ 0 ].Normalize();
newAxes[ 2 ] = normal;
newAxes[ 1 ] = newAxes[ 2 ].Cross( newAxes[ 0 ] );
} else {
newAxes.Identity();
}
idQuat newQuat;
newQuat.Slerp( leg.lastAnkleAxes.ToQuat(), newAxes.ToQuat(), 0.1f );
leg.lastAnkleAxes = newQuat.ToMat3();
leg.lastAnkleAxes.FixDenormals();
if ( ik_debug.GetBool() && footModel ) {
idFixedWinding w;
for ( int j = 0; j < footModel->GetTraceModel()->numVerts; j++ ) {
w += footModel->GetTraceModel()->verts[j];
}
gameRenderWorld->DebugWinding( colorRed, w, results.endpos, results.endAxis );
}
}
// adjust heights of the ankles
float smallestShift = idMath::INFINITY;
float largestAnkleHeight = -idMath::INFINITY;
for ( int i = 0; i < legs.Num(); i++ ) {
leg_t& leg = legs[ i ];
idVec3 ankleOrigin;
idVec3 footOrigin;
animator->GetJointTransform( leg.ankleJoint, gameLocal.time, ankleOrigin, leg.current.ankleAxis );
animator->GetJointTransform( leg.footJoint, gameLocal.time, footOrigin );
float shift = leg.current.floorHeight - modelHeight + footShift;
if ( shift < smallestShift ) {
smallestShift = shift;
}
leg.current.jointWorldOrigin = modelOrigin + ankleOrigin * modelAxis;
float height = leg.current.jointWorldOrigin * normal;
if ( oldHeightsValid ) {
float step = height + shift - leg.oldAnkleHeight;
if ( step < 0 ) {
shift -= smoothing * step;
} else {
shift -= downsmoothing * step;
}
}
float newHeight = height + shift;
if ( newHeight > largestAnkleHeight ) {
largestAnkleHeight = newHeight;
}
leg.oldAnkleHeight = newHeight;
leg.current.jointWorldOrigin += shift * normal;
}
idVec3 waistOrigin;
animator->GetJointTransform( waistJoint, gameLocal.time, waistOrigin );
waistOrigin = modelOrigin + waistOrigin * modelAxis;
// adjust position of the waist
waistOffset = ( smallestShift + waistShift ) * normal;
// if the waist should be at least a certain distance above the floor
if ( minWaistFloorDist > 0.0f && waistOffset * normal < 0.0f ) {
idVec3 start = waistOrigin;
idVec3 end = waistOrigin + waistOffset - normal * minWaistFloorDist;
gameLocal.clip.Translation( CLIP_DEBUG_PARMS results, start, end, footModel, modelAxis, CONTENTS_SOLID, self );
float height = ( waistOrigin + waistOffset - results.endpos ) * normal;
if ( height < minWaistFloorDist ) {
waistOffset += ( minWaistFloorDist - height ) * normal;
}
}
// if the waist should be at least a certain distance above the ankles
if ( minWaistAnkleDist > 0.0f ) {
float height = ( waistOrigin + waistOffset ) * normal;
if ( height - largestAnkleHeight < minWaistAnkleDist ) {
waistOffset += ( minWaistAnkleDist - ( height - largestAnkleHeight ) ) * normal;
}
}
if ( oldHeightsValid ) {
// smoothly adjust height of waist
float newHeight = ( waistOrigin + waistOffset ) * normal;
float step = newHeight - oldWaistHeight;
waistOffset -= waistSmoothing * step * normal;
}
// save height of waist for smoothing
oldWaistHeight = ( waistOrigin + waistOffset ) * normal;
if ( !oldHeightsValid ) {
oldHeightsValid = true;
return false;
}
// solve IK
for ( int i = 0; i < legs.Num(); i++ ) {
leg_t& leg = legs[ i ];
idVec3 hipOrigin;
idMat3 axis;
// get the position of the hip in world space
animator->GetJointTransform( leg.hipJoint, gameLocal.time, hipOrigin, axis );
hipOrigin = modelOrigin + waistOffset + hipOrigin * modelAxis;
// DebugAxis( hipOrigin, axis * modelAxis );
idVec3 hipDir = leg.hipForward * axis * modelAxis;
idVec3 midOrigin;
// get the IK bend direction
animator->GetJointTransform( leg.midJoint, gameLocal.time, midOrigin, axis );
midOrigin = modelOrigin + waistOffset + midOrigin * modelAxis;
// DebugAxis( midOrigin, axis * modelAxis );
idVec3 midDir = leg.midForward * axis * modelAxis;
idVec3 midSideDir = leg.midSide * axis * modelAxis;
idVec3 kneeOrigin;
// get the IK bend direction
animator->GetJointTransform( leg.kneeJoint, gameLocal.time, kneeOrigin, axis );
kneeOrigin = modelOrigin + waistOffset + kneeOrigin * modelAxis;
// DebugAxis( kneeOrigin, axis * modelAxis );
idVec3 kneeDir = leg.kneeForward * axis * modelAxis;
idVec3 ankleOrigin;
animator->GetJointTransform( leg.ankleJoint, gameLocal.time, ankleOrigin, axis );
ankleOrigin = modelOrigin + waistOffset + ankleOrigin * modelAxis;
float len1 = leg.upperLegLength;
float minLen2 = fabs( leg.midLegLength - leg.lowerLegLength );
float maxLen2 = leg.midLegLength + leg.lowerLegLength;
float wantLen = ( hipOrigin - leg.current.jointWorldOrigin ).Length();
float constrainedMaxLen2, constrainedMinLen2;
float lenTotal = 0;
if( minLen2 < fabs( wantLen - len1 ) ) {
minLen2 = fabs( wantLen - len1 );
}
float minTotal = FindSmallest( len1, minLen2, maxLen2, constrainedMaxLen2, constrainedMinLen2 );
float maxTotal = len1 + maxLen2;
if ( maxTotal < wantLen ) {
lenTotal = maxTotal;
} else if ( minTotal > wantLen ) {
lenTotal = constrainedMaxLen2;
} else {
float scale = compressionInterpolate.GetCurrentValue( MS2SEC( gameLocal.time ) );
lenTotal = Lerp( minLen2, maxLen2, scale );
}
// solve IK and calculate upper knee position
SolveTwoBones( hipOrigin, leg.current.jointWorldOrigin, midDir, leg.upperLegLength, lenTotal, midOrigin );
float d1 = ( hipOrigin - midOrigin ).Length();
idVec3 kneeDirTest = kneeDir;
kneeDirTest.z += invertedLegDirOffset;
kneeDirTest.NormalizeFast();
// solve IK and calculate lower knee position, using -kneeDir, as lower leg is inverted
SolveTwoBones( midOrigin, leg.current.jointWorldOrigin, -kneeDirTest, leg.midLegLength, leg.lowerLegLength, kneeOrigin );
float d2 = ( midOrigin - kneeOrigin ).Length();
if ( ik_debug.GetBool() ) {
gameRenderWorld->DebugLine( colorCyan, hipOrigin, midOrigin );
gameRenderWorld->DebugLine( colorRed, midOrigin, kneeOrigin );
gameRenderWorld->DebugLine( colorBlue, kneeOrigin, leg.current.jointWorldOrigin );
gameRenderWorld->DebugLine( colorYellow, midOrigin, midOrigin + ( midSideDir * 32 ) );
gameRenderWorld->DebugLine( colorGreen, hipOrigin, hipOrigin + ( hipDir * 32 ) );
gameRenderWorld->DebugLine( colorGreen, midOrigin, midOrigin + ( midDir * 32 ) );
gameRenderWorld->DebugLine( colorGreen, kneeOrigin, kneeOrigin + ( -kneeDirTest * 32 ) );
}
// get the axis for the hip joint
GetBoneAxis( hipOrigin, midOrigin, hipDir, axis );
leg.current.hipAxis = leg.upperLegToHipJoint * ( axis * modelAxis.Transpose() );
// get the axis for the knee joint
GetBoneAxis( midOrigin, kneeOrigin, midSideDir, axis );
leg.current.midAxis = leg.midToUpperLegJoint * ( axis * modelAxis.Transpose() );
// get the axis for the knee joint
GetBoneAxis( kneeOrigin, leg.current.jointWorldOrigin, kneeDir, axis );
leg.current.kneeAxis = leg.lowerLegToKneeJoint * ( axis * modelAxis.Transpose() );
}
// set the joint mods
animator->SetJointPos( waistJoint, JOINTMOD_WORLD_OVERRIDE, ( waistOrigin + waistOffset - modelOrigin ) * modelAxis.Transpose() );
for ( int i = 0; i < legs.Num(); i++ ) {
leg_t& leg = legs[ i ];
animator->SetJointAxis( leg.hipJoint, JOINTMOD_WORLD_OVERRIDE, leg.current.hipAxis );
animator->SetJointAxis( leg.midJoint, JOINTMOD_WORLD_OVERRIDE, leg.current.midAxis );
animator->SetJointAxis( leg.kneeJoint, JOINTMOD_WORLD_OVERRIDE, leg.current.kneeAxis );
animator->SetJointAxis( leg.ankleJoint, JOINTMOD_WORLD_OVERRIDE, leg.current.ankleAxis * leg.lastAnkleAxes );
}
return true;
}
/*
================
sdIK_Walker::ClearJointMods
================
*/
void sdIK_Walker::ClearJointMods( void ) {
animator->SetJointPos( waistJoint, JOINTMOD_NONE, vec3_origin );
for ( int i = 0; i < legs.Num(); i++ ) {
leg_t& leg = legs[ i ];
animator->SetJointAxis( leg.hipJoint, JOINTMOD_NONE, mat3_identity );
animator->SetJointAxis( leg.midJoint, JOINTMOD_NONE, mat3_identity );
animator->SetJointAxis( leg.kneeJoint, JOINTMOD_NONE, mat3_identity );
animator->SetJointAxis( leg.ankleJoint, JOINTMOD_NONE, mat3_identity );
}
}
/*
===============================================================================
sdVehicleIKSystem
===============================================================================
*/
ABSTRACT_DECLARATION( idClass, sdVehicleIKSystem )
END_CLASS
/*
================
sdVehicleIKSystem::Setup
================
*/
bool sdVehicleIKSystem::Setup( sdTransport* _vehicle, const angleClamp_t& yaw, const angleClamp_t& pitch, const idDict& ikParms ) {
vehicle = _vehicle;
clampYaw = yaw;
clampPitch = pitch;
const char* weaponName = ikParms.GetString( "weapon" );
if ( *weaponName ) {
weapon = _vehicle->GetWeapon( weaponName );
if ( !weapon ) {
gameLocal.Warning( "sdVehicleIKSystem::Setup Invalid Weapon '%s'", weaponName );
return false;
}
} else {
weapon = NULL;
}
return true;
}
/*
================
sdVehicleIKSystem::GetPlayer
================
*/
idPlayer* sdVehicleIKSystem::GetPlayer( void ) {
return weapon ? weapon->GetPlayer() : position->GetPlayer();
}
/*
===============================================================================
sdVehicleIKArms
===============================================================================
*/
CLASS_DECLARATION( sdVehicleIKSystem, sdVehicleIKArms )
END_CLASS
/*
================
sdVehicleIKArms::Setup
================
*/
bool sdVehicleIKArms::Setup( sdTransport* _vehicle, const angleClamp_t& yaw, const angleClamp_t& pitch, const idDict& ikParms ) {
for ( int i = 0; i < ARM_JOINT_NUM_JOINTS; i++ ) {
ikJoints[ i ] = INVALID_JOINT;
}
if ( !sdVehicleIKSystem::Setup( _vehicle, yaw, pitch, ikParms ) ) {
return false;
}
yawSound = NULL;
if ( clampYaw.sound != NULL ) {
yawSound = vehicle->GetMotorSounds().Alloc();
yawSound->Start( clampYaw.sound );
}
pitchSound = NULL;
if ( clampPitch.sound != NULL ) {
pitchSound = vehicle->GetMotorSounds().Alloc();
pitchSound->Start( clampPitch.sound );
}
jointAngles.Zero();
idAnimator* animator = vehicle->GetAnimator();
const idDict& dict = ikParms;
const char* joint;
joint = dict.GetString( "jointWrist" );
if ( *joint ) {
ikJoints[ ARM_JOINT_INDEX_WRIST ] = animator->GetJointHandle( joint );
}
joint = dict.GetString( "jointMuzzle" );
if ( *joint ) {
ikJoints[ ARM_JOINT_INDEX_MUZZLE ] = animator->GetJointHandle( joint );
}
joint = dict.GetString( "jointElbow" );
if ( *joint ) {
ikJoints[ ARM_JOINT_INDEX_ELBOW ] = animator->GetJointHandle( joint );
}
ikJoints[ ARM_JOINT_INDEX_SHOULDER ] = animator->GetJointParent( ikJoints[ ARM_JOINT_INDEX_ELBOW ] );
for ( int i = 0; i < ARM_JOINT_NUM_JOINTS; i++ ) {
animator->GetJointTransform( ikJoints[ i ], gameLocal.time, baseJointPositions[ i ], baseJointAxes[ i ] );
}
pitchAxis = dict.GetInt( "pitchAxis", "2" );
requireTophat = dict.GetBool( "require_tophat" );
oldParentAxis = mat3_identity;
return true;
}
/*
================
sdVehicleIKArms::Update
================
*/
void sdVehicleIKArms::Update( void ) {
idEntity* vehicleEnt = vehicle;
if ( !vehicleEnt ) {
return;
}
idVec3 shoulderPos;
idMat3 temp;
idAnimator* animator = vehicleEnt->GetAnimator();
animator->GetJointTransform( ikJoints[ ARM_JOINT_INDEX_SHOULDER ], gameLocal.time, shoulderPos, temp );
idVec3 shoulderToElbow = baseJointPositions[ ARM_JOINT_INDEX_ELBOW ] - baseJointPositions[ ARM_JOINT_INDEX_SHOULDER ];
idVec3 elbowToWrist = baseJointPositions[ ARM_JOINT_INDEX_WRIST ] - baseJointPositions[ ARM_JOINT_INDEX_ELBOW ];
idVec3 wristToMuzzle = baseJointPositions[ ARM_JOINT_INDEX_MUZZLE ] - baseJointPositions[ ARM_JOINT_INDEX_WRIST ];
idVec3 elbowToMuzzle = baseJointPositions[ ARM_JOINT_INDEX_MUZZLE ] - baseJointPositions[ ARM_JOINT_INDEX_ELBOW ];
idMat3 shoulderAxis;
TransposeMultiply( baseJointAxes[ ARM_JOINT_INDEX_SHOULDER ], temp, shoulderAxis );
idMat3 transposedShoulderAxis = shoulderAxis.Transpose();
idPlayer* player = GetPlayer();
if ( player && requireTophat ) {
if ( !gameLocal.usercmds[ player->entityNumber ].buttons.btn.tophat ) {
player = NULL;
}
}
bool changed = false;
changed |= !oldParentAxis.Compare( temp, 0.005f );
idAngles newAngles;
renderView_t* view = player ? player->GetRenderView() : NULL;
renderEntity_t* renderEnt = vehicleEnt->GetRenderEntity();
trace_t trace;
idVec3 modelTarget;
if( view ) {
idVec3 end = view->vieworg + ( 8192 * view->viewaxis[ 0 ] );
gameLocal.clip.TracePoint( CLIP_DEBUG_PARMS trace, view->vieworg, end, CONTENTS_SOLID | CONTENTS_OPAQUE, player );
modelTarget = trace.endpos;
modelTarget -= renderEnt->origin;
modelTarget *= renderEnt->axis.Transpose();
modelTarget -= shoulderPos;
modelTarget *= transposedShoulderAxis;
modelTarget -= shoulderToElbow;
}
if ( view ) {
idVec3 target = modelTarget;
const idVec3& dir = baseJointAxes[ ARM_JOINT_INDEX_MUZZLE ][ 0 ];
target -= elbowToMuzzle - ( ( dir * elbowToMuzzle ) * dir );
target *= baseJointAxes[ ARM_JOINT_INDEX_MUZZLE ].Transpose();
newAngles.yaw = RAD2DEG( atan2( target[ 1 ], target[ 0 ] ) );
} else {
newAngles.yaw = 0;
}
bool yawChanged = !sdVehiclePosition::ClampAngle( newAngles, jointAngles, clampYaw, 1, 0.1f );
if ( yawSound != NULL ) {
yawSound->Update( yawChanged );
}
changed |= yawChanged;
idMat3 yawMat;
idAngles::YawToMat3( newAngles.yaw, yawMat );
if ( view ) {
idVec3 target = modelTarget;
idVec3 newElbowToWrist = elbowToWrist * yawMat;
idVec3 newWristToMuzzle = wristToMuzzle * yawMat;
idMat3 muzzleAxis = baseJointAxes[ ARM_JOINT_INDEX_MUZZLE ] * yawMat;
target -= newElbowToWrist;
target -= newWristToMuzzle - ( ( muzzleAxis[ 0 ] * newWristToMuzzle ) * muzzleAxis[ 0 ] );
target *= muzzleAxis.Transpose();
newAngles.pitch = -RAD2DEG( atan2( target[ 2 ], target[ 0 ] ) );
} else {
newAngles.pitch = 0;
}
bool pitchChanged = !sdVehiclePosition::ClampAngle( newAngles, jointAngles, clampPitch, 0, 0.1f );
if ( pitchSound != NULL ) {
pitchSound->Update( pitchChanged );
}
changed |= pitchChanged;
// configurable pitching axis - to support vertically oriented arms (eg badger, bumblebee)
// as well as horizontally oriented arms (eg goliath)
int truePitchAxis = pitchAxis;
if ( truePitchAxis < 0 ) {
newAngles.pitch = -newAngles.pitch;
truePitchAxis = -truePitchAxis;
}
idAngles pitchAngles( 0.0f, 0.0f, 0.0f );
if ( truePitchAxis == 1 ) { // x-axis
pitchAngles.roll = newAngles.pitch;
} else if ( truePitchAxis == 3 ) { // z-axis
pitchAngles.yaw = newAngles.pitch;
} else { // y-axis
pitchAngles.pitch = newAngles.pitch;
}
idMat3 pitchMat = pitchAngles.ToMat3();
if( changed ) {
oldParentAxis = temp;
jointAngles = newAngles;
animator->SetJointAxis( ikJoints[ ARM_JOINT_INDEX_ELBOW ], JOINTMOD_WORLD_OVERRIDE, baseJointAxes[ ARM_JOINT_INDEX_ELBOW ] * yawMat * shoulderAxis );
animator->SetJointAxis( ikJoints[ ARM_JOINT_INDEX_WRIST ], JOINTMOD_WORLD_OVERRIDE, pitchMat * baseJointAxes[ ARM_JOINT_INDEX_WRIST ] * yawMat * shoulderAxis );
}
}
/*
===============================================================================
sdVehicleSwivel
===============================================================================
*/
CLASS_DECLARATION( sdVehicleIKSystem, sdVehicleSwivel )
END_CLASS
/*
================
sdVehicleSwivel::Setup
================
*/
bool sdVehicleSwivel::Setup( sdTransport* _vehicle, const angleClamp_t& yaw, const angleClamp_t& pitch, const idDict& ikParms ) {
joint = INVALID_JOINT;
if ( !sdVehicleIKSystem::Setup( _vehicle, yaw, pitch, ikParms ) ) {
return false;
}
angles.Zero();
idAnimator* animator = vehicle->GetAnimator();
joint = animator->GetJointHandle( ikParms.GetString( "joint" ) );
animator->GetJointTransform( joint, gameLocal.time, baseAxis );
yawSound = NULL;
if ( clampYaw.sound != NULL ) {
yawSound = vehicle->GetMotorSounds().Alloc();
yawSound->Start( clampYaw.sound );
}
return true;
}
/*
================
sdVehicleSwivel::Update
================
*/
void sdVehicleSwivel::Update( void ) {
idEntity* vehicleEnt = vehicle;
if ( !vehicleEnt ) {
return;
}
idAnimator* animator = vehicleEnt->GetAnimator();
idPlayer* player = GetPlayer();
idAngles newAngles;
if ( player ) {
float diff = idMath::AngleDelta( player->clientViewAngles.yaw, angles.yaw );
newAngles.yaw = angles.yaw + diff * (1.f-clampYaw.filter);//clampYaw.filter * angles.yaw + (1.f - clampYaw.filter) * player->clientViewAngles.yaw;
} else {
newAngles.yaw = 0;
}
bool changed = !sdVehiclePosition::ClampAngle( newAngles, angles, clampYaw, 1, 0.1f );
if ( yawSound != NULL ) {
yawSound->Update( changed );
}
if ( changed ) {
angles = newAngles;
idMat3 yawAxis;
idAngles::YawToMat3( angles.yaw, yawAxis );
animator->SetJointAxis( joint, JOINTMOD_WORLD, yawAxis );
}
}
/*
===============================================================================
sdVehicleJointAimer
===============================================================================
*/
CLASS_DECLARATION( sdVehicleIKSystem, sdVehicleJointAimer )
END_CLASS
/*
================
sdVehicleJointAimer::Setup
================
*/
bool sdVehicleJointAimer::Setup( sdTransport* _vehicle, const angleClamp_t& yaw, const angleClamp_t& pitch, const idDict& ikParms ) {
joint = INVALID_JOINT;
if ( !sdVehicleIKSystem::Setup( _vehicle, yaw, pitch, ikParms ) ) {
return false;
}
yawSound = NULL;
if ( clampYaw.sound != NULL ) {
yawSound = vehicle->GetMotorSounds().Alloc();
yawSound->Start( clampYaw.sound );
}
pitchSound = NULL;
if ( clampPitch.sound != NULL ) {
pitchSound = vehicle->GetMotorSounds().Alloc();
pitchSound->Start( clampPitch.sound );
}
idAnimator* animator = vehicle->GetAnimator();
joint = animator->GetJointHandle( ikParms.GetString( "joint" ) );
if ( joint == INVALID_JOINT ) {
return false;
}
animator->GetJointTransform( joint, gameLocal.time, baseAxis );
angles = baseAxis.ToAngles();
const char* weapon2Name = ikParms.GetString( "weapon2" );
if ( *weapon2Name ) {
weapon2 = _vehicle->GetWeapon( weapon2Name );
if ( !weapon2 ) {
gameLocal.Warning( "sdVehicleIKSystem::Setup Invalid Weapon '%s'", weapon2Name );
return false;
}
} else {
weapon2 = NULL;
}
return true;
}
/*
================
sdVehicleIKSystem::GetPlayer
================
*/
idPlayer* sdVehicleJointAimer::GetPlayer( void ) {
idPlayer* player = weapon ? weapon->GetPlayer() : position->GetPlayer();
if ( !player ) {
return weapon2 ? weapon2->GetPlayer() : position->GetPlayer();
}
return player;
}
/*
================
sdVehicleJointAimer::Update
================
*/
void sdVehicleJointAimer::Update( void ) {
idEntity* vehicleEnt = vehicle;
if ( !vehicleEnt ) {
return;
}
if ( joint == INVALID_JOINT ) {
return;
}
idAnimator* animator = vehicleEnt->GetAnimator();
idPlayer* player = GetPlayer();
idMat3 tempJointAxis;
idVec3 jointPos;
animator->GetJointTransform( joint, gameLocal.time, jointPos, tempJointAxis );
renderView_t* view = player ? player->GetRenderView() : NULL;
renderEntity_t* renderEnt = vehicleEnt->GetRenderEntity();
trace_t trace;
idVec3 modelTarget;
if( view ) {
// find what is being aimed at
idVec3 end = view->vieworg + ( 8192 * view->viewaxis[ 0 ] );
gameLocal.clip.TracePoint( CLIP_DEBUG_PARMS trace, view->vieworg, end, CONTENTS_SOLID | CONTENTS_OPAQUE, player );
modelTarget = trace.endpos;
// transform modelTarget into entity space
modelTarget -= renderEnt->origin;
modelTarget *= renderEnt->axis.Transpose();
// make target relative to joint
modelTarget -= jointPos;
// calculate the vector to the target
idVec3 direction = modelTarget;
direction.Normalize();
idMat3 newAxis = direction.ToMat3();
idAngles newAngles = newAxis.ToAngles();
// clamp the angles
bool yawChanged = !sdVehiclePosition::ClampAngle( newAngles, angles, clampYaw, 1, 0.1f );
if ( yawSound != NULL ) {
yawSound->Update( yawChanged );
}
bool pitchChanged = !sdVehiclePosition::ClampAngle( newAngles, angles, clampPitch, 0, 0.1f );
if ( pitchSound != NULL ) {
pitchSound->Update( pitchChanged );
}
if ( yawChanged || pitchChanged ) {
// set the angles
angles = newAngles;
animator->SetJointAxis( joint, JOINTMOD_WORLD_OVERRIDE, newAngles.ToMat3() );
}
}
}
/*
===============================================================================
sdVehicleIK_Steering
===============================================================================
*/
CLASS_DECLARATION( sdVehicleIKSystem, sdVehicleIK_Steering )
END_CLASS
/*
================
sdVehicleIK_Steering::sdVehicleIK_Steering
================
*/
sdVehicleIK_Steering::sdVehicleIK_Steering( void ) {
}
/*
================
sdVehicleIK_Steering::~sdVehicleIK_Steering
================
*/
sdVehicleIK_Steering::~sdVehicleIK_Steering( void ) {
}
/*
================
sdVehicleIK_Steering::Init
================
*/
bool sdVehicleIK_Steering::Setup( sdTransport* _vehicle, const angleClamp_t& yaw, const angleClamp_t& pitch, const idDict& ikParms ) {
if ( !sdVehicleIKSystem::Setup( _vehicle, yaw, pitch, ikParms ) ) {
return false;
}
if ( !ik.Init( vehicle, ikParms ) ) {
return false;
}
return true;
}
/*
================
sdVehicleIK_Steering::Update
================
*/
void sdVehicleIK_Steering::Update( void ) {
idPlayer* player = GetPlayer();
if ( player == NULL ) {
return;
}
ik.Update( player, vehicle );
}
/*
===============================================================================
sdVehicleWeaponAimer
===============================================================================
*/
CLASS_DECLARATION( sdVehicleIKSystem, sdVehicleWeaponAimer )
END_CLASS
/*
================
sdVehicleWeaponAimer::Update
================
*/
void sdVehicleWeaponAimer::Update( void ) {
idPlayer* player = GetPlayer();
if ( player ) {
const renderView_t& renderView = player->renderView;
trace_t trace;
gameLocal.clip.TracePoint( CLIP_DEBUG_PARMS trace, renderView.vieworg, renderView.vieworg + ( renderView.viewaxis[ 0 ] * 4096 ), CONTENTS_SOLID | CONTENTS_OPAQUE, player );
aimer.SetTarget( trace.endpos );
} else {
aimer.ClearTarget();
}
aimer.Update();
}
/*
================
sdVehicleWeaponAimer::Setup
================
*/
bool sdVehicleWeaponAimer::Setup( sdTransport* _vehicle, const angleClamp_t& yaw, const angleClamp_t& pitch, const idDict& ikParms ) {
if ( !sdVehicleIKSystem::Setup( _vehicle, yaw, pitch, ikParms ) ) {
return false;
}
idAnimator* animator = vehicle->GetAnimator();
jointHandle_t pitchJoint = animator->GetJointHandle( ikParms.GetString( "jointWrist" ) );
jointHandle_t yawJoint = animator->GetJointHandle( ikParms.GetString( "jointElbow" ) );
jointHandle_t muzzleJoint = animator->GetJointHandle( ikParms.GetString( "jointMuzzle" ) );
jointHandle_t shoulderJoint = animator->GetJointHandle( ikParms.GetString( "jointShoulder" ) );
if ( shoulderJoint == INVALID_JOINT ) {
shoulderJoint = animator->GetJointParent( yawJoint );
}
int anim = animator->GetAnim( ikParms.GetString( "deployed_anim" ) );
aimer.Init( ikParms.GetBool( "fix_barrel" ), ikParms.GetBool( "invert_pitch" ), _vehicle, anim, yawJoint, pitchJoint, muzzleJoint, shoulderJoint, clampYaw, clampPitch );
return true;
}