2032 lines
63 KiB
C++
2032 lines
63 KiB
C++
#include "stdafx.h"
|
|
#include <Cry_Camera.h>
|
|
#include <CryCharMorphParams.h>
|
|
#include <StlUtils.h>
|
|
#include "CryModel.h"
|
|
#include "CryModelState.h"
|
|
#include "ControllerManager.h"
|
|
#include "ChunkFileReader.h"
|
|
#include "StringUtils.h"
|
|
#include "CVars.h"
|
|
#include "CryCharDecalManager.h"
|
|
#include "BoneLightDynamicBind.h"
|
|
#include "CryModEffMorph.h"
|
|
#include "DebugUtils.h"
|
|
#include "MathUtils.h"
|
|
#include "CryModelSubmesh.h"
|
|
#include "CryCharBody.h"
|
|
#include "CryCharAnimationParams.h"
|
|
#include "CryCharFxTrail.h"
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
using namespace CryStringUtils;
|
|
|
|
#define m_pMesh GetMesh()
|
|
|
|
CryModelState::ActiveLayerArray CryModelState::g_arrActiveLayers;
|
|
|
|
unsigned CryModelState::g_nInstanceCount = 0;
|
|
|
|
CryModelState::CryModelState (CryModel* pMesh):
|
|
#ifdef DEBUG_STD_CONTAINERS
|
|
m_arrBones ("CryModelState.Bones"),
|
|
m_arrBoneGlobalMatrices ("CryModelState.arrBoneGlobalMatrices"),
|
|
m_arrAnimationLayers ("CryModelState.AnimationLayers"),
|
|
m_arrMorphEffectors ("CryModelState.MorphTargets"),
|
|
m_arrHeatSources ("CryModelState.HeatSources"),
|
|
m_arrSinks ("CryModelState.Sinks"),
|
|
#endif
|
|
m_vRuntimeScale(1,1,1),
|
|
m_nLastSkinBBoxUpdateFrameId (0),
|
|
m_pShaderStateCull(NULL),
|
|
m_pShaderStateShadowCull(NULL)
|
|
#ifdef _DEBUG
|
|
,m_bOriginalPose (true)
|
|
#endif
|
|
{
|
|
m_uFlags = nFlagsNeedReskinAllLODs;
|
|
m_ModelMatrix44.SetIdentity();
|
|
m_nInstanceNumber = g_nInstanceCount++;
|
|
m_nLastTangentsUpdatedFrameId = 0;
|
|
m_nLastTangentsUpdatedLOD = -1;
|
|
|
|
SelfValidate();
|
|
m_arrAnimationLayers.reserve(2);
|
|
//m_pqLast.reset();
|
|
//m_pBoneHead = m_pBoneSpine2 = m_pBoneLeftArm = NULL;
|
|
m_pCharPhysics = 0;
|
|
m_bHasPhysics = 0;
|
|
for (int i = 0; i < 4; ++i) {
|
|
m_pIKEffectors[i] = 0;
|
|
m_IKpos0[i].zero();
|
|
}
|
|
m_vOffset(0,0,0);
|
|
m_fScale = 0.01f;
|
|
m_bPhysicsAwake = 0;
|
|
m_bPhysicsWasAwake = 1;
|
|
m_nAuxPhys = 0;
|
|
m_nLodLevel=0;
|
|
m_fPhysBlendTime = 1E6f;
|
|
m_fPhysBlendMaxTime = m_frPhysBlendMaxTime = 1.0f;
|
|
|
|
AddSubmesh(pMesh, true);
|
|
}
|
|
|
|
|
|
CryModelState::~CryModelState()
|
|
{
|
|
m_arrBones.clear();
|
|
m_arrBoneGlobalMatrices.clear();
|
|
|
|
if (m_pCharPhysics)
|
|
GetPhysicalWorld()->DestroyPhysicalEntity(m_pCharPhysics);
|
|
|
|
int i;
|
|
for(i=0;i<m_nAuxPhys;i++) {
|
|
GetPhysicalWorld()->DestroyPhysicalEntity(m_auxPhys[i].pPhysEnt);
|
|
delete[] m_auxPhys[i].pauxBoneInfo;
|
|
}
|
|
m_nAuxPhys = 0;
|
|
|
|
for (i = 0; i < 4; ++i)
|
|
if (m_pIKEffectors[i])
|
|
delete m_pIKEffectors[i];
|
|
|
|
m_arrAnimationLayers.clear();
|
|
RemoveAllDynBoundLights();
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Generate Base Layer and Detail Layer Effectors for animation, and set up initial state
|
|
void CryModelState::InitBones(bool bBoneInfoDefPoseNeedInitialization)
|
|
{
|
|
SelfValidate();
|
|
AnimationLayer layer;
|
|
layer.pEffector = new CCryModEffAnimation(this);
|
|
m_arrAnimationLayers.push_back(layer);
|
|
|
|
InitDefaultPose(bBoneInfoDefPoseNeedInitialization,!bBoneInfoDefPoseNeedInitialization);
|
|
}
|
|
|
|
|
|
// sets the default position, getting it from the inverse default matrix in the bone infos
|
|
void CryModelState::SetPoseFromBoneInfos()
|
|
{
|
|
for (unsigned nBone = 0; nBone < numBones(); ++nBone)
|
|
{
|
|
Matrix44& matGlobal = getBoneMatrixGlobal (nBone);
|
|
const Matrix44& matInvGlobal = getBoneInfo (nBone)->getInvDefGlobal();
|
|
assert (IsOrthoUniform(matInvGlobal));
|
|
// calculate the global from the inverse
|
|
matGlobal = OrthoUniformGetInverted(matInvGlobal);
|
|
Matrix44& matRelative = getBone(nBone).m_matRelativeToParent;
|
|
if (nBone)
|
|
{
|
|
unsigned nParent = (unsigned)(nBone + getBoneInfo(nBone)->getParentIndexOffset());
|
|
assert (nParent < nBone);
|
|
const Matrix44& matInvGlobalParent = getBoneInfo(nParent)->getInvDefGlobal();
|
|
// this has a parent: calculate the relative-to-parent transform
|
|
matRelative = matGlobal * matInvGlobalParent;
|
|
}
|
|
else
|
|
matRelative = matGlobal;
|
|
getBone(nBone).BuildQPFromRelToParent();
|
|
}
|
|
|
|
UpdateBoneMatricesGlobal();
|
|
}
|
|
|
|
CryModel* CryModelState::GetMesh()
|
|
{
|
|
return GetCryModelSubmesh(0)->GetCryModel();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Puts the bones into default pose, 0th frame
|
|
// If there's no default pose animation, then resets the bone to default position/orientation
|
|
// Calculates the relative to parent and global matrices
|
|
// creates the initial pose, initializes the bones (builds the inverse transform of the global) and IK limb pose
|
|
void CryModelState::InitDefaultPose(bool bInitBoneInfos, bool bTakePoseFromBoneInfos)
|
|
{
|
|
SelfValidate();
|
|
|
|
if (bTakePoseFromBoneInfos)
|
|
{
|
|
SetPoseFromBoneInfos();
|
|
}
|
|
else
|
|
{
|
|
// apply the default animation
|
|
// for those bones that have no controller in the default animation, we can do little
|
|
ApplyAnimationToBones (CAnimationLayerInfo(0,0,1));
|
|
}
|
|
m_uFlags &= ~nFlagNeedBoneUpdate;
|
|
|
|
if (bInitBoneInfos)
|
|
{
|
|
// calculate the real inverse-global matrix and put it into the bone info;
|
|
// reset the relative to default matrix (since it's invalid)
|
|
CryBoneInfo* pBoneInfo = m_pMesh->getBoneInfos(), *pBoneInfoEnd = pBoneInfo + m_pMesh->numBoneInfos();
|
|
Matrix44* pmatGlobal = m_arrBoneGlobalMatrices.begin();
|
|
for (; pBoneInfo != pBoneInfoEnd; ++pBoneInfo, ++pmatGlobal)
|
|
//pBoneInfo->m_matInvDefGlobal.AssignInverseOf(*pmatGlobal);
|
|
pBoneInfo->m_matInvDefGlobal=GetInverted44(*pmatGlobal);
|
|
}
|
|
|
|
// reset the relative to default matrix (since it's invalid)
|
|
|
|
|
|
for (unsigned i = 0; i < 4; ++i)
|
|
m_IKpos0[i].zero() = GetLimbEndPos (i, 1);
|
|
}
|
|
|
|
// Creates the bones
|
|
void CryModelState::CreateBones()
|
|
{
|
|
SelfValidate();
|
|
unsigned numBones = m_pMesh->numBoneInfos();
|
|
m_arrBones.reinit (numBones);
|
|
Matrix44 matDefault;
|
|
matDefault.SetIdentity();
|
|
m_arrBoneGlobalMatrices.reinit (numBones, matDefault);
|
|
//assert (((unsigned)m_arrBoneGlobalMatrices.begin() & 0xF) == 0);
|
|
setBoneParent();
|
|
}
|
|
|
|
void CryModelState::InitBBox()
|
|
{
|
|
if (!m_pMesh->getGeometryInfo(0)->computeBBox (m_BBox))
|
|
UpdateBBox();
|
|
|
|
if (m_BBox.empty())
|
|
g_GetLog()->LogError("\003CryModel::LoadPostInitialize:Character %s bbox initialized to empty {%g,%g,%g}{%g,%g,%g}", m_pMesh->getFilePathCStr(), m_BBox.vMin.x, m_BBox.vMin.y, m_BBox.vMin.z, m_BBox.vMax.x, m_BBox.vMax.y, m_BBox.vMax.z);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// out of the bone positions, calculates the bounding box for this character and puts it
|
|
// into m_vBoxMin,m_vBoxMax
|
|
// NOTE: There is a huge potential for optimizing this for SSE
|
|
void CryModelState::UpdateBBox()
|
|
{
|
|
|
|
#if !BBOX_FROM_SKIN
|
|
SelfValidate();
|
|
//PROFILE_FRAME(BBoxUpdate);
|
|
if (m_nLastSkinBBoxUpdateFrameId + 1 >= (unsigned)g_nFrameID && (m_nLastSkinBBoxUpdateFrameId&g_GetCVars()->ca_SkinBasedBBoxMask()))
|
|
{
|
|
ValidateBBox();
|
|
return; // the bbox is being updated by SSE skin calculation routine right now
|
|
}
|
|
|
|
if (m_arrBones.empty())
|
|
return;
|
|
|
|
// this is the number of bones to take into account when calculating the bbox;
|
|
// and the number of first (root) bones to skip
|
|
// by default, take all bones
|
|
unsigned numSkipBones = 0;
|
|
unsigned numBones = this->numBones();
|
|
if (m_pMesh->getGeometryInfo()->hasGeomSkin())
|
|
{
|
|
CrySkinFull* pSkin = m_pMesh->getGeometryInfo()->getGeomSkin();
|
|
if (pSkin)
|
|
{
|
|
// if there is a skin, it knows which bones don't affect it; use only the bones that do affect it
|
|
numSkipBones = pSkin->numSkipBones();
|
|
numBones = pSkin->numBones();
|
|
}
|
|
}
|
|
const CryBBoxA16* pBoneBBox = m_pMesh->getBoneBBoxes();
|
|
#if !defined(LINUX) && defined(_CPU_X86)
|
|
if (g_GetCVars()->ca_SSEEnable() && cpu::hasSSE())
|
|
{
|
|
if (pBoneBBox)
|
|
// if the data about dimension of each bbox is unavalable, use the simlpe algorithm
|
|
getBBoxSSE (&m_arrBoneGlobalMatrices[numSkipBones], pBoneBBox+numSkipBones, numBones-numSkipBones, &m_BBox);
|
|
else
|
|
getBBoxSSE (&m_arrBoneGlobalMatrices[numSkipBones], numBones-numSkipBones, &m_BBox);
|
|
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (!pBoneBBox)
|
|
{
|
|
// if the data about dimension of each bbox is unavalable, use the simlpe algorithm
|
|
Vec3 c = getBoneMatrixGlobal(numSkipBones).GetTranslationOLD();
|
|
m_BBox = CryAABB(c,c);
|
|
|
|
for (unsigned i = numSkipBones+1; i < numBones; ++i)
|
|
m_BBox.include(getBoneMatrixGlobal(i).GetTranslationOLD());
|
|
}
|
|
else
|
|
{
|
|
const Matrix44* pBoneMtx = &getBoneMatrixGlobal(numSkipBones), *pBoneMtxEnd = &getBoneMatrixGlobal(0)+numBones;
|
|
|
|
m_BBox = CryAABB(pBoneMtx->GetTranslationOLD(),pBoneMtx->GetTranslationOLD());
|
|
|
|
for (; pBoneMtx < pBoneMtxEnd; ++pBoneMtx, ++pBoneBBox)
|
|
{
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
m_BBox.include(pBoneMtx->GetTranslationOLD() + pBoneMtx->GetRow (i)*pBoneBBox->vMin.v[i]);
|
|
m_BBox.include(pBoneMtx->GetTranslationOLD() + pBoneMtx->GetRow (i)*pBoneBBox->vMax.v[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
void CryModelState::ValidateBBox()
|
|
{
|
|
if (m_BBox.vMax.x < m_BBox.vMin.x
|
|
||m_BBox.vMax.y < m_BBox.vMin.y
|
|
||m_BBox.vMax.z < m_BBox.vMin.z
|
|
||!(m_BBox.vMax.x < 1000)
|
|
||!(m_BBox.vMax.z < 1000)
|
|
||!(m_BBox.vMax.z < 1000)
|
|
||!(m_BBox.vMin.x < 1000)
|
|
||!(m_BBox.vMin.z < 1000)
|
|
||!(m_BBox.vMin.z < 1000)
|
|
||!(m_BBox.vMax.x > -1000)
|
|
||!(m_BBox.vMax.z > -1000)
|
|
||!(m_BBox.vMax.z > -1000)
|
|
||!(m_BBox.vMin.x > -1000)
|
|
||!(m_BBox.vMin.z > -1000)
|
|
||!(m_BBox.vMin.z > -1000)
|
|
)
|
|
{
|
|
g_GetLog()->LogWarning ("\001[Animation] invalid BBox{{%.3f,%.3f,%.3f}-{%.3f,%.3f,%.3f}}. Model state 0x%X, model 0x%X, \"%s\" instance %d, frame %d",
|
|
m_BBox.vMin.x, m_BBox.vMin.y, m_BBox.vMin.z, m_BBox.vMax.x, m_BBox.vMax.y, m_BBox.vMax.z,
|
|
this, m_pMesh, m_pMesh->getFilePathCStr(), m_nInstanceNumber, g_nFrameID);
|
|
}
|
|
}
|
|
|
|
bool CryModelState::IsAnimStopped()
|
|
{
|
|
SelfValidate();
|
|
if (m_bPhysicsAwake)
|
|
return false;
|
|
|
|
for (unsigned nLayer = 0; nLayer < m_arrAnimationLayers.size(); ++nLayer)
|
|
{
|
|
AnimationLayer& layer = m_arrAnimationLayers[nLayer];
|
|
if (!layer.pEffector)
|
|
continue;
|
|
if (!layer.pEffector->IsStopped())
|
|
return false;
|
|
}
|
|
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin(); it != m_arrSubmeshes.end(); ++it)
|
|
#if !defined(LINUX64)
|
|
if (*it != NULL && (*it)->NeedMorph())
|
|
#else
|
|
if (*it != 0 && (*it)->NeedMorph())
|
|
#endif
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// updates the *ModEff* - adds the given delta to the current time,
|
|
// calls the callbacks, etc. Returns the array describing the updated anim layers,
|
|
// it can be applied to the bones
|
|
void CryModelState::UpdateAnimatedEffectors (float fDeltaTimeSec, ActiveLayerArray& arrActiveLayers)
|
|
{
|
|
DEFINE_PROFILER_FUNCTION();
|
|
SelfValidate();
|
|
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin(); it != m_arrSubmeshes.end(); ++it)
|
|
if (*it)(*it)->UpdateMorphEffectors(fDeltaTimeSec);
|
|
|
|
// we'll remember each active layer blending
|
|
unsigned numLayers = (unsigned)m_arrAnimationLayers.size();
|
|
arrActiveLayers.reserve(numLayers*2);
|
|
|
|
for (unsigned nLayer = 0; nLayer < numLayers; ++nLayer)
|
|
{
|
|
AnimationLayer& layer = m_arrAnimationLayers[nLayer];
|
|
if(!layer.pEffector)
|
|
continue;
|
|
|
|
#ifdef _DEBUG
|
|
AnimationLayer bakLayer = layer;
|
|
layer = bakLayer;
|
|
#endif
|
|
layer.pEffector->Tick (fDeltaTimeSec * g_GetCVars()->ca_UpdateSpeed(nLayer) * getAnimationSpeed(nLayer), m_arrSinks, arrActiveLayers);
|
|
|
|
if (!layer.pEffector->isActive())
|
|
{
|
|
if (layer.queDelayed.empty())
|
|
{
|
|
if (layer.nDefaultIdleAnimID >= 0 && layer.bEnableDefaultIdleAnimRestart)
|
|
{
|
|
// re-run the default loop animation
|
|
assert ((unsigned)layer.nDefaultIdleAnimID < m_pMesh->numAnimations());
|
|
if (g_GetCVars()->ca_Debug())
|
|
g_GetLog()->Log ("\003Restarting default idle animation %s in layer %d, blend in=%.2f, out=%.2f", m_pMesh->getAnimation(layer.nDefaultIdleAnimID).strName.c_str(), nLayer, layer.fDefaultAnimBlendTime, layer.fDefaultAnimBlendTime);
|
|
// we run the new animation with the current speed
|
|
CryCharAnimationParams Params(layer.fDefaultAnimBlendTime, layer.fDefaultAnimBlendTime, nLayer);
|
|
|
|
if (layer.bKeepLayer0Phase)
|
|
Params.nFlags |= CryCharAnimationParams::FLAGS_SYNCHRONIZE_WITH_LAYER_0;
|
|
|
|
RunAnimation(layer.nDefaultIdleAnimID, Params, fDeltaTimeSec);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// run the aligned animation
|
|
AnimationRecord record = layer.queDelayed.front();
|
|
layer.queDelayed.pop_front();
|
|
if (g_GetCVars()->ca_Debug())
|
|
g_GetLog()->Log("\003Autostarting queued animation %s in layer %d, blend in=%.2f, out=%.2f",
|
|
m_pMesh->getAnimation(record.nAnimId).strName.c_str(), nLayer, record.fBlendInTime, record.fBlendOutTime);
|
|
RunAnimation(record.nAnimId, static_cast<CryCharAnimationParams&>(record), record.fSpeed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!layer.queDelayed.empty() && layer.pEffector->GetTimeTillEnd() <= layer.queDelayed.front().fBlendInTime)
|
|
{
|
|
AnimationRecord record = layer.queDelayed.front();
|
|
layer.queDelayed.pop_front();
|
|
if (g_GetCVars()->ca_Debug())
|
|
g_GetLog()->Log("\003Autostarting queued animation %s in layer %d, blend in=%.2f, out=%.2f",
|
|
m_pMesh->getAnimation(record.nAnimId).strName.c_str(), nLayer, record.fBlendInTime, record.fBlendOutTime);
|
|
|
|
RunAnimation(record.nAnimId, static_cast<CryCharAnimationParams&>(record), record.fSpeed, true);
|
|
}
|
|
}
|
|
}
|
|
m_fPhysBlendTime += fDeltaTimeSec;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Moves the animations in time by the given time interval, applies the
|
|
// animation to bones, sends required notification to the animation sinks
|
|
// Calculates bone matrices. Processes physics. Updates BBox.
|
|
void CryModelState::ProcessAnimations (float fDeltaTimeSec, bool bUpdateBones, CryCharInstance* instance)
|
|
{
|
|
FUNCTION_PROFILER( g_GetISystem(),PROFILE_ANIMATION );
|
|
|
|
SelfValidate();
|
|
|
|
if (g_GetCVars()->ca_NoAnim())
|
|
return;
|
|
|
|
g_arrActiveLayers.clear();
|
|
UpdateAnimatedEffectors (fDeltaTimeSec, g_arrActiveLayers);
|
|
|
|
if (g_GetCVars()->ca_Debug() && *g_GetCVars()->ca_LogAnimation() && stristr(m_pMesh->getFilePathCStr(), g_GetCVars()->ca_LogAnimation()))
|
|
{
|
|
// this test code outputs the blending information every frame
|
|
string strLayers;
|
|
for (AnimationLayerArray::iterator it = m_arrAnimationLayers.begin(); it != m_arrAnimationLayers.end(); ++it)
|
|
if (it->pEffector)
|
|
strLayers += " " + it->pEffector->dump();
|
|
else
|
|
strLayers += " <I>";
|
|
string strInfo;
|
|
for (CAnimationLayerInfoArray::iterator it = g_arrActiveLayers.begin(); it != g_arrActiveLayers.end(); ++it)
|
|
{
|
|
const AnimData& anim = getAnimationSet()->getAnimation(it->nAnimId);
|
|
strInfo += " \"" + anim.strName + "\"";
|
|
char szBuf[0x100];
|
|
sprintf (szBuf, " (t=%.2f, b=%.2f)", it->fTime, it->fBlending);
|
|
strInfo += szBuf;
|
|
}
|
|
g_GetLog()->LogToFile ("\003%d.Effectors %s, layer info %s", g_GetIRenderer()->GetFrameID(),strLayers.c_str(), strInfo.c_str());
|
|
}
|
|
|
|
if (g_GetCVars()->ca_EnableCubicBlending())
|
|
{
|
|
ActiveLayerArray::iterator it, itEnd = g_arrActiveLayers.end();
|
|
for (it = g_arrActiveLayers.begin(); it != itEnd; ++it)
|
|
it->fBlending = SmoothBlendValue(it->fBlending);
|
|
}
|
|
|
|
if (!g_arrActiveLayers.empty())
|
|
{
|
|
|
|
/*
|
|
if (g_YLine==16.0f) {
|
|
|
|
extern std::vector<String> AnimStrings;
|
|
extern std::vector<u32> FrameID;
|
|
extern std::vector<u32> LayerID;
|
|
|
|
u32 size=AnimStrings.size();
|
|
float fColor[4] = {0,1,0,1};
|
|
|
|
u32 Counter=0;
|
|
if (size>20) Counter=size-20;
|
|
for (u32 x=Counter; x<size; x++) {
|
|
g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"Valerie: FrameID: %04x LayerID: %04x %s", FrameID[x], LayerID[x],AnimStrings[x] );
|
|
g_YLine+=16.0f;
|
|
}
|
|
|
|
}*/
|
|
|
|
|
|
/*
|
|
if (instance) {
|
|
|
|
float fColor[4] = {0,1,0,1};
|
|
String TestName = "objects\\characters\\story_characters\\valerie\\valeri.cgf";
|
|
String ModelName = instance->GetBody()->GetFileName();
|
|
if (TestName==ModelName) {
|
|
int i=0;
|
|
}
|
|
|
|
g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"model: %1x %s",bUpdateBones, ModelName );
|
|
g_YLine+=16.0f;
|
|
|
|
u32 NumLayer=g_arrActiveLayers.size();
|
|
g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"g_arrActiveLayers %d %s",g_arrActiveLayers.size(),m_pMesh->getAnimationInfo(0)->strName );
|
|
g_YLine+=0x10;
|
|
|
|
for (ActiveLayerArray::const_iterator it = g_arrActiveLayers.begin(); it != g_arrActiveLayers.end(); ++it)
|
|
{
|
|
float fColor[4] = {1,1,1,1};
|
|
const char* AnimationName = m_pMesh->GetName(it->nAnimId);
|
|
g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"g_arrActiveLayers.nAnimId %d %s",it->nAnimId, AnimationName );
|
|
g_YLine+=0x10;
|
|
}
|
|
g_YLine+=0x10;
|
|
|
|
}
|
|
*/
|
|
|
|
if (bUpdateBones)
|
|
{
|
|
UpdateBones (g_arrActiveLayers);
|
|
UpdateBBox(); //use vertices for update
|
|
m_uFlags |= nFlagsNeedReskinAllLODs;
|
|
}
|
|
else
|
|
m_uFlags |= nFlagNeedBoneUpdate|nFlagsNeedReskinAllLODs;
|
|
}
|
|
}
|
|
|
|
|
|
// updates the bone matrices using the given array of animations -
|
|
// applies the animation layers to the bones
|
|
void CryModelState::UpdateBones (const ActiveLayerArray& arrActiveLayers)
|
|
{
|
|
FUNCTION_PROFILER( g_GetISystem(),PROFILE_ANIMATION );
|
|
|
|
/* u32 NumLayer=arrActiveLayers.size();
|
|
float fColor[4] = {0,1,0,1};
|
|
g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"arrActiveLayers %d %s",arrActiveLayers.size(),m_pMesh->getAnimationInfo(0)->strName );
|
|
// g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"arrActiveLayers %d %s",arrActiveLayers.size(),m_pMesh->Animation->getGeometryInfo()-> );
|
|
// g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"arrActiveLayers %d %s",arrActiveLayers.size(),m_pMesh->GetName(0) );
|
|
g_YLine+=0x10;
|
|
|
|
for (ActiveLayerArray::const_iterator it = arrActiveLayers.begin(); it != arrActiveLayers.end(); ++it)
|
|
{
|
|
float fColor[4] = {1,1,1,1};
|
|
u32 id=it->nAnimId;
|
|
const CAnimationLayerInfo& rLayer = *it;
|
|
g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"arrActiveLayers.nAnimId %d %s",it->nAnimId, m_pMesh->GetName(it->nAnimId) );
|
|
g_YLine+=0x10;
|
|
}
|
|
g_YLine+=0x10;
|
|
*/
|
|
|
|
SelfValidate();
|
|
//PROFILE_FRAME(BoneUpdate);
|
|
unsigned numBones = this->numBones();
|
|
// now calculate the actual target PQ for each bone and apply that to it
|
|
if (arrActiveLayers.empty())
|
|
return;
|
|
|
|
#ifdef _DEBUG
|
|
//if (g_GetCVars()->ca_AnimWarningLevel()>=2)
|
|
{
|
|
string strLayers;
|
|
for (unsigned nLayer = 0; nLayer < arrActiveLayers.size(); ++nLayer)
|
|
{
|
|
const CAnimationLayerInfo& rLayer = arrActiveLayers[nLayer];
|
|
const char* szName = getAnimationSet()->getAnimation (rLayer.nAnimId).strName.c_str();
|
|
char szBuf[0x200];
|
|
sprintf (szBuf, "\"%s\" @%8.3f,%3d%%", szName, rLayer.fTime/getAnimationSet()->getTicksPerSecond(), int(rLayer.fBlending*100));
|
|
if (!strLayers.empty())
|
|
strLayers += ", ";
|
|
strLayers += szBuf;
|
|
}
|
|
|
|
g_Info ("%2d layers: %s", arrActiveLayers.size(), strLayers.c_str());
|
|
}
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
if (!arrActiveLayers.empty())
|
|
{
|
|
m_bOriginalPose = true;
|
|
for (ActiveLayerArray::const_iterator it = arrActiveLayers.begin(); it != arrActiveLayers.end(); ++it)
|
|
{
|
|
if (it->nAnimId > 0 || (it->nAnimId == 0 && m_pMesh->getAnimationInfo(0)->strName != "default"))
|
|
m_bOriginalPose = false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (arrActiveLayers.size() == 1)
|
|
ApplyAnimationToBones (arrActiveLayers.back());
|
|
else
|
|
ApplyAnimationsToBones (&arrActiveLayers[0], (unsigned)arrActiveLayers.size());
|
|
|
|
m_uFlags &= ~nFlagNeedBoneUpdate;
|
|
|
|
for (CryCharFxTrailArray::iterator it = m_arrFxTrails.begin(); it != m_arrFxTrails.end(); ++it)
|
|
if (*it)(*it)->Deform (getBoneGlobalMatrices());
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Calculates the relative-to-parent position and rotation of the bone
|
|
// applies the given set of animations, the last overrides the first
|
|
// NOTE:
|
|
// Anim.fWeight - the weight of the change. 0 means no change, 1 - full change,
|
|
// 1/3 - the resulting Q/P will be 1/3 of the controller's and 2/3 of the previous bone's Q/P
|
|
// calculate the position and orientation of the bone according to the current time
|
|
// set the fBlendWeight parameter to 1 in order to disable blending at all. 0 effectively turns animation off
|
|
void CryModelState::ApplyAnimationsToBones (const CAnimationLayerInfo* pAnims, unsigned numAnims)
|
|
{
|
|
FUNCTION_PROFILER( g_GetISystem(),PROFILE_ANIMATION );
|
|
SelfValidate();
|
|
for (unsigned i = 0; i < numAnims; ++i)
|
|
m_pMesh->OnAnimationApply(pAnims[i].nAnimId);
|
|
assert(numAnims > 1);
|
|
CryBone* pBoneBegin = &m_arrBones[0], *pBone = pBoneBegin;
|
|
CryBone* pBoneEnd = pBone + numBones();
|
|
const CryBoneInfo* pBoneInfoBegin = getBoneInfo(0), *pBoneInfo = pBoneInfoBegin;
|
|
for (; pBone != pBoneEnd; ++pBone, ++pBoneInfo)
|
|
{
|
|
if(!pBone->m_bUseReadyRelativeToParentMatrix || pBone->m_bUseMatPlus)
|
|
// if we use external matrix, we don't build our own
|
|
// if we use Plus matrix (which doesn't allow to use the external matrix), we rebuild internal matrix anyway
|
|
{
|
|
float fMaxBlending = 1;
|
|
|
|
IController::PQLog pqTarget = pBone->m_pqTransform;
|
|
|
|
// for each layer, blend the layer's pq with the target pq
|
|
// we blend smoothly each layer's animation, according to the blending weight.
|
|
// the algorithm does override the blending of the highest layer, i.e. if the highest layer
|
|
// has blending of 0.8 and the lower layer has 1, then it will be 0.8-0.2 proportion after recalculation
|
|
for (int nLayer = (int)numAnims - 1; nLayer >= 0 && fMaxBlending; --nLayer)
|
|
{
|
|
const CAnimationLayerInfo& rAnim = pAnims[nLayer];
|
|
if ((unsigned)rAnim.nAnimId >= (unsigned)pBoneInfo->m_arrControllers.size() || rAnim.fBlending <= 0)
|
|
continue;
|
|
IController* pController = pBoneInfo->m_arrControllers[rAnim.nAnimId];
|
|
if (!pController)
|
|
continue; // no animation
|
|
|
|
// get the underlayer transform and blend it into the target transform
|
|
IController::PQLog pqNewTransform;
|
|
pController->GetValue2 (rAnim.fTime, pqNewTransform);
|
|
AdjustLogRotations (pqTarget.vRotLog, pqNewTransform.vRotLog);
|
|
pqTarget.blendPQ (pqTarget, pqNewTransform, fMaxBlending * rAnim.fBlending);
|
|
|
|
// each underlayer gather as little control as the overlay leaves for it;
|
|
// if the overlay animation doesn't leave any (if it's weight is 0.99+) we don't have to continue
|
|
fMaxBlending *= 1 - rAnim.fBlending;
|
|
assert (fMaxBlending >= 0);
|
|
if (fMaxBlending <= 0.001f)
|
|
break;
|
|
}
|
|
|
|
// we lock the position/rotation of the bone if it's 100% determined by the animation, so that if
|
|
// the ainmation abruptly stops (which should not happen), the bone doesn't move; if the animation fades out
|
|
// (which happens often), the bone still won't return to its pre-animated position, if ht eanimation reached
|
|
// 100% blend weight during its play
|
|
if (fMaxBlending <= 0.01f)
|
|
{
|
|
#ifdef _DEBUG
|
|
if (g_GetCVars()->ca_Debug())
|
|
{
|
|
float fDistance = (pBone->m_pqTransform.vRotLog - pqTarget.vRotLog).Length();
|
|
if (fDistance > 0.3)
|
|
g_GetLog()->Log("\005animation jump: %.5f on %s", fDistance, pBoneInfo->getNameCStr());
|
|
}
|
|
#endif
|
|
pBone->m_pqTransform = pqTarget;
|
|
}
|
|
pBone->BuildRelToParentFromQP (pqTarget);
|
|
AddModelOffsets(pBone);
|
|
}
|
|
}
|
|
|
|
|
|
// finalize: build the global matrices
|
|
// from the already given relative to parent matrix
|
|
UpdateBoneMatricesGlobal();
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Calculates the relative-to-parent position and rotation of the bone
|
|
void CryModelState::ApplyAnimationToBones (CAnimationLayerInfo rAnim)
|
|
{
|
|
FUNCTION_PROFILER( g_GetISystem(),PROFILE_ANIMATION );
|
|
SelfValidate();
|
|
m_pMesh->OnAnimationApply(rAnim.nAnimId);
|
|
CryBone* pBoneBegin = &m_arrBones[0], *pBone = pBoneBegin;
|
|
CryBone* pBoneEnd = pBone + numBones();
|
|
const CryBoneInfo* pBoneInfoBegin = getBoneInfo(0), *pBoneInfo = pBoneInfoBegin;
|
|
for (; pBone != pBoneEnd; ++pBone, ++pBoneInfo)
|
|
{
|
|
if ((unsigned)rAnim.nAnimId >= pBoneInfo->m_arrControllers.size() || rAnim.fBlending <= 0)
|
|
continue;
|
|
IController* pController = pBoneInfo->m_arrControllers[rAnim.nAnimId];
|
|
if (!pController)
|
|
continue;
|
|
if (!pBone->m_bUseReadyRelativeToParentMatrix || pBone->m_bUseMatPlus)
|
|
{
|
|
if (rAnim.fBlending >= 1)
|
|
{
|
|
pController->GetValue2(rAnim.fTime, pBone->m_pqTransform);
|
|
pBone->BuildRelToParentFromQP (pBone->m_pqTransform);
|
|
}
|
|
else
|
|
{
|
|
// suppose we rarely get the fWeight 0 so we won't optimize for that case
|
|
IController::PQLog pqNewTransform;
|
|
pController->GetValue2(rAnim.fTime, pqNewTransform);
|
|
AdjustLogRotations (pBone->m_pqTransform.vRotLog, pqNewTransform.vRotLog);
|
|
pqNewTransform.blendPQ (pBone->m_pqTransform, pqNewTransform, rAnim.fBlending);
|
|
pBone->BuildRelToParentFromQP(pqNewTransform);
|
|
}
|
|
AddModelOffsets(pBone);
|
|
}
|
|
}
|
|
|
|
|
|
// finalize: build the global matrices
|
|
// from the already given relative to parent matrix
|
|
UpdateBoneMatricesGlobal();
|
|
}
|
|
|
|
|
|
void CryModelState::AddModelOffsets(CryBone* pBone)
|
|
{
|
|
// apply the model offset to the root bone
|
|
if (&m_arrBones[0] == pBone)
|
|
{
|
|
pBone->AddOffsetRelToParent(m_pMesh->getModelOffset());
|
|
pBone->ScaleRelToParent (m_vRuntimeScale * g_GetCVars()->ca_RuntimeScale());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// calculates the global matrices
|
|
// from relative to parent matrices
|
|
void CryModelState::UpdateBoneMatricesGlobal()
|
|
{
|
|
// [marco] it crashes here while loading a mission - I added
|
|
// a check if this should never happens plz fix this properly
|
|
|
|
if (m_arrBones.empty())
|
|
return;
|
|
|
|
FUNCTION_PROFILER( g_GetISystem(),PROFILE_ANIMATION );
|
|
|
|
// update the global matrices and the relative-to-default matrices
|
|
Matrix44* pmatGlobal = &m_arrBoneGlobalMatrices[0];
|
|
const CryBone* pBone = &m_arrBones[0], *pBoneEnd = pBone + m_arrBones.size();
|
|
const CryBoneInfo* pBoneInfo = getBoneInfo(0);
|
|
|
|
// the first step is for the root bone
|
|
*pmatGlobal = pBone->m_matRelativeToParent;
|
|
|
|
// go to the 1st bone and go on as if there is a parent for each and every bone
|
|
for (++pBone, ++pBoneInfo, ++pmatGlobal;
|
|
pBone != pBoneEnd;
|
|
++pBone, ++pBoneInfo, ++pmatGlobal)
|
|
{
|
|
assert (IsOrthoUniform(pBone->m_matRelativeToParent));
|
|
// calculate the global matrix
|
|
// to calculate the global, we should search for the global matrix of the parent.
|
|
// the global matrix of the parent has the known offset, we apply it directly to the pointer to the child's global matrix
|
|
*pmatGlobal = pBone->m_matRelativeToParent * *(pmatGlobal+pBoneInfo->getParentIndexOffset());
|
|
assert (IsOrthoUniform(*pmatGlobal));
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// based on the distance from camera, determines the best LOD
|
|
// for this character and memorizes it in the m_nLodLevel member
|
|
void CryModelState::CalculateLOD (float fDistance)
|
|
{
|
|
SelfValidate();
|
|
unsigned numLODs = m_pMesh->numLODs();
|
|
// Set LOD depending on distance, lod bias, and size of object
|
|
if(m_pMesh->getStaticBSphereRadius()>0 && ((INT_PTR)g_GetIRenderer()->EF_Query(EFQ_RecurseLevel)==1))
|
|
{
|
|
float fZoomFactor = RAD2DEG(GetViewCamera().GetFov())/90.f;
|
|
float fVal = fDistance * g_GetCVars()->ca_LodBias() /*0.125f*//*m_pMesh->m_fRadius*/*fZoomFactor;
|
|
|
|
m_nLodLevel = 0;
|
|
frexp (fVal,&m_nLodLevel);
|
|
/*
|
|
while(fVal>1)
|
|
{
|
|
m_nLodLevel++;
|
|
fVal*=0.5;
|
|
}
|
|
*/
|
|
|
|
if(m_nLodLevel<0)
|
|
m_nLodLevel=0;
|
|
if(m_nLodLevel>=(int)numLODs)
|
|
m_nLodLevel = numLODs-1;
|
|
}
|
|
}
|
|
|
|
|
|
bool CryModelState::IsCharacterActive()
|
|
{
|
|
SelfValidate();
|
|
return !IsAnimStopped();
|
|
}
|
|
|
|
|
|
void CryModelState::Render(const struct SRendParams & RendParams, Matrix44& mtxObjMatrix, CryCharInstanceRenderParams& rCharParams, const Vec3& translation )
|
|
{
|
|
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin(); it != m_arrSubmeshes.end(); ++it)
|
|
if (*it)(*it)->Render(RendParams, mtxObjMatrix, rCharParams,translation);
|
|
|
|
for (CryCharFxTrailArray::iterator it = m_arrFxTrails.begin(); it != m_arrFxTrails.end(); ++it)
|
|
if (*it)(*it)->Render(RendParams, mtxObjMatrix, rCharParams);
|
|
|
|
unsigned nFrameId = g_GetIRenderer()->GetFrameID();
|
|
|
|
// every 32-th frame, try to shrink the pool of shadow buffers, if there are unused
|
|
// for some time shadow buffers
|
|
if ((nFrameId & 0x1F) == (m_nInstanceNumber & 0x1F))
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin(); it != m_arrSubmeshes.end(); ++it)
|
|
if (*it)(*it)->ShrinkShadowPool();
|
|
}
|
|
|
|
/*
|
|
void CryModelState::DrawForShadow()
|
|
{
|
|
SelfValidate();
|
|
if(m_pLeafBuffers[m_nLodLevel] && m_pLeafBuffers[m_nLodLevel]->m_pVertexBuffer)
|
|
{
|
|
//g_GetRenderer()->m_RP.mStateIgnore |= (RBSI_ALPHAGEN | RBSI_RGBGEN);
|
|
g_GetRenderer()->DrawBuffer(m_pLeafBuffers[m_nLodLevel]->m_pVertexBuffer,
|
|
&m_pLeafBuffers[m_nLodLevel]->GetIndices()[0],
|
|
m_pLeafBuffers[m_nLodLevel]->GetIndices().Count(),
|
|
m_pLeafBuffers[m_nLodLevel]->m_nPrimetiveType);
|
|
}
|
|
}
|
|
*/
|
|
|
|
void CryModelState::setBoneParent()
|
|
{
|
|
SelfValidate();
|
|
for (unsigned nBone = 0; nBone < m_arrBones.size(); ++nBone)
|
|
m_arrBones[nBone].setParent (this);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// This function should be as fast as possible...
|
|
CryModelState* CryModelState::MakeCopy()
|
|
{
|
|
SelfValidate();
|
|
assert(this && m_pMesh);
|
|
if(!getRootBone() && !m_pMesh->numMorphTargets())
|
|
return NULL;
|
|
|
|
CryModelState * pCopy = new CryModelState(m_pMesh);
|
|
assert(pCopy);
|
|
|
|
pCopy->m_pShaderStateCull = m_pShaderStateCull;
|
|
pCopy->m_pShaderStateShadowCull = m_pShaderStateShadowCull;
|
|
|
|
pCopy->m_mapAnimEvents = m_mapAnimEvents;
|
|
pCopy->m_arrSinks = m_arrSinks;
|
|
|
|
pCopy->CreateBones();
|
|
for (unsigned nBone = 0; nBone < numBones(); ++nBone)
|
|
{
|
|
pCopy->m_arrBones[nBone] = m_arrBones[nBone];
|
|
pCopy->m_arrBoneGlobalMatrices[nBone] = m_arrBoneGlobalMatrices[nBone];
|
|
}
|
|
pCopy->setBoneParent();
|
|
|
|
int nLod = 0;
|
|
|
|
pCopy->m_bHasPhysics = m_bHasPhysics;
|
|
|
|
// Generate Base Layer and Detail Layer Effectors for animation, and set up initial state
|
|
AnimationLayer layer;
|
|
layer.pEffector = new CCryModEffAnimation (pCopy);
|
|
pCopy->m_arrAnimationLayers.push_back(layer);
|
|
|
|
pCopy->InitDefaultPose(false,true);
|
|
|
|
pCopy->m_BBox = m_BBox;
|
|
|
|
return pCopy;
|
|
}
|
|
|
|
|
|
ICryBone * CryModelState::GetBoneByName(const char * szBoneName)
|
|
{
|
|
SelfValidate ();
|
|
int nBone = m_pMesh->findBone(szBoneName);
|
|
if(nBone == -1)
|
|
return NULL;
|
|
|
|
return &getBone(nBone);
|
|
}
|
|
|
|
|
|
// Enables/Disables the Default Idle Animation restart.
|
|
// If this feature is enabled, then the last looped animation will be played back after the current (non-loop) animation is finished.
|
|
// Only those animations started with the flag bTreatAsDefaultIdleAnimation == true will be taken into account
|
|
void CryModelState::EnableIdleAnimationRestart (unsigned nLayer, bool bEnable)
|
|
{
|
|
SelfValidate();
|
|
if (nLayer < 0 || nLayer > 0x400)
|
|
return;
|
|
if (m_arrAnimationLayers.size() <= nLayer)
|
|
m_arrAnimationLayers.resize (nLayer + 1);
|
|
m_arrAnimationLayers[nLayer].bEnableDefaultIdleAnimRestart = bEnable;
|
|
}
|
|
|
|
|
|
|
|
// returns the animation currently being played in the given layer, or -1
|
|
int CryModelState::GetCurrentAnimation (unsigned nLayer)
|
|
{
|
|
if (nLayer >= m_arrAnimationLayers.size())
|
|
return -1; // no such layer exists
|
|
const CCryModEffAnimation* pEffector = m_arrAnimationLayers[nLayer].pEffector;
|
|
if (pEffector)
|
|
return pEffector->GetAnyCurrentAnimation();
|
|
return -1; // no effector -> no animation
|
|
}
|
|
|
|
|
|
bool CryModelState::RunMorph (const char* szMorphTarget,const CryCharMorphParams&Params)
|
|
{
|
|
bool bRes = false;
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin(); it != m_arrSubmeshes.end(); ++it)
|
|
if (*it)
|
|
bRes = (*it)->RunMorph (szMorphTarget, Params, false) || bRes;
|
|
return bRes;
|
|
}
|
|
|
|
bool CryModelState::RunMorph (int nMorphTargetId, float fBlendInTime, float fBlendOutTime)
|
|
{
|
|
SelfValidate();
|
|
|
|
GetCryModelSubmesh(0)->RunMorph (nMorphTargetId, CryCharMorphParams(fBlendInTime, 0, fBlendOutTime));
|
|
return true;
|
|
}
|
|
|
|
|
|
//! Finds the morph with the given id and sets its relative time.
|
|
//! Returns false if the operation can't be performed (no morph)
|
|
|
|
void CryModelState::StopAllMorphs()
|
|
{
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin(); it != m_arrSubmeshes.end(); ++it)
|
|
if (*it)(*it)->StopAllMorphs();
|
|
}
|
|
|
|
void CryModelState::FreezeAllMorphs()
|
|
{
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin(); it != m_arrSubmeshes.end(); ++it)
|
|
if (*it)(*it)->FreezeAllMorphs();
|
|
}
|
|
|
|
|
|
// stops the animation at the given layer, and returns true if the animation was
|
|
// actually stopped (if the layer existed and the animation was played there)
|
|
bool CryModelState::StopAnimation (int nLayer)
|
|
{
|
|
SelfValidate();
|
|
bool bResult = false;
|
|
if ((size_t)nLayer < m_arrAnimationLayers.size())
|
|
{
|
|
AnimationLayer& rLayer = m_arrAnimationLayers[nLayer];
|
|
rLayer.nDefaultIdleAnimID = -1; // forget the default idle animation
|
|
if (rLayer.pEffector)
|
|
{
|
|
bResult = rLayer.pEffector->IsStopped();
|
|
rLayer.pEffector->stop();
|
|
}
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
|
|
|
|
// starts playing animation
|
|
// returns true if the animation was found and run, false otherwise
|
|
bool CryModelState::RunAnimation (const char * szAnimName, const struct CryCharAnimationParams& Params, float fSpeed)
|
|
{
|
|
SelfValidate ();
|
|
assert(szAnimName);
|
|
|
|
bool bPrintDebugInfo = g_GetCVars()->ca_AnimWarningLevel() > 2;
|
|
|
|
int nAnimID = m_pMesh->findAnimation(szAnimName);
|
|
if(nAnimID<0)
|
|
{
|
|
int nMorphTargetId = m_pMesh->findMorphTarget(szAnimName);
|
|
if (nMorphTargetId < 0)
|
|
{
|
|
if (m_pMesh->isDummyAnimation(szAnimName))
|
|
return true; // pretend we started it; just ignore the call
|
|
|
|
if(bPrintDebugInfo)
|
|
{
|
|
CryWarning (VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, "!Animation \"%s\" Not Found for character \"%s\"", szAnimName, m_pMesh->getFilePathCStr());
|
|
//g_GetLog()->LogWarning ("\002CryModelState::RunAnimation: Animation \"%s\" Not Found for character \"%s\"", szAnimName, m_pMesh->getFilePathCStr());
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
return RunMorph (nMorphTargetId, Params.fBlendInTime, Params.fBlendOutTime);
|
|
}
|
|
else
|
|
return RunAnimation(nAnimID, Params, fSpeed);
|
|
}
|
|
|
|
// The fSpeed is the external speed, not taking into account the internal per-animation layer multiplier
|
|
bool CryModelState::RunAnimation (int nAnimID, const CryCharAnimationParams& Params, float fSpeed, bool bInternal)
|
|
{
|
|
SelfValidate();
|
|
assert (nAnimID >= 0 && (unsigned)nAnimID < m_pMesh->numAnimations());
|
|
const AnimData* pAnim = m_pMesh->getAnimationInfo(nAnimID);
|
|
if (!pAnim)
|
|
return false;
|
|
|
|
if (m_arrAnimationLayers.size() <= (size_t)Params.nLayerID)
|
|
m_arrAnimationLayers.resize (Params.nLayerID+1);
|
|
AnimationLayer& rLayer = m_arrAnimationLayers[Params.nLayerID];
|
|
|
|
// make effector if not created yet
|
|
if(!rLayer.pEffector)
|
|
{
|
|
rLayer.pEffector = new CCryModEffAnimation(this);
|
|
}
|
|
|
|
if ((Params.nFlags&Params.FLAGS_NO_DEFAULT_IDLE)==0 && pAnim->bLoop)
|
|
{
|
|
// if this animation is looped, it becomes the default loop animation
|
|
// for this rLayer. THe settings of the current call become the default settings for it
|
|
rLayer.nDefaultIdleAnimID = nAnimID;
|
|
rLayer.fDefaultAnimBlendTime = Params.fBlendInTime;
|
|
rLayer.bKeepLayer0Phase = (Params.nFlags&Params.FLAGS_SYNCHRONIZE_WITH_LAYER_0)!=0;
|
|
}
|
|
|
|
assert(rLayer.pEffector != (CCryModEffAnimation*)NULL);
|
|
|
|
if (!bInternal && rLayer.pEffector->isActive() && 0!=((Params.nFlags|rLayer.pEffector->getStartAnimFlags())&CryCharAnimationParams::FLAGS_ALIGNED))
|
|
{
|
|
CryCharAnimationParams NewParams = Params;
|
|
if (rLayer.pEffector->getStartAnimFlags() & Params.FLAGS_ALIGNED)
|
|
NewParams.fBlendInTime = 0;
|
|
// this animation must be queued
|
|
if (/*(Params.nFlags & Params.FLAGS_ALIGNED)!=0 || */rLayer.queDelayed.empty())
|
|
rLayer.queDelayed.push_back (AnimationRecord(nAnimID, NewParams, fSpeed));
|
|
else
|
|
rLayer.queDelayed.back().assign (nAnimID, NewParams, fSpeed);
|
|
rLayer.pEffector->SetNoLoop/*NoBlendOut*/(); // prepare the current loop to stop and give way to the aligned animation
|
|
}
|
|
else
|
|
rLayer.pEffector->StartAnimation (nAnimID, Params.fBlendInTime, Params.fBlendOutTime, (Params.nFlags&Params.FLAGS_SYNCHRONIZE_WITH_LAYER_0)!=0 ? m_arrAnimationLayers[0].pEffector : rLayer.pEffector,fSpeed * g_GetCVars()->ca_UpdateSpeed(Params.nLayerID) * getAnimationSpeed(Params.nLayerID), Params.nFlags);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
void CryModelState::ResetAllAnimations()
|
|
{
|
|
SelfValidate ();
|
|
|
|
for (unsigned nLayer = 0; nLayer < m_arrAnimationLayers.size(); ++nLayer)
|
|
{ // TODO: Allow more detail layers
|
|
AnimationLayer& layer = m_arrAnimationLayers[nLayer];
|
|
if(layer.pEffector)
|
|
layer.pEffector->Reset();
|
|
layer.bEnableDefaultIdleAnimRestart = false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool CryModelState::SetAnimationFrame(const char * szString, int nFrame)
|
|
{
|
|
SelfValidate ();
|
|
ResetAllAnimations();
|
|
// we don't know the speed - actually, this set frame is for setting the frame and freezing
|
|
// so we pass 0
|
|
CryCharAnimationParams NullAnim(0, 0);
|
|
NullAnim.nFlags = NullAnim.FLAGS_NO_DEFAULT_IDLE;
|
|
bool bRes = RunAnimation(szString, NullAnim,0);
|
|
ProcessAnimations(0,true,0);
|
|
return bRes;
|
|
}
|
|
|
|
|
|
int CryModelState::GetDamageTableValue(int nId)
|
|
{
|
|
SelfValidate ();
|
|
if (nId < 0 || nId >= (int)numBones())
|
|
return 0;
|
|
return m_pMesh->m_arrDamageTable[nId];
|
|
}
|
|
|
|
Matrix44 GetModelMatrix (CCObject* pObj)
|
|
{
|
|
|
|
//Matrix44 matModel = GetTranslationMat(pObj->m_Trans); //matModel.Translate(pObj->m_Trans);
|
|
//matModel=matModel*Matrix33::GetRotationX33( DEG2RAD(-pObj->m_Angs[0]) );
|
|
//matModel=matModel*Matrix33::GetRotationY33( DEG2RAD(+pObj->m_Angs[1]) ); //IMPORTANT: radian-angle must be negated
|
|
//matModel=matModel*Matrix33::GetRotationZ33( DEG2RAD(-pObj->m_Angs[2]) );
|
|
//if (!isUnit(pObj->m_Scale))
|
|
//matModel=GetScale33( Vec3d(pObj->m_Scale.x,pObj->m_Scale.y,pObj->m_Scale.z) )*matModel;
|
|
|
|
//OPTIMISED_BY_IVO
|
|
//Matrix33diag diag = pObj->m_Scale; //use diag-matrix for scaling
|
|
//Matrix34 t = Matrix34::GetTransMat34(pObj->m_Trans);
|
|
//Matrix33 r33 = Matrix33::GetRotationXYZ33( Deg2Rad(Ang3(+pObj->m_Angs[0],-pObj->m_Angs[1],+pObj->m_Angs[2])) );
|
|
//Matrix44 matModel = r33*t*diag; //optimised concatenation: m34*t*diag
|
|
|
|
//matModel = GetTransposed44( matModel); //TODO: remove this after E3 and use Matrix34 instead of Matrix44
|
|
|
|
return pObj->m_Matrix;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// updates all heat sources (read from CGF, kept in the Model) in the given object
|
|
// The global ones can later be extracted via GetBoundLight() member of ICryCharInstance interface
|
|
void CryModelState::UpdateHeatSources (CCObject * pObj, const SRendParams & RendParams)
|
|
{
|
|
SelfValidate();
|
|
unsigned numBindings = m_pMesh->numBoneLights();
|
|
|
|
// construct the DLights for the first time this function is called
|
|
if (m_arrHeatSources.size() != numBindings)
|
|
{
|
|
m_arrHeatSources.reinit(numBindings);
|
|
for (unsigned nBinding = 0; nBinding < numBindings; ++nBinding)
|
|
{
|
|
CBoneLightBindInfo& rBinding = m_pMesh->getBoneLight(nBinding);
|
|
CryBone& rBone = getBone(rBinding.getBone());
|
|
CDLight& rDLight = m_arrHeatSources[nBinding];
|
|
rBinding.initDLight (rDLight);
|
|
}
|
|
}
|
|
|
|
// the self-heat intensity of the character.
|
|
// if it's 0, there's no point in adding the heat sources to the renderer
|
|
float fHeatIntensity = 1.0f;
|
|
if (pObj->m_ShaderParams)
|
|
fHeatIntensity = SShaderParam::GetFloat("heatintensity", pObj->m_ShaderParams, -1);
|
|
|
|
bool bHeatVisionMode = g_GetIRenderer()->EF_GetHeatVision();
|
|
|
|
|
|
// the model matrix in the world space
|
|
Matrix44 matModel = GetModelMatrix (pObj);
|
|
|
|
// update DLight positions and add them to the renerer
|
|
if (!bHeatVisionMode || fHeatIntensity > 0)
|
|
for (unsigned nBinding = 0; nBinding < numBindings; ++nBinding)
|
|
{
|
|
CBoneLightBindInfo& rBinding = m_pMesh->getBoneLight(nBinding);
|
|
|
|
if (bHeatVisionMode)
|
|
{
|
|
if (!rBinding.isHeatSource())
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (!rBinding.isLightSource())
|
|
continue;
|
|
}
|
|
|
|
CryBone& rBone = getBone(rBinding.getBone());
|
|
CDLight& rDLight = m_arrHeatSources[nBinding];
|
|
|
|
// unwrap (change #if 0 to #if 1) the following code and pass matModel to the updateDLight
|
|
// instead of the rBone global matrix, in order to have the light position
|
|
// returned in World coordinates
|
|
rDLight.m_fDirectFactor = 0;
|
|
|
|
Matrix44 matBone = rBone.GetGlobalMatrix() * matModel;
|
|
|
|
if (g_GetCVars()->ca_Debug()) {
|
|
Matrix34 m34 = Matrix34(GetTransposed44(matBone)); //fix this
|
|
debugDrawBBox (m34, CryAABB(Vec3d(-0.1f,-0.2f,-0.1f), Vec3d(0.1f, 0.2f, 0.1f)), 5);
|
|
}
|
|
|
|
rBinding.updateDLight (matBone, fHeatIntensity, rDLight);
|
|
|
|
if (rBinding.isLocal())
|
|
{
|
|
g_GetIRenderer()->EF_ADDDlight (&rDLight);
|
|
pObj->m_DynLMMask |= (1<<rDLight.m_Id);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// adds a new dynamically bound light
|
|
CDLight* CryModelState::AddDynBoundLight (ICryCharInstance* pParent,CDLight* pDLight, unsigned nBone, bool bCopyLight)
|
|
{
|
|
// we don't support multiple instances of the same light bound
|
|
// because we use the light pointer as the handle
|
|
if (IsDynLightBound(pDLight) && !bCopyLight)
|
|
return NULL;
|
|
|
|
m_arrDynBoundLights.resize (m_arrDynBoundLights.size() + 1);
|
|
m_arrDynBoundLights.back().init(pParent, pDLight, nBone, getBoneMatrixGlobal(nBone), bCopyLight);
|
|
return m_arrDynBoundLights.back().getDLight();
|
|
}
|
|
|
|
// checks if such light is already bound
|
|
bool CryModelState::IsDynLightBound (CDLight*pDLight)
|
|
{
|
|
for (DynamicBoundLightArray::iterator it = m_arrDynBoundLights.begin(); it != m_arrDynBoundLights.end(); ++it)
|
|
if (it->getDLight() == pDLight)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// removes the dynamically bound light
|
|
void CryModelState::RemoveDynBoundLight (CDLight* pDLight)
|
|
{
|
|
unsigned i;
|
|
// scan through
|
|
for (i = 0; i < m_arrDynBoundLights.size(); )
|
|
{
|
|
CBoneLightDynamicBind& rBinding = m_arrDynBoundLights[i];
|
|
if (rBinding.getDLight() == pDLight)
|
|
{
|
|
rBinding.done();
|
|
m_arrDynBoundLights.erase(m_arrDynBoundLights.begin() + i);
|
|
}
|
|
else
|
|
++i;
|
|
}
|
|
}
|
|
|
|
unsigned CryModelState::numDynBoundLights()const
|
|
{
|
|
return (unsigned)m_arrDynBoundLights.size();
|
|
}
|
|
|
|
CDLight* CryModelState::getDynBoundLight(unsigned i)
|
|
{
|
|
return m_arrDynBoundLights[i].getDLight();
|
|
}
|
|
|
|
|
|
void CryModelState::RemoveAllDynBoundLights()
|
|
{
|
|
for (DynamicBoundLightArray::iterator it = m_arrDynBoundLights.begin(); it != m_arrDynBoundLights.end(); ++it)
|
|
it->done();
|
|
m_arrDynBoundLights.clear();
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// updates the dynamically (via ICryCharInstance at runtime) bound lights
|
|
void CryModelState::UpdateDynBoundLights (CCObject * pObj, const SRendParams & RendParams)
|
|
{
|
|
unsigned i, numDynBoundLights = (unsigned)m_arrDynBoundLights.size();
|
|
if (!numDynBoundLights)
|
|
return;
|
|
|
|
Matrix44 matModel = GetModelMatrix (pObj);
|
|
for (i = 0; i < numDynBoundLights; ++i)
|
|
{
|
|
CBoneLightDynamicBind& rBinding = m_arrDynBoundLights[i];
|
|
|
|
rBinding.updateDLight (getBoneMatrixGlobal(rBinding.getBone()), matModel, 1);
|
|
|
|
if (rBinding.isLocal())
|
|
{
|
|
CDLight& rDLight = *rBinding.getDLight();
|
|
g_GetIRenderer()->EF_ADDDlight (&rDLight);
|
|
pObj->m_DynLMMask |= (1<<rDLight.m_Id);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Interface for the renderer - returns the CDLight describing the light in this character;
|
|
// returns NULL if there's no light with such index
|
|
const CDLight* CryModelState::getGlobalBoundLight (unsigned nIndex)
|
|
{
|
|
SelfValidate();
|
|
return &m_arrHeatSources[nIndex+m_pMesh->numLocalBoneLights()];
|
|
}
|
|
|
|
|
|
// Set the current time of the given layer, in seconds
|
|
void CryModelState::SetLayerTime (unsigned nLayer, float fTimeSeconds)
|
|
{
|
|
AnimationLayer& layer = m_arrAnimationLayers[nLayer];
|
|
if(!layer.pEffector)
|
|
return;
|
|
// sets the current time of the effector, translating seconds into ticks.
|
|
layer.pEffector->SetCurrentTime (fTimeSeconds);
|
|
ProcessAnimations(0,true,0);
|
|
}
|
|
|
|
// Get the current time of the given layer, in seconds
|
|
float CryModelState::GetLayerTime (unsigned nLayer) const
|
|
{
|
|
const AnimationLayer& layer = m_arrAnimationLayers[nLayer];
|
|
if(!layer.pEffector)
|
|
return 0;
|
|
// sets the current time of the effector, translating seconds into ticks.
|
|
return layer.pEffector->GetCurrentTime();
|
|
}
|
|
|
|
// forgets about all default idle animations
|
|
void CryModelState::ForgetDefaultIdleAnimations()
|
|
{
|
|
SelfValidate();
|
|
for (AnimationLayerArray::iterator it = m_arrAnimationLayers.begin(); it != m_arrAnimationLayers.end(); ++it)
|
|
it->ForgetDefaultIdleAnimation();
|
|
}
|
|
|
|
|
|
// sets the given aniimation to the given layer as the default
|
|
void CryModelState::SetDefaultIdleAnimation (unsigned nLayer, const char* szAnimName)
|
|
{
|
|
SelfValidate();
|
|
if (nLayer >= m_arrAnimationLayers.size())
|
|
return;
|
|
|
|
if (!szAnimName||!szAnimName[0])
|
|
{
|
|
m_arrAnimationLayers[nLayer].ForgetDefaultIdleAnimation();
|
|
return;
|
|
}
|
|
|
|
int nAnimID = -1;
|
|
if (szAnimName)
|
|
nAnimID = m_pMesh->findAnimation(szAnimName);
|
|
|
|
if (nAnimID >= 0 && m_arrAnimationLayers.size() <= nLayer)
|
|
m_arrAnimationLayers.resize (nLayer+1);
|
|
|
|
if (m_arrAnimationLayers.size() > nLayer)
|
|
m_arrAnimationLayers[nLayer].nDefaultIdleAnimID = nAnimID;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
// checks for possible memory corruptions in this object and its children
|
|
void CryModelState::SelfValidate ()const
|
|
{
|
|
}
|
|
#endif
|
|
|
|
// returns the approximate bounding box for this character in the passed in vectors
|
|
void CryModelState::GetBoundingBox (Vec3d& vMin, Vec3d& vMax) const
|
|
{
|
|
vMin = m_BBox.vMin;
|
|
vMax = m_BBox.vMax;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Adds a decal to the character
|
|
void CryModelState::AddDecal (CryEngineDecalInfo& Decal)
|
|
{
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin(); it != m_arrSubmeshes.end(); ++it)
|
|
if (*it)(*it)->AddDecal(Decal);
|
|
}
|
|
|
|
void CryModelState::DumpDecals()
|
|
{
|
|
//if (m_pDecalManager)
|
|
// m_pDecalManager->debugDump();
|
|
}
|
|
|
|
// discards all outstanding decal requests - the decals that have not been meshed (realized) yet
|
|
// and have no chance to be correctly meshed in the future
|
|
void CryModelState::DiscardDecalRequests()
|
|
{
|
|
SelfValidate();
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin(); it != m_arrSubmeshes.end(); ++it)
|
|
if (*it)(*it)->DiscardDecalRequests();
|
|
}
|
|
|
|
|
|
|
|
Vec3d CryModelState::GetCenter ()const
|
|
{
|
|
SelfValidate();
|
|
return m_BBox.getCenter();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// given the bone index, (INDEX, NOT ID), returns this bone's parent index
|
|
int CryModelState::getBoneParentIndex (int nBoneIndex)
|
|
{
|
|
assert (nBoneIndex >= 0 && nBoneIndex < (int)numBones());
|
|
return nBoneIndex + getBoneInfo(nBoneIndex)->getParentIndexOffset();
|
|
}
|
|
|
|
CryModelState::AnimationLayer::AnimationLayer ():
|
|
nDefaultIdleAnimID(-1),
|
|
fDefaultAnimBlendTime(0),
|
|
bEnableDefaultIdleAnimRestart (false),
|
|
bKeepLayer0Phase(false)
|
|
{}
|
|
|
|
// forgets about the default idle animation
|
|
void CryModelState::AnimationLayer::ForgetDefaultIdleAnimation()
|
|
{
|
|
nDefaultIdleAnimID = -1;
|
|
}
|
|
|
|
|
|
void CryModelState::initClass()
|
|
{
|
|
}
|
|
|
|
void CryModelState::deinitClass()
|
|
{
|
|
//assert(g_arrEmptyAnimEventArray.empty());
|
|
g_arrActiveLayers.clear();
|
|
}
|
|
|
|
|
|
ICharInstanceSink* CryModelState::getAnimationEventSink (int nAnimId)
|
|
{
|
|
if (nAnimId>= 0 && nAnimId < (int)m_arrSinks.size())
|
|
return m_arrSinks[nAnimId];
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void CryModelState::setAnimationEventSink (int nAnimId, ICharInstanceSink* pSink)
|
|
{
|
|
//g_GetLog()->LogToFile ("AnimDebug: 0x%08X->setAnimationEventSink(anim:%d, sink:%08X)", this, nAnimId, pSink);
|
|
if (nAnimId>= 0 && nAnimId < (int)m_pMesh->numAnimations())
|
|
{
|
|
if ((int) m_arrSinks.size() <= nAnimId)
|
|
m_arrSinks.resize (nAnimId+1, NULL);
|
|
m_arrSinks[nAnimId] = pSink;
|
|
}
|
|
}
|
|
|
|
|
|
void CryModelState::removeAllAnimationEvents ()
|
|
{
|
|
m_mapAnimEvents.clear();
|
|
}
|
|
|
|
void CryModelState::removeAnimationEventSink(ICharInstanceSink * pCharInstanceSink)
|
|
{
|
|
for (std::vector<ICharInstanceSink*>::iterator it = m_arrSinks.begin(); it != m_arrSinks.end(); ++it)
|
|
if (*it == pCharInstanceSink)
|
|
*it = NULL;
|
|
}
|
|
|
|
// this is an empty array used to return the reference in teh getAnimEvents();
|
|
// it's always empty, so doesn't require cleaning and doesn't cause troubles with memory manager
|
|
// DEBUG NOTE: this is not a strict array, it's a class that takes care about assigning the parent to the array
|
|
static CryModelState::AnimEventArray g_arrEmptyAnimEventArray;
|
|
|
|
CryModelState::AnimEventArray& CryModelState::getAnimEvents(int nAnimId)
|
|
{
|
|
return find_in_map_ref (m_mapAnimEvents, nAnimId, g_arrEmptyAnimEventArray);
|
|
}
|
|
|
|
void CryModelState::addAnimEvent (int nAnimId, int nFrame, AnimSinkEventData UserData)
|
|
{
|
|
if (g_GetCVars()->ca_Debug())
|
|
g_GetLog()->LogToFile ("\003AnimDebug: 0x%08X->addAnimEvent(anim:%d, frame:%d, userdata:%p,%d) model %s", this, nAnimId, nFrame, UserData.p, UserData.n, m_pMesh->getFilePathCStr());
|
|
|
|
if (nAnimId >= 0 && nAnimId < (int)m_pMesh->numAnimations())
|
|
{
|
|
const AnimData* pAnim = m_pMesh->getAnimationInfo(nAnimId);
|
|
if (!pAnim)
|
|
return;
|
|
|
|
AnimEventArray& arrEvents = m_mapAnimEvents[nAnimId];
|
|
AnimEvent event;
|
|
event.UserData = UserData;
|
|
event.fTime = pAnim->fStart + nFrame * m_pMesh->getSecondsPerFrame();
|
|
|
|
AnimEventArray::iterator it, itBegin = arrEvents.begin(), itEnd = itBegin + arrEvents.size();
|
|
//it = std::lower_bound(itBegin, itEnd, event);
|
|
for (it = itBegin; it != itEnd; ++it)
|
|
if (isEqual(*it, event))
|
|
return;
|
|
arrEvents.push_back(event);
|
|
}
|
|
}
|
|
|
|
void CryModelState::removeAnimEvent (int nAnimId, int nFrame, AnimSinkEventData UserData)
|
|
{
|
|
//g_GetLog()->LogToFile ("AnimDebug: 0x08X->removeAnimEvent(anim:%d, frame:%d, userdata:%d)", this, nAnimId, nFrame, nUserData);
|
|
if (nAnimId >= 0 && nAnimId < (int)m_pMesh->numAnimations())
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
// draws the skeleton
|
|
void CryModelState::debugDrawBones(const Matrix44* pModelMatrix)
|
|
{
|
|
static float fColorBones[4] = {1,0.75f,0.75f,0.75};
|
|
static float fColorBoneBBWithPhysics[4] = {0.5f,0.5f,1,0.75};
|
|
static float fColorBoneBBWithoutPhysics[4] = {0.65f,0.65f,0.65f,0.75};
|
|
|
|
if (!pModelMatrix)
|
|
pModelMatrix = &m_ModelMatrix44;
|
|
|
|
debugDrawRootBone (getBone(0).GetGlobalMatrix() * *pModelMatrix, 0.02f, fColorBones);
|
|
for (unsigned nBone = 1; nBone < numBones(); ++nBone)
|
|
{
|
|
int nParentBone = getBoneParentIndex(nBone);
|
|
Matrix44 matBoneGlobalWCS = getBone(nBone).GetGlobalMatrix() * *pModelMatrix;
|
|
debugDrawBone (matBoneGlobalWCS, getBone(nParentBone).GetGlobalMatrix() * *pModelMatrix, fColorBones);
|
|
|
|
if (int((g_GetTimer()->GetCurrTime()+m_nInstanceNumber*0.2f)*3)&1)
|
|
{
|
|
fColorBoneBBWithPhysics[3] = 1;
|
|
fColorBoneBBWithoutPhysics[3] = 0.5f;
|
|
}
|
|
else
|
|
{
|
|
fColorBoneBBWithPhysics[3] = 0.75f;
|
|
fColorBoneBBWithoutPhysics[3] = 0.75f;
|
|
}
|
|
|
|
const CryBBoxA16* pBoneBBoxes = m_pMesh->getBoneBBoxes ();
|
|
|
|
if (pBoneBBoxes)
|
|
{
|
|
const CryBoneInfo &rBoneInfo = m_pMesh->getBoneInfo(nBone);
|
|
Matrix34 m34=Matrix34( GetTransposed44(matBoneGlobalWCS) );
|
|
|
|
CryAABB caabb;
|
|
caabb.vMin=pBoneBBoxes[nBone].vMin.v;
|
|
caabb.vMax=pBoneBBoxes[nBone].vMax.v;
|
|
|
|
debugDrawBBox (m34, caabb, 1, rBoneInfo.m_PhysInfo[0].pPhysGeom?fColorBoneBBWithPhysics:fColorBoneBBWithoutPhysics);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CryModelState::debugDrawBoundingBox(const Matrix44* pModelMatrix, int nBBoxSegments)
|
|
{
|
|
static const float fColorBBox[4] = {1,1,1,1};
|
|
|
|
if (!pModelMatrix)
|
|
pModelMatrix = &m_ModelMatrix44;
|
|
|
|
// this draws the bounding box around the character
|
|
Matrix34 m34=Matrix34(GetTransposed44(*pModelMatrix));
|
|
debugDrawBBox (m34, m_BBox, nBBoxSegments, fColorBBox);
|
|
|
|
int nAxis[3];
|
|
for (nAxis[0] = 0; nAxis[0] < 3; ++nAxis[0])
|
|
{
|
|
nAxis[1] = (nAxis[0]+1)%3;
|
|
nAxis[2] = (nAxis[0]+2)%3;
|
|
Vec3d vDown(0,0,0), vUp(0,0,0);
|
|
vDown[nAxis[0]] = m_BBox.vMin[nAxis[0]];
|
|
float fUp = m_BBox.vMax[nAxis[0]];
|
|
vUp[nAxis[0]] = fUp+0.05f;
|
|
float fColor[4] = {1,1,1,1};
|
|
fColor[nAxis[1]] = fColor[nAxis[2]] = 0.75;
|
|
debugDrawLine (*pModelMatrix, vDown, vUp, fColor);
|
|
Vec3d vA;
|
|
vA[nAxis[0]] = m_BBox.vMax[nAxis[0]]-0.025f;
|
|
for (int i= 0; i < 4; ++i)
|
|
{
|
|
for (int j = 0; j < 2; ++j)
|
|
vA[nAxis[j+1]] = (i&(1<<j))?0.025f:-0.025f;
|
|
debugDrawLine (*pModelMatrix, vUp, vA, fColor);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//returns the j-th child of i-th child of the given bone
|
|
CryBone* CryModelState::getBoneChild (int nBone, int i)
|
|
{
|
|
return &getBone(getBoneChildIndex(nBone, i));
|
|
}
|
|
|
|
|
|
//returns the j-th child of i-th child of the given bone
|
|
CryBone* CryModelState::getBoneGrandChild (int nBone, int i, int j)
|
|
{
|
|
return &getBone(getBoneGrandChildIndex(nBone, i, j));
|
|
}
|
|
|
|
//returns the j-th child of i-th child of the given bone
|
|
int CryModelState::getBoneChildIndex (int nBone, int i)
|
|
{
|
|
assert (i >= 0 && i < (int)getBoneInfo(nBone)->numChildren());
|
|
return nBone + getBoneInfo(nBone)->getFirstChildIndexOffset() + i;
|
|
}
|
|
|
|
|
|
//returns the j-th child of i-th child of the given bone
|
|
int CryModelState::getBoneGrandChildIndex (int nBone, int i, int j)
|
|
{
|
|
return getBoneChildIndex(getBoneChildIndex(nBone, i), j);
|
|
}
|
|
|
|
|
|
/*
|
|
CryModel* CryModelState::getAnimationSet()
|
|
{
|
|
SelfValidate();
|
|
return m_pMesh;
|
|
}
|
|
*/
|
|
|
|
// calculates the mem usage
|
|
void CryModelState::GetSize(ICrySizer* pSizer)
|
|
{
|
|
#if ENABLE_GET_MEMORY_USAGE
|
|
pSizer->AddContainer(g_arrActiveLayers);
|
|
|
|
unsigned i;
|
|
size_t nSize = sizeof(*this);
|
|
nSize += sizeofArray (m_arrAnimationLayers);
|
|
nSize += sizeofArray (m_arrBoneGlobalMatrices, numBones());
|
|
nSize += sizeofArray (m_arrBones);
|
|
nSize += sizeofArray (m_arrHeatSources);
|
|
//nSize += sizeofArray (m_arrMorphEffectors);
|
|
//nSize += sizeofArray (m_arrShaderTemplates[0]);
|
|
//nSize += sizeofArray (m_arrShaderTemplates[1]);
|
|
nSize += sizeofArray (m_arrSinks);
|
|
nSize += m_mapAnimEvents.size() * sizeof(AnimEventMap::value_type);
|
|
for (i = 0; i < 4; ++i)
|
|
if (m_pIKEffectors[i])
|
|
nSize += sizeof(CCryModEffIKSolver);
|
|
|
|
pSizer->AddObject(this, nSize);
|
|
|
|
//if (m_pDecalManager)
|
|
//{
|
|
// SIZER_SUBCOMPONENT_NAME(pSizer, "Decals");
|
|
// m_pDecalManager->GetMemoryUsage(pSizer);
|
|
//}
|
|
/*
|
|
{
|
|
SIZER_SUBCOMPONENT_NAME(pSizer, "Materials");
|
|
for (i = 0; i < g_nMaxGeomLodLevels; ++i)
|
|
{
|
|
CLeafBuffer *lb = m_pLeafBuffers[i];
|
|
list2<CMatInfo>* pMats;
|
|
if (lb && (pMats = lb->m_pMats))
|
|
pSizer->Add(pMats, pMats->capacity());
|
|
}
|
|
}
|
|
|
|
{
|
|
SIZER_SUBCOMPONENT_NAME (pSizer,"Shadows&Particles");
|
|
m_ReShadowManager.GetMemoryUsage (pSizer);
|
|
m_ParticleManager.GetMemoryUsage (pSizer);
|
|
}
|
|
*/
|
|
// TODO: getsize on each submesh
|
|
#endif
|
|
}
|
|
|
|
void CryModelState::setAnimationSpeed (unsigned nLayer, float fSpeed)
|
|
{
|
|
if (nLayer > 16 || !(fSpeed>=0 && fSpeed < 1e6))
|
|
{
|
|
g_GetLog()->LogError ("\002invalid layer %d speed scale %g (0x%08X)", nLayer, fSpeed, *(unsigned*)&fSpeed);
|
|
return;
|
|
}
|
|
|
|
if (m_arrLayerSpeedScale.size() <= nLayer)
|
|
m_arrLayerSpeedScale.resize (nLayer+1, 1);
|
|
m_arrLayerSpeedScale[nLayer] = fSpeed;
|
|
}
|
|
|
|
float CryModelState::getAnimationSpeed (unsigned nLayer)
|
|
{
|
|
if (nLayer >= m_arrLayerSpeedScale.size())
|
|
return 1;
|
|
return m_arrLayerSpeedScale[nLayer];
|
|
}
|
|
|
|
|
|
void CryModelState::setScale (const Vec3d& vScale)
|
|
{
|
|
m_vRuntimeScale = vScale;
|
|
#ifdef _DEBUG
|
|
for (int i = 0; i < 3; ++i)
|
|
if (!(m_vRuntimeScale[i] > 1e-3 && m_vRuntimeScale[i] < 1e3))
|
|
g_GetLog()->LogWarning ("\003character scale[%d] is out of range: %g", i, m_vRuntimeScale[i]);
|
|
#endif
|
|
|
|
bool bParityOdd = vScale.x * vScale.y * vScale.z < 0; // can replace with a XOR
|
|
if (bParityOdd)
|
|
{
|
|
m_pShaderStateCull = g_GetIRenderer()->EF_LoadShader("FrontCull", eSH_World, EF_SYSTEM);
|
|
m_pShaderStateShadowCull = g_GetIRenderer()->EF_LoadShader("StencilState_FrontCull", eSH_World, EF_SYSTEM);
|
|
}
|
|
else
|
|
{
|
|
m_pShaderStateCull = NULL;
|
|
m_pShaderStateShadowCull = NULL;
|
|
}
|
|
}
|
|
|
|
// cleans up all decals
|
|
void CryModelState::ClearDecals()
|
|
{
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin(); it != m_arrSubmeshes.end(); ++it)
|
|
if (*it)(*it)->ClearDecals();
|
|
}
|
|
|
|
void CryModelState::DumpState()
|
|
{
|
|
string strLayers;
|
|
size_t i;
|
|
for (i = 0; i < m_arrAnimationLayers.size(); ++i)
|
|
{
|
|
AnimationLayer& rLayer = m_arrAnimationLayers[i];
|
|
if (rLayer.pEffector)
|
|
{
|
|
strLayers += " " + rLayer.pEffector->dump();
|
|
|
|
if (rLayer.bEnableDefaultIdleAnimRestart)
|
|
{
|
|
strLayers += ":";
|
|
if (rLayer.bKeepLayer0Phase)
|
|
strLayers += " KeepLayer0Phase";
|
|
|
|
char szBuf[32];
|
|
sprintf (szBuf, "%g", rLayer.fDefaultAnimBlendTime );
|
|
|
|
strLayers += " Idle blend: ";
|
|
strLayers += szBuf;
|
|
|
|
if (rLayer.nDefaultIdleAnimID >= 0)
|
|
{
|
|
const AnimData* pAnim = m_pMesh->getAnimationInfo(rLayer.nDefaultIdleAnimID);
|
|
strLayers += " Idle:" + pAnim->strName;
|
|
|
|
if (pAnim->bLoop)
|
|
strLayers += " (looped)";
|
|
}
|
|
|
|
strLayers += "; ";
|
|
}
|
|
else
|
|
strLayers += "(no def idle)";
|
|
|
|
if (i < m_arrLayerSpeedScale.size())
|
|
{
|
|
float fSpeed = m_arrLayerSpeedScale[i];
|
|
if (fSpeed != 1)
|
|
{
|
|
char szBuf[32];
|
|
sprintf (szBuf, "*(%g)", fSpeed);
|
|
strLayers += szBuf;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
strLayers += " <Idle>";
|
|
}
|
|
g_GetLog()->LogToFile("\001--ModelState %p-----------------------", this);
|
|
g_GetLog()->LogToFile("\001%u layers: %s", m_arrAnimationLayers.size(), strLayers.c_str());
|
|
|
|
/*string strMorphs;
|
|
for (i = 0; i < m_arrMorphEffectors.size(); ++i)
|
|
{
|
|
if (i)
|
|
strMorphs += " ";
|
|
char szBuf[128];
|
|
sprintf (szBuf, "%u. ", i);
|
|
strMorphs += szBuf;
|
|
CryModEffMorph& rMorph = m_arrMorphEffectors[i];
|
|
if (rMorph.isActive())
|
|
{
|
|
sprintf (szBuf, "\"%s\" w=%.2f, t=%.2f", m_pMesh->GetNameMorphTarget(rMorph.getMorphTargetId()), rMorph.getBlending(), rMorph.getTime());
|
|
strMorphs += szBuf;
|
|
}
|
|
else
|
|
strMorphs += "Off";
|
|
}
|
|
|
|
g_GetLog()->LogToFile("\001%u morphs: %s", m_arrMorphEffectors.size(), strMorphs.c_str());
|
|
*/
|
|
g_GetLog()->LogToFile ("\002 %d bones", m_arrBones.size());
|
|
for (unsigned i = 0; i < m_arrBones.size(); ++i)
|
|
{
|
|
g_GetLog()->LogToFile ("\003 \"%s\" relative %s, global %s pq %s%s%s",
|
|
m_pMesh->getBoneInfo(i).getNameCStr(),
|
|
toString(getBone(i).m_matRelativeToParent).c_str(),
|
|
toString(getBoneMatrixGlobal(i)).c_str(),
|
|
getBone(i).m_pqTransform.toString().c_str(),
|
|
getBone(i).m_bUseMatPlus?" (Mat+)":"",
|
|
getBone(i).m_bUseReadyRelativeToParentMatrix?" (Use Ready Matrix)":"");
|
|
}
|
|
}
|
|
|
|
// this is the array that's returned from the LeafBuffer
|
|
list2<CMatInfo>* CryModelState::getLeafBufferMaterials()
|
|
{
|
|
return GetCryModelSubmesh(0)->getLeafBufferMaterials();
|
|
}
|
|
|
|
void CryModelState::PreloadResources(float fDistance, float fTime, int nFlags)
|
|
{
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin(); it != m_arrSubmeshes.end(); ++it)
|
|
if (*it)(*it)->PreloadResources(fDistance, fTime, nFlags);
|
|
}
|
|
|
|
// Forces skinning on the next frame
|
|
void CryModelState::ForceReskin()
|
|
{
|
|
m_uFlags |= nFlagsNeedReskinAllLODs;
|
|
m_bPhysicsWasAwake = true;
|
|
}
|
|
|
|
// the model that's coherent with the current model state: bones etc. are taken from there
|
|
CryModel* CryModelState::GetModel()
|
|
{
|
|
// TODO: maybe it's faster to cache this pointer
|
|
return m_arrSubmeshes.empty()?NULL:m_arrSubmeshes[0]->GetCryModel();
|
|
}
|
|
const CryModel* CryModelState::GetModel()const
|
|
{
|
|
// TODO: maybe it's faster to cache this pointer
|
|
return m_arrSubmeshes.empty()?NULL:m_arrSubmeshes[0]->GetCryModel();
|
|
}
|
|
|
|
CryModelSubmesh* CryModelState::AddSubmesh (ICryCharModel* pModel, bool bVisible)
|
|
{
|
|
if (pModel->GetClass() != ICryCharModel::CLASS_CRYCHARBODY)
|
|
return NULL; // not supported
|
|
|
|
return AddSubmesh (static_cast<CryCharBody*>(pModel)->GetModel(), bVisible);
|
|
}
|
|
|
|
CryModelSubmesh* CryModelState::SetSubmesh (unsigned nSlot, ICryCharModel* pModel, bool bVisible)
|
|
{
|
|
if (!pModel)
|
|
{
|
|
RemoveSubmesh (nSlot);
|
|
return NULL;
|
|
}
|
|
|
|
if (pModel->GetClass() != ICryCharModel::CLASS_CRYCHARBODY)
|
|
return NULL; // not supported
|
|
|
|
return SetSubmesh (nSlot,static_cast<CryCharBody*>(pModel)->GetModel(), bVisible);
|
|
}
|
|
|
|
|
|
CryModelSubmesh* CryModelState::SetSubmesh (unsigned nSlot, CryModel* pCryModel, bool bVisible)
|
|
{
|
|
if (nSlot & ~0xFF)
|
|
return NULL; // we don't support more than 256 submeshes
|
|
|
|
if (m_arrSubmeshes.size() <= nSlot)
|
|
m_arrSubmeshes.resize (nSlot+1);
|
|
|
|
CryModelSubmesh* pNewSubmesh = new CryModelSubmesh(this, pCryModel);
|
|
pNewSubmesh->SetVisible(bVisible);
|
|
m_arrSubmeshes[nSlot] = pNewSubmesh;
|
|
return pNewSubmesh;
|
|
}
|
|
|
|
// adds a submesh, returns handle to it which can be used to delete the submesh
|
|
// submesh is created either visible or invisible
|
|
// submesh creation/destruction is heavy operations, so the clients must use they rarely,
|
|
// and set visible/invisible when they need to turn them on/off
|
|
// But creating many submeshes is memory-consuming so the number of them must be kept low at all times
|
|
CryModelSubmesh* CryModelState::AddSubmesh (CryModel* pCryModel, bool bVisible)
|
|
{
|
|
// TODO: thorough check for compatibility between the models here
|
|
if (!IsEmpty() && pCryModel->numBoneInfos() != GetCryModelSubmesh(0)->GetCryModel()->numBoneInfos())
|
|
{
|
|
g_GetLog()->LogError ("Trying to add submesh %s to an existing instance with main model %s: incompatible skeletons", pCryModel->getFilePathCStr(), GetCryModelSubmesh(0)->GetCryModel()->getFilePathCStr());
|
|
return NULL;
|
|
}
|
|
|
|
CryModelSubmesh* pNewSubmesh = new CryModelSubmesh(this, pCryModel);
|
|
pNewSubmesh->SetVisible(bVisible);
|
|
|
|
// find a free slot to insert the new submesh
|
|
for (unsigned nSlot = 1; nSlot < m_arrSubmeshes.size(); ++nSlot)
|
|
if (!m_arrSubmeshes[nSlot])
|
|
{
|
|
m_arrSubmeshes[nSlot] = pNewSubmesh;
|
|
return pNewSubmesh;
|
|
}
|
|
|
|
m_arrSubmeshes.push_back (pNewSubmesh);
|
|
return pNewSubmesh;
|
|
}
|
|
|
|
// removes submesh from the character
|
|
void CryModelState::RemoveSubmesh (ICryCharSubmesh* pSubmesh)
|
|
{
|
|
// we may not delete the 0th submesh
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin()+1; it != m_arrSubmeshes.end(); ++it)
|
|
if (*it == pSubmesh)
|
|
*it = NULL;
|
|
}
|
|
|
|
void CryModelState::RemoveSubmesh(unsigned nSlot)
|
|
{
|
|
if (nSlot > 1 && nSlot < m_arrSubmeshes.size())
|
|
{
|
|
m_arrSubmeshes[nSlot] = NULL;
|
|
|
|
// we may auto-shrink the array
|
|
while (!m_arrSubmeshes.empty() && !m_arrSubmeshes.back())
|
|
m_arrSubmeshes.resize (m_arrSubmeshes.size()-1);
|
|
}
|
|
}
|
|
|
|
ICryCharSubmesh* CryModelState::GetSubmesh(unsigned i)
|
|
{
|
|
#if defined(LINUX)
|
|
ICryCharSubmesh* pRes( 0 );
|
|
if( i < m_arrSubmeshes.size() )
|
|
{
|
|
pRes = m_arrSubmeshes[i];
|
|
}
|
|
return( pRes );
|
|
#else
|
|
return i < m_arrSubmeshes.size() ? m_arrSubmeshes[i]:NULL;
|
|
#endif
|
|
}
|
|
|
|
CryModelSubmesh* CryModelState::GetCryModelSubmesh(unsigned i)
|
|
{
|
|
#if defined(LINUX)
|
|
CryModelSubmesh* pRes( 0 );
|
|
if( i < m_arrSubmeshes.size() )
|
|
{
|
|
pRes = m_arrSubmeshes[i];
|
|
}
|
|
return( pRes );
|
|
#else
|
|
return i < m_arrSubmeshes.size() ? m_arrSubmeshes[i]:NULL;
|
|
#endif
|
|
}
|
|
|
|
bool CryModelState::SetShaderTemplateName (const char *TemplName, int Id, const char *ShaderName,IMatInfo *pCustomMaterial,unsigned nFlags)
|
|
{
|
|
bool bRes = false;
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin(); it != m_arrSubmeshes.end(); ++it)
|
|
if (*it)
|
|
bRes = (*it)->SetShaderTemplateName (TemplName, Id, ShaderName, pCustomMaterial,nFlags) || bRes;
|
|
return bRes;
|
|
}
|
|
|
|
CLeafBuffer* CryModelState::GetLeafBuffer ()
|
|
{
|
|
return GetCryModelSubmesh(0)->GetLeafBuffer();
|
|
}
|
|
|
|
void CryModelState::SetShaderFloat(const char *Name, float fVal, const char *ShaderName)
|
|
{
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin(); it != m_arrSubmeshes.end(); ++it)
|
|
if (*it)(*it)->SetShaderFloat (Name, fVal, ShaderName);
|
|
}
|
|
|
|
int CryModelState::numLODs() // number of LODs in the 0th submesh
|
|
{
|
|
return GetCryModelSubmesh(0)->GetCryModel()->numLODs();
|
|
}
|
|
|
|
//Calculate shadow volumes,fill buffers and render shadow volumes into the stencil buffer
|
|
//TODO: Optimize everything
|
|
//////////////////////////////////////////////////////////////////////
|
|
void CryModelState::RenderShadowVolumes (const SRendParams *rParams, int nLimitLOD)
|
|
{
|
|
assert(Get3DEngine()); // should be always there
|
|
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin(); it != m_arrSubmeshes.end(); ++it)
|
|
if (*it)(*it)->RenderShadowVolumes(rParams, nLimitLOD);
|
|
}
|
|
|
|
|
|
// renders the decals - adds the render element to the renderer
|
|
/*
|
|
void CryModelState::AddDecalRenderData (CCObject *pObj, const SRendParams & rRendParams)
|
|
{
|
|
if (m_nLodLevel == 0) // we don't render the character decals in lod!=0
|
|
{
|
|
for (SubmeshArray::iterator it = m_arrSubmeshes.begin(); it != m_arrSubmeshes.end(); ++it)
|
|
if (*it)(*it)->AddDecalRenderData (pObj, rRendParams);
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
class CryCharFxTrail* CryModelState::NewFxTrail (unsigned nSlot, const struct CryCharFxTrailParams& rParams)
|
|
{
|
|
if (m_arrFxTrails.size() <= nSlot)
|
|
m_arrFxTrails.resize (nSlot+1);
|
|
return m_arrFxTrails[nSlot] = new CryCharFxTrail(this, rParams);
|
|
}
|
|
|
|
void CryModelState::RemoveFxTrail (unsigned nSlot)
|
|
{
|
|
if (nSlot < m_arrFxTrails.size())
|
|
{
|
|
m_arrFxTrails[nSlot] = NULL;
|
|
|
|
// remove unnecessary slots
|
|
#if !defined(LINUX64)
|
|
while (!m_arrFxTrails.empty() && m_arrFxTrails.back() == NULL)
|
|
#else
|
|
while (!m_arrFxTrails.empty() && m_arrFxTrails.back() == 0)
|
|
#endif
|
|
m_arrFxTrails.resize (m_arrFxTrails.size()-1);
|
|
}
|
|
}
|