Files
GTASource/game/ik/IkManager.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

3377 lines
93 KiB
C++

//
// ik/IkManager.cpp
//
// Copyright (C) 1999-2010 Rockstar Games. All Rights Reserved.
//
#include "IkManager.h"
// Rage headers
#include "creature/componentextradofs.h"
#include "crskeleton/skeleton.h"
#include "crskeleton/skeletondata.h"
#include "crbody/iksolver.h"
#include "entity/camerarelativeextension.h"
#include "fwanimation/directorcomponentmotiontree.h"
#include "fwdebug/picker.h"
// Game headers
#include "animation/anim_channel.h"
#include "animation/AnimBones.h"
#include "animation/MovePed.h"
#include "camera/CamInterface.h"
#include "debug/DebugScene.h"
#include "ik/IkConfig.h"
#include "ik/solvers/ArmIkSolver.h"
#include "ik/solvers/BodyLookSolverProxy.h"
#include "ik/solvers/BodyLookSolver.h"
#include "ik/solvers/BodyRecoilSolver.h"
#include "ik/solvers/HeadSolver.h"
#include "ik/solvers/QuadLegSolverProxy.h"
#include "ik/solvers/QuadLegSolver.h"
#include "ik/solvers/LegSolverProxy.h"
#include "ik/solvers/LegSolver.h"
#include "ik/solvers/TorsoSolver.h"
#include "ik/solvers/QuadrupedReactSolver.h"
#include "ik/solvers/TorsoReactSolver.h"
#include "ik/solvers/TorsoVehicleSolverProxy.h"
#include "ik/solvers/TorsoVehicleSolver.h"
#include "ik/solvers/RootSlopeFixupSolver.h"
#include "Peds/Horse/horseComponent.h"
#include "Peds/Ped.h"
#include "Peds/PedTuning.h"
#include "Peds/PedIKSettings.h"
#include "Peds/PedIntelligence.h"
#include "scene/world/GameWorld.h"
#include "Task/System/Tuning.h"
#include "Task/Vehicle/TaskInVehicle.h"
#if __IK_DEV
#include "scene/world/GameWorld.h"
#endif // __IK_DEV
#include "ik/Metadata_parser.h"
//////////////////////////////////////////////////////////////////////////
RAGE_DEFINE_CHANNEL(ik)
//////////////////////////////////////////////////////////////////////////
#define REGISTER_SOLVER_POOL_SIZE(_T, _P) CBaseIkManager::ms_SolverPoolSizes[(int)_T] = (u16)(_P->GetSize())
ANIM_OPTIMISATIONS();
#if __IK_DEV
CDebugRenderer CBaseIkManager::ms_DebugHelper;
bool CBaseIkManager::ms_DisplayMemory = false;
bool CBaseIkManager::ms_DisplaySolvers = false;
#endif // _DEV
#if __IK_DEV || __BANK
extern const char* parser_IKManagerSolverTypes__Flags_Strings[];
bool CBaseIkManager::ms_DisableAllSolvers = false;
bool CBaseIkManager::ms_DisableSolvers[] = {0};
#endif // __IK_DEV || __BANK
u16 CBaseIkManager::ms_SolverPoolSizes[] = {0};
const CPed* CBaseIkManager::ms_pPedDeletingSolver = NULL;
CBaseIkManager::DebugInfo CBaseIkManager::ms_DebugInfo;
bool CIkManager::ms_bSampleHighHeelHeight = false;
using namespace IKManagerSolverTypes;
////////////////////////////////////////////////////////////////////////////////
#if __IK_DEV
const char *eLookIkReturnCodeToString(eLookIkReturnCode eReturnCode)
{
switch(eReturnCode)
{
case LOOKIK_RC_NONE: return "LOOKIK_RC_NONE";
case LOOKIK_RC_DISABLED: return "LOOKIK_RC_DISABLED";
case LOOKIK_RC_INVALID_PARAM: return "LOOKIK_RC_INVALID_PARAM";
case LOOKIK_RC_CHAR_DEAD: return "LOOKIK_RC_CHAR_DEAD";
case LOOKIK_RC_BLOCKED: return "LOOKIK_RC_BLOCKED";
case LOOKIK_RC_BLOCKED_BY_ANIM: return "LOOKIK_RC_BLOCKED_BY_ANIM";
case LOOKIK_RC_BLOCKED_BY_TAG: return "LOOKIK_RC_BLOCKED_BY_TAG";
case LOOKIK_RC_OFFSCREEN: return "LOOKIK_RC_OFFSCREEN";
case LOOKIK_RC_RANGE: return "LOOKIK_RC_RANGE";
case LOOKIK_RC_FOV: return "LOOKIK_RC_FOV";
case LOOKIK_RC_PRIORITY: return "LOOKIK_RC_PRIORITY";
case LOOKIK_RC_INVALID_TARGET: return "LOOKIK_RC_INVALID_TARGET";
case LOOKIK_RC_TIMED_OUT: return "LOOKIK_RC_TIMED_OUT";
case LOOKIK_RC_ABORT: return "LOOKIK_RC_ABORT";
default: return "UNKNOWN";
}
}
#endif // __IK_DEV
////////////////////////////////////////////////////////////////////////////////
CBaseIkManager::CBaseIkManager() :
m_pPed(NULL),
m_flags(0)
{
for(u32 i=0; i<ikSolverTypeCount; i++)
{
m_Solvers[i]=NULL;
}
}
////////////////////////////////////////////////////////////////////////////////
CBaseIkManager::~CBaseIkManager()
{
RemoveAllSolvers();
}
////////////////////////////////////////////////////////////////////////////////
void CBaseIkManager::InitClass()
{
ms_DebugInfo.infoAsUInt = 0;
}
////////////////////////////////////////////////////////////////////////////////
bool CBaseIkManager::Init(CPed* pPed)
{
ikAssert(pPed);
m_pPed = pPed;
m_Kinematics.Init(*m_pPed->GetSkeleton());
return true;
}
////////////////////////////////////////////////////////////////////////////////
u32 CBaseIkManager::GetNumSolvers() const
{
u32 count=0;
for(u32 i=0; i<ikSolverTypeCount; i++)
{
if (m_Solvers[i])
{
count++;
}
}
return count;
}
////////////////////////////////////////////////////////////////////////////////
const crIKSolverBase* CBaseIkManager::GetSolver(eIkSolverType solverType) const
{
return m_Solvers[solverType];
}
////////////////////////////////////////////////////////////////////////////////
crIKSolverBase* CBaseIkManager::GetSolver(eIkSolverType solverType)
{
return m_Solvers[solverType];
}
////////////////////////////////////////////////////////////////////////////////
crIKSolverBase* CBaseIkManager::GetOrAddSolver(eIkSolverType solverType)
{
ikAssertf(!(m_pPed && m_pPed->GetIKSettings().IsIKFlagDisabled(solverType)) IK_QUAD_LEG_SOLVER_ONLY( || (solverType == ikSolverTypeQuadLeg) ) IK_QUAD_REACT_SOLVER_ONLY( || (solverType == ikSolverTypeQuadrupedReact) ), "Modelname = (%s) Trying to get or add an ik solver that has been disabled for this ped type!", m_pPed ? m_pPed->GetModelName() : "Unknown");
if (!HasSolver(solverType))
{
AddSolver(solverType);
}
return m_Solvers[solverType];
}
////////////////////////////////////////////////////////////////////////////////
bool CBaseIkManager::HasSolver(eIkSolverType solverType) const
{
return m_Solvers[solverType]!=NULL;
}
////////////////////////////////////////////////////////////////////////////////
bool CBaseIkManager::AddSolver(eIkSolverType solverType)
{
#if __IK_DEV || __BANK
if (!CBaseIkManager::ms_DisableAllSolvers && !ms_DisableSolvers[solverType])
{
#endif //__IK_DEV || __BANK
ikAssertf(!(m_pPed && m_pPed->GetIKSettings().IsIKFlagDisabled(solverType)) IK_QUAD_LEG_SOLVER_ONLY( || (solverType == ikSolverTypeQuadLeg) ) IK_QUAD_REACT_SOLVER_ONLY( || (solverType == ikSolverTypeQuadrupedReact) ), "Modelname = (%s) Trying to add an ik solver that has been disabled for this ped type!", m_pPed ? m_pPed->GetModelName() : "Unknown");
ikAssertf(!m_Solvers[solverType], "Trying to add a duplicate Ik solver!");
if (!m_Solvers[solverType])
{
switch(solverType)
{
case ikSolverTypeRootSlopeFixup:
if(CRootSlopeFixupIkSolver::GetPool()->GetNoOfFreeSpaces() != 0)
{
m_Solvers[solverType] = rage_new CRootSlopeFixupIkSolver(m_pPed);
}
break;
case ikSolverTypeTorso:
if(CTorsoIkSolver::GetPool()->GetNoOfFreeSpaces() != 0)
{
m_Solvers[solverType] = rage_new CTorsoIkSolver(m_pPed);
}
break;
case ikSolverTypeTorsoReact:
if(CTorsoReactIkSolver::GetPool()->GetNoOfFreeSpaces() != 0)
{
m_Solvers[solverType] = rage_new CTorsoReactIkSolver(m_pPed);
}
break;
#if IK_QUAD_REACT_SOLVER_ENABLE
case ikSolverTypeQuadrupedReact:
#if IK_QUAD_REACT_SOLVER_USE_POOL
if(CQuadrupedReactSolver::GetPool()->GetNoOfFreeSpaces() != 0)
{
m_Solvers[solverType] = rage_new CQuadrupedReactSolver(m_pPed);
}
#else
if(CQuadrupedReactSolver::ms_NoOfFreeSpaces > 0)
{
m_Solvers[solverType] = rage_new CQuadrupedReactSolver(m_pPed);
if (m_Solvers[solverType])
{
CQuadrupedReactSolver::ms_NoOfFreeSpaces--;
}
}
#endif // IK_QUAD_REACT_SOLVER_USE_POOL
break;
#endif // IK_QUAD_REACT_SOLVER_ENABLE
case ikSolverTypeTorsoVehicle:
if(CTorsoVehicleIkSolverProxy::GetPool()->GetNoOfFreeSpaces() != 0)
{
m_Solvers[solverType] = rage_new CTorsoVehicleIkSolverProxy();
}
break;
case ikSolverTypeHead:
#if ENABLE_HEAD_SOLVER
if(CHeadIkSolver::GetPool()->GetNoOfFreeSpaces() != 0)
{
m_Solvers[solverType] = rage_new CHeadIkSolver(m_pPed);
}
#endif // ENABLE_HEAD_SOLVER
break;
case ikSolverTypeBodyLook:
if(CBodyLookIkSolverProxy::GetPool()->GetNoOfFreeSpaces() != 0)
{
m_Solvers[solverType] = rage_new CBodyLookIkSolverProxy(m_pPed);
}
break;
case ikSolverTypeBodyRecoil:
if(CBodyRecoilIkSolver::GetPool()->GetNoOfFreeSpaces() != 0)
{
m_Solvers[solverType] = rage_new CBodyRecoilIkSolver(m_pPed);
}
break;
case ikSolverTypeLeg:
if(CLegIkSolverProxy::GetPool()->GetNoOfFreeSpaces() != 0)
{
m_Solvers[solverType] = rage_new CLegIkSolverProxy();
}
break;
#if IK_QUAD_LEG_SOLVER_ENABLE
case ikSolverTypeQuadLeg:
#if IK_QUAD_LEG_SOLVER_USE_POOL
if(CQuadLegIkSolverProxy::GetPool()->GetNoOfFreeSpaces() != 0)
{
m_Solvers[solverType] = rage_new CQuadLegIkSolverProxy();
}
#else
if(CQuadLegIkSolverProxy::ms_NoOfFreeSpaces > 0)
{
m_Solvers[solverType] = rage_new CQuadLegIkSolverProxy();
if (m_Solvers[solverType])
{
CQuadLegIkSolverProxy::ms_NoOfFreeSpaces--;
}
}
#endif // IK_QUAD_LEG_SOLVER_USE_POOL
break;
#endif // IK_QUAD_LEG_SOLVER_ENABLE
case ikSolverTypeArm:
if(CArmIkSolver::GetPool()->GetNoOfFreeSpaces() != 0)
{
m_Solvers[solverType] = rage_new CArmIkSolver(m_pPed);
}
break;
default:
ikAssertf(0, "Trying to create an invalid Ik solver type. Have you remembered to add the solver type to the Ik manager AddSolver method?");
return false;
}
if (m_Solvers[solverType])
{
RefreshKinematics();
}
else
{
//#if __IK_DEV || __BANK
// ikWarningf("Unable to create IK solver of type %d (%s). Solver pool reached new peak or solver not supported", (int)solverType, GetSolverName(solverType));
//#else
// ikWarningf("Unable to create IK solver of type %d. Solver pool reached new peak or solver not supported", (int)solverType);
//#endif // #if __IK_DEV || __BANK
return false;
}
return true;
}
#if __IK_DEV || __BANK
}
#endif //__IK_DEV || __BANK
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool CBaseIkManager::RemoveSolver(eIkSolverType solverType)
{
ikAssert(sysThreadType::IsUpdateThread());
if (m_Solvers[solverType])
{
#if IK_QUAD_LEG_SOLVER_ENABLE && !IK_QUAD_LEG_SOLVER_USE_POOL
if (solverType == ikSolverTypeQuadLeg)
{
CQuadLegIkSolverProxy* pProxy = static_cast<CQuadLegIkSolverProxy*>(m_Solvers[solverType]);
if (pProxy->FreeSolver())
{
CQuadLegIkSolver::ms_NoOfFreeSpaces++;
}
CQuadLegIkSolverProxy::ms_NoOfFreeSpaces++;
}
#endif // !IK_QUAD_LEG_SOLVER_USE_POOL
#if IK_QUAD_REACT_SOLVER_ENABLE && !IK_QUAD_REACT_SOLVER_USE_POOL
if (solverType == ikSolverTypeQuadrupedReact)
{
CQuadrupedReactSolver::ms_NoOfFreeSpaces++;
}
#endif // !IK_QUAD_REACT_SOLVER_USE_POOL
ms_pPedDeletingSolver = m_pPed;
delete m_Solvers[solverType];
m_Solvers[solverType] = NULL;
ms_pPedDeletingSolver = NULL;
RefreshKinematics();
return true;
}
else
{
return false;
}
}
////////////////////////////////////////////////////////////////////////////////
void CBaseIkManager::RefreshKinematics()
{
m_Kinematics.RemoveAllSolvers();
for (int i=ikSolverTypeCount-1; i >= 0; --i)
{
if (m_Solvers[i])
{
crIKSolverBase* pSolverToAdd = m_Solvers[i];
if (m_Solvers[i]->GetType() == crIKSolverBase::IK_SOLVER_TYPE_PROXY)
{
crIKSolverBase* pRealSolver = static_cast<CIkSolverProxy*>(m_Solvers[i])->GetSolver();
// Add the real solver if it exists. Otherwise, let the proxy run in its place.
if (pRealSolver)
{
pSolverToAdd = pRealSolver;
}
}
m_Kinematics.AddSolver(pSolverToAdd);
}
}
}
////////////////////////////////////////////////////////////////////////////////
void CBaseIkManager::InitPools()
{
CArmIkSolver::InitPool( MEMBUCKET_ANIMATION );
#if ENABLE_HEAD_SOLVER
CHeadIkSolver::InitPool( MEMBUCKET_ANIMATION );
#endif // ENABLE_HEAD_SOLVER
CLegIkSolver::InitPool( MEMBUCKET_ANIMATION );
#if IK_QUAD_LEG_SOLVER_ENABLE && IK_QUAD_LEG_SOLVER_USE_POOL
CQuadLegIkSolver::InitPool( MEMBUCKET_ANIMATION );
#endif // IK_QUAD_LEG_SOLVER_USE_POOL
#if IK_QUAD_REACT_SOLVER_ENABLE && IK_QUAD_REACT_SOLVER_USE_POOL
CQuadrupedReactSolver::InitPool( MEMBUCKET_ANIMATION );
#endif // IK_QUAD_REACT_SOLVER_USE_POOL
CTorsoIkSolver::InitPool( MEMBUCKET_ANIMATION );
CTorsoReactIkSolver::InitPool( MEMBUCKET_ANIMATION );
CRootSlopeFixupIkSolver::InitPool( MEMBUCKET_ANIMATION );
CBodyLookIkSolver::InitPool( MEMBUCKET_ANIMATION );
CBodyRecoilIkSolver::InitPool( MEMBUCKET_ANIMATION );
CTorsoVehicleIkSolver::InitPool( MEMBUCKET_ANIMATION );
CBodyLookIkSolverProxy::InitPool( MEMBUCKET_ANIMATION );
CLegIkSolverProxy::InitPool( MEMBUCKET_ANIMATION );
#if IK_QUAD_LEG_SOLVER_ENABLE && IK_QUAD_LEG_SOLVER_USE_POOL
CQuadLegIkSolverProxy::InitPool( MEMBUCKET_ANIMATION );
#endif // IK_QUAD_LEG_SOLVER_USE_POOL
CTorsoVehicleIkSolverProxy::InitPool( MEMBUCKET_ANIMATION );
// Register pool size for solvers with proxies
REGISTER_SOLVER_POOL_SIZE(ikSolverTypeBodyLook, CBodyLookIkSolver::GetPool());
REGISTER_SOLVER_POOL_SIZE(ikSolverTypeLeg, CLegIkSolver::GetPool());
#if IK_QUAD_LEG_SOLVER_ENABLE
#if IK_QUAD_LEG_SOLVER_USE_POOL
REGISTER_SOLVER_POOL_SIZE(ikSolverTypeQuadLeg, CQuadLegIkSolver::GetPool());
#else
CBaseIkManager::ms_SolverPoolSizes[(int)ikSolverTypeQuadLeg] = QUAD_LEG_SOLVER_PROXY_POOL_MAX;
#endif // IK_QUAD_LEG_SOLVER_USE_POOL
#endif // IK_QUAD_LEG_SOLVER_ENABLE
REGISTER_SOLVER_POOL_SIZE(ikSolverTypeTorsoVehicle, CTorsoVehicleIkSolver::GetPool());
}
////////////////////////////////////////////////////////////////////////////////
void CBaseIkManager::ShutdownPools()
{
CArmIkSolver::ShutdownPool();
#if ENABLE_HEAD_SOLVER
CHeadIkSolver::ShutdownPool();
#endif // ENABLE_HEAD_SOLVER
CLegIkSolver::ShutdownPool();
#if IK_QUAD_LEG_SOLVER_ENABLE && IK_QUAD_LEG_SOLVER_USE_POOL
CQuadLegIkSolver::ShutdownPool();
#endif // IK_QUAD_LEG_SOLVER_USE_POOL
#if IK_QUAD_REACT_SOLVER_ENABLE && IK_QUAD_REACT_SOLVER_USE_POOL
CQuadrupedReactSolver::ShutdownPool();
#endif // IK_QUAD_REACT_SOLVER_USE_POOL
CTorsoIkSolver::ShutdownPool();
CTorsoReactIkSolver::ShutdownPool();
CRootSlopeFixupIkSolver::ShutdownPool();
CBodyLookIkSolver::ShutdownPool();
CBodyRecoilIkSolver::ShutdownPool();
CTorsoVehicleIkSolver::ShutdownPool();
CBodyLookIkSolverProxy::ShutdownPool();
CLegIkSolverProxy::ShutdownPool();
#if IK_QUAD_LEG_SOLVER_ENABLE && IK_QUAD_LEG_SOLVER_USE_POOL
CQuadLegIkSolverProxy::ShutdownPool();
#endif // IK_QUAD_LEG_SOLVER_USE_POOL
CTorsoVehicleIkSolverProxy::ShutdownPool();
}
////////////////////////////////////////////////////////////////////////////////
void CBaseIkManager::ProcessSolvers()
{
// Matched to CPedAILodManager::kMaxPeds
const int kMaxPeds = 256;
PedIkPriority aPedIkPriority[kMaxPeds];
// Reconstruct ped scoring information calculated by CPedAILodManager earlier
const CSpatialArray& spatialArray = *CPed::ms_spatialArray;
const int nbPeds = spatialArray.GetNumObjects();
ikAssert(nbPeds <= kMaxPeds);
for (int pedIndex = 0; pedIndex < nbPeds; ++pedIndex)
{
PedIkPriority& ikPriority = aPedIkPriority[pedIndex];
CPed* pPed = CPed::GetPedFromSpatialArrayNode(spatialArray.GetNodePointer(pedIndex));
ikPriority.m_pPed = pPed;
ikPriority.m_Order = (u16)pPed->GetPedAiLod().GetAiScoreOrder();
ikPriority.m_Dirty = 0;
}
std::sort(aPedIkPriority, aPedIkPriority + nbPeds, PedIkPriority::OrderLessThan);
// Assign solvers to highest scoring peds.
int aiNumSolversAvailable[IKManagerSolverTypes::ikSolverTypeCount];
for (int solverType = 0; solverType < IKManagerSolverTypes::ikSolverTypeCount; ++solverType)
{
aiNumSolversAvailable[solverType] = ms_SolverPoolSizes[solverType];
}
for (int pedIndex = 0; pedIndex < nbPeds; ++pedIndex)
{
PedIkPriority& ikPriority = aPedIkPriority[pedIndex];
CIkManager& ikManager = ikPriority.m_pPed->GetIkManager();
ikManager.RemoveCompletedSolvers();
for (int solverType = 0; solverType < IKManagerSolverTypes::ikSolverTypeCount; ++solverType)
{
if (ikManager.m_Solvers[solverType] && (ikManager.m_Solvers[solverType]->GetType() == crIKSolverBase::IK_SOLVER_TYPE_PROXY))
{
CIkSolverProxy* pProxy = static_cast<CIkSolverProxy*>(ikManager.m_Solvers[solverType]);
pProxy->SetBlocked(aiNumSolversAvailable[solverType] <= 0);
aiNumSolversAvailable[solverType]--;
}
}
}
// Allocate solvers for proxies if necessary
for (int pedIndex = 0; pedIndex < nbPeds; ++pedIndex)
{
PedIkPriority& ikPriority = aPedIkPriority[pedIndex];
CIkManager& ikManager = ikPriority.m_pPed->GetIkManager();
for (int solverType = 0; solverType < IKManagerSolverTypes::ikSolverTypeCount; ++solverType)
{
if (ikManager.m_Solvers[solverType] && (ikManager.m_Solvers[solverType]->GetType() == crIKSolverBase::IK_SOLVER_TYPE_PROXY))
{
CIkSolverProxy* pProxy = static_cast<CIkSolverProxy*>(ikManager.m_Solvers[solverType]);
if (!pProxy->IsBlocked() && (pProxy->GetSolver() == NULL))
{
ikPriority.m_Dirty |= (u16)pProxy->AllocateSolver(ikPriority.m_pPed);
}
}
}
if (ikPriority.m_Dirty != 0)
{
ikManager.RefreshKinematics();
}
}
}
////////////////////////////////////////////////////////////////////////////////
void CBaseIkManager::RemoveCompletedSolvers()
{
ikAssert(sysThreadType::IsUpdateThread());
for (int i = 0; i < ikSolverTypeCount; i++)
{
crIKSolverBase* solver = GetSolver((eIkSolverType)i);
if (solver && ((eIkSolverType)i == ikSolverTypeTorso) && !CTorsoIkSolver::GetPool()->IsValidPtr(solver))
{
ikWarningf("Deleted torso solver being tracked by IK manager for ped %p", m_pPed);
Assertf(false, "Deleted torso solver being tracked by IK manager for ped %p", m_pPed);
m_Solvers[i] = NULL;
RefreshKinematics();
continue;
}
#if __IK_DEV || __BANK
if (solver && (solver->IsDead() || CBaseIkManager::ms_DisableSolvers[i] || CBaseIkManager::ms_DisableAllSolvers))
#else
if (solver && solver->IsDead())
#endif // __IK_DEV || __BANK
{
RemoveSolver((eIkSolverType)i);
}
}
}
////////////////////////////////////////////////////////////////////////////////
void CBaseIkManager::RemoveAllSolvers()
{
ikAssert(sysThreadType::IsUpdateThread());
ms_pPedDeletingSolver = m_pPed;
for (int i = 0; i < ikSolverTypeCount; i++)
{
if (m_Solvers[i])
{
#if IK_QUAD_LEG_SOLVER_ENABLE && !IK_QUAD_LEG_SOLVER_USE_POOL
if (i == (int)ikSolverTypeQuadLeg)
{
CQuadLegIkSolverProxy* pProxy = static_cast<CQuadLegIkSolverProxy*>(m_Solvers[i]);
if (pProxy->FreeSolver())
{
CQuadLegIkSolver::ms_NoOfFreeSpaces++;
}
CQuadLegIkSolverProxy::ms_NoOfFreeSpaces++;
}
#endif // !IK_QUAD_LEG_SOLVER_USE_POOL
#if IK_QUAD_REACT_SOLVER_ENABLE && !IK_QUAD_REACT_SOLVER_USE_POOL
if (i == (int)ikSolverTypeQuadrupedReact)
{
CQuadrupedReactSolver::ms_NoOfFreeSpaces++;
}
#endif // !IK_QUAD_REACT_SOLVER_USE_POOL
delete m_Solvers[i];
m_Solvers[i] = NULL;
}
}
ms_pPedDeletingSolver = NULL;
RefreshKinematics();
}
////////////////////////////////////////////////////////////////////////////////
void CBaseIkManager::ValidateSolverDeletion(CPed* pPed, eIkSolverType solverType)
{
Assertf(GetPedDeletingSolver() && (GetPedDeletingSolver() == pPed), "Solver type %d being deleted by (%p) instead of owning ped (%p)", (int)solverType, GetPedDeletingSolver(), pPed);
if (!GetPedDeletingSolver() || (GetPedDeletingSolver() != pPed))
{
u8 ownedBy = 0xFF; // Unknown
u8 deletedBy = 0xFF; // Unknown
if (pPed)
{
ownedBy = (u8)CPed::GetPool()->GetIndex(pPed);
}
if (GetPedDeletingSolver())
{
deletedBy = (u8)CPed::GetPool()->GetIndex(GetPedDeletingSolver());
}
ms_DebugInfo.info.ownedBy = ownedBy;
ms_DebugInfo.info.deletedBy = deletedBy;
ms_DebugInfo.info.deletedType = (u8)solverType;
}
}
////////////////////////////////////////////////////////////////////////////////
#if __IK_DEV
void CBaseIkManager::DebugRender()
{
if (ms_DisplayMemory)
{
DisplayMemory();
}
if (ms_DisplaySolvers)
{
DisplaySolvers();
}
CEntity* pSelectedEntity = (CEntity*) g_PickerManager.GetSelectedEntity();
if (pSelectedEntity && pSelectedEntity->GetIsTypePed())
{
CPed* pSelectedPed = static_cast<CPed*>(pSelectedEntity);
if (pSelectedPed)
{
pSelectedPed->GetIkManager().DebugRenderPed();
}
}
}
void CBaseIkManager::DisplayMemory()
{
char szText[128];
s32 lineNb = 0;
size_t usedMem = 0;
size_t totalMem = 0;
#define DisplaySolverMemory(_T, _P) \
formatf(szText, "%-28s: Used(%3d) Free(%3d) Total(%3d) sizeof(%4" SIZETFMT "d) UsedMem(%6" SIZETFMT "d) TotalMem (%6" SIZETFMT "d)\n", \
GetSolverName(_T), \
_P->GetNoOfUsedSpaces(), \
_P->GetNoOfFreeSpaces(), \
_P->GetSize(), \
_P->GetStorageSize(), \
_P->GetNoOfUsedSpaces() * _P->GetStorageSize(), \
_P->GetSize() * _P->GetStorageSize()); \
usedMem += _P->GetNoOfUsedSpaces() * _P->GetStorageSize(); \
totalMem += _P->GetSize() * _P->GetStorageSize(); \
ms_DebugHelper.Text2d(lineNb++, Color_white, szText);
ms_DebugHelper.Reset();
// Solvers
formatf(szText, "Solvers");
ms_DebugHelper.Text2d(lineNb++, Color_white, szText);
DisplaySolverMemory(ikSolverTypeArm, CArmIkSolver::GetPool());
DisplaySolverMemory(ikSolverTypeBodyLook, CBodyLookIkSolver::GetPool());
DisplaySolverMemory(ikSolverTypeBodyRecoil, CBodyRecoilIkSolver::GetPool());
#if ENABLE_HEAD_SOLVER
DisplaySolverMemory(ikSolverTypeHead, CHeadIkSolver::GetPool());
#endif // ENABLE_HEAD_SOLVER
DisplaySolverMemory(ikSolverTypeLeg, CLegIkSolver::GetPool());
#if IK_QUAD_LEG_SOLVER_ENABLE && IK_QUAD_LEG_SOLVER_USE_POOL
DisplaySolverMemory(ikSolverTypeQuadLeg, CQuadLegIkSolver::GetPool());
#endif // IK_QUAD_LEG_SOLVER_USE_POOL
#if IK_QUAD_REACT_SOLVER_ENABLE && IK_QUAD_REACT_SOLVER_USE_POOL
DisplaySolverMemory(ikSolverTypeQuadrupedReact, CQuadrupedReactSolver::GetPool());
#endif // IK_QUAD_REACT_SOLVER_USE_POOL
DisplaySolverMemory(ikSolverTypeTorso, CTorsoIkSolver::GetPool());
DisplaySolverMemory(ikSolverTypeTorsoReact, CTorsoReactIkSolver::GetPool());
DisplaySolverMemory(ikSolverTypeTorsoVehicle, CTorsoVehicleIkSolver::GetPool());
DisplaySolverMemory(ikSolverTypeRootSlopeFixup, CRootSlopeFixupIkSolver::GetPool());
lineNb++;
// Solver Proxies
formatf(szText, "Proxies");
ms_DebugHelper.Text2d(lineNb++, Color_white, szText);
DisplaySolverMemory(ikSolverTypeBodyLook, CBodyLookIkSolverProxy::GetPool());
DisplaySolverMemory(ikSolverTypeLeg, CLegIkSolverProxy::GetPool());
#if IK_QUAD_LEG_SOLVER_ENABLE && IK_QUAD_LEG_SOLVER_USE_POOL
DisplaySolverMemory(ikSolverTypeQuadLeg, CQuadLegIkSolverProxy::GetPool());
#endif // IK_QUAD_LEG_SOLVER_USE_POOL
DisplaySolverMemory(ikSolverTypeTorsoVehicle, CTorsoVehicleIkSolverProxy::GetPool());
lineNb++;
// Non-Pooled Solvers
formatf(szText, "Non-Pooled Solvers");
ms_DebugHelper.Text2d(lineNb++, Color_white, szText);
#if IK_QUAD_LEG_SOLVER_ENABLE && !IK_QUAD_LEG_SOLVER_USE_POOL
formatf(szText, "%-28s: Used(%3d) Free(%3d) Total(%3d) sizeof(%4" SIZETFMT "d) UsedMem(%6" SIZETFMT "d) TotalMem (%6" SIZETFMT "d)\n", \
GetSolverName(ikSolverTypeQuadLeg), \
(QUAD_LEG_SOLVER_POOL_MAX - CQuadLegIkSolver::ms_NoOfFreeSpaces), \
CQuadLegIkSolver::ms_NoOfFreeSpaces, \
QUAD_LEG_SOLVER_POOL_MAX, \
sizeof(CQuadLegIkSolver), \
(QUAD_LEG_SOLVER_POOL_MAX - CQuadLegIkSolver::ms_NoOfFreeSpaces) * sizeof(CQuadLegIkSolver), \
(QUAD_LEG_SOLVER_POOL_MAX - CQuadLegIkSolver::ms_NoOfFreeSpaces) * sizeof(CQuadLegIkSolver)); \
usedMem += (QUAD_LEG_SOLVER_POOL_MAX - CQuadLegIkSolver::ms_NoOfFreeSpaces) * sizeof(CQuadLegIkSolver); \
totalMem += (QUAD_LEG_SOLVER_POOL_MAX - CQuadLegIkSolver::ms_NoOfFreeSpaces) * sizeof(CQuadLegIkSolver); \
ms_DebugHelper.Text2d(lineNb++, Color_white, szText);
#endif // IK_QUAD_LEG_SOLVER_USE_POOL
#if IK_QUAD_REACT_SOLVER_ENABLE && !IK_QUAD_REACT_SOLVER_USE_POOL
formatf(szText, "%-28s: Used(%3d) Free(%3d) Total(%3d) sizeof(%4" SIZETFMT "d) UsedMem(%6" SIZETFMT "d) TotalMem (%6" SIZETFMT "d)\n", \
GetSolverName(ikSolverTypeQuadrupedReact), \
(QUAD_REACT_SOLVER_POOL_MAX - CQuadrupedReactSolver::ms_NoOfFreeSpaces), \
CQuadrupedReactSolver::ms_NoOfFreeSpaces, \
QUAD_REACT_SOLVER_POOL_MAX, \
sizeof(CQuadrupedReactSolver), \
(QUAD_REACT_SOLVER_POOL_MAX - CQuadrupedReactSolver::ms_NoOfFreeSpaces) * sizeof(CQuadrupedReactSolver), \
(QUAD_REACT_SOLVER_POOL_MAX - CQuadrupedReactSolver::ms_NoOfFreeSpaces) * sizeof(CQuadrupedReactSolver)); \
usedMem += (QUAD_REACT_SOLVER_POOL_MAX - CQuadrupedReactSolver::ms_NoOfFreeSpaces) * sizeof(CQuadrupedReactSolver); \
totalMem += (QUAD_REACT_SOLVER_POOL_MAX - CQuadrupedReactSolver::ms_NoOfFreeSpaces) * sizeof(CQuadrupedReactSolver); \
ms_DebugHelper.Text2d(lineNb++, Color_white, szText);
#endif // IK_QUAD_REACT_SOLVER_USE_POOL
lineNb++;
// Non-Pooled Proxies
formatf(szText, "Non-Pooled Proxies");
ms_DebugHelper.Text2d(lineNb++, Color_white, szText);
#if IK_QUAD_LEG_SOLVER_ENABLE && !IK_QUAD_LEG_SOLVER_USE_POOL
formatf(szText, "%-28s: Used(%3d) Free(%3d) Total(%3d) sizeof(%4" SIZETFMT "d) UsedMem(%6" SIZETFMT "d) TotalMem (%6" SIZETFMT "d)\n", \
GetSolverName(ikSolverTypeQuadLeg), \
(QUAD_LEG_SOLVER_PROXY_POOL_MAX - CQuadLegIkSolverProxy::ms_NoOfFreeSpaces), \
CQuadLegIkSolverProxy::ms_NoOfFreeSpaces, \
QUAD_LEG_SOLVER_PROXY_POOL_MAX, \
sizeof(CQuadLegIkSolverProxy), \
(QUAD_LEG_SOLVER_PROXY_POOL_MAX - CQuadLegIkSolverProxy::ms_NoOfFreeSpaces) * sizeof(CQuadLegIkSolverProxy), \
(QUAD_LEG_SOLVER_PROXY_POOL_MAX - CQuadLegIkSolverProxy::ms_NoOfFreeSpaces) * sizeof(CQuadLegIkSolverProxy)); \
usedMem += (QUAD_LEG_SOLVER_PROXY_POOL_MAX - CQuadLegIkSolverProxy::ms_NoOfFreeSpaces) * sizeof(CQuadLegIkSolverProxy); \
totalMem += (QUAD_LEG_SOLVER_PROXY_POOL_MAX - CQuadLegIkSolverProxy::ms_NoOfFreeSpaces) * sizeof(CQuadLegIkSolverProxy); \
ms_DebugHelper.Text2d(lineNb++, Color_white, szText);
#endif // IK_QUAD_LEG_SOLVER_USE_POOL
lineNb++;
// Totals
formatf(szText, " (%7" SIZETFMT "d) (%7" SIZETFMT "d) ", usedMem, totalMem);
ms_DebugHelper.Text2d(lineNb++, Color_white, szText);
ms_DebugHelper.Render();
}
void CBaseIkManager::DisplaySolvers()
{
const Vec3V vSourcePosition(VECTOR3_TO_VEC3V(CGameWorld::FindLocalPlayerCoors()));
const CSpatialArray& spatialArray = *CPed::ms_spatialArray;
const int nbPeds = spatialArray.GetNumObjects();
for (int pedIndex = 0; pedIndex < nbPeds; ++pedIndex)
{
CPed* pPed = CPed::GetPedFromSpatialArrayNode(spatialArray.GetNodePointer(pedIndex));
CIkManager& ikManager = pPed->GetIkManager();
Vec3V vPedPosition(pPed->GetTransform().GetPosition());
float fDisplaySolversOffset = 0.0f;
for (int solverType = 0; solverType < IKManagerSolverTypes::ikSolverTypeCount; ++solverType)
{
if (ikManager.m_Solvers[solverType] && (ikManager.m_Solvers[solverType]->GetType() == crIKSolverBase::IK_SOLVER_TYPE_PROXY))
{
CIkSolverProxy* pProxy = static_cast<CIkSolverProxy*>(ikManager.m_Solvers[solverType]);
Color32 aColours[2] = { Color_green4, Color_green1 };
if (solverType == ikSolverTypeBodyLook) { aColours[0] = Color_maroon4; aColours[1] = Color_maroon1; }
#if IK_QUAD_LEG_SOLVER_ENABLE
if (solverType == ikSolverTypeQuadLeg) { aColours[0] = Color_orange4; aColours[1] = Color_orange1; }
#endif // IK_QUAD_LEG_SOLVER_ENABLE
if (solverType == ikSolverTypeTorsoVehicle) { aColours[0] = Color_cyan4; aColours[1] = Color_cyan1; }
grcDebugDraw::Line(vSourcePosition, Add(vPedPosition, Vec3V(0.0f, 0.0f, fDisplaySolversOffset)), aColours[!pProxy->IsBlocked() && pProxy->GetSolver() ? 1 : 0]);
fDisplaySolversOffset += 0.075f;
}
}
}
}
#endif //__IK_DEV
////////////////////////////////////////////////////////////////////////////////
#if __IK_DEV || __BANK
const char* CBaseIkManager::GetSolverName(eIkSolverType solverType)
{
return parser_IKManagerSolverTypes__Flags_Strings[solverType];
}
#endif // __IK_DEV || __BANK
///////////////////////////////////////////////////////////////////////////////
CIkManager::~CIkManager()
{
#if FPS_MODE_SUPPORTED
if (m_pFirstPersonRequests)
{
delete m_pFirstPersonRequests;
m_pFirstPersonRequests = NULL;
}
if (m_pThirdPersonSkeleton)
{
delete m_pThirdPersonSkeleton;
m_pThirdPersonSkeleton = NULL;
}
if (m_pThirdPersonInputFrame)
{
ikAssert(m_pPed);
ikAssert(m_pPed->GetAnimDirector());
m_pPed->GetAnimDirector()->GetFrameBuffer().ReleaseFrame(m_pThirdPersonInputFrame);
m_pThirdPersonInputFrame = NULL;
}
#endif // FPS_MODE_SUPPORTED
}
bool CIkManager::Init(CPed* pPed)
{
ikAssert(pPed);
CBaseIkManager::Init(pPed);
InitHighHeelData();
//make sure fade defaults to on
ClearFlag(PEDIK_LEGS_AND_PELVIS_FADE_OFF);
//make sure leg ik defaults to on
ClearFlag(PEDIK_LEGS_AND_PELVIS_OFF);
#if FPS_MODE_SUPPORTED
SetExternallyDrivenPelvisOffset(0.0f);
m_pFirstPersonRequests = NULL;
m_pThirdPersonSkeleton = NULL;
m_pThirdPersonInputFrame = NULL;
// Initialise requests
FirstPersonPassRequests& requests = GetFpsRequests();
requests.Reset();
m_fDeltaTime = 0.0f;
m_bFPSUpdatePass = false;
#endif // FPS_MODE_SUPPORTED
IK_DEV_SET_LOOK_AT_RETURN_CODE(LOOKIK_RC_NONE, "CIkManager::Init");
return true;
}
//////////////////////////////////////////////////////////////////////////
#if __BANK
void CIkManager::InitWidgets()
{
bkBank* pBank = &BANKMGR.CreateBank("IK Manager");
pBank->PushGroup("Disable solvers", false);
pBank->AddToggle("Disable all solvers", &CBaseIkManager::ms_DisableAllSolvers);
for (int i = 0; i < ikSolverTypeCount; ++i)
{
pBank->AddToggle(CBaseIkManager::GetSolverName(eIkSolverType(i)), &ms_DisableSolvers[i]);
}
pBank->PopGroup();
#if __IK_DEV
pBank->AddToggle("Display memory", &CBaseIkManager::ms_DisplayMemory);
pBank->AddToggle("Display solvers", &CBaseIkManager::ms_DisplaySolvers);
CArmIkSolver::AddWidgets(pBank);
#if ENABLE_HEAD_SOLVER
CHeadIkSolver::AddWidgets(pBank);
#endif // ENABLE_HEAD_SOLVER
CLegIkSolver::AddWidgets(pBank);
#if IK_QUAD_LEG_SOLVER_ENABLE
CQuadLegIkSolver::AddWidgets(pBank);
#endif // IK_QUAD_LEG_SOLVER_ENABLE
#if IK_QUAD_REACT_SOLVER_ENABLE
CQuadrupedReactSolver::AddWidgets(pBank);
#endif // IK_QUAD_REACT_SOLVER_ENABLE
CTorsoIkSolver::AddWidgets(pBank);
CTorsoReactIkSolver::AddWidgets(pBank);
CTorsoVehicleIkSolver::AddWidgets(pBank);
CRootSlopeFixupIkSolver::AddWidgets(pBank);
CBodyLookIkSolver::AddWidgets(pBank);
CBodyRecoilIkSolver::AddWidgets(pBank);
#endif //__IK_DEV
}
#endif // _BANK
//////////////////////////////////////////////////////////////////////////
void CIkManager::PreTaskUpdate()
{
// Leg ik is on by default
ClearFlag(PEDIK_LEGS_AND_PELVIS_OFF);
// Blending out slowly (rather than instantly) is on by default
ClearFlag(PEDIK_LEGS_AND_PELVIS_FADE_OFF);
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::PreIkUpdate(float deltaTime)
{
#if __ASSERT
static bool bPreIkAlreadyAsserted = false;
if (!bPreIkAlreadyAsserted)
{
s32 foreArmIdx = m_pPed->GetBoneIndexFromBoneTag(BONETAG_L_FOREARM);
if (foreArmIdx!=-1)
{
const Matrix34 &localForeArmMtx = RCC_MATRIX34(m_pPed->GetSkeleton()->GetLocalMtx(foreArmIdx));
if (fabsf(localForeArmMtx.d.x) > 1.0f)
{
bPreIkAlreadyAsserted = true;
// Find out what animations are running at the time of the bad matrix
m_pPed->GetAnimDirector()->DebugDump();
// Find out what tasks are running at the time of the bad matrix
if (m_pPed->GetPedIntelligence())
{
m_pPed->GetPedIntelligence()->PrintTasks();
}
// Find if the left arm solver is running and what its target is at the time of the bad matrix
if (HasSolver(ikSolverTypeArm))
{
Printf("CIkManager::PreIkUpdate() : Left Arm IK running target = \n");
static_cast<const CArmIkSolver*>(GetSolver(ikSolverTypeArm))->GetArmGoal(crIKSolverArms::LEFT_ARM).m_Target.Print();
}
else
{
Printf("CIkManager::PreIkUpdate() : No Left Arm IK running\n");
}
Assertf(0, "CIkManager::PreIkUpdate() : The x component of the local left forearm matrix is > 1.0f. (please include TTY)");
}
}
s32 footIdx = m_pPed->GetBoneIndexFromBoneTag(BONETAG_R_FOOT);
if (footIdx!=-1)
{
Matrix34 globalFootMtx;
m_pPed->GetSkeleton()->GetGlobalMtx(footIdx, RC_MAT34V(globalFootMtx));
// Use error of 0.05 to allow for slightly non orthonormal matrices
if(!globalFootMtx.IsOrthonormal(0.05f))
{
// Find out what animations are running at the time of the bad matrix
m_pPed->GetAnimDirector()->DebugDump();
// Find out what tasks are running at the time of the bad matrix
if (m_pPed->GetPedIntelligence())
{
m_pPed->GetPedIntelligence()->PrintTasks();
}
Assertf(0, "CIkManager::PreIkUpdate() : The matrix of the global right foot matrix is not orthonormal (0.05) for model - %s. (please include TTY)", m_pPed->GetModelName());
}
}
}
#endif //__ASSERT
#if FPS_MODE_SUPPORTED
if (deltaTime > 0.0f)
{
m_fDeltaTime = deltaTime;
m_bFPSUpdatePass = false;
}
#endif // FPS_MODE_SUPPORTED
// Dampen the root motion. Used when camera is in close to stop bobbing
if (m_pPed->IsLocalPlayer() && m_pPed->GetPlayerInfo())
{
m_pPed->GetPlayerInfo()->ProcessDampenRoot();
}
BeginHighHeelData(deltaTime);
// TODO: virtualize PreIkUpdate as PreIterate in crIKSolverBase to simplify below?
crIKSolverBase* pSolver = GetSolver(ikSolverTypeBodyRecoil);
if (pSolver)
{
static_cast<CBodyRecoilIkSolver*>(pSolver)->PreIkUpdate(deltaTime);
}
pSolver = GetSolver(ikSolverTypeArm);
if (pSolver)
{
static_cast<CArmIkSolver*>(pSolver)->PreIkUpdate(deltaTime);
}
pSolver = GetSolver(ikSolverTypeBodyLook);
if (pSolver)
{
static_cast<CBodyLookIkSolverProxy*>(pSolver)->PreIkUpdate(deltaTime);
}
pSolver = GetSolver(ikSolverTypeRootSlopeFixup);
if (pSolver)
{
static_cast<CRootSlopeFixupIkSolver*>(pSolver)->PreIkUpdate(deltaTime);
}
pSolver = GetSolver(ikSolverTypeLeg);
if (pSolver)
{
static_cast<CLegIkSolverProxy*>(pSolver)->PreIkUpdate(deltaTime);
}
#if FPS_MODE_SUPPORTED
pSolver = GetSolver(ikSolverTypeTorso);
if (pSolver)
{
ms_DebugInfo.info.nonOwnedSolverPre |= (m_pPed != static_cast<CTorsoIkSolver*>(pSolver)->GetPed());
static_cast<CTorsoIkSolver*>(pSolver)->PreIkUpdate(deltaTime);
}
#endif // FPS_MODE_SUPPORTED
#if IK_QUAD_LEG_SOLVER_ENABLE
pSolver = GetSolver(ikSolverTypeQuadLeg);
if (pSolver)
{
static_cast<CQuadLegIkSolverProxy*>(pSolver)->PreIkUpdate(deltaTime);
}
#endif // IK_QUAD_LEG_SOLVER_ENABLE
pSolver = GetSolver(ikSolverTypeTorsoVehicle);
if (pSolver)
{
static_cast<CTorsoVehicleIkSolverProxy*>(pSolver)->PreIkUpdate(deltaTime);
}
}
///////////////////////////////////////////////////////////////////////////////
void CIkManager::PostIkUpdate(float deltaTime)
{
EndHighHeelData(deltaTime);
// TODO: virtualize PostIkUpdate as PostSolve in crIKSolverBase to simplify below?
if (HasSolver(ikSolverTypeTorso))
{
ms_DebugInfo.info.nonOwnedSolverPost |= (m_pPed != static_cast<CTorsoIkSolver*>(GetSolver(ikSolverTypeTorso))->GetPed());
}
// Now the pre-render motion trees have all finished we can update the head solver target without blocking the pre-render motion tree
#if ENABLE_HEAD_SOLVER
if (HasSolver(ikSolverTypeHead))
{
static_cast<CHeadIkSolver*>(GetSolver(ikSolverTypeHead))->PostIkUpdate(deltaTime);
}
#endif // ENABLE_HEAD_SOLVER
if (HasSolver(ikSolverTypeArm))
{
static_cast<CArmIkSolver*>(GetSolver(ikSolverTypeArm))->PostIkUpdate(deltaTime);
}
if (HasSolver(ikSolverTypeBodyLook))
{
float fSolverDeltaTime = deltaTime;
#if FPS_MODE_SUPPORTED
if (m_bFPSUpdatePass)
{
fSolverDeltaTime = (deltaTime == 0.0f) ? m_fDeltaTime : 0.0f;
}
#endif // FPS_MODE_SUPPORTED
static_cast<CBodyLookIkSolverProxy*>(GetSolver(ikSolverTypeBodyLook))->PostIkUpdate(fSolverDeltaTime);
}
if (HasSolver(ikSolverTypeBodyRecoil))
{
static_cast<CBodyRecoilIkSolver*>(GetSolver(ikSolverTypeBodyRecoil))->PostIkUpdate(deltaTime);
}
if (HasSolver(ikSolverTypeLeg))
{
static_cast<CLegIkSolverProxy*>(GetSolver(ikSolverTypeLeg))->PostIkUpdate(deltaTime);
}
#if IK_QUAD_LEG_SOLVER_ENABLE
if (HasSolver(ikSolverTypeQuadLeg))
{
static_cast<CQuadLegIkSolverProxy*>(GetSolver(ikSolverTypeQuadLeg))->PostIkUpdate(deltaTime);
}
#endif // IK_QUAD_LEG_SOLVER_ENABLE
#if IK_QUAD_REACT_SOLVER_ENABLE
if (HasSolver(ikSolverTypeQuadrupedReact))
{
static_cast<CQuadrupedReactSolver*>(GetSolver(ikSolverTypeQuadrupedReact))->PostIkUpdate(deltaTime);
}
#endif // IK_QUAD_REACT_SOLVER_ENABLE
if (HasSolver(ikSolverTypeTorsoReact))
{
static_cast<CTorsoReactIkSolver*>(GetSolver(ikSolverTypeTorsoReact))->PostIkUpdate(deltaTime);
}
if (HasSolver(ikSolverTypeTorsoVehicle))
{
static_cast<CTorsoVehicleIkSolverProxy*>(GetSolver(ikSolverTypeTorsoVehicle))->PostIkUpdate(deltaTime);
}
if (deltaTime > 0.0f)
{
RemoveCompletedSolvers();
#if FPS_MODE_SUPPORTED
if (m_pFirstPersonRequests)
{
m_pFirstPersonRequests->Reset();
}
#endif // FPS_MODE_SUPPORTED
}
#if __ASSERT
static bool bPostIKAlreadyAsserted = false;
if (!bPostIKAlreadyAsserted)
{
s32 foreArmIdx = m_pPed->GetBoneIndexFromBoneTag(BONETAG_L_FOREARM);
if (foreArmIdx!=-1)
{
const Matrix34 &localForeArmMtx = RCC_MATRIX34(m_pPed->GetSkeleton()->GetLocalMtx(foreArmIdx));
if (fabsf(localForeArmMtx.d.x) > 1.0f)
{
bPostIKAlreadyAsserted = true;
// Find out what animations are running at the time of the bad matrix
m_pPed->GetAnimDirector()->DebugDump();
// Find out what tasks are running at the time of the bad matrix
if (m_pPed->GetPedIntelligence())
{
m_pPed->GetPedIntelligence()->PrintTasks();
}
// Find if the left arm solver is running and what its target is at the time of the bad matrix
if (HasSolver(ikSolverTypeArm))
{
Printf("CIkManager::PostIkUpdate() : Left Arm IK running target = \n");
static_cast<const CArmIkSolver*>(GetSolver(ikSolverTypeArm))->GetArmGoal(crIKSolverArms::LEFT_ARM).m_Target.Print();
}
else
{
Printf("CIkManager::PostIkUpdate() : No Left Arm IK running\n");
}
Assertf(0, "CIkManager::PostIkUpdate() : The x component of the local left forearm matrix is > 1.0f for model - %s. (please include TTY)", m_pPed->GetModelName());
}
}
s32 footIdx = m_pPed->GetBoneIndexFromBoneTag(BONETAG_R_FOOT);
if (footIdx!=-1)
{
Matrix34 globalFootMtx;
m_pPed->GetSkeleton()->GetGlobalMtx(footIdx, RC_MAT34V(globalFootMtx));
// Use error of 0.05 to allow for slightly non orthonormal matrices
if(!globalFootMtx.IsOrthonormal(0.05f))
{
// Find out what animations are running at the time of the bad matrix
m_pPed->GetAnimDirector()->GetMove().Dump();
// Find out what tasks are running at the time of the bad matrix
if (m_pPed->GetPedIntelligence())
{
m_pPed->GetPedIntelligence()->PrintTasks();
}
Assertf(0, "CIkManager::PostIkUpdate() : The matrix of the global right foot matrix is not orthonormal (0.05) for model - %s. (please include TTY)", m_pPed->GetModelName());
}
}
}
#endif //__ASSERT
}
///////////////////////////////////////////////////////////////////////////////
void CIkManager::ResetAllSolvers()
{
for(u32 i=0; i<ikSolverTypeCount; i++)
{
crIKSolverBase* pSolver = GetSolver((eIkSolverType)i);
if (pSolver)
{
pSolver->Reset();
}
}
}
///////////////////////////////////////////////////////////////////////////////
void CIkManager::ResetAllNonLegSolvers()
{
for (u32 i = 0; i < ikSolverTypeCount; i++)
{
if ((i != ikSolverTypeLeg) IK_QUAD_LEG_SOLVER_ONLY( && (i != ikSolverTypeQuadLeg) ))
{
crIKSolverBase* pSolver = GetSolver((eIkSolverType)i);
if (pSolver)
{
pSolver->Reset();
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
bool CIkManager::IsActive(eIkSolverType solverType) const
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(solverType) IK_QUAD_LEG_SOLVER_ONLY( && (solverType != ikSolverTypeQuadLeg) ) IK_QUAD_REACT_SOLVER_ONLY( && (solverType != ikSolverTypeQuadrupedReact) ))
return false;
const crIKSolverBase* pSolver = GetSolver(solverType);
if (pSolver)
{
switch (solverType)
{
case ikSolverTypeArm:
case ikSolverTypeBodyLook:
case ikSolverTypeBodyRecoil:
case ikSolverTypeHead:
case ikSolverTypeTorso:
{
return true;
}
case ikSolverTypeLeg:
{
const CLegIkSolver* pLegSolver = static_cast<const CLegIkSolver*>(static_cast<const CLegIkSolverProxy*>(pSolver)->GetSolver());
if (pLegSolver)
{
return (pLegSolver->GetLegBlend(CLegIkSolver::LEFT_LEG) == 1.0f) && (pLegSolver->GetLegBlend(CLegIkSolver::RIGHT_LEG) == 1.0f);
}
break;
}
#if IK_QUAD_LEG_SOLVER_ENABLE
case ikSolverTypeQuadLeg:
{
const CQuadLegIkSolver* pLegSolver = static_cast<const CQuadLegIkSolver*>(static_cast<const CQuadLegIkSolverProxy*>(pSolver)->GetSolver());
if (pLegSolver)
{
return true;
}
break;
}
#endif // IK_QUAD_LEG_SOLVER_ENABLE
#if IK_QUAD_REACT_SOLVER_ENABLE
case ikSolverTypeQuadrupedReact:
{
const CQuadrupedReactSolver* pReactSolver = static_cast<const CQuadrupedReactSolver*>(pSolver);
return pReactSolver->IsActive();
}
#endif // IK_QUAD_REACT_SOLVER_ENABLE
case ikSolverTypeTorsoReact:
{
const CTorsoReactIkSolver* pTorsoReactSolver = static_cast<const CTorsoReactIkSolver*>(pSolver);
return pTorsoReactSolver->IsActive();
}
case ikSolverTypeTorsoVehicle:
{
const CTorsoVehicleIkSolver* pTorsoVehicleSolver = static_cast<const CTorsoVehicleIkSolver*>(static_cast<const CTorsoVehicleIkSolverProxy*>(pSolver)->GetSolver());
if (pTorsoVehicleSolver)
{
return true;
}
break;
}
case ikSolverTypeRootSlopeFixup:
{
const CRootSlopeFixupIkSolver* pRootSlopeFixupSolver = static_cast<const CRootSlopeFixupIkSolver*>(pSolver);
return pRootSlopeFixupSolver->IsActive();
}
default:
{
ikAssertf(0, "CIkManager::IsActive - Solver type not handled");
break;
}
};
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
bool CIkManager::IsSupported(eIkSolverType solverType) const
{
#if IK_QUAD_LEG_SOLVER_ENABLE
// Ideally quad leg ik support would be read from PedIKSettings (peds.meta) only, but since
// this is strictly to support horse DLC, using horse component existence to define usage.
if (solverType == ikSolverTypeQuadLeg)
{
if ((m_pPed->GetHorseComponent() == NULL) && m_pPed->GetIKSettings().IsIKFlagDisabled(solverType))
{
return false;
}
}
else
#endif // IK_QUAD_LEG_SOLVER_ENABLE
#if IK_QUAD_REACT_SOLVER_ENABLE
// Ideally quad react ik support would be read from PedIKSettings (peds.meta) only, but since
// this is strictly to support horse DLC, using horse component existence to define usage.
if (solverType == ikSolverTypeQuadrupedReact)
{
if ((m_pPed->GetHorseComponent() == NULL) && m_pPed->GetIKSettings().IsIKFlagDisabled(solverType))
{
return false;
}
Vec3V vPedToCamera(VECTOR3_TO_VEC3V(camInterface::GetPos()) - m_pPed->GetTransform().GetPosition());
return (MagSquared(vPedToCamera).Getf() < square(CQuadrupedReactSolver::ms_Params.fMaxCameraDistance));
}
else
#endif // IK_QUAD_REACT_SOLVER_ENABLE
if (m_pPed->GetIKSettings().IsIKFlagDisabled(solverType))
{
return false;
}
if (solverType == ikSolverTypeTorsoReact)
{
if (!m_pPed->GetCapsuleInfo()->IsBiped())
{
return false;
}
if (!m_pPed->IsDead() && m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle))
{
const CVehicle* pVehicle = m_pPed->GetMyVehicle();
bool bFirstPerson = m_pPed->IsInFirstPersonVehicleCamera();
if (!bFirstPerson && pVehicle && !pVehicle->InheritsFromBike() && !pVehicle->InheritsFromQuadBike() && !pVehicle->InheritsFromAmphibiousQuadBike() && !pVehicle->GetIsJetSki())
{
return false;
}
}
Vector3 vPedToCamera(camInterface::GetPos() - VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition()));
return (vPedToCamera.Mag2() < square(CTorsoReactIkSolver::ms_Params.fMaxCameraDistance));
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
void CIkManager::SetFlag(u32 flag)
{
CBaseIkManager::SetFlag(flag);
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::LookAt(u32 hashKey, const CEntity* pEntity, s32 time,
eAnimBoneTag offsetBoneTag, const Vector3* pOffsetPos, u32 flags,
s32 UNUSED_PARAM(blendInTime), s32 UNUSED_PARAM(blendOutTime), eLookAtPriority priority)
{
ikAssert(m_pPed);
// The entity does not support head ik
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeHead) || !IsSupported(IKManagerSolverTypes::ikSolverTypeBodyLook))
{
IK_DEV_SET_LOOK_AT_RETURN_CODE(LOOKIK_RC_DISABLED, "CIkManager::LookAt");
return;
}
// If a valid bonetag was specified
if (offsetBoneTag != -1)
{
// A bonetag was specified but the entity is NULL
if ( pEntity == NULL )
{
ikAssertf(0, "Boneid : %d specified but no target entity", (u16)offsetBoneTag);
IK_DEV_SET_LOOK_AT_RETURN_CODE(LOOKIK_RC_INVALID_PARAM, "CIkManager::LookAt");
return;
}
// A bonetag was specified but the entities skeleton is NULL
if (pEntity->GetSkeleton() == NULL)
{
ikAssertf(0, "Boneid : %d specified but target entity : %s has no skeleton", (u16)offsetBoneTag, pEntity->GetModelName());
IK_DEV_SET_LOOK_AT_RETURN_CODE(LOOKIK_RC_INVALID_PARAM, "CIkManager::LookAt");
return;
}
// A bonetag was specified that is invalid for the skeleton
s32 offsetBoneIdx = -1;
pEntity->GetSkeletonData().ConvertBoneIdToIndex((u16)offsetBoneTag, offsetBoneIdx);
if (offsetBoneIdx == -1)
{
ikAssertf(0, "Unknown boneid : %d specified for target entity : %s", (u16)offsetBoneTag, pEntity->GetModelName());
IK_DEV_SET_LOOK_AT_RETURN_CODE(LOOKIK_RC_INVALID_PARAM, "CIkManager::LookAt");
return;
}
}
// Is the ped alive?
if (m_pPed->ShouldBeDead())
{
IK_DEV_SET_LOOK_AT_RETURN_CODE(LOOKIK_RC_CHAR_DEAD, "CIkManager::LookAt");
return;
}
// Is the head blocked using the ped reset flags (i.e via script or code)
if (m_pPed->GetHeadIkBlocked())
{
IK_DEV_SET_LOOK_AT_RETURN_CODE(LOOKIK_RC_BLOCKED, "CIkManager::LookAt");
return;
}
// Is the head blocked using the ped reset flags specifically for code driven head ik
if (m_pPed->GetCodeHeadIkBlocked() && !(flags&LF_FROM_SCRIPT))
{
IK_DEV_SET_LOOK_AT_RETURN_CODE(LOOKIK_RC_BLOCKED, "CIkManager::LookAt");
return;
}
// Is the ped playing an animation that is incompatible with the ik?
int iInvalidFlags = APF_BLOCK_IK|APF_BLOCK_HEAD_IK;
if (m_pPed->GetMovePed().GetBlendHelper().IsFlagSet(iInvalidFlags))
{
IK_DEV_SET_LOOK_AT_RETURN_CODE(LOOKIK_RC_BLOCKED_BY_ANIM, "CIkManager::LookAt");
return;
}
// Is the call not from script?
bool fromScript = (flags & LF_FROM_SCRIPT) != 0;
if (!fromScript)
{
if (m_pPed->GetIsVisibleInSomeViewportThisFrame() == false)
{
IK_DEV_SET_LOOK_AT_RETURN_CODE(LOOKIK_RC_OFFSCREEN, "CIkManager::LookAt");
return;
}
if (IsGreaterThanAll(MagSquared(Subtract(VECTOR3_TO_VEC3V(camInterface::GetPos()), m_pPed->GetTransform().GetPosition())), ScalarV(square(20.0f))))
{
IK_DEV_SET_LOOK_AT_RETURN_CODE(LOOKIK_RC_RANGE, "CIkManager::LookAt");
return;
}
}
// Does the IK task exist?
if (HasSolver(ikSolverTypeBodyLook))
{
CBodyLookIkSolverProxy* pSolver = static_cast<CBodyLookIkSolverProxy*>(GetSolver(ikSolverTypeBodyLook));
if ((s8)priority < pSolver->GetPriority())
{
IK_DEV_SET_LOOK_AT_RETURN_CODE(LOOKIK_RC_PRIORITY, "CIkManager::LookAt");
return;
}
}
CBodyLookIkSolverProxy* pSolver = static_cast<CBodyLookIkSolverProxy*>(GetOrAddSolver(ikSolverTypeBodyLook));
if (pSolver)
{
// Build request
Vec3V vTargetOffset(V_ZERO);
if (pOffsetPos)
{
vTargetOffset = VECTOR3_TO_VEC3V(*pOffsetPos);
}
CIkRequestBodyLook request(pEntity, offsetBoneTag, vTargetOffset, !(flags & LF_WHILE_NOT_IN_FOV) ? LOOKIK_USE_FOV : 0, (s32)priority, hashKey);
request.SetLookAtFlags((s32)flags);
if (flags > 0)
{
ConvertLookAtFlagsToLookIk(flags, request);
}
if (!(flags & LF_USE_EYES_ONLY))
{
// Enable neck component by default
request.SetRotLimNeck(LOOKIK_ROT_ANGLE_YAW, Max(request.GetRotLimHead(LOOKIK_ROT_ANGLE_YAW), LOOKIK_ROT_LIM_WIDE));
request.SetRotLimNeck(LOOKIK_ROT_ANGLE_PITCH, (m_pPed->GetPedType() != PEDTYPE_ANIMAL) ? LOOKIK_ROT_LIM_NARROWEST : LOOKIK_ROT_LIM_WIDE);
if (!(flags & LF_USE_REF_DIR_ABSOLUTE))
{
// Set facing dir mode
request.SetRefDirHead(LOOKIK_REF_DIR_FACING);
request.SetRefDirNeck(LOOKIK_REF_DIR_FACING);
}
}
request.SetHoldTime(time >= 0 ? (u16)time : (u16)-1);
pSolver->Request(request);
}
}
//////////////////////////////////////////////////////////////////////////
bool CIkManager::IsLooking()
{
return HasSolver(ikSolverTypeBodyLook);
}
//////////////////////////////////////////////////////////////////////////
bool CIkManager::IsLooking(u32 hashKey)
{
if(HasSolver(ikSolverTypeBodyLook))
{
CBodyLookIkSolverProxy* pSolver = static_cast<CBodyLookIkSolverProxy*>(GetSolver(ikSolverTypeBodyLook));
if (pSolver->GetHashKey() == hashKey)
{
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::AbortLookAt(s32 UNUSED_PARAM(blendTime))
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeBodyLook))
{
//return;
}
//check if the solver exists
if (HasSolver(ikSolverTypeBodyLook))
{
static_cast<CBodyLookIkSolverProxy*>(GetSolver(ikSolverTypeBodyLook))->Abort();
}
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::AbortLookAt(s32 UNUSED_PARAM(blendTime), u32 hashKey)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeBodyLook))
{
//return;
}
//check if the solver exists
if (HasSolver(ikSolverTypeBodyLook))
{
CBodyLookIkSolverProxy* pSolver = static_cast<CBodyLookIkSolverProxy*>(GetSolver(ikSolverTypeBodyLook));
if (pSolver->GetHashKey() == hashKey)
{
pSolver->Abort();
}
}
}
//////////////////////////////////////////////////////////////////////////
const CEntity* CIkManager::GetLookAtEntity() const
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeBodyLook))
{
//return NULL;
}
if (HasSolver(ikSolverTypeBodyLook))
{
return static_cast<const CBodyLookIkSolverProxy*>(GetSolver(ikSolverTypeBodyLook))->GetTargetEntity();
}
return NULL;
}
//////////////////////////////////////////////////////////////////////////
const u32 CIkManager::GetLookAtFlags() const
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeBodyLook))
{
//return NULL;
}
if (HasSolver(ikSolverTypeBodyLook))
{
const CBodyLookIkSolverProxy* pSolver = static_cast<const CBodyLookIkSolverProxy*>(GetSolver(ikSolverTypeBodyLook));
s32 sLookAtFlags = pSolver->GetLookAtFlags();
if (sLookAtFlags == -1)
{
// LookIk was requested directly instead of through LookAt(), so generate an equivalent set of LookAt flags for now
return ConvertLookIkToLookAtFlags(pSolver);
}
return (u32)sLookAtFlags;
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
#if __BANK
const u32 CIkManager::GetLookAtHashKey() const
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeBodyLook))
{
//return 0;
}
if (HasSolver(ikSolverTypeBodyLook))
{
const CBodyLookIkSolverProxy* pSolver = static_cast<const CBodyLookIkSolverProxy*>(GetSolver(ikSolverTypeBodyLook));
return pSolver->GetHashKey();
}
return 0;
}
#endif
//////////////////////////////////////////////////////////////////////////
bool CIkManager::GetLookAtTargetPosition(Vector3& pos)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeBodyLook))
{
//return NULL;
}
if (HasSolver(ikSolverTypeBodyLook))
{
CBodyLookIkSolverProxy* pSolver = static_cast<CBodyLookIkSolverProxy*>(GetSolver(ikSolverTypeBodyLook));
if (pSolver)
{
pos = VEC3V_TO_VECTOR3(pSolver->GetTargetPosition());
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::ConvertLookAtFlagsToLookIk(u32 uFlags, CIkRequestBodyLook& request)
{
LookIkTurnRate eTurnRate = LOOKIK_TURN_RATE_NORMAL;
LookIkRotationLimit eYawLimit = LOOKIK_ROT_LIM_WIDE;
LookIkRotationLimit ePitchLimit = LOOKIK_ROT_LIM_WIDE;
u32 uRequestFlags = 0;
if (uFlags & LF_FROM_SCRIPT)
uRequestFlags |= LOOKIK_SCRIPT_REQUEST;
if (uFlags & LF_USE_CAMERA_FOCUS)
uRequestFlags |= LOOKIK_USE_CAMERA_FOCUS;
if (uFlags & LF_FAST_TURN_RATE)
eTurnRate = LOOKIK_TURN_RATE_FAST;
if (uFlags & LF_SLOW_TURN_RATE)
eTurnRate = LOOKIK_TURN_RATE_SLOW;
if (!(uFlags & LF_USE_EYES_ONLY))
{
if (uFlags & LF_WIDEST_YAW_LIMIT)
{
eYawLimit = LOOKIK_ROT_LIM_WIDEST;
}
else if (uFlags & LF_NARROW_YAW_LIMIT)
{
eYawLimit = LOOKIK_ROT_LIM_NARROW;
}
else if (uFlags & LF_NARROWEST_YAW_LIMIT)
{
eYawLimit = LOOKIK_ROT_LIM_NARROWEST;
}
if (uFlags & LF_WIDEST_PITCH_LIMIT)
{
ePitchLimit = LOOKIK_ROT_LIM_WIDEST;
}
else if (uFlags & LF_NARROW_PITCH_LIMIT)
{
ePitchLimit = LOOKIK_ROT_LIM_NARROW;
}
else if (uFlags & LF_NARROWEST_PITCH_LIMIT)
{
ePitchLimit = LOOKIK_ROT_LIM_NARROWEST;
}
}
else
{
// Disable head
eYawLimit = LOOKIK_ROT_LIM_OFF;
ePitchLimit = LOOKIK_ROT_LIM_OFF;
}
request.SetTurnRate(eTurnRate);
request.SetRotLimHead(LOOKIK_ROT_ANGLE_YAW, eYawLimit);
request.SetRotLimHead(LOOKIK_ROT_ANGLE_PITCH, ePitchLimit);
request.SetFlags(request.GetFlags() | uRequestFlags);
}
//////////////////////////////////////////////////////////////////////////
u32 CIkManager::ConvertLookIkToLookAtFlags(const CBodyLookIkSolverProxy* pSolver) const
{
ikAssert(pSolver);
u32 uLookAtFlags = 0;
u32 uFlags = pSolver->GetFlags();
if (uFlags & LOOKIK_USE_CAMERA_FOCUS)
uLookAtFlags |= LF_USE_CAMERA_FOCUS;
if (uFlags & LOOKIK_SCRIPT_REQUEST)
uLookAtFlags |= LF_FROM_SCRIPT;
if (!(uFlags & LOOKIK_USE_FOV))
uLookAtFlags |= LF_WHILE_NOT_IN_FOV;
LookIkTurnRate eTurnRate = (LookIkTurnRate)pSolver->GetTurnRate();
if (eTurnRate == LOOKIK_TURN_RATE_SLOW)
{
uLookAtFlags |= LF_SLOW_TURN_RATE;
}
else if (eTurnRate == LOOKIK_TURN_RATE_FAST)
{
uLookAtFlags |= LF_FAST_TURN_RATE;
}
LookIkRotationLimit eYawLimit = (LookIkRotationLimit)pSolver->GetHeadRotLim(LOOKIK_ROT_ANGLE_YAW);
LookIkRotationLimit ePitchLimit = (LookIkRotationLimit)pSolver->GetHeadRotLim(LOOKIK_ROT_ANGLE_PITCH);
if ((eYawLimit == LOOKIK_ROT_LIM_OFF) && (ePitchLimit == LOOKIK_ROT_LIM_OFF))
{
uLookAtFlags |= LF_USE_EYES_ONLY;
}
else
{
const u32 aLookAtYawLimitMapping[LOOKIK_ROT_LIM_NUM] =
{
0, LF_NARROWEST_YAW_LIMIT, LF_NARROW_YAW_LIMIT, LF_WIDE_YAW_LIMIT, LF_WIDEST_YAW_LIMIT
};
const u32 aLookAtPitchLimitMapping[LOOKIK_ROT_LIM_NUM] =
{
0, LF_NARROWEST_PITCH_LIMIT, LF_NARROW_PITCH_LIMIT, LF_WIDE_PITCH_LIMIT, LF_WIDEST_PITCH_LIMIT
};
uLookAtFlags |= aLookAtYawLimitMapping[eYawLimit];
uLookAtFlags |= aLookAtPitchLimitMapping[ePitchLimit];
}
return uLookAtFlags;
}
//////////////////////////////////////////////////////////////////////////
bool CIkManager::IsTargetWithinFOV(const Vector3& target, const Vector3 &min, const Vector3 &max)
{
ikAssert(m_pPed);
s32 headBoneIndex = m_pPed->GetBoneIndexFromBoneTag(BONETAG_HEAD);
Matrix34 globalHeadBoneMat;
m_pPed->GetGlobalMtx(headBoneIndex, globalHeadBoneMat);
s32 neckBoneIndex = m_pPed->GetBoneIndexFromBoneTag(BONETAG_NECK);
Matrix34 globalNeckBoneMat;
m_pPed->GetGlobalMtx(neckBoneIndex, globalNeckBoneMat);
Matrix34 globalNeckRotHeadPosMat(globalNeckBoneMat);
globalNeckRotHeadPosMat.d = globalHeadBoneMat.d;
// Find the goal relative to the neck bones rotation and the head bones position
Vector3 localTarget(VEC3_ZERO);
globalNeckRotHeadPosMat.UnTransform(target, localTarget);
// Avoid gimbal lock
static dev_float fEpsilon = 0.001f;
if (rage::Abs(localTarget.y) < fEpsilon && rage::Abs(localTarget.z) < fEpsilon)
{
localTarget.y = fEpsilon;
localTarget.z = 0.0f;
}
// Calculate the target yaw and the target pitch
float targetYaw = rage::Atan2f(localTarget.z, localTarget.y);
float targetPitch = rage::Atan2f(-localTarget.x, sqrtf(localTarget.y*localTarget.y+localTarget.z*localTarget.z));
bool validYaw = true;
if(targetYaw > max.x)
{
validYaw = false;
}
else if(targetYaw < min.x)
{
validYaw = false;
}
bool validPitch = true;
if(targetPitch > max.z)
{
validPitch = false;
}
else if(targetPitch < min.z)
{
validPitch = false;
}
return (validYaw && validPitch);
}
//////////////////////////////////////////////////////////////////////////
bool CIkManager::Request(const CIkRequestArm& request FPS_MODE_SUPPORTED_ONLY(, s32 pass))
{
#if FPS_MODE_SUPPORTED
bool bFirstPersonRequestAdded = false;
if ((pass&IK_PASS_FPS)!=0)
{
// lazy init the request structure if need be:
FirstPersonPassRequests& requests = GetFpsRequests();
s32 arm = (request.GetArm() == IK_ARM_LEFT) ? CArmIkSolver::LEFT_ARM : CArmIkSolver::RIGHT_ARM;
requests.m_armRequests[arm] = request;
requests.m_hasRequests[(arm == CArmIkSolver::LEFT_ARM ? kRequestArmLeft : kRequestArmRight)] = true;
bFirstPersonRequestAdded = true;
}
if ((pass&IK_PASS_MAIN)==0)
{
return bFirstPersonRequestAdded;
}
#endif // FPS_MODE_SUPPORTED
if (!IsSupported(IKManagerSolverTypes::ikSolverTypeArm))
{
return false;
}
CArmIkSolver* pSolver = static_cast<CArmIkSolver*>(GetOrAddSolver(ikSolverTypeArm));
if (pSolver)
{
pSolver->Request(request);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CIkManager::Request(const CIkRequestResetArm& request)
{
if (!IsSupported(IKManagerSolverTypes::ikSolverTypeArm))
{
return false;
}
CArmIkSolver* pSolver = static_cast<CArmIkSolver*>(GetSolver(ikSolverTypeArm));
if (pSolver)
{
pSolver->ResetArm((request.GetArm() == IK_ARM_RIGHT) ? crIKSolverArms::RIGHT_ARM : crIKSolverArms::LEFT_ARM);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CIkManager::Request(const CIkRequestBodyLook& request FPS_MODE_SUPPORTED_ONLY(, s32 pass))
{
#if FPS_MODE_SUPPORTED
bool bFirstPersonRequestAdded = false;
if ((pass & IK_PASS_FPS) != 0)
{
// lazy init the request structure if need be:
FirstPersonPassRequests& requests = GetFpsRequests();
requests.m_lookRequest = request;
requests.m_pLookTarget = request.GetTargetEntity();
requests.m_bLookTargetParam = requests.m_pLookTarget != NULL;
requests.m_hasRequests[kRequestLook] = true;
bFirstPersonRequestAdded = true;
}
if ((pass & IK_PASS_MAIN) == 0)
{
return bFirstPersonRequestAdded;
}
#endif // FPS_MODE_SUPPORTED
if (!IsSupported(IKManagerSolverTypes::ikSolverTypeBodyLook))
{
return false;
}
if (HasSolver(ikSolverTypeBodyLook))
{
if (request.GetRequestPriority() < (s32)static_cast<CBodyLookIkSolverProxy*>(GetSolver(ikSolverTypeBodyLook))->GetPriority())
{
return false;
}
}
if ((request.GetFlags() & LOOKIK_SCRIPT_REQUEST) == 0)
{
if (m_pPed->GetIsVisibleInSomeViewportThisFrame() == false)
{
return false;
}
if (IsGreaterThanAll(MagSquared(Subtract(VECTOR3_TO_VEC3V(camInterface::GetPos()), m_pPed->GetTransform().GetPosition())), ScalarV(square(60.0f))))
{
return false;
}
}
CBodyLookIkSolverProxy* pSolver = static_cast<CBodyLookIkSolverProxy*>(GetOrAddSolver(ikSolverTypeBodyLook));
if (pSolver)
{
pSolver->Request(request);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::SetAbsoluteArmIKTarget(crIKSolverArms::eArms arm, const Vector3& target, s32 controlFlags, float blendInTime, float blendOutTime, float blendInRange, float blendOutRange)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeArm))
{
//return;
}
CArmIkSolver* pSolver = static_cast<CArmIkSolver*>(GetOrAddSolver(ikSolverTypeArm));
if (pSolver)
{
pSolver->SetAbsoluteTarget(arm, RCC_VEC3V(target), controlFlags, blendInTime, blendOutTime, blendInRange, blendOutRange);
}
}
///////////////////////////////////////////////////////////////////////////////
void CIkManager::SetRelativeArmIKTarget(crIKSolverArms::eArms arm, const CDynamicEntity* entity, int boneIdx, const Vector3& offset, s32 controlFlags, float blendInTime, float blendOutTime, float blendInRange, float blendOutRange)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeArm))
{
//return;
}
CArmIkSolver* pSolver = static_cast<CArmIkSolver*>(GetOrAddSolver(ikSolverTypeArm));
if (pSolver)
{
pSolver->SetRelativeTarget(arm, entity, boneIdx, RCC_VEC3V(offset), controlFlags, blendInTime, blendOutTime, blendInRange, blendOutRange);
}
}
///////////////////////////////////////////////////////////////////////////////
#if FPS_MODE_SUPPORTED
void CIkManager::SetRelativeArmIKTarget(crIKSolverArms::eArms arm, const CDynamicEntity* targetEntity, int targetBoneIdx, const Vector3& targetOffset, const Quaternion& targetRotation, s32 flags, float blendInTime, float blendOutTime, float blendInRange, float blendOutRange)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeArm))
{
//return;
}
CArmIkSolver* pSolver = static_cast<CArmIkSolver*>(GetOrAddSolver(ikSolverTypeArm));
if (pSolver)
{
pSolver->SetRelativeTarget(arm, targetEntity, targetBoneIdx, RCC_VEC3V(targetOffset), RCC_QUATV(targetRotation), flags, blendInTime, blendOutTime, blendInRange, blendOutRange);
}
}
#endif // FPS_MODE_SUPPORTED
///////////////////////////////////////////////////////////////////////////////
void CIkManager::AimWeapon(crIKSolverArms::eArms eArm, s32 sFlags)
{
if (m_pPed->GetWeaponManager() == NULL)
return;
const CWeapon* pWeapon = m_pPed->GetWeaponManager()->GetEquippedWeapon();
if (pWeapon == NULL)
return;
eAnimBoneTag eBoneTag = (eArm == crIKSolverArms::RIGHT_ARM) ? BONETAG_R_HAND : BONETAG_L_HAND;
SetRelativeArmIKTarget(eArm, m_pPed, m_pPed->GetBoneIndexFromBoneTag(eBoneTag), Vector3(Vector3::ZeroType), AIK_TARGET_WRT_HANDBONE | sFlags, PEDIK_ARMS_FADEIN_TIME_QUICK, PEDIK_ARMS_FADEOUT_TIME_QUICK);
}
///////////////////////////////////////////////////////////////////////////////
bool CIkManager::PointGunAtPosition(const Vector3& pos, float fBlendAmount)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return false;
}
CTorsoIkSolver *gunSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (gunSolver)
{
return gunSolver->PointGunAtPosition(pos,fBlendAmount);
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
bool CIkManager::PointGunInDirection(float yaw, float pitch, bool bRollPitch, float fBlendAmount)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return false;
}
CTorsoIkSolver *gunSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (gunSolver)
{
return gunSolver->PointGunInDirection(yaw,pitch,bRollPitch,fBlendAmount);
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
void CIkManager::GetGunAimAnglesFromPosition(const Vector3 &pos, float &yaw, float &pitch)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return;
}
CTorsoIkSolver *gunSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (gunSolver)
{
gunSolver->GetGunAimAnglesFromPosition(pos,yaw,pitch);
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
}
}
///////////////////////////////////////////////////////////////////////////////
void CIkManager::GetGunAimPositionFromAngles(float yaw, float pitch, Vector3 &pos)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return;
}
CTorsoIkSolver *gunSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (gunSolver)
{
gunSolver->GetGunAimPositionFromAngles(yaw,pitch,pos);
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
}
}
///////////////////////////////////////////////////////////////////////////////
void CIkManager::GetGunAimDirFromGlobalTarget(const Vector3 &globalTarget, Vector3 &normAimDir)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return;
}
CTorsoIkSolver *gunSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (gunSolver)
{
gunSolver->GetGunAimDirFromGlobalTarget(globalTarget,normAimDir);
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
}
}
///////////////////////////////////////////////////////////////////////////////
void CIkManager::GetGunGlobalTargetFromAimDir(Vector3 &globalTarget, const Vector3 &normAimDir)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return;
}
CTorsoIkSolver *gunSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (gunSolver)
{
gunSolver->GetGunGlobalTargetFromAimDir(globalTarget, normAimDir);
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
}
}
//////////////////////////////////////////////////////////////////////////
u32 CIkManager::GetTorsoSolverStatus()
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
return 0;
}
CTorsoIkSolver* pSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (pSolver)
{
return pSolver->GetTorsoSolverStatus();
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
return 0;
}
}
//////////////////////////////////////////////////////////////////////////
float CIkManager::GetTorsoMinYaw()
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return 0.0f;
}
CTorsoIkSolver* pSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (pSolver)
{
return pSolver->GetTorsoMinYaw();
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
return 0.0f;
}
}
//////////////////////////////////////////////////////////////////////////
float CIkManager::GetTorsoMaxYaw()
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return 0.0f;
}
CTorsoIkSolver* pSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (pSolver)
{
return pSolver->GetTorsoMaxYaw();
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
return 0.0f;
}
}
//////////////////////////////////////////////////////////////////////////
float CIkManager::GetTorsoMinPitch()
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return 0.0f;
}
CTorsoIkSolver* pSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (pSolver)
{
return pSolver->GetTorsoMinPitch();
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
return 0.0f;
}
}
//////////////////////////////////////////////////////////////////////////
float CIkManager::GetTorsoMaxPitch()
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return 0.0f;
}
CTorsoIkSolver* pSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (pSolver)
{
return pSolver->GetTorsoMaxPitch();
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
return 0.0f;
}
}
//////////////////////////////////////////////////////////////////////////
float CIkManager::GetTorsoOffsetYaw() const
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return 0.0f;
}
const CTorsoIkSolver* pSolver = static_cast<const CTorsoIkSolver*>(GetSolver(ikSolverTypeTorso));
if (pSolver)
{
return pSolver->GetTorsoOffsetYaw();
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
return 0.0f;
}
}
//////////////////////////////////////////////////////////////////////////
float CIkManager::GetTorsoBlend() const
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return 0.0f;
}
const CTorsoIkSolver* pSolver = static_cast<const CTorsoIkSolver*>(GetSolver(ikSolverTypeTorso));
if (pSolver)
{
return pSolver->GetTorsoBlend();
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
return 0.0f;
}
}
//////////////////////////////////////////////////////////////////////////
const Vector3& CIkManager::GetTorsoTarget()
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return VEC3_ZERO;
}
CTorsoIkSolver* pSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (pSolver)
{
return pSolver->GetTorsoTarget(); //need fatal assert here
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
return VEC3_ZERO;
}
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::SetTorsoMinYaw(float minYaw)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return;
}
CTorsoIkSolver* pSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (pSolver)
{
pSolver->SetTorsoMinYaw(minYaw);
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
}
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::SetTorsoMaxYaw(float maxYaw)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return;
}
CTorsoIkSolver* pSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (pSolver)
{
pSolver->SetTorsoMaxYaw(maxYaw);
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
}
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::SetTorsoMinPitch( float minPitch )
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return;
}
CTorsoIkSolver *gunSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (gunSolver)
{
gunSolver->SetTorsoMinPitch( minPitch );
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
}
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::SetTorsoMaxPitch( float maxPitch )
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return;
}
CTorsoIkSolver *gunSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (gunSolver)
{
gunSolver->SetTorsoMaxPitch( maxPitch );
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
}
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::SetTorsoOffsetYaw(float torsoOffsetYaw)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return;
}
CTorsoIkSolver* pSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (pSolver)
{
pSolver->SetTorsoOffsetYaw(torsoOffsetYaw);
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
}
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::SetTorsoYawDeltaLimits(float yawDelta, float yawDeltaSmoothRate, float yawDeltaSmoothEase)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return;
}
CTorsoIkSolver* pSolver = static_cast<CTorsoIkSolver*>(GetOrAddSolver(ikSolverTypeTorso));
if (pSolver)
{
pSolver->SetTorsoYawDeltaLimits(yawDelta, yawDeltaSmoothRate, yawDeltaSmoothEase);
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
}
}
//////////////////////////////////////////////////////////////////////////
float CIkManager::GetTorsoYaw() const
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return 0.0f;
}
const CTorsoIkSolver* pSolver = static_cast<const CTorsoIkSolver*>(GetSolver(ikSolverTypeTorso));
if (pSolver)
{
return pSolver->GetTorsoYaw();
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
return 0.0f;
}
}
//////////////////////////////////////////////////////////////////////////
float CIkManager::GetTorsoYawDelta() const
{
const CTorsoIkSolver* pSolver = static_cast<const CTorsoIkSolver*>(GetSolver(ikSolverTypeTorso));
if (pSolver)
{
return pSolver->GetYawDelta();
}
return 0.0f;
}
//////////////////////////////////////////////////////////////////////////
float CIkManager::GetTorsoPitch() const
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return 0.0f;
}
const CTorsoIkSolver* pSolver = static_cast<const CTorsoIkSolver*>(GetSolver(ikSolverTypeTorso));
if (pSolver)
{
return pSolver->GetTorsoPitch();
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
return 0.0f;
}
}
///////////////////////////////////////////////////////////////////////////////
void CIkManager::SetDisablePitchFixUp(bool disablePitchFixUp)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeTorso))
{
//return;
}
CTorsoIkSolver* pSolver = static_cast<CTorsoIkSolver*>(GetSolver(ikSolverTypeTorso));
if (pSolver)
{
pSolver->SetDisablePitchFixUp(disablePitchFixUp);
}
else
{
//ikAssertf(0, "This ped's IK manager does not contain an torso solver.");
}
}
//////////////////////////////////////////////////////////////////////////
bool CIkManager::CanUseLegIK(bool& bCanUsePelvis, bool& bCanUseFade)
{
if (IsFlagSet(PEDIK_LEGS_AND_PELVIS_OFF))
{
return false;
}
if (m_pPed->GetMovePed().GetBlendHelper().IsFlagSet(APF_BLOCK_IK | APF_BLOCK_LEG_IK))
{
return false;
}
// Allows script to turn off leg IK
if (m_pPed->m_PedConfigFlags.GetPedLegIkMode() == LEG_IK_MODE_OFF)
{
if (!IsFlagSet(PEDIK_LEGS_AND_PELVIS_FADE_ON))
{
// Blend out instantly
bCanUseFade = false;
}
return false;
}
// Don't use Leg Ik when in ragdoll or in water
if (m_pPed->GetUsingRagdoll() || (m_pPed->GetRagdollState() > RAGDOLL_STATE_PHYS_ACTIVATE) || m_pPed->GetIsInWater())
{
// Blend out instantly
bCanUseFade = false;
return false;
}
bCanUsePelvis = true;
// Don't use Leg Ik when attached (unless attached to the ground or still on a bike).
bool bIsValid = false;
if (m_pPed->GetPedResetFlag(CPED_RESET_FLAG_IsVaulting))
{
if (m_pPed->GetPedResetFlag(CPED_RESET_FLAG_RequiresLegIk))
{
bCanUsePelvis = false;
}
}
else if (m_pPed->GetIsAttached())
{
CEntity* pAttachEntity = static_cast<CEntity*>(m_pPed->GetAttachParent());
if (pAttachEntity && pAttachEntity->GetIsTypeVehicle())
{
bIsValid = m_pPed->GetPedResetFlag(CPED_RESET_FLAG_RequiresLegIk);
if (bIsValid)
{
bCanUsePelvis = false;
}
}
if (!bIsValid && !m_pPed->GetIsAttachedToGround())
{
if (!m_pPed->GetIkManager().IsFlagSet(PEDIK_LEGS_AND_PELVIS_FADE_ON))
{
// Blend out instantly
bCanUseFade = false;
}
return false;
}
}
const bool bInAir = m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsInTheAir) && !m_pPed->GetPedResetFlag(CPED_RESET_FLAG_IsJumping);
const bool bInWrithe = m_pPed->GetPedResetFlag(CPED_RESET_FLAG_IsInWrithe);
const bool bIsDivingOutOfVehicle = false; // m_pPed->GetPedIntelligence()->GetQueriableInterface()->GetStateForTaskType(CTaskTypes::TASK_EXIT_VEHICLE_EXIT_SEAT) == CTaskExitVehicleExitSeat::State_JumpOutOfSeat ? true : false;
bool bIsClimbingLadder = m_pPed->GetPedIntelligence()->IsPedClimbingLadder();
if (m_pPed->GetPedResetFlag(CPED_RESET_FLAG_RequiresLegIk) && bIsClimbingLadder)
{
bCanUseFade = false;
bIsClimbingLadder = false;
}
if ((!bIsValid && m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle))
|| bInAir
|| bIsClimbingLadder
|| bIsDivingOutOfVehicle
|| bInWrithe)
{
if (!IsFlagSet(PEDIK_LEGS_AND_PELVIS_FADE_ON))
{
if (m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle))
{
// Blend out instantly
bCanUseFade = false;
}
}
// Blend out normally
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
#if FPS_MODE_SUPPORTED
bool CIkManager::IsFirstPersonStealthTransition() const
{
if (HasSolver(ikSolverTypeLeg))
{
const CLegIkSolver* pSolver = static_cast<const CLegIkSolver*>(static_cast<const CLegIkSolverProxy*>(GetSolver(ikSolverTypeLeg))->GetSolver());
if (pSolver)
{
return pSolver->IsFirstPersonStealthTransition();
}
}
return false;
}
#endif // FPS_MODE_SUPPORTED
//////////////////////////////////////////////////////////////////////////
#if FPS_MODE_SUPPORTED
bool CIkManager::IsFirstPersonModeTransition() const
{
if (HasSolver(ikSolverTypeLeg))
{
const CLegIkSolver* pSolver = static_cast<const CLegIkSolver*>(static_cast<const CLegIkSolverProxy*>(GetSolver(ikSolverTypeLeg))->GetSolver());
if (pSolver)
{
return pSolver->IsFirstPersonModeTransition();
}
}
return false;
}
#endif // FPS_MODE_SUPPORTED
//////////////////////////////////////////////////////////////////////////
float CIkManager::GetPelvisDeltaZForCamera(bool shouldIgnoreIkControl) const
{
if (HasSolver(ikSolverTypeLeg))
{
const CLegIkSolver* pSolver = static_cast<const CLegIkSolver*>(static_cast<const CLegIkSolverProxy*>(GetSolver(ikSolverTypeLeg))->GetSolver());
if (pSolver)
{
return pSolver->GetPelvisDeltaZForCamera(shouldIgnoreIkControl);
}
}
return 0.0f;
}
//////////////////////////////////////////////////////////////////////////
u32 CIkManager::GetDefaultLegIkMode(const CPed* pPed)
{
ikAssert(pPed);
return pPed->IsLocalPlayer() ? LEG_IK_MODE_FULL : LEG_IK_MODE_PARTIAL;
}
//////////////////////////////////////////////////////////////////////////
bool CIkManager::Request(const CIkRequestBodyReact& request)
{
eIkSolverType solverType = IKManagerSolverTypes::ikSolverTypeTorsoReact;
#if IK_QUAD_REACT_SOLVER_ENABLE
const CBaseCapsuleInfo* pCapsuleInfo = m_pPed->GetCapsuleInfo();
if (pCapsuleInfo && pCapsuleInfo->IsQuadruped())
{
solverType = IKManagerSolverTypes::ikSolverTypeQuadrupedReact;
}
#endif // IK_QUAD_REACT_SOLVER_ENABLE
if (!IsSupported(solverType))
{
return false;
}
if (m_pPed->GetPedIntelligence()->IsPedClimbingLadder())
{
return false;
}
if (m_pPed->GetIsVisibleInSomeViewportThisFrame() == false)
{
return false;
}
crIKSolverBase* pSolver = GetOrAddSolver(solverType);
if (pSolver)
{
if (solverType == IKManagerSolverTypes::ikSolverTypeTorsoReact)
{
CTorsoReactIkSolver* pReactSolver = static_cast<CTorsoReactIkSolver*>(pSolver);
pReactSolver->Request(request);
}
#if IK_QUAD_REACT_SOLVER_ENABLE
else if (solverType == IKManagerSolverTypes::ikSolverTypeQuadrupedReact)
{
CQuadrupedReactSolver* pReactSolver = static_cast<CQuadrupedReactSolver*>(pSolver);
pReactSolver->Request(request);
}
#endif // IK_QUAD_REACT_SOLVER_ENABLE
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CIkManager::Request(const CIkRequestBodyRecoil& request)
{
if (!m_pPed->GetCapsuleInfo()->IsBiped())
{
return false;
}
if (!IsSupported(IKManagerSolverTypes::ikSolverTypeBodyRecoil))
{
return false;
}
if (!m_pPed->GetIsVisibleInSomeViewportThisFrame())
{
return false;
}
if (IsGreaterThanAll(MagSquared(Subtract(VECTOR3_TO_VEC3V(camInterface::GetPos()), m_pPed->GetTransform().GetPosition())), ScalarV(square(50.0f))))
{
return false;
}
CBodyRecoilIkSolver* pSolver = static_cast<CBodyRecoilIkSolver*>(GetOrAddSolver(ikSolverTypeBodyRecoil));
if (pSolver)
{
pSolver->Request(request);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CIkManager::Request(const CIkRequestLeg& request FPS_MODE_SUPPORTED_ONLY(, s32 pass))
{
#if FPS_MODE_SUPPORTED
bool bFirstPersonRequestAdded = false;
if ((pass&IK_PASS_FPS)!=0)
{
// lazy init the request structure if need be:
FirstPersonPassRequests& requests = GetFpsRequests();
requests.m_legRequest = request;
requests.m_hasRequests[kRequestLeg] = true;
bFirstPersonRequestAdded = true;
}
if ((pass&IK_PASS_MAIN)==0)
{
return bFirstPersonRequestAdded;
}
#endif // FPS_MODE_SUPPORTED
CIkRequestLeg solverRequest = request;
bool bCanUsePelvis = true;
bool bCanUseFade = true;
eIkSolverType solverType = IKManagerSolverTypes::ikSolverTypeLeg;
#if IK_QUAD_LEG_SOLVER_ENABLE
const CBaseCapsuleInfo* pCapsuleInfo = m_pPed->GetCapsuleInfo();
if (pCapsuleInfo && pCapsuleInfo->IsQuadruped())
{
// Default is low LOD mode unless the local player is the rider
const CPed* pRider = m_pPed->GetHorseComponent() ? m_pPed->GetHorseComponent()->GetRider() : NULL;
if (pRider && pRider->IsLocalPlayer())
{
solverRequest.SetMode(LEG_IK_MODE_FULL);
}
solverType = IKManagerSolverTypes::ikSolverTypeQuadLeg;
}
#endif // IK_QUAD_LEG_SOLVER_ENABLE
if (!IsSupported(solverType))
{
return false;
}
if (!m_pPed->IsLocalPlayer() && !m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IgnoreLegIkRestrictions))
{
if (m_pPed->GetPedAiLod().IsLodFlagSet(CPedAILod::AL_LodMotionTask))
{
return false;
}
if (!m_pPed->GetIsVisibleInSomeViewportThisFrame())
{
return false;
}
if (IsGreaterThanAll(MagSquared(Subtract(VECTOR3_TO_VEC3V(camInterface::GetPos()), m_pPed->GetTransform().GetPosition())), ScalarV(square(25.0f))))
{
return false;
}
}
if (!CanUseLegIK(bCanUsePelvis, bCanUseFade))
{
return false;
}
if (IsFlagSet(PEDIK_LEGS_USE_ANIM_BLOCK_TAGS | PEDIK_LEGS_USE_ANIM_ALLOW_TAGS))
{
fwAnimDirector* pAnimDirector = m_pPed->GetAnimDirector();
if (pAnimDirector)
{
const CClipEventTags::CLegsIKEventTag* pProp = static_cast<const CClipEventTags::CLegsIKEventTag*>(pAnimDirector->FindPropertyInMoveNetworks(CClipEventTags::LegsIk));
if (IsFlagSet(PEDIK_LEGS_USE_ANIM_BLOCK_TAGS) && pProp && !pProp->GetAllowed())
{
return false;
}
if (IsFlagSet(PEDIK_LEGS_USE_ANIM_ALLOW_TAGS) && ((pProp == NULL) || !pProp->GetAllowed()))
{
return false;
}
}
}
crIKSolverBase* pSolver = GetOrAddSolver(solverType);
if (pSolver)
{
if (solverType == IKManagerSolverTypes::ikSolverTypeLeg)
{
CLegIkSolverProxy* pLegSolver = static_cast<CLegIkSolverProxy*>(pSolver);
pLegSolver->Request(solverRequest);
}
#if IK_QUAD_LEG_SOLVER_ENABLE
else if (solverType == IKManagerSolverTypes::ikSolverTypeQuadLeg)
{
CQuadLegIkSolverProxy* pQuadLegSolver = static_cast<CQuadLegIkSolverProxy*>(pSolver);
pQuadLegSolver->Request(solverRequest);
}
#endif // IK_QUAD_LEG_SOLVER_ENABLE
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CIkManager::Request(const CIkRequestTorsoVehicle& request)
{
if (!m_pPed->GetCapsuleInfo()->IsBiped())
{
return false;
}
if (!IsSupported(IKManagerSolverTypes::ikSolverTypeTorsoVehicle))
{
return false;
}
if (!m_pPed->GetPedResetFlag(CPED_RESET_FLAG_IsEnteringVehicle))
{
fwAttachmentEntityExtension* pExtension = m_pPed->GetAttachmentExtension();
if ((pExtension == NULL) || (pExtension->GetAttachParentForced() == NULL))
{
return false;
}
}
if (m_pPed->GetMovePed().GetBlendHelper().IsFlagSet(APF_BLOCK_IK) || m_pPed->GetMovePed().GetSecondaryBlendHelper().IsFlagSet(APF_BLOCK_IK))
{
return false;
}
if (m_pPed->GetIsVisibleInSomeViewportThisFrame() == false)
{
return false;
}
CTorsoVehicleIkSolverProxy* pSolver = static_cast<CTorsoVehicleIkSolverProxy*>(GetOrAddSolver(ikSolverTypeTorsoVehicle));
if (pSolver)
{
pSolver->Request(request);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::AddRootSlopeFixup(float fBlendRate)
{
if (m_pPed->GetIKSettings().IsIKFlagDisabled(ikSolverTypeRootSlopeFixup))
{
return;
}
CRootSlopeFixupIkSolver* pSolver = static_cast<CRootSlopeFixupIkSolver*>(GetOrAddSolver(ikSolverTypeRootSlopeFixup));
if (pSolver)
{
pSolver->AddFixup(fBlendRate);
}
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::OrientateBoneATowardBoneB(s32 boneAIdx, s32 boneBIdx)
{
crSkeleton* pSkel = m_pPed->GetSkeleton();
if (ikVerifyf(pSkel, "No valid skeleton!"))
{
// Orientate (the x-axis of) matrix A toward the position of matrix b
Matrix34 MA;
pSkel->GetGlobalMtx(boneAIdx, RC_MAT34V(MA));
Matrix34 MB;
pSkel->GetGlobalMtx(boneBIdx, RC_MAT34V(MB));
Vector3 dir= MB.d-MA.d;
ikAssert(dir.Mag()>0.0f);
dir.NormalizeSafe();
Matrix34 res;
res.a=dir;
res.c=MA.c;
res.b.Cross(res.c,res.a);
res.c.Cross(res.a,res.b);
res.Normalize();
MA.a=res.a;
MA.b=res.b;
MA.c=res.c;
ikAssert(rage::FPIsFinite(MA.a.x));
ikAssert(rage::FPIsFinite(MA.b.y));
ikAssert(rage::FPIsFinite(MA.c.z));
pSkel->SetGlobalMtx(boneAIdx, RC_MAT34V(MA));
}
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::SetOverrideLegIKBlendTime(float fTime)
{
if (HasSolver(ikSolverTypeLeg))
{
static_cast<CLegIkSolverProxy*>(GetSolver(ikSolverTypeLeg))->OverrideBlendTime(fTime);
}
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::ResetOverrideLegIKBlendTime()
{
if (HasSolver(ikSolverTypeLeg))
{
static_cast<CLegIkSolverProxy*>(GetSolver(ikSolverTypeLeg))->OverrideBlendTime(0.0f);
}
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::SetIkTarget(eIkPart ePart, const CEntity* pTargetEntity, eAnimBoneTag eTargetBoneTag, const Vector3& vTargetOffset, u32 uFlags, int iBlendInTimeMS, int iBlendOutTimeMS, int iPriority)
{
switch (ePart)
{
case IK_PART_HEAD:
{
CIkRequestBodyLook request(pTargetEntity, eTargetBoneTag, RCC_VEC3V(vTargetOffset), uFlags, iPriority);
// Enable neck component by default to go along with head and eyes
request.SetRotLimNeck(LOOKIK_ROT_ANGLE_YAW, LOOKIK_ROT_LIM_WIDE);
request.SetRotLimNeck(LOOKIK_ROT_ANGLE_PITCH, LOOKIK_ROT_LIM_NARROW);
// Use facing direction mode
request.SetRefDirHead(LOOKIK_REF_DIR_FACING);
request.SetRefDirNeck(LOOKIK_REF_DIR_FACING);
Request(request);
break;
}
case IK_PART_ARM_LEFT:
case IK_PART_ARM_RIGHT:
{
const CDynamicEntity* pDynamicEntity = dynamic_cast<const CDynamicEntity*>(pTargetEntity);
int iTargetBoneIdx = -1;
if (pDynamicEntity && (eTargetBoneTag != BONETAG_INVALID))
{
crSkeleton* pSkeleton = pDynamicEntity->GetSkeleton();
if (pSkeleton)
{
pSkeleton->GetSkeletonData().ConvertBoneIdToIndex((u16)eTargetBoneTag, iTargetBoneIdx);
}
}
SetRelativeArmIKTarget((ePart == IK_PART_ARM_LEFT) ? crIKSolverArms::LEFT_ARM : crIKSolverArms::RIGHT_ARM,
pDynamicEntity,
iTargetBoneIdx,
vTargetOffset,
(s32)uFlags,
(iBlendInTimeMS >= 0) ? (float)iBlendInTimeMS / 1000.0f : PEDIK_ARMS_FADEIN_TIME,
(iBlendOutTimeMS >= 0) ? (float)iBlendOutTimeMS / 1000.0f : PEDIK_ARMS_FADEOUT_TIME);
break;
}
case IK_PART_SPINE:
case IK_PART_LEG_LEFT:
case IK_PART_LEG_RIGHT:
{
ikAssertf(false, "CIkManager::SetIkTarget - Unsupported IK part");
break;
}
case IK_PART_INVALID:
default:
{
ikAssertf(false, "CIkManager::SetIkTarget - Invalid IK part");
}
};
}
//////////////////////////////////////////////////////////////////////////
float CIkManager::GetSpringStrengthForStairs()
{
if (HasSolver(ikSolverTypeLeg))
{
CLegIkSolver* pSolver = static_cast<CLegIkSolver*>(static_cast<CLegIkSolverProxy*>(GetSolver(ikSolverTypeLeg))->GetSolver());
if (pSolver)
{
return pSolver->GetSpringStrengthForStairs();
}
}
return 1.0f;
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::InitHighHeelData()
{
// Offsets taken from high heel expression. Needs to be standardized into a meta value.
const float kfHighHeelOffset = 0.0864783f;
m_HighHeelData.m_fHeight = kfHighHeelOffset;
m_HighHeelData.m_fPreExpressionHeight = 0.0f;
m_HighHeelData.m_RootBoneIdx = -1;
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::BeginHighHeelData(float deltaTime)
{
if (ms_bSampleHighHeelHeight && m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_HasHighHeels) && !m_pPed->GetUsingRagdoll() && (deltaTime > 0.0f))
{
crSkeleton* pSkeleton = m_pPed->GetSkeleton();
if (pSkeleton)
{
if (m_HighHeelData.m_RootBoneIdx == -1)
{
pSkeleton->GetSkeletonData().ConvertBoneIdToIndex((u16)BONETAG_ROOT, m_HighHeelData.m_RootBoneIdx);
}
if (m_HighHeelData.m_RootBoneIdx >= 0)
{
m_HighHeelData.m_fPreExpressionHeight = pSkeleton->GetObjectMtx(m_HighHeelData.m_RootBoneIdx).GetCol3().GetZf();
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::EndHighHeelData(float deltaTime)
{
if (ms_bSampleHighHeelHeight && m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_HasHighHeels) && !m_pPed->GetUsingRagdoll() && (deltaTime > 0.0f))
{
crSkeleton* pSkeleton = m_pPed->GetSkeleton();
if (pSkeleton)
{
if (m_HighHeelData.m_RootBoneIdx >= 0)
{
float fPostExpressionHeight = pSkeleton->GetObjectMtx(m_HighHeelData.m_RootBoneIdx).GetCol3().GetZf();
// Remove leg solver contribution
if (HasSolver(ikSolverTypeLeg))
{
const CLegIkSolver* pLegSolver = static_cast<const CLegIkSolver*>(static_cast<const CLegIkSolverProxy*>(GetSolver(ikSolverTypeLeg))->GetSolver());
if (pLegSolver)
{
fPostExpressionHeight -= pLegSolver->GetPelvisDeltaZForCamera(true);
}
}
fPostExpressionHeight -= m_HighHeelData.m_fPreExpressionHeight;
const float fHighHeelBlend = m_pPed->GetHighHeelExpressionDOF();
const float kfTolerance = 0.9999f;
if (fHighHeelBlend >= kfTolerance)
{
fPostExpressionHeight /= fHighHeelBlend;
TUNE_GROUP_FLOAT(LEG_IK, HighHeelOffsetBlendRate, 8.0f, 0.0f, 20.0f, 0.01f);
m_HighHeelData.m_fHeight = Lerp(Min(HighHeelOffsetBlendRate * deltaTime, 1.0f), m_HighHeelData.m_fHeight, fabsf(fPostExpressionHeight));
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
#if __IK_DEV
void CIkManager::DebugRenderPed()
{
if (CBaseIkManager::ms_DisableAllSolvers)
{
return;
}
for(u32 i=0; i<ikSolverTypeCount; i++)
{
crIKSolverBase* solver = GetSolver((eIkSolverType)i);
if (solver && !solver->IsDead() && !CBaseIkManager::ms_DisableSolvers[i] )
{
solver->DebugRender();
}
}
}
#endif //__IK_DEV
///////////////////////////////////////////////////////////////////////////////
#if FPS_MODE_SUPPORTED
CIkManager::FirstPersonPassRequests& CIkManager::GetFpsRequests()
{
if (!m_pFirstPersonRequests)
{
m_pFirstPersonRequests = rage_new FirstPersonPassRequests();
}
ikAssertf(m_pFirstPersonRequests, "Failed to allocate Fps IK request structure! Is the game out of memory?");
return *m_pFirstPersonRequests;
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::CacheInputFrameForFpsUpdatePass(CPed& ped)
{
crSkeleton* pSkel = ped.GetSkeleton();
if (pSkel)
{
if (!m_pThirdPersonInputFrame)
{
m_pThirdPersonInputFrame = ped.GetAnimDirector()->GetFrameBuffer().AllocateFrame();
}
m_pThirdPersonInputFrame->InversePose(*pSkel);
crCreature *pCreature = ped.GetCreature();
if(pCreature)
{
const crCreatureComponentExtraDofs *pExtraDofs = pCreature->FindComponent<crCreatureComponentExtraDofs>();
if(pExtraDofs)
{
m_pThirdPersonInputFrame->Set(pExtraDofs->GetPoseFrame());
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::PerformFpsUpdatePass(CPed& ped)
{
crSkeleton* pSkel = ped.GetSkeleton();
if (pSkel)
{
m_bFPSUpdatePass = true;
ped.GetAnimDirector()->WaitForPreRenderUpdateToComplete();
PostIkUpdate(0.0f);
// store the matrix set from the third person update.
if (!m_pThirdPersonSkeleton)
{
m_pThirdPersonSkeleton = rage_new crSkeleton();
m_pThirdPersonSkeleton->Init(pSkel->GetSkeletonData());
m_pThirdPersonSkeleton->SetParentMtx(&ped.GetMatrixRef()); // ???
}
TUNE_GROUP_BOOL(FPS_CAM_RELATIVE, bUpdatePedAttachmentsThirdPerson, false);
if (bUpdatePedAttachmentsThirdPerson && ped.GetExtension<fwCameraRelativeExtension>() && ped.GetAttachmentExtension())
{
// if we're running a camera relative extension
// process any attachments now and remove the child
// attachments from the late attachment update
CGameWorld::ProcessDelayedAttachmentsForPhysical(&ped);
}
m_pThirdPersonSkeleton->CopyFrom(*pSkel);
// restore the values from before the first pre-render update.
if (m_pThirdPersonInputFrame)
{
m_pThirdPersonInputFrame->Pose(*pSkel);
crCreature *pCreature = ped.GetCreature();
if(pCreature)
{
crCreatureComponentExtraDofs *pExtraDofs = pCreature->FindComponent<crCreatureComponentExtraDofs>();
if(pExtraDofs)
{
pExtraDofs->GetPoseFrame().Set(*m_pThirdPersonInputFrame);
}
}
}
// Update the skeleton
pSkel->Update();
// apply the cached requests
FirstPersonPassRequests& requests = GetFpsRequests();
if (requests.m_hasRequests[kRequestArmLeft]) Request(requests.m_armRequests[IK_ARM_LEFT], IK_PASS_MAIN);
if (requests.m_hasRequests[kRequestArmRight]) Request(requests.m_armRequests[IK_ARM_RIGHT], IK_PASS_MAIN);
if (requests.m_hasRequests[kRequestLeg]) Request(requests.m_legRequest, IK_PASS_MAIN);
if (requests.m_hasRequests[kRequestLook] && (!requests.m_bLookTargetParam || (requests.m_pLookTarget != NULL)))
{
Request(requests.m_lookRequest, IK_PASS_MAIN);
}
// reset the requests for next frame
requests.Reset();
// run the pre-render update again, blocking on the result.
PreIkUpdate(0.0f);
camInterface::ApplyPedBoneOverridesForFirstPerson(ped, *pSkel);
if (ped.GetAnimDirector()->IsUpdated())
{
ped.GetAnimDirector()->StartUpdatePreRender(0.0f, NULL, false);
}
}
}
//////////////////////////////////////////////////////////////////////////
void CIkManager::FirstPersonPassRequests::Reset()
{
for (s32 i=0; i<kNumRequests; i++)
{
m_hasRequests[i] = false;
}
m_pLookTarget = NULL;
m_bLookTargetParam = false;
}
#endif // FPS_MODE_SUPPORTED