mirror of
https://github.com/ProjectDreamland/area51.git
synced 2024-11-01 03:01:49 +01:00
197 lines
6.1 KiB
C++
197 lines
6.1 KiB
C++
//=========================================================================
|
|
//
|
|
// LocoIKSolver.cpp
|
|
//
|
|
//=========================================================================
|
|
|
|
//=========================================================================
|
|
// INCLUDES
|
|
//=========================================================================
|
|
|
|
#include "LocoIKSolver.hpp"
|
|
|
|
|
|
//=========================================================================
|
|
// CONSTRAINT FUNCTIONS
|
|
//=========================================================================
|
|
|
|
// Applies corrective force to bone
|
|
void loco_ik_solver::constraint::SolveBone( matrix4& M,
|
|
const vector3& Pos,
|
|
const vector3& Dir,
|
|
f32 Amount,
|
|
f32 MassRatio,
|
|
f32 Inertia )
|
|
{
|
|
// Apply to rotation?
|
|
if( Inertia != 0.0f )
|
|
{
|
|
// Compute world space axis to rotate around for correction
|
|
vector3 T = M.GetTranslation();
|
|
vector3 RelPos = Pos - T;
|
|
vector3 Axis = v3_Cross( RelPos, Dir );
|
|
if( Axis.SafeNormalize() )
|
|
{
|
|
// Rotate around pivot
|
|
matrix4 DeltaRot;
|
|
DeltaRot.Setup( Axis, Amount * Inertia );
|
|
M.SetTranslation( vector3(0,0,0) );
|
|
M = DeltaRot * M;
|
|
M.SetTranslation( T );
|
|
}
|
|
}
|
|
|
|
// Apply to translation?
|
|
if( MassRatio != 0.0f )
|
|
M.Translate( Dir * Amount * MassRatio );
|
|
}
|
|
|
|
//=========================================================================
|
|
|
|
void loco_ik_solver::constraint::Solve( matrix4* pMatrices, s32 nActiveBones, f32 Weight )
|
|
{
|
|
ASSERT( pMatrices );
|
|
ASSERT( m_iBone0 >= 0 );
|
|
ASSERT( m_iBone1 >= 0 );
|
|
|
|
// Bones not present?
|
|
if( m_iBone0 >= nActiveBones )
|
|
return;
|
|
if( m_iBone1 >= nActiveBones )
|
|
return;
|
|
|
|
// Lookup bone matrices
|
|
matrix4& M0 = pMatrices[m_iBone0];
|
|
matrix4& M1 = pMatrices[m_iBone1];
|
|
|
|
// Compute world space positions
|
|
vector3 WorldPos0 = M0 * m_LocalPos0;
|
|
vector3 WorldPos1 = M1 * m_LocalPos1;
|
|
|
|
// Compute distance squared between points
|
|
vector3 Dir = WorldPos1 - WorldPos0;
|
|
f32 DistSqr = Dir.LengthSquared();
|
|
|
|
// Already satisfied?
|
|
if( ( DistSqr >= x_sqr( m_MinDist ) ) && ( DistSqr <= x_sqr( m_MaxDist ) ) )
|
|
return;
|
|
|
|
// Compute distance between points
|
|
f32 Dist = 0.0f;
|
|
if( DistSqr > 0.00001f )
|
|
Dist = x_sqrt( DistSqr );
|
|
|
|
// Normalize direction
|
|
if( Dist > 0.00001f )
|
|
Dir *= 1.0f / Dist;
|
|
|
|
// Compute deviation distance from constraint
|
|
if( Dist < m_MinDist )
|
|
Dist -= m_MinDist;
|
|
else if( Dist > m_MaxDist )
|
|
Dist -= m_MaxDist;
|
|
|
|
// Take solver weight into account
|
|
Dist *= Weight;
|
|
|
|
// Solve constraint on each bone matrix
|
|
vector3 MidPos = ( WorldPos0 + WorldPos1 ) * 0.5f;
|
|
SolveBone( M0, MidPos, Dir, Dist, m_MassRatio0, m_Inertia0 );
|
|
SolveBone( M1, MidPos, -Dir, Dist, m_MassRatio1, m_Inertia1 );
|
|
}
|
|
|
|
//=========================================================================
|
|
// SOLVER FUNCTIONS
|
|
//=========================================================================
|
|
|
|
loco_ik_solver::loco_ik_solver() :
|
|
m_pBoneMappings ( NULL ), // Pointer to list of bone mappings
|
|
m_nBoneMappings ( 0 ), // # if bones to solve
|
|
|
|
m_pConstraints ( NULL ), // Pointer to list of constraints
|
|
m_nConstraints ( 0 ), // # of constraints
|
|
|
|
m_nIterations ( 2 ), // # of iterations to do when solving
|
|
m_Weight ( 1.0f ) // Weight of IK. 0 = none, 1= full
|
|
{
|
|
}
|
|
|
|
//=========================================================================
|
|
|
|
loco_ik_solver::~loco_ik_solver()
|
|
{
|
|
}
|
|
|
|
//=========================================================================
|
|
|
|
// Initializes solver
|
|
void loco_ik_solver::Init( bone_mapping* pBoneMappings, s32 nBoneMappings,
|
|
constraint* pConstraints, s32 nConstraints,
|
|
s32 nIterations )
|
|
{
|
|
// This solver is pretty useless if we have nothing to solve!
|
|
ASSERT( pConstraints );
|
|
ASSERT( nConstraints > 0 );
|
|
|
|
// Keep the info
|
|
m_pBoneMappings = pBoneMappings;
|
|
m_nBoneMappings = nBoneMappings;
|
|
m_pConstraints = pConstraints;
|
|
m_nConstraints = nConstraints;
|
|
m_nIterations = nIterations;
|
|
}
|
|
|
|
//=========================================================================
|
|
|
|
// Solves all constraints
|
|
void loco_ik_solver::SolveConstraints( matrix4* pMatrices, s32 nActiveBones )
|
|
{
|
|
// Loop over all constraints
|
|
for( s32 i = 0; i < m_nConstraints; i++ )
|
|
m_pConstraints[i].Solve( pMatrices, nActiveBones, m_Weight );
|
|
}
|
|
|
|
//=========================================================================
|
|
|
|
// Applies all constraints and solves matrices
|
|
void loco_ik_solver::Solve( matrix4* pMatrices, s32 nActiveBones )
|
|
{
|
|
s32 i;
|
|
|
|
// Nothing to do?
|
|
if( m_Weight == 0.0f )
|
|
return;
|
|
|
|
// Step1: Convert from bone space to solver space
|
|
for( i = 0; i < m_nBoneMappings; i++ )
|
|
{
|
|
// Lookup mapping info
|
|
bone_mapping& Mapping = m_pBoneMappings[i];
|
|
if( Mapping.m_iBone >= nActiveBones )
|
|
continue;
|
|
|
|
// Convert bone space to solver space
|
|
matrix4& BoneMatrix = pMatrices[ Mapping.m_iBone ];
|
|
BoneMatrix = BoneMatrix * Mapping.m_B2S;
|
|
}
|
|
|
|
// Step2: Iterate over and solve constraints
|
|
for( i = 0; i < m_nIterations; i++ )
|
|
SolveConstraints( pMatrices, nActiveBones );
|
|
|
|
// Step3: Finally convert from solver space to bone space
|
|
for( i = 0; i < m_nBoneMappings; i++ )
|
|
{
|
|
// Lookup mapping info
|
|
bone_mapping& Mapping = m_pBoneMappings[i];
|
|
if( Mapping.m_iBone >= nActiveBones )
|
|
continue;
|
|
|
|
// Convert solver space to bone space
|
|
matrix4& BoneMatrix = pMatrices[ Mapping.m_iBone ];
|
|
BoneMatrix = BoneMatrix * Mapping.m_S2B;
|
|
}
|
|
}
|
|
|
|
//=========================================================================
|
|
|