// 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 "ScriptEntityHelpers.h" #include "../ScriptEntity.h" #include "../IK.h" /* =============================================================================== sdScriptedEntityHelper =============================================================================== */ sdScriptedEntityHelper::factoryType_t sdScriptedEntityHelper::helperFactory; /* ================ sdScriptedEntityHelper::AllocHelper ================ */ sdScriptedEntityHelper* sdScriptedEntityHelper::AllocHelper( const char* type ) { return helperFactory.CreateType( type ); } /* ================ sdScriptedEntityHelper::Startup ================ */ void sdScriptedEntityHelper::Startup( void ) { helperFactory.RegisterType( "legIK", factoryType_t::Allocator< sdScriptedEntityHelper_LegIk > ); helperFactory.RegisterType( "playerIK", factoryType_t::Allocator< sdScriptedEntityHelper_PlayerIK > ); } /* ================ sdScriptedEntityHelper::Shutdown ================ */ void sdScriptedEntityHelper::Shutdown( void ) { helperFactory.Shutdown(); } /* ================ sdScriptedEntityHelper::Init ================ */ void sdScriptedEntityHelper::Init( sdScriptEntity* owner, const sdDeclStringMap* map ) { _owner = owner; _node.AddToEnd( owner->GetHelpers() ); } /* =============================================================================== sdScriptedEntityHelper_LegIk =============================================================================== */ /* ================ sdScriptedEntityHelper_LegIk::Update ================ */ void sdScriptedEntityHelper_LegIk::Update( bool postThink ) { if ( !gameLocal.isNewFrame || postThink ) { return; } if ( gameLocal.time - startTime > lifetime ) { delete this; return; } idMat3 upperJointAxis, middleJointAxis, lowerJointAxis; idVec3 upperJointOrg, middleJointOrg, lowerJointOrg; ClearJointMods(); idAnimator* animator = _owner->GetAnimator(); if ( !animator->GetJointTransform( upperLegJoint, gameLocal.time, upperJointOrg, upperJointAxis ) ) { delete this; return; } if ( !animator->GetJointTransform( middleLegJoint, gameLocal.time, middleJointOrg, middleJointAxis ) ) { delete this; return; } if ( animator->GetJointTransform( lowerLegJoint, gameLocal.time, lowerJointOrg, lowerJointAxis ) ) { delete this; return; } { idVec3 traceOrg = _owner->GetRenderEntity()->origin + ( lowerJointOrg * _owner->GetRenderEntity()->axis ); trace_t tr; gameLocal.clip.TracePoint( CLIP_DEBUG_PARMS tr, traceOrg + idVec3( 0, 0, maxUpTrace ), traceOrg - idVec3( 0, 0, maxDownTrace ), CONTENTS_SOLID, _owner ); /* gameRenderWorld->DebugCircle( colorGreen, tr.endpos, idVec3( 0, 0, 1 ), 8, 16 ); gameRenderWorld->DebugLine( colorRed, tr.endpos, tr.endpos + idVec3( 0.f, 0.f, currentGroundOffset ) ); gameRenderWorld->DebugLine( colorBlue, tr.endpos, tr.endpos + idVec3( 0.f, 0.f, lowerJointOrg.z ) );*/ currentGroundOffset = Lerp( currentGroundOffset, lowerJointOrg.z, blendRate * MS2SEC( gameLocal.msec ) ); idVec3 jointPos = ( ( tr.endpos + idVec3( 0.f, 0.f, currentGroundOffset ) ) - _owner->GetRenderEntity()->origin ) * _owner->GetRenderEntity()->axis.Transpose(); idVec3 endPos; idIK::SolveTwoBones( upperJointOrg, jointPos, -upDir, upperLength, lowerLength, endPos ); idMat3 axis; idIK::GetBoneAxis( upperJointOrg, endPos, upDir, axis ); idMat3 upperAxis = midToUpperJoint * axis; idIK::GetBoneAxis( endPos, jointPos, upDir, axis ); idMat3 middleAxis = lowerToMidJoint * axis; animator->SetJointAxis( upperLegJoint, JOINTMOD_WORLD_OVERRIDE, upperAxis ); animator->SetJointAxis( middleLegJoint, JOINTMOD_WORLD_OVERRIDE, middleAxis ); /* idVec3 worldOrg; _owner->GetWorldOrigin( upperLegJoint, worldOrg ); gameRenderWorld->DebugLine( colorGreen, worldOrg, worldOrg + ( ( side * _owner->GetRenderEntity()->axis ) * 16 ) ); gameRenderWorld->DebugLine( colorBlue, worldOrg, worldOrg + ( ( up * _owner->GetRenderEntity()->axis ) * 16 ) ); gameRenderWorld->DebugLine( colorRed, worldOrg, worldOrg + ( ( temp * _owner->GetRenderEntity()->axis ) * 16 ) );*/ } } /* ================ sdScriptedEntityHelper_LegIk::Init ================ */ void sdScriptedEntityHelper_LegIk::Init( sdScriptEntity* owner, const sdDeclStringMap* map ) { sdScriptedEntityHelper::Init( owner, map ); const idDict& dict = map->GetDict(); idAnimator* animator = _owner->GetAnimator(); upperLegJoint = animator->GetJointHandle( dict.GetString( "joint_upper" ) ); if ( upperLegJoint == INVALID_JOINT ) { gameLocal.Warning( "sdScriptedEntityHelper_LegIk::Init Invalid Upper Leg Joint" ); } middleLegJoint = animator->GetJointHandle( dict.GetString( "joint_middle" ) ); if ( middleLegJoint == INVALID_JOINT ) { gameLocal.Warning( "sdScriptedEntityHelper_LegIk::Init Invalid Middle Leg Joint" ); } lowerLegJoint = animator->GetJointHandle( dict.GetString( "joint_lower" ) ); if ( lowerLegJoint == INVALID_JOINT ) { gameLocal.Warning( "sdScriptedEntityHelper_LegIk::Init Invalid Lower Leg Joint" ); } upDir = dict.GetVector( "direction", "0 0 1" ); groundOffset = dict.GetFloat( "ground_offset" ); blendRate = dict.GetFloat( "blend_rate", "0.95" ); maxUpTrace = dict.GetFloat( "max_up_trace", "64" ); maxDownTrace = dict.GetFloat( "max_down_trace", "64" ); lifetime = dict.GetInt( "lifetime", "5000" ); startTime = gameLocal.time; idMat3 axis; idMat3 upperJointAxis, middleJointAxis, lowerJointAxis; idVec3 upperJointOrg, middleJointOrg, lowerJointOrg; animator->GetJointTransform( upperLegJoint, gameLocal.time, upperJointOrg, upperJointAxis ); animator->GetJointTransform( middleLegJoint, gameLocal.time, middleJointOrg, middleJointAxis ); animator->GetJointTransform( lowerLegJoint, gameLocal.time, lowerJointOrg, lowerJointAxis ); upperLength = idIK::GetBoneAxis( upperJointOrg, middleJointOrg, upDir, axis ); midToUpperJoint = upperJointAxis * axis.Transpose(); lowerLength = idIK::GetBoneAxis( middleJointOrg, lowerJointOrg, upDir, axis ); lowerToMidJoint = middleJointAxis * axis.Transpose(); { idVec3 lowerJointOrg; animator->GetJointTransform( lowerLegJoint, gameLocal.time, lowerJointOrg ); idVec3 traceOrg = _owner->GetRenderEntity()->origin + ( lowerJointOrg * _owner->GetRenderEntity()->axis ); trace_t tr; gameLocal.clip.TracePoint( CLIP_DEBUG_PARMS tr, traceOrg + idVec3( 0, 0, maxUpTrace ), traceOrg - idVec3( 0, 0, maxDownTrace ), CONTENTS_SOLID, _owner ); currentGroundOffset = traceOrg.z - tr.endpos.z; } owner->IncForcedAnimUpdate(); } /* ================ sdScriptedEntityHelper_LegIk::ClearJointMods ================ */ void sdScriptedEntityHelper_LegIk::ClearJointMods( void ) { idAnimator* animator = _owner->GetAnimator(); animator->SetJointAxis( upperLegJoint, JOINTMOD_NONE, mat3_identity ); animator->SetJointAxis( middleLegJoint, JOINTMOD_NONE, mat3_identity ); animator->SetJointAxis( lowerLegJoint, JOINTMOD_NONE, mat3_identity ); } /* ================ sdScriptedEntityHelper_LegIk::~sdScriptedEntityHelper_LegIk ================ */ sdScriptedEntityHelper_LegIk::~sdScriptedEntityHelper_LegIk( void ) { if ( _owner != NULL ) { _owner->DecForcedAnimUpdate(); } } /* =============================================================================== sdScriptedEntityHelper_Aimer =============================================================================== */ /* ================ sdScriptedEntityHelper_Aimer::Update ================ */ void sdScriptedEntityHelper_Aimer::Init( bool fixupBarrel, bool invertPitch, sdScriptEntity* owner, int anim, jointHandle_t yawJoint, jointHandle_t pitchJoint, jointHandle_t barrelJoint, jointHandle_t shoulderJoint, const angleClamp_t& _yawInfo, const angleClamp_t& _pitchInfo ) { _owner = owner; idAnimator* animator = _owner->GetAnimator(); gunJoints[ AIMER_JOINT_YAW ] = yawJoint; gunJoints[ AIMER_JOINT_PITCH ] = pitchJoint; gunJoints[ AIMER_JOINT_BARREL ] = barrelJoint; gunJoints[ AIMER_JOINT_SHOULDER ] = shoulderJoint; yawInfo.current = 0.f; yawInfo.ideal = 0.f; yawInfo.old = 0.f; yawInfo.clamp = _yawInfo; yawInfo.sound = NULL; if ( yawInfo.clamp.sound != NULL ) { yawInfo.sound = owner->GetMotorSounds().Alloc(); yawInfo.sound->Start( yawInfo.clamp.sound ); } pitchInfo.current = 0.f; pitchInfo.ideal = 0.f; pitchInfo.old = 0.f; pitchInfo.clamp = _pitchInfo; pitchInfo.sound = NULL; if ( pitchInfo.clamp.sound != NULL ) { pitchInfo.sound = owner->GetMotorSounds().Alloc(); pitchInfo.sound->Start( pitchInfo.clamp.sound ); } idVec3 baseJointPositions[ AIMER_NUM_JOINTS ]; for ( int i = 0; i < AIMER_NUM_JOINTS; i++ ) { if ( gunJoints[ i ] == INVALID_JOINT ) { baseJointPositions[ i ].Zero(); baseAxes[ i ].Identity(); } else { _owner->GetJointTransformForAnim( gunJoints[ i ], anim, gameLocal.time, baseJointPositions[ i ], baseAxes[ i ] ); } } ikPaths[ AIMER_IK_SHOULDER ] = baseJointPositions[ AIMER_JOINT_SHOULDER ]; ikPaths[ AIMER_IK_BASE ] = baseJointPositions[ AIMER_JOINT_YAW ] - baseJointPositions[ AIMER_JOINT_SHOULDER ]; ikPaths[ AIMER_IK_YAW ] = baseJointPositions[ AIMER_JOINT_PITCH ] - baseJointPositions[ AIMER_JOINT_YAW ]; ikPaths[ AIMER_IK_PITCH ] = baseJointPositions[ AIMER_JOINT_BARREL ] - baseJointPositions[ AIMER_JOINT_PITCH ]; yawTranspose = baseAxes[ AIMER_JOINT_YAW ].Transpose(); pitchTranspose = baseAxes[ AIMER_JOINT_PITCH ].Transpose(); if ( fixupBarrel ) { // Gordon: HACK... _owner->GetAnimator()->SetJointAxis( gunJoints[ AIMER_JOINT_BARREL ], JOINTMOD_LOCAL, baseAxes[ AIMER_JOINT_BARREL ].Transpose() ); animator->GetJointTransform( gunJoints[ AIMER_JOINT_BARREL ], gameLocal.time, baseAxes[ AIMER_JOINT_BARREL ] ); } { idVec3 temp = ( baseJointPositions[ AIMER_JOINT_BARREL ] - baseJointPositions[ AIMER_JOINT_YAW ] ); yawInfo.initialAngle = RAD2DEG( atan2( temp.y, temp.x ) ); temp *= baseAxes[ AIMER_JOINT_BARREL ].Transpose(); temp.z = 0.f; yawInfo.arcLength = temp.Normalize(); yawInfo.offsetAngle = 180 - RAD2DEG( atan2( temp.y, temp.x ) ); } { idVec3 temp = ( baseJointPositions[ AIMER_JOINT_BARREL ] - baseJointPositions[ AIMER_JOINT_PITCH ] ); pitchInfo.initialAngle = RAD2DEG( atan2( temp.z, temp.x ) ); temp *= baseAxes[ AIMER_JOINT_BARREL ].Transpose(); temp.y = 0.f; pitchInfo.arcLength = temp.Normalize(); pitchInfo.offsetAngle = 180 - RAD2DEG( atan2( temp.z, temp.x ) ); if ( invertPitch ) { pitchInfo.offsetAngle = -pitchInfo.offsetAngle; } } } /* ================ sdScriptedEntityHelper_Aimer::Update ================ */ void sdScriptedEntityHelper_Aimer::Update( bool force ) { if ( gunJoints[ AIMER_JOINT_YAW ] == gunJoints[ AIMER_JOINT_PITCH ] ) { bool changed = false; changed |= UpdateAngles( yawInfo, force ); changed |= UpdateAngles( pitchInfo, force ); if ( changed ) { idMat3 yawAxes, pitchAxes; idAngles::YawToMat3( yawInfo.current, yawAxes ); idAngles::PitchToMat3( pitchInfo.current, pitchAxes ); yawAxes *= pitchAxes; yawAxes = baseAxes[ AIMER_JOINT_YAW ] * yawAxes * yawTranspose; _owner->GetAnimator()->SetJointAxis( gunJoints[ AIMER_JOINT_YAW ], JOINTMOD_LOCAL, yawAxes ); } } else { if ( UpdateAngles( yawInfo, force ) ) { idMat3 yawAxes; idAngles::YawToMat3( yawInfo.current, yawAxes ); yawAxes = baseAxes[ AIMER_JOINT_YAW ] * yawAxes * yawTranspose; _owner->GetAnimator()->SetJointAxis( gunJoints[ AIMER_JOINT_YAW ], JOINTMOD_LOCAL, yawAxes ); } if ( UpdateAngles( pitchInfo, force ) ) { idMat3 pitchAxes; idAngles::PitchToMat3( pitchInfo.current, pitchAxes ); pitchAxes = baseAxes[ AIMER_JOINT_PITCH ] * pitchAxes * pitchTranspose; _owner->GetAnimator()->SetJointAxis( gunJoints[ AIMER_JOINT_PITCH ], JOINTMOD_LOCAL, pitchAxes ); } } } /* ================ sdScriptedEntityHelper_Aimer::UpdateAngles ================ */ bool sdScriptedEntityHelper_Aimer::UpdateAngles( angleInfo_t& info, bool force ) { if ( idMath::Fabs( info.current - info.ideal ) > idMath::FLT_EPSILON ) { if ( info.clamp.filter > 0.f && !force ) { float angle = idMath::AngleNormalize180( info.ideal - info.current ); angle *= ( 1.f - info.clamp.filter ); info.ideal = info.current + angle; } if ( info.clamp.flags.limitRate && !force ) { float angle = idMath::AngleNormalize180( info.ideal - info.current ); float frac = idMath::Fabs( angle / 180.f ); float maxTurn = Lerp( info.clamp.rate[ 0 ], info.clamp.rate[ 1 ], frac ) * MS2SEC( gameLocal.msec ); if ( angle < -maxTurn ) { info.current -= maxTurn; } else if ( angle > maxTurn ) { info.current += maxTurn; } else { info.current = info.ideal; } } else { info.current = info.ideal; } } // Gordon: stop really small deltas updating sounds + animation, we need to cache the old value though to stop them accumulating float delta = idMath::Fabs( info.old - info.current ); bool changed = delta > 0.001f; if ( changed ) { info.old = info.current; } else { delta = 0.0f; } float changeVelocity = delta / MS2SEC( gameLocal.msec ); float soundValue = changeVelocity / 30.0f; if ( soundValue > 1.0f ) { soundValue = 1.0f; } if ( info.sound != NULL ) { info.sound->Update( soundValue ); } return changed; } /* ================ sdScriptedEntityHelper_Aimer::ClearTarget ================ */ void sdScriptedEntityHelper_Aimer::ClearTarget( void ) { yawInfo.ideal = 0.f; pitchInfo.ideal = 0.f; } /* ================ sdScriptedEntityHelper_Aimer::LockTarget ================ */ void sdScriptedEntityHelper_Aimer::LockTarget( void ) { Update( true ); } /* ================ sdScriptedEntityHelper_Aimer::CalcAngle ================ */ bool sdScriptedEntityHelper_Aimer::CalcAngle( float targetDistance, float arcLength, float angle, float& out ) { float sidea = arcLength; float sideb = targetDistance; float angleb = DEG2RAD( angle ); if ( sidea == 0.f || sideb == 0.f ) { return false; } float temp = sidea * sin( angleb ) / sideb; if ( idMath::Fabs( temp ) > 1.f ) { return false; } float anglea = asin( temp ); float anglec = 180 - RAD2DEG( anglea + angleb ); out = anglec; return true; } /* ================ sdScriptedEntityHelper_Aimer::SetTarget ================ */ void sdScriptedEntityHelper_Aimer::SetTarget( const idVec3& _target ) { const idMat3& bindAxes = _owner->GetRenderEntity()->axis; const idVec3& bindOrg = _owner->GetRenderEntity()->origin; idMat3 yawJointAxis; idVec3 yawJointOrg; if ( gunJoints[ AIMER_JOINT_SHOULDER ] != INVALID_JOINT ) { idMat3 currentAxes; idVec3 currentOrigin; _owner->GetAnimator()->GetJointTransform( gunJoints[ AIMER_JOINT_SHOULDER ], gameLocal.time, currentOrigin, currentAxes ); idMat3 transform; TransposeMultiply( baseAxes[ AIMER_JOINT_SHOULDER ], currentAxes, transform ); idMat3 shoulderAxis = bindAxes; idVec3 shoulderOrg = bindOrg + ( currentOrigin * shoulderAxis ); yawJointAxis = transform * shoulderAxis; yawJointOrg = shoulderOrg + ( yawJointAxis * ikPaths[ AIMER_IK_BASE ] ); } else { yawJointAxis = bindAxes; yawJointOrg = bindOrg + ( yawJointAxis * ikPaths[ AIMER_IK_BASE ] ); } { idVec3 target = _target; target -= yawJointOrg; target *= yawJointAxis.Transpose(); target.z = 0.f; yawInfo.ideal = RAD2DEG( atan2( target.y, target.x ) ); float angle; if ( CalcAngle( target.Length(), yawInfo.arcLength, yawInfo.offsetAngle, angle ) ) { yawInfo.ideal += angle - yawInfo.initialAngle; } } if ( yawInfo.clamp.flags.enabled ) { yawInfo.ideal = idMath::ClampFloat( yawInfo.clamp.extents[ 0 ], yawInfo.clamp.extents[ 1 ], yawInfo.ideal ); } idMat3 yawAxes; idAngles::YawToMat3( yawInfo.ideal, yawAxes ); idMat3 pitchJointAxis = yawAxes * yawJointAxis; idVec3 pitchJointOrg = yawJointOrg + ( pitchJointAxis * ikPaths[ AIMER_IK_YAW ] ); { idVec3 target = _target; target -= pitchJointOrg; target *= pitchJointAxis.Transpose(); target.y = 0.f; pitchInfo.ideal = RAD2DEG( atan2( target.z, target.x ) ); float angle; if ( CalcAngle( target.Length(), pitchInfo.arcLength, pitchInfo.offsetAngle, angle ) ) { pitchInfo.ideal += angle - pitchInfo.initialAngle; } pitchInfo.ideal = -pitchInfo.ideal; // pitch is inverted for some reason... } if ( pitchInfo.clamp.flags.enabled ) { pitchInfo.ideal = idMath::ClampFloat( pitchInfo.clamp.extents[ 0 ], pitchInfo.clamp.extents[ 1 ], pitchInfo.ideal ); } } /* ================ sdScriptedEntityHelper_Aimer::TargetClose ================ */ bool sdScriptedEntityHelper_Aimer::TargetClose( void ) const { if ( idMath::Fabs( idMath::AngleNormalize180( yawInfo.current - yawInfo.ideal ) ) > 3 ) { return false; } if ( idMath::Fabs( idMath::AngleNormalize180( pitchInfo.current - pitchInfo.ideal ) ) > 3 ) { return false; } return true; } /* ================ sdScriptedEntityHelper_Aimer::CanAimTo ================ */ bool sdScriptedEntityHelper_Aimer::CanAimTo( const idAngles& angles ) const { if ( yawInfo.clamp.flags.enabled ) { if ( angles.yaw < yawInfo.clamp.extents[ 0 ] || angles.yaw > yawInfo.clamp.extents[ 1 ] ) { return false; } } if ( pitchInfo.clamp.flags.enabled ) { if ( angles.pitch < pitchInfo.clamp.extents[ 0 ] || angles.pitch > pitchInfo.clamp.extents[ 1 ] ) { return false; } } return true; } /* =============================================================================== sdScriptedEntityHelper_PlayerIK =============================================================================== */ /* ================ sdScriptedEntityHelper_PlayerIK::Update ================ */ void sdScriptedEntityHelper_PlayerIK::Update( bool postThink ) { if ( !gameLocal.isNewFrame || !postThink ) { return; } idPlayer* player = target; if ( player == NULL ) { return; } if ( player->aorFlags & AOR_INHIBIT_IK ) { return; } ik.Update( target, _owner ); } /* ================ sdScriptedEntityHelper_PlayerIK::Init ================ */ void sdScriptedEntityHelper_PlayerIK::Init( sdScriptEntity* owner, const sdDeclStringMap* map ) { sdScriptedEntityHelper::Init( owner, map ); playerIndex = map->GetDict().GetInt( "player_index", "0" ); ik.Init( _owner, map->GetDict() ); } /* ================ sdScriptedEntityHelper_PlayerIK::WantsToThink ================ */ bool sdScriptedEntityHelper_PlayerIK::WantsToThink( void ) { return target.IsValid(); } /* =============================================================================== sdPlayerArmIK =============================================================================== */ /* ================ sdPlayerArmIK::sdPlayerArmIK ================ */ sdPlayerArmIK::sdPlayerArmIK( void ) { for ( int i = 0; i < NUM_ARMS; i++ ) { armTargets[ i ].joint = INVALID_JOINT; } } /* ================ sdPlayerArmIK::Init ================ */ bool sdPlayerArmIK::Init( idEntity* target, const idDict& parms ) { const char* jointName; idAnimator* animator = target->GetAnimator(); assert( animator != NULL ); jointName = parms.GetString( "joint_left" ); armTargets[ 0 ].joint = animator->GetJointHandle( jointName ); if ( armTargets[ 0 ].joint == INVALID_JOINT ) { gameLocal.Warning( "sdPlayerArmIK::Init Invalid Joint '%s'", jointName ); return false; } jointName = parms.GetString( "joint_right" ); armTargets[ 1 ].joint = animator->GetJointHandle( jointName ); if ( armTargets[ 1 ].joint == INVALID_JOINT ) { gameLocal.Warning( "sdPlayerArmIK::Init Invalid Joint '%s'", jointName ); return false; } return true; } /* ================ sdPlayerArmIK::Update ================ */ void sdPlayerArmIK::Update( idPlayer* player, idEntity* target ) { struct arm_t { idMat3 targetShoulderAxis; idMat3 targetElbowAxis; idMat3 targetHandAxis; }; arm_t arms[ NUM_ARMS ]; ClearJointMods( player ); idAnimator* animator = player->GetAnimator(); idMat3 tempAxis; idVec3 modelOrigin = player->GetRenderEntity()->origin; idMat3 modelAxis = player->GetRenderEntity()->axis; idMat3 transposeModelAxis = modelAxis.Transpose(); // get the arm bone lengths and rotation matrices for ( int i = 0; i < NUM_ARMS; i++ ) { if ( armTargets[ i ].joint == INVALID_JOINT ) { continue; } idMat3 handAxis; idVec3 handOrigin; animator->GetJointTransform( player->GetHandJoint( i ), gameLocal.time, handOrigin, handAxis ); idMat3 elbowAxis; idVec3 elbowOrigin; animator->GetJointTransform( player->GetElbowJoint( i ), gameLocal.time, elbowOrigin, elbowAxis ); idMat3 shoulderAxis; idVec3 shoulderOrigin; animator->GetJointTransform( player->GetShoulderJoint( i ), gameLocal.time, shoulderOrigin, shoulderAxis ); idVec3 t1 = ( elbowOrigin - shoulderOrigin ); // t1.Normalize(); idVec3 t2 = ( elbowOrigin - handOrigin ); // t2.Normalize(); idVec3 dir = t1 + t2; dir.Normalize(); // conversion from upper arm bone axis to should joint axis float upperArmLength = idIK::GetBoneAxis( shoulderOrigin, elbowOrigin, dir, tempAxis ); idMat3 upperArmToShoulderJoint = shoulderAxis * tempAxis.Transpose(); // conversion from lower arm bone axis to elbow joint axis float lowerArmLength = idIK::GetBoneAxis( elbowOrigin, handOrigin, dir, tempAxis ); idMat3 lowerArmToElbowJoint = elbowAxis * tempAxis.Transpose(); // get target idVec3 targetOrigin; target->GetWorldOriginAxis( armTargets[ i ].joint, targetOrigin, arms[ i ].targetHandAxis ); idVec3 targetOriginLocal = targetOrigin - modelOrigin; targetOriginLocal *= transposeModelAxis; arms[ i ].targetHandAxis *= transposeModelAxis; // solve IK and calculate elbow position idIK::SolveTwoBones( shoulderOrigin, targetOriginLocal, dir, upperArmLength, lowerArmLength, elbowOrigin ); if ( ik_debug.GetBool() ) { idVec3 shoulderWorld = ( shoulderOrigin * modelAxis ) + modelOrigin; idVec3 elbowWorld = ( elbowOrigin * modelAxis ) + modelOrigin; gameRenderWorld->DebugLine( colorCyan, shoulderWorld, elbowWorld ); gameRenderWorld->DebugLine( colorRed, elbowWorld, targetOrigin ); gameRenderWorld->DebugBox( colorYellow, idBox( targetOrigin, idVec3( 2, 2, 2 ), mat3_identity ) ); gameRenderWorld->DebugLine( colorGreen, elbowWorld, elbowWorld + ( ( dir * modelAxis ) * 8 ) ); } // get the axis for the shoulder joint idIK::GetBoneAxis( shoulderOrigin, elbowOrigin, dir, tempAxis ); arms[ i ].targetShoulderAxis = upperArmToShoulderJoint * tempAxis; // get the axis for the elbow joint idIK::GetBoneAxis( elbowOrigin, targetOriginLocal, dir, tempAxis ); arms[ i ].targetElbowAxis = lowerArmToElbowJoint * tempAxis; } for ( int i = 0; i < NUM_ARMS; i++ ) { if ( armTargets[ i ].joint == INVALID_JOINT ) { continue; } animator->SetJointAxis( player->GetShoulderJoint( i ), JOINTMOD_WORLD_OVERRIDE, arms[ i ].targetShoulderAxis ); animator->SetJointAxis( player->GetElbowJoint( i ), JOINTMOD_WORLD_OVERRIDE, arms[ i ].targetElbowAxis ); animator->SetJointAxis( player->GetHandJoint( i ), JOINTMOD_WORLD_OVERRIDE, arms[ i ].targetHandAxis ); } } /* ================ sdPlayerArmIK::ClearJointMods ================ */ void sdPlayerArmIK::ClearJointMods( idPlayer* player ) { idAnimator* animator = player->GetAnimator(); for ( int i = 0; i < NUM_ARMS; i++ ) { animator->SetJointAxis( player->GetShoulderJoint( i ), JOINTMOD_NONE, mat3_identity ); animator->SetJointAxis( player->GetElbowJoint( i ), JOINTMOD_NONE, mat3_identity ); animator->SetJointAxis( player->GetHandJoint( i ), JOINTMOD_NONE, mat3_identity ); } }