1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129 |
- /*
- ===========================================================================
- Doom 3 BFG Edition GPL Source Code
- Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
- Doom 3 BFG Edition 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 BFG Edition 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 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 BFG Edition 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 BFG Edition 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 "../idlib/precompiled.h"
- #pragma hdrstop
- #include "Game_local.h"
- /*
- ===============================================================================
- idIK
- ===============================================================================
- */
- /*
- ================
- idIK::idIK
- ================
- */
- idIK::idIK() {
- ik_activate = false;
- initialized = false;
- self = NULL;
- animator = NULL;
- modifiedAnim = 0;
- modelOffset.Zero();
- }
- /*
- ================
- idIK::~idIK
- ================
- */
- idIK::~idIK() {
- }
- /*
- ================
- 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) );
- return;
- }
- 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() 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() {
- }
- /*
- ================
- idIK::ClearJointMods
- ================
- */
- void idIK::ClearJointMods() {
- 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 (TAG_PHYSICS_CLIP) idClipModel( trm );
- }
- initialized = true;
- return true;
- }
- /*
- ================
- idIK_Walk::Evaluate
- ================
- */
- void idIK_Walk::Evaluate() {
- int i, newPivotFoot = -1;
- 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() {
- 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() {
- enabledLegs = ( 1 << numLegs ) - 1;
- oldHeightsValid = false;
- }
- /*
- ================
- idIK_Walk::DisableAll
- ================
- */
- void idIK_Walk::DisableAll() {
- 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() {
- 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() {
- 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;
- }
|