mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-12-11 13:30:59 +00:00
736ec20d4d
Don't include the lazy precompiled.h everywhere, only what's required for the compilation unit. platform.h needs to be included instead to provide all essential defines and types. All includes use the relative path to the neo or the game specific root. Move all idlib related includes from idlib/Lib.h to precompiled.h. precompiled.h still exists for the MFC stuff in tools/. Add some missing header guards.
1130 lines
32 KiB
C++
1130 lines
32 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 GPL Source Code
|
|
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
|
|
|
|
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "sys/platform.h"
|
|
#include "idlib/geometry/JointTransform.h"
|
|
|
|
#include "gamesys/SysCvar.h"
|
|
#include "Mover.h"
|
|
|
|
#include "IK.h"
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idIK
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
idIK::idIK
|
|
================
|
|
*/
|
|
idIK::idIK( void ) {
|
|
ik_activate = false;
|
|
initialized = false;
|
|
self = NULL;
|
|
animator = NULL;
|
|
modifiedAnim = 0;
|
|
modelOffset.Zero();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK::~idIK
|
|
================
|
|
*/
|
|
idIK::~idIK( void ) {
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK::Save
|
|
================
|
|
*/
|
|
void idIK::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteBool( initialized );
|
|
savefile->WriteBool( ik_activate );
|
|
savefile->WriteObject( self );
|
|
savefile->WriteString( animator != NULL && animator->GetAnim( modifiedAnim ) ? animator->GetAnim( modifiedAnim )->Name() : "" );
|
|
savefile->WriteVec3( modelOffset );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK::Restore
|
|
================
|
|
*/
|
|
void idIK::Restore( idRestoreGame *savefile ) {
|
|
idStr anim;
|
|
|
|
savefile->ReadBool( initialized );
|
|
savefile->ReadBool( ik_activate );
|
|
savefile->ReadObject( reinterpret_cast<idClass *&>( self ) );
|
|
savefile->ReadString( anim );
|
|
savefile->ReadVec3( modelOffset );
|
|
|
|
if ( self ) {
|
|
animator = self->GetAnimator();
|
|
if ( animator == NULL || animator->ModelDef() == NULL ) {
|
|
gameLocal.Warning( "idIK::Restore: IK for entity '%s' at (%s) has no model set.",
|
|
self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0) );
|
|
}
|
|
modifiedAnim = animator->GetAnim( anim );
|
|
if ( modifiedAnim == 0 ) {
|
|
gameLocal.Warning( "idIK::Restore: IK for entity '%s' at (%s) has no modified animation.",
|
|
self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0) );
|
|
}
|
|
} else {
|
|
animator = NULL;
|
|
modifiedAnim = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK::IsInitialized
|
|
================
|
|
*/
|
|
bool idIK::IsInitialized( void ) const {
|
|
return initialized && ik_enable.GetBool();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK::Init
|
|
================
|
|
*/
|
|
bool idIK::Init( idEntity *self, const char *anim, const idVec3 &modelOffset ) {
|
|
idRenderModel *model;
|
|
|
|
if ( self == NULL ) {
|
|
return false;
|
|
}
|
|
|
|
this->self = self;
|
|
|
|
animator = self->GetAnimator();
|
|
if ( animator == NULL || animator->ModelDef() == NULL ) {
|
|
gameLocal.Warning( "idIK::Init: IK for entity '%s' at (%s) has no model set.",
|
|
self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0) );
|
|
return false;
|
|
}
|
|
if ( animator->ModelDef()->ModelHandle() == NULL ) {
|
|
gameLocal.Warning( "idIK::Init: IK for entity '%s' at (%s) uses default model.",
|
|
self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0) );
|
|
return false;
|
|
}
|
|
model = animator->ModelHandle();
|
|
if ( model == NULL ) {
|
|
gameLocal.Warning( "idIK::Init: IK for entity '%s' at (%s) has no model set.",
|
|
self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0) );
|
|
return false;
|
|
}
|
|
modifiedAnim = animator->GetAnim( anim );
|
|
if ( modifiedAnim == 0 ) {
|
|
gameLocal.Warning( "idIK::Init: IK for entity '%s' at (%s) has no modified animation.",
|
|
self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0) );
|
|
return false;
|
|
}
|
|
|
|
this->modelOffset = modelOffset;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK::Evaluate
|
|
================
|
|
*/
|
|
void idIK::Evaluate( void ) {
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK::ClearJointMods
|
|
================
|
|
*/
|
|
void idIK::ClearJointMods( void ) {
|
|
ik_activate = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK::SolveTwoBones
|
|
================
|
|
*/
|
|
bool idIK::SolveTwoBones( const idVec3 &startPos, const idVec3 &endPos, const idVec3 &dir, float len0, float len1, idVec3 &jointPos ) {
|
|
float length, lengthSqr, lengthInv, x, y;
|
|
idVec3 vec0, vec1;
|
|
|
|
vec0 = endPos - startPos;
|
|
lengthSqr = vec0.LengthSqr();
|
|
lengthInv = idMath::InvSqrt( lengthSqr );
|
|
length = lengthInv * lengthSqr;
|
|
|
|
// if the start and end position are too far out or too close to each other
|
|
if ( length > len0 + len1 || length < idMath::Fabs( len0 - len1 ) ) {
|
|
jointPos = startPos + 0.5f * vec0;
|
|
return false;
|
|
}
|
|
|
|
vec0 *= lengthInv;
|
|
vec1 = dir - vec0 * dir * vec0;
|
|
vec1.Normalize();
|
|
|
|
x = ( length * length + len0 * len0 - len1 * len1 ) * ( 0.5f * lengthInv );
|
|
y = idMath::Sqrt( len0 * len0 - x * x );
|
|
|
|
jointPos = startPos + x * vec0 + y * vec1;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK::GetBoneAxis
|
|
================
|
|
*/
|
|
float idIK::GetBoneAxis( const idVec3 &startPos, const idVec3 &endPos, const idVec3 &dir, idMat3 &axis ) {
|
|
float length;
|
|
axis[0] = endPos - startPos;
|
|
length = axis[0].Normalize();
|
|
axis[1] = dir - axis[0] * dir * axis[0];
|
|
axis[1].Normalize();
|
|
axis[2].Cross( axis[1], axis[0] );
|
|
return length;
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idIK_Walk
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
idIK_Walk::idIK_Walk
|
|
================
|
|
*/
|
|
idIK_Walk::idIK_Walk() {
|
|
int i;
|
|
|
|
initialized = false;
|
|
footModel = NULL;
|
|
numLegs = 0;
|
|
enabledLegs = 0;
|
|
for ( i = 0; i < MAX_LEGS; i++ ) {
|
|
footJoints[i] = INVALID_JOINT;
|
|
ankleJoints[i] = INVALID_JOINT;
|
|
kneeJoints[i] = INVALID_JOINT;
|
|
hipJoints[i] = INVALID_JOINT;
|
|
dirJoints[i] = INVALID_JOINT;
|
|
hipForward[i].Zero();
|
|
kneeForward[i].Zero();
|
|
upperLegLength[i] = 0.0f;
|
|
lowerLegLength[i] = 0.0f;
|
|
upperLegToHipJoint[i].Identity();
|
|
lowerLegToKneeJoint[i].Identity();
|
|
oldAnkleHeights[i] = 0.0f;
|
|
}
|
|
waistJoint = INVALID_JOINT;
|
|
|
|
smoothing = 0.75f;
|
|
waistSmoothing = 0.5f;
|
|
footShift = 0.0f;
|
|
waistShift = 0.0f;
|
|
minWaistFloorDist = 0.0f;
|
|
minWaistAnkleDist = 0.0f;
|
|
footUpTrace = 32.0f;
|
|
footDownTrace = 32.0f;
|
|
tiltWaist = false;
|
|
usePivot = false;
|
|
|
|
pivotFoot = -1;
|
|
pivotYaw = 0.0f;
|
|
pivotPos.Zero();
|
|
|
|
oldHeightsValid = false;
|
|
oldWaistHeight = 0.0f;
|
|
waistOffset.Zero();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Walk::~idIK_Walk
|
|
================
|
|
*/
|
|
idIK_Walk::~idIK_Walk() {
|
|
if ( footModel ) {
|
|
delete footModel;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Walk::Save
|
|
================
|
|
*/
|
|
void idIK_Walk::Save( idSaveGame *savefile ) const {
|
|
int i;
|
|
|
|
idIK::Save( savefile );
|
|
|
|
savefile->WriteClipModel( footModel );
|
|
|
|
savefile->WriteInt( numLegs );
|
|
savefile->WriteInt( enabledLegs );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->WriteInt( footJoints[i] );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->WriteInt( ankleJoints[i] );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->WriteInt( kneeJoints[i] );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->WriteInt( hipJoints[i] );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->WriteInt( dirJoints[i] );
|
|
savefile->WriteInt( waistJoint );
|
|
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->WriteVec3( hipForward[i] );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->WriteVec3( kneeForward[i] );
|
|
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->WriteFloat( upperLegLength[i] );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->WriteFloat( lowerLegLength[i] );
|
|
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->WriteMat3( upperLegToHipJoint[i] );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->WriteMat3( lowerLegToKneeJoint[i] );
|
|
|
|
savefile->WriteFloat( smoothing );
|
|
savefile->WriteFloat( waistSmoothing );
|
|
savefile->WriteFloat( footShift );
|
|
savefile->WriteFloat( waistShift );
|
|
savefile->WriteFloat( minWaistFloorDist );
|
|
savefile->WriteFloat( minWaistAnkleDist );
|
|
savefile->WriteFloat( footUpTrace );
|
|
savefile->WriteFloat( footDownTrace );
|
|
savefile->WriteBool( tiltWaist );
|
|
savefile->WriteBool( usePivot );
|
|
|
|
savefile->WriteInt( pivotFoot );
|
|
savefile->WriteFloat( pivotYaw );
|
|
savefile->WriteVec3( pivotPos );
|
|
savefile->WriteBool( oldHeightsValid );
|
|
savefile->WriteFloat( oldWaistHeight );
|
|
for ( i = 0; i < MAX_LEGS; i++ ) {
|
|
savefile->WriteFloat( oldAnkleHeights[i] );
|
|
}
|
|
savefile->WriteVec3( waistOffset );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Walk::Restore
|
|
================
|
|
*/
|
|
void idIK_Walk::Restore( idRestoreGame *savefile ) {
|
|
int i;
|
|
|
|
idIK::Restore( savefile );
|
|
|
|
savefile->ReadClipModel( footModel );
|
|
|
|
savefile->ReadInt( numLegs );
|
|
savefile->ReadInt( enabledLegs );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->ReadInt( (int&)footJoints[i] );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->ReadInt( (int&)ankleJoints[i] );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->ReadInt( (int&)kneeJoints[i] );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->ReadInt( (int&)hipJoints[i] );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->ReadInt( (int&)dirJoints[i] );
|
|
savefile->ReadInt( (int&)waistJoint );
|
|
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->ReadVec3( hipForward[i] );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->ReadVec3( kneeForward[i] );
|
|
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->ReadFloat( upperLegLength[i] );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->ReadFloat( lowerLegLength[i] );
|
|
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->ReadMat3( upperLegToHipJoint[i] );
|
|
for ( i = 0; i < MAX_LEGS; i++ )
|
|
savefile->ReadMat3( lowerLegToKneeJoint[i] );
|
|
|
|
savefile->ReadFloat( smoothing );
|
|
savefile->ReadFloat( waistSmoothing );
|
|
savefile->ReadFloat( footShift );
|
|
savefile->ReadFloat( waistShift );
|
|
savefile->ReadFloat( minWaistFloorDist );
|
|
savefile->ReadFloat( minWaistAnkleDist );
|
|
savefile->ReadFloat( footUpTrace );
|
|
savefile->ReadFloat( footDownTrace );
|
|
savefile->ReadBool( tiltWaist );
|
|
savefile->ReadBool( usePivot );
|
|
|
|
savefile->ReadInt( pivotFoot );
|
|
savefile->ReadFloat( pivotYaw );
|
|
savefile->ReadVec3( pivotPos );
|
|
savefile->ReadBool( oldHeightsValid );
|
|
savefile->ReadFloat( oldWaistHeight );
|
|
for ( i = 0; i < MAX_LEGS; i++ ) {
|
|
savefile->ReadFloat( oldAnkleHeights[i] );
|
|
}
|
|
savefile->ReadVec3( waistOffset );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Walk::Init
|
|
================
|
|
*/
|
|
bool idIK_Walk::Init( idEntity *self, const char *anim, const idVec3 &modelOffset ) {
|
|
int i;
|
|
float footSize;
|
|
idVec3 verts[4];
|
|
idTraceModel trm;
|
|
const char *jointName;
|
|
idVec3 dir, ankleOrigin, kneeOrigin, hipOrigin, dirOrigin;
|
|
idMat3 axis, ankleAxis, kneeAxis, hipAxis;
|
|
|
|
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 )
|
|
};
|
|
|
|
if ( !self ) {
|
|
return false;
|
|
}
|
|
|
|
numLegs = Min( self->spawnArgs.GetInt( "ik_numLegs", "0" ), MAX_LEGS );
|
|
if ( numLegs == 0 ) {
|
|
return true;
|
|
}
|
|
|
|
if ( !idIK::Init( self, anim, modelOffset ) ) {
|
|
return false;
|
|
}
|
|
|
|
int numJoints = animator->NumJoints();
|
|
idJointMat *joints = ( idJointMat * )_alloca16( numJoints * sizeof( joints[0] ) );
|
|
|
|
// create the animation frame used to setup the IK
|
|
gameEdit->ANIM_CreateAnimFrame( animator->ModelHandle(), animator->GetAnim( modifiedAnim )->MD5Anim( 0 ), numJoints, joints, 1, animator->ModelDef()->GetVisualOffset() + modelOffset, animator->RemoveOrigin() );
|
|
|
|
enabledLegs = 0;
|
|
|
|
// get all the joints
|
|
for ( i = 0; i < numLegs; i++ ) {
|
|
|
|
jointName = self->spawnArgs.GetString( va( "ik_foot%d", i+1 ) );
|
|
footJoints[i] = animator->GetJointHandle( jointName );
|
|
if ( footJoints[i] == INVALID_JOINT ) {
|
|
gameLocal.Error( "idIK_Walk::Init: invalid foot joint '%s'", jointName );
|
|
}
|
|
|
|
jointName = self->spawnArgs.GetString( va( "ik_ankle%d", i+1 ) );
|
|
ankleJoints[i] = animator->GetJointHandle( jointName );
|
|
if ( ankleJoints[i] == INVALID_JOINT ) {
|
|
gameLocal.Error( "idIK_Walk::Init: invalid ankle joint '%s'", jointName );
|
|
}
|
|
|
|
jointName = self->spawnArgs.GetString( va( "ik_knee%d", i+1 ) );
|
|
kneeJoints[i] = animator->GetJointHandle( jointName );
|
|
if ( kneeJoints[i] == INVALID_JOINT ) {
|
|
gameLocal.Error( "idIK_Walk::Init: invalid knee joint '%s'\n", jointName );
|
|
}
|
|
|
|
jointName = self->spawnArgs.GetString( va( "ik_hip%d", i+1 ) );
|
|
hipJoints[i] = animator->GetJointHandle( jointName );
|
|
if ( hipJoints[i] == INVALID_JOINT ) {
|
|
gameLocal.Error( "idIK_Walk::Init: invalid hip joint '%s'\n", jointName );
|
|
}
|
|
|
|
jointName = self->spawnArgs.GetString( va( "ik_dir%d", i+1 ) );
|
|
dirJoints[i] = animator->GetJointHandle( jointName );
|
|
|
|
enabledLegs |= 1 << i;
|
|
}
|
|
|
|
jointName = self->spawnArgs.GetString( "ik_waist" );
|
|
waistJoint = animator->GetJointHandle( jointName );
|
|
if ( waistJoint == INVALID_JOINT ) {
|
|
gameLocal.Error( "idIK_Walk::Init: invalid waist joint '%s'\n", jointName );
|
|
}
|
|
|
|
// get the leg bone lengths and rotation matrices
|
|
for ( i = 0; i < numLegs; i++ ) {
|
|
oldAnkleHeights[i] = 0.0f;
|
|
|
|
ankleAxis = joints[ ankleJoints[ i ] ].ToMat3();
|
|
ankleOrigin = joints[ ankleJoints[ i ] ].ToVec3();
|
|
|
|
kneeAxis = joints[ kneeJoints[ i ] ].ToMat3();
|
|
kneeOrigin = joints[ kneeJoints[ i ] ].ToVec3();
|
|
|
|
hipAxis = joints[ hipJoints[ i ] ].ToMat3();
|
|
hipOrigin = joints[ hipJoints[ i ] ].ToVec3();
|
|
|
|
// get the IK direction
|
|
if ( dirJoints[i] != INVALID_JOINT ) {
|
|
dirOrigin = joints[ dirJoints[ i ] ].ToVec3();
|
|
dir = dirOrigin - kneeOrigin;
|
|
} else {
|
|
dir.Set( 1.0f, 0.0f, 0.0f );
|
|
}
|
|
|
|
hipForward[i] = dir * hipAxis.Transpose();
|
|
kneeForward[i] = dir * kneeAxis.Transpose();
|
|
|
|
// conversion from upper leg bone axis to hip joint axis
|
|
upperLegLength[i] = GetBoneAxis( hipOrigin, kneeOrigin, dir, axis );
|
|
upperLegToHipJoint[i] = hipAxis * axis.Transpose();
|
|
|
|
// conversion from lower leg bone axis to knee joint axis
|
|
lowerLegLength[i] = GetBoneAxis( kneeOrigin, ankleOrigin, dir, axis );
|
|
lowerLegToKneeJoint[i] = kneeAxis * axis.Transpose();
|
|
}
|
|
|
|
smoothing = self->spawnArgs.GetFloat( "ik_smoothing", "0.75" );
|
|
waistSmoothing = self->spawnArgs.GetFloat( "ik_waistSmoothing", "0.75" );
|
|
footShift = self->spawnArgs.GetFloat( "ik_footShift", "0" );
|
|
waistShift = self->spawnArgs.GetFloat( "ik_waistShift", "0" );
|
|
minWaistFloorDist = self->spawnArgs.GetFloat( "ik_minWaistFloorDist", "0" );
|
|
minWaistAnkleDist = self->spawnArgs.GetFloat( "ik_minWaistAnkleDist", "0" );
|
|
footUpTrace = self->spawnArgs.GetFloat( "ik_footUpTrace", "32" );
|
|
footDownTrace = self->spawnArgs.GetFloat( "ik_footDownTrace", "32" );
|
|
tiltWaist = self->spawnArgs.GetBool( "ik_tiltWaist", "0" );
|
|
usePivot = self->spawnArgs.GetBool( "ik_usePivot", "0" );
|
|
|
|
// setup a clip model for the feet
|
|
footSize = self->spawnArgs.GetFloat( "ik_footSize", "4" ) * 0.5f;
|
|
if ( footSize > 0.0f ) {
|
|
for ( i = 0; i < 4; i++ ) {
|
|
verts[i] = footWinding[i] * footSize;
|
|
}
|
|
trm.SetupPolygon( verts, 4 );
|
|
footModel = new idClipModel( trm );
|
|
}
|
|
|
|
initialized = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Walk::Evaluate
|
|
================
|
|
*/
|
|
void idIK_Walk::Evaluate( void ) {
|
|
int i, newPivotFoot = 0;
|
|
float modelHeight, jointHeight, lowestHeight, floorHeights[MAX_LEGS];
|
|
float shift, smallestShift, newHeight, step, newPivotYaw, height, largestAnkleHeight;
|
|
idVec3 modelOrigin, normal, hipDir, kneeDir, start, end, jointOrigins[MAX_LEGS];
|
|
idVec3 footOrigin, ankleOrigin, kneeOrigin, hipOrigin, waistOrigin;
|
|
idMat3 modelAxis, waistAxis, axis;
|
|
idMat3 hipAxis[MAX_LEGS], kneeAxis[MAX_LEGS], ankleAxis[MAX_LEGS];
|
|
trace_t results;
|
|
|
|
if ( !self || !gameLocal.isNewFrame ) {
|
|
return;
|
|
}
|
|
|
|
// if no IK enabled on any legs
|
|
if ( !enabledLegs ) {
|
|
return;
|
|
}
|
|
|
|
normal = - self->GetPhysics()->GetGravityNormal();
|
|
modelOrigin = self->GetPhysics()->GetOrigin();
|
|
modelAxis = self->GetRenderEntity()->axis;
|
|
modelHeight = modelOrigin * normal;
|
|
|
|
modelOrigin += modelOffset * modelAxis;
|
|
|
|
// create frame without joint mods
|
|
animator->CreateFrame( gameLocal.time, false );
|
|
|
|
// get the joint positions for the feet
|
|
lowestHeight = idMath::INFINITY;
|
|
for ( i = 0; i < numLegs; i++ ) {
|
|
animator->GetJointTransform( footJoints[i], gameLocal.time, footOrigin, axis );
|
|
jointOrigins[i] = modelOrigin + footOrigin * modelAxis;
|
|
jointHeight = jointOrigins[i] * normal;
|
|
if ( jointHeight < lowestHeight ) {
|
|
lowestHeight = jointHeight;
|
|
newPivotFoot = i;
|
|
}
|
|
}
|
|
|
|
if ( usePivot ) {
|
|
|
|
newPivotYaw = modelAxis[0].ToYaw();
|
|
|
|
// change pivot foot
|
|
if ( newPivotFoot != pivotFoot || idMath::Fabs( idMath::AngleNormalize180( newPivotYaw - pivotYaw ) ) > 30.0f ) {
|
|
pivotFoot = newPivotFoot;
|
|
pivotYaw = newPivotYaw;
|
|
animator->GetJointTransform( footJoints[pivotFoot], gameLocal.time, footOrigin, axis );
|
|
pivotPos = modelOrigin + footOrigin * modelAxis;
|
|
}
|
|
|
|
// keep pivot foot in place
|
|
jointOrigins[pivotFoot] = pivotPos;
|
|
}
|
|
|
|
// get the floor heights for the feet
|
|
for ( i = 0; i < numLegs; i++ ) {
|
|
|
|
if ( !( enabledLegs & ( 1 << i ) ) ) {
|
|
continue;
|
|
}
|
|
|
|
start = jointOrigins[i] + normal * footUpTrace;
|
|
end = jointOrigins[i] - normal * footDownTrace;
|
|
gameLocal.clip.Translation( results, start, end, footModel, mat3_identity, CONTENTS_SOLID|CONTENTS_IKCLIP, self );
|
|
floorHeights[i] = results.endpos * normal;
|
|
|
|
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 );
|
|
}
|
|
}
|
|
|
|
const idPhysics *phys = self->GetPhysics();
|
|
|
|
// test whether or not the character standing on the ground
|
|
bool onGround = phys->HasGroundContacts();
|
|
|
|
// test whether or not the character is standing on a plat
|
|
bool onPlat = false;
|
|
for ( i = 0; i < phys->GetNumContacts(); i++ ) {
|
|
idEntity *ent = gameLocal.entities[ phys->GetContact( i ).entityNum ];
|
|
if ( ent != NULL && ent->IsType( idPlat::Type ) ) {
|
|
onPlat = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// adjust heights of the ankles
|
|
smallestShift = idMath::INFINITY;
|
|
largestAnkleHeight = -idMath::INFINITY;
|
|
for ( i = 0; i < numLegs; i++ ) {
|
|
|
|
if ( onGround && ( enabledLegs & ( 1 << i ) ) ) {
|
|
shift = floorHeights[i] - modelHeight + footShift;
|
|
} else {
|
|
shift = 0.0f;
|
|
}
|
|
|
|
if ( shift < smallestShift ) {
|
|
smallestShift = shift;
|
|
}
|
|
|
|
animator->GetJointTransform( ankleJoints[i], gameLocal.time, ankleOrigin, ankleAxis[i] );
|
|
jointOrigins[i] = modelOrigin + ankleOrigin * modelAxis;
|
|
|
|
height = jointOrigins[i] * normal;
|
|
|
|
if ( oldHeightsValid && !onPlat ) {
|
|
step = height + shift - oldAnkleHeights[i];
|
|
shift -= smoothing * step;
|
|
}
|
|
|
|
newHeight = height + shift;
|
|
if ( newHeight > largestAnkleHeight ) {
|
|
largestAnkleHeight = newHeight;
|
|
}
|
|
|
|
oldAnkleHeights[i] = newHeight;
|
|
|
|
jointOrigins[i] += shift * normal;
|
|
}
|
|
|
|
animator->GetJointTransform( waistJoint, gameLocal.time, waistOrigin, waistAxis );
|
|
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 ) {
|
|
start = waistOrigin;
|
|
end = waistOrigin + waistOffset - normal * minWaistFloorDist;
|
|
gameLocal.clip.Translation( results, start, end, footModel, modelAxis, CONTENTS_SOLID|CONTENTS_IKCLIP, self );
|
|
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 ) {
|
|
height = ( waistOrigin + waistOffset ) * normal;
|
|
if ( height - largestAnkleHeight < minWaistAnkleDist ) {
|
|
waistOffset += ( minWaistAnkleDist - ( height - largestAnkleHeight ) ) * normal;
|
|
}
|
|
}
|
|
|
|
if ( oldHeightsValid ) {
|
|
// smoothly adjust height of waist
|
|
newHeight = ( waistOrigin + waistOffset ) * normal;
|
|
step = newHeight - oldWaistHeight;
|
|
waistOffset -= waistSmoothing * step * normal;
|
|
}
|
|
|
|
// save height of waist for smoothing
|
|
oldWaistHeight = ( waistOrigin + waistOffset ) * normal;
|
|
|
|
if ( !oldHeightsValid ) {
|
|
oldHeightsValid = true;
|
|
return;
|
|
}
|
|
|
|
// solve IK
|
|
for ( i = 0; i < numLegs; i++ ) {
|
|
|
|
// get the position of the hip in world space
|
|
animator->GetJointTransform( hipJoints[i], gameLocal.time, hipOrigin, axis );
|
|
hipOrigin = modelOrigin + waistOffset + hipOrigin * modelAxis;
|
|
hipDir = hipForward[i] * axis * modelAxis;
|
|
|
|
// get the IK bend direction
|
|
animator->GetJointTransform( kneeJoints[i], gameLocal.time, kneeOrigin, axis );
|
|
kneeDir = kneeForward[i] * axis * modelAxis;
|
|
|
|
// solve IK and calculate knee position
|
|
SolveTwoBones( hipOrigin, jointOrigins[i], kneeDir, upperLegLength[i], lowerLegLength[i], kneeOrigin );
|
|
|
|
if ( ik_debug.GetBool() ) {
|
|
gameRenderWorld->DebugLine( colorCyan, hipOrigin, kneeOrigin );
|
|
gameRenderWorld->DebugLine( colorRed, kneeOrigin, jointOrigins[i] );
|
|
gameRenderWorld->DebugLine( colorYellow, kneeOrigin, kneeOrigin + hipDir );
|
|
gameRenderWorld->DebugLine( colorGreen, kneeOrigin, kneeOrigin + kneeDir );
|
|
}
|
|
|
|
// get the axis for the hip joint
|
|
GetBoneAxis( hipOrigin, kneeOrigin, hipDir, axis );
|
|
hipAxis[i] = upperLegToHipJoint[i] * ( axis * modelAxis.Transpose() );
|
|
|
|
// get the axis for the knee joint
|
|
GetBoneAxis( kneeOrigin, jointOrigins[i], kneeDir, axis );
|
|
kneeAxis[i] = lowerLegToKneeJoint[i] * ( axis * modelAxis.Transpose() );
|
|
}
|
|
|
|
// set the joint mods
|
|
animator->SetJointAxis( waistJoint, JOINTMOD_WORLD_OVERRIDE, waistAxis );
|
|
animator->SetJointPos( waistJoint, JOINTMOD_WORLD_OVERRIDE, ( waistOrigin + waistOffset - modelOrigin ) * modelAxis.Transpose() );
|
|
for ( i = 0; i < numLegs; i++ ) {
|
|
animator->SetJointAxis( hipJoints[i], JOINTMOD_WORLD_OVERRIDE, hipAxis[i] );
|
|
animator->SetJointAxis( kneeJoints[i], JOINTMOD_WORLD_OVERRIDE, kneeAxis[i] );
|
|
animator->SetJointAxis( ankleJoints[i], JOINTMOD_WORLD_OVERRIDE, ankleAxis[i] );
|
|
}
|
|
|
|
ik_activate = true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Walk::ClearJointMods
|
|
================
|
|
*/
|
|
void idIK_Walk::ClearJointMods( void ) {
|
|
int i;
|
|
|
|
if ( !self || !ik_activate ) {
|
|
return;
|
|
}
|
|
|
|
animator->SetJointAxis( waistJoint, JOINTMOD_NONE, mat3_identity );
|
|
animator->SetJointPos( waistJoint, JOINTMOD_NONE, vec3_origin );
|
|
for ( i = 0; i < numLegs; i++ ) {
|
|
animator->SetJointAxis( hipJoints[i], JOINTMOD_NONE, mat3_identity );
|
|
animator->SetJointAxis( kneeJoints[i], JOINTMOD_NONE, mat3_identity );
|
|
animator->SetJointAxis( ankleJoints[i], JOINTMOD_NONE, mat3_identity );
|
|
}
|
|
|
|
ik_activate = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Walk::EnableAll
|
|
================
|
|
*/
|
|
void idIK_Walk::EnableAll( void ) {
|
|
enabledLegs = ( 1 << numLegs ) - 1;
|
|
oldHeightsValid = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Walk::DisableAll
|
|
================
|
|
*/
|
|
void idIK_Walk::DisableAll( void ) {
|
|
enabledLegs = 0;
|
|
oldHeightsValid = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Walk::EnableLeg
|
|
================
|
|
*/
|
|
void idIK_Walk::EnableLeg( int num ) {
|
|
enabledLegs |= 1 << num;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Walk::DisableLeg
|
|
================
|
|
*/
|
|
void idIK_Walk::DisableLeg( int num ) {
|
|
enabledLegs &= ~( 1 << num );
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idIK_Reach
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
idIK_Reach::idIK_Reach
|
|
================
|
|
*/
|
|
idIK_Reach::idIK_Reach() {
|
|
int i;
|
|
|
|
initialized = false;
|
|
numArms = 0;
|
|
enabledArms = 0;
|
|
for ( i = 0; i < MAX_ARMS; i++ ) {
|
|
handJoints[i] = INVALID_JOINT;
|
|
elbowJoints[i] = INVALID_JOINT;
|
|
shoulderJoints[i] = INVALID_JOINT;
|
|
dirJoints[i] = INVALID_JOINT;
|
|
shoulderForward[i].Zero();
|
|
elbowForward[i].Zero();
|
|
upperArmLength[i] = 0.0f;
|
|
lowerArmLength[i] = 0.0f;
|
|
upperArmToShoulderJoint[i].Identity();
|
|
lowerArmToElbowJoint[i].Identity();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Reach::~idIK_Reach
|
|
================
|
|
*/
|
|
idIK_Reach::~idIK_Reach() {
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Reach::Save
|
|
================
|
|
*/
|
|
void idIK_Reach::Save( idSaveGame *savefile ) const {
|
|
int i;
|
|
idIK::Save( savefile );
|
|
|
|
savefile->WriteInt( numArms );
|
|
savefile->WriteInt( enabledArms );
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->WriteInt( handJoints[i] );
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->WriteInt( elbowJoints[i] );
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->WriteInt( shoulderJoints[i] );
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->WriteInt( dirJoints[i] );
|
|
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->WriteVec3( shoulderForward[i] );
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->WriteVec3( elbowForward[i] );
|
|
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->WriteFloat( upperArmLength[i] );
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->WriteFloat( lowerArmLength[i] );
|
|
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->WriteMat3( upperArmToShoulderJoint[i] );
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->WriteMat3( lowerArmToElbowJoint[i] );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Reach::Restore
|
|
================
|
|
*/
|
|
void idIK_Reach::Restore( idRestoreGame *savefile ) {
|
|
int i;
|
|
idIK::Restore( savefile );
|
|
|
|
savefile->ReadInt( numArms );
|
|
savefile->ReadInt( enabledArms );
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->ReadInt( (int&)handJoints[i] );
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->ReadInt( (int&)elbowJoints[i] );
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->ReadInt( (int&)shoulderJoints[i] );
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->ReadInt( (int&)dirJoints[i] );
|
|
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->ReadVec3( shoulderForward[i] );
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->ReadVec3( elbowForward[i] );
|
|
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->ReadFloat( upperArmLength[i] );
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->ReadFloat( lowerArmLength[i] );
|
|
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->ReadMat3( upperArmToShoulderJoint[i] );
|
|
for ( i = 0; i < MAX_ARMS; i++ )
|
|
savefile->ReadMat3( lowerArmToElbowJoint[i] );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Reach::Init
|
|
================
|
|
*/
|
|
bool idIK_Reach::Init( idEntity *self, const char *anim, const idVec3 &modelOffset ) {
|
|
int i;
|
|
const char *jointName;
|
|
idTraceModel trm;
|
|
idVec3 dir, handOrigin, elbowOrigin, shoulderOrigin, dirOrigin;
|
|
idMat3 axis, handAxis, elbowAxis, shoulderAxis;
|
|
|
|
if ( !self ) {
|
|
return false;
|
|
}
|
|
|
|
numArms = Min( self->spawnArgs.GetInt( "ik_numArms", "0" ), MAX_ARMS );
|
|
if ( numArms == 0 ) {
|
|
return true;
|
|
}
|
|
|
|
if ( !idIK::Init( self, anim, modelOffset ) ) {
|
|
return false;
|
|
}
|
|
|
|
int numJoints = animator->NumJoints();
|
|
idJointMat *joints = ( idJointMat * )_alloca16( numJoints * sizeof( joints[0] ) );
|
|
|
|
// create the animation frame used to setup the IK
|
|
gameEdit->ANIM_CreateAnimFrame( animator->ModelHandle(), animator->GetAnim( modifiedAnim )->MD5Anim( 0 ), numJoints, joints, 1, animator->ModelDef()->GetVisualOffset() + modelOffset, animator->RemoveOrigin() );
|
|
|
|
enabledArms = 0;
|
|
|
|
// get all the joints
|
|
for ( i = 0; i < numArms; i++ ) {
|
|
|
|
jointName = self->spawnArgs.GetString( va( "ik_hand%d", i+1 ) );
|
|
handJoints[i] = animator->GetJointHandle( jointName );
|
|
if ( handJoints[i] == INVALID_JOINT ) {
|
|
gameLocal.Error( "idIK_Reach::Init: invalid hand joint '%s'", jointName );
|
|
}
|
|
|
|
jointName = self->spawnArgs.GetString( va( "ik_elbow%d", i+1 ) );
|
|
elbowJoints[i] = animator->GetJointHandle( jointName );
|
|
if ( elbowJoints[i] == INVALID_JOINT ) {
|
|
gameLocal.Error( "idIK_Reach::Init: invalid elbow joint '%s'\n", jointName );
|
|
}
|
|
|
|
jointName = self->spawnArgs.GetString( va( "ik_shoulder%d", i+1 ) );
|
|
shoulderJoints[i] = animator->GetJointHandle( jointName );
|
|
if ( shoulderJoints[i] == INVALID_JOINT ) {
|
|
gameLocal.Error( "idIK_Reach::Init: invalid shoulder joint '%s'\n", jointName );
|
|
}
|
|
|
|
jointName = self->spawnArgs.GetString( va( "ik_elbowDir%d", i+1 ) );
|
|
dirJoints[i] = animator->GetJointHandle( jointName );
|
|
|
|
enabledArms |= 1 << i;
|
|
}
|
|
|
|
// get the arm bone lengths and rotation matrices
|
|
for ( i = 0; i < numArms; i++ ) {
|
|
|
|
handAxis = joints[ handJoints[ i ] ].ToMat3();
|
|
handOrigin = joints[ handJoints[ i ] ].ToVec3();
|
|
|
|
elbowAxis = joints[ elbowJoints[ i ] ].ToMat3();
|
|
elbowOrigin = joints[ elbowJoints[ i ] ].ToVec3();
|
|
|
|
shoulderAxis = joints[ shoulderJoints[ i ] ].ToMat3();
|
|
shoulderOrigin = joints[ shoulderJoints[ i ] ].ToVec3();
|
|
|
|
// get the IK direction
|
|
if ( dirJoints[i] != INVALID_JOINT ) {
|
|
dirOrigin = joints[ dirJoints[ i ] ].ToVec3();
|
|
dir = dirOrigin - elbowOrigin;
|
|
} else {
|
|
dir.Set( -1.0f, 0.0f, 0.0f );
|
|
}
|
|
|
|
shoulderForward[i] = dir * shoulderAxis.Transpose();
|
|
elbowForward[i] = dir * elbowAxis.Transpose();
|
|
|
|
// conversion from upper arm bone axis to should joint axis
|
|
upperArmLength[i] = GetBoneAxis( shoulderOrigin, elbowOrigin, dir, axis );
|
|
upperArmToShoulderJoint[i] = shoulderAxis * axis.Transpose();
|
|
|
|
// conversion from lower arm bone axis to elbow joint axis
|
|
lowerArmLength[i] = GetBoneAxis( elbowOrigin, handOrigin, dir, axis );
|
|
lowerArmToElbowJoint[i] = elbowAxis * axis.Transpose();
|
|
}
|
|
|
|
initialized = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Reach::Evaluate
|
|
================
|
|
*/
|
|
void idIK_Reach::Evaluate( void ) {
|
|
int i;
|
|
idVec3 modelOrigin, shoulderOrigin, elbowOrigin, handOrigin, shoulderDir, elbowDir;
|
|
idMat3 modelAxis, axis;
|
|
idMat3 shoulderAxis[MAX_ARMS], elbowAxis[MAX_ARMS];
|
|
trace_t trace;
|
|
|
|
modelOrigin = self->GetRenderEntity()->origin;
|
|
modelAxis = self->GetRenderEntity()->axis;
|
|
|
|
// solve IK
|
|
for ( i = 0; i < numArms; i++ ) {
|
|
|
|
// get the position of the shoulder in world space
|
|
animator->GetJointTransform( shoulderJoints[i], gameLocal.time, shoulderOrigin, axis );
|
|
shoulderOrigin = modelOrigin + shoulderOrigin * modelAxis;
|
|
shoulderDir = shoulderForward[i] * axis * modelAxis;
|
|
|
|
// get the position of the hand in world space
|
|
animator->GetJointTransform( handJoints[i], gameLocal.time, handOrigin, axis );
|
|
handOrigin = modelOrigin + handOrigin * modelAxis;
|
|
|
|
// get first collision going from shoulder to hand
|
|
gameLocal.clip.TracePoint( trace, shoulderOrigin, handOrigin, CONTENTS_SOLID, self );
|
|
handOrigin = trace.endpos;
|
|
|
|
// get the IK bend direction
|
|
animator->GetJointTransform( elbowJoints[i], gameLocal.time, elbowOrigin, axis );
|
|
elbowDir = elbowForward[i] * axis * modelAxis;
|
|
|
|
// solve IK and calculate elbow position
|
|
SolveTwoBones( shoulderOrigin, handOrigin, elbowDir, upperArmLength[i], lowerArmLength[i], elbowOrigin );
|
|
|
|
if ( ik_debug.GetBool() ) {
|
|
gameRenderWorld->DebugLine( colorCyan, shoulderOrigin, elbowOrigin );
|
|
gameRenderWorld->DebugLine( colorRed, elbowOrigin, handOrigin );
|
|
gameRenderWorld->DebugLine( colorYellow, elbowOrigin, elbowOrigin + elbowDir );
|
|
gameRenderWorld->DebugLine( colorGreen, elbowOrigin, elbowOrigin + shoulderDir );
|
|
}
|
|
|
|
// get the axis for the shoulder joint
|
|
GetBoneAxis( shoulderOrigin, elbowOrigin, shoulderDir, axis );
|
|
shoulderAxis[i] = upperArmToShoulderJoint[i] * ( axis * modelAxis.Transpose() );
|
|
|
|
// get the axis for the elbow joint
|
|
GetBoneAxis( elbowOrigin, handOrigin, elbowDir, axis );
|
|
elbowAxis[i] = lowerArmToElbowJoint[i] * ( axis * modelAxis.Transpose() );
|
|
}
|
|
|
|
for ( i = 0; i < numArms; i++ ) {
|
|
animator->SetJointAxis( shoulderJoints[i], JOINTMOD_WORLD_OVERRIDE, shoulderAxis[i] );
|
|
animator->SetJointAxis( elbowJoints[i], JOINTMOD_WORLD_OVERRIDE, elbowAxis[i] );
|
|
}
|
|
|
|
ik_activate = true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idIK_Reach::ClearJointMods
|
|
================
|
|
*/
|
|
void idIK_Reach::ClearJointMods( void ) {
|
|
int i;
|
|
|
|
if ( !self || !ik_activate ) {
|
|
return;
|
|
}
|
|
|
|
for ( i = 0; i < numArms; i++ ) {
|
|
animator->SetJointAxis( shoulderJoints[i], JOINTMOD_NONE, mat3_identity );
|
|
animator->SetJointAxis( elbowJoints[i], JOINTMOD_NONE, mat3_identity );
|
|
animator->SetJointAxis( handJoints[i], JOINTMOD_NONE, mat3_identity );
|
|
}
|
|
|
|
ik_activate = false;
|
|
}
|