123
This commit is contained in:
716
CryAnimation/AnimObject.cpp
Normal file
716
CryAnimation/AnimObject.cpp
Normal file
@@ -0,0 +1,716 @@
|
||||
#include "StdAfx.h"
|
||||
#include "AnimObject.h"
|
||||
#include "StringUtils.h"
|
||||
#include "cvars.h"
|
||||
#include "DebugUtils.h"
|
||||
|
||||
#include <I3DEngine.h>
|
||||
#include <IMovieSystem.h>
|
||||
#include <ITimer.h>
|
||||
#include "CryCharAnimationParams.h"
|
||||
#include "Cry_Geo.h"
|
||||
|
||||
using namespace CryStringUtils;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CAnimObject::CAnimObject()
|
||||
{
|
||||
m_nRefCount = 0;
|
||||
m_characterModel.animSet.obj = this;
|
||||
m_characterModel.pAnimObject = this;
|
||||
|
||||
m_bbox[0]=SetMaxBB();
|
||||
m_bbox[1]=SetMinBB();
|
||||
|
||||
m_angles(0,0,0);
|
||||
|
||||
m_nFlags = CS_FLAG_UPDATE|CS_FLAG_DRAW_MODEL;
|
||||
m_bNoTimeUpdate = false;
|
||||
m_currAnimation = 0;
|
||||
|
||||
m_bAllNodesValid = false;
|
||||
m_bboxValid = false;
|
||||
|
||||
m_time = 0;
|
||||
m_animSpeed = 1;
|
||||
m_physic = 0;
|
||||
m_lastAnimTime = -1;
|
||||
m_lastScale = 1.0f;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CAnimObject::~CAnimObject()
|
||||
{
|
||||
ReleaseNodes();
|
||||
ReleaseAnims();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObject::ReleaseNodes()
|
||||
{
|
||||
I3DEngine *engine = Get3DEngine();
|
||||
for (unsigned int i = 0; i < m_nodes.size(); i++)
|
||||
{
|
||||
Node *pNode = m_nodes[i];
|
||||
if (pNode->m_object)
|
||||
engine->ReleaseObject( pNode->m_object );
|
||||
|
||||
delete pNode;
|
||||
}
|
||||
m_nodes.clear();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObject::ReleaseAnims()
|
||||
{
|
||||
for (unsigned int i = 0; i < m_animations.size(); i++)
|
||||
{
|
||||
delete m_animations[i];
|
||||
}
|
||||
m_animations.clear();
|
||||
m_currAnimation = 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! set name of geometry for this animated object.
|
||||
void CAnimObject::SetFileName( const char *szFileName )
|
||||
{
|
||||
m_fileName = szFileName;
|
||||
UnifyFilePath( m_fileName );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CAnimObject::Node* CAnimObject::CreateNode( const char *szNodeName )
|
||||
{
|
||||
// Try to create object.
|
||||
I3DEngine *engine = Get3DEngine();
|
||||
IStatObj *obj = engine->MakeObject( m_fileName.c_str(),szNodeName,evs_ShareAndSortForCache,true,true );
|
||||
if (obj)
|
||||
{
|
||||
AddToBounds( obj->GetBoxMin(), m_bbox[0],m_bbox[1] );
|
||||
AddToBounds( obj->GetBoxMax(), m_bbox[0],m_bbox[1] );
|
||||
}
|
||||
|
||||
Node *node = new Node;
|
||||
node->m_name = szNodeName;
|
||||
node->m_object = obj;
|
||||
node->m_id = (int)m_nodes.size();
|
||||
|
||||
m_nodes.push_back(node);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
//! Render object ( register render elements into renderer )
|
||||
void CAnimObject::Render(const struct SRendParams & rParams,const Vec3& t, int nLodLevel)
|
||||
{
|
||||
Draw (rParams,Vec3(123,123,123));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObject::Draw( const SRendParams &rp, const Vec3& t )
|
||||
{
|
||||
SRendParams nodeRP = rp;
|
||||
Matrix44 renderTM;
|
||||
if (rp.pMatrix)
|
||||
{
|
||||
renderTM = *rp.pMatrix;
|
||||
}
|
||||
else
|
||||
{
|
||||
//renderTM.Identity();
|
||||
//renderTM=GetTranslationMat(rp.vPos)*renderTM;
|
||||
//renderTM=GetRotationZYX44(-gf_DEGTORAD*rp.vAngles )*renderTM; //NOTE: angles in radians and negated
|
||||
//renderTM=GetScale33(Vec3(rp.fScale,rp.fScale,rp.fScale))*renderTM;
|
||||
|
||||
//OPTIMISED_BY_IVO
|
||||
Matrix33diag diag = Vec3(rp.fScale,rp.fScale,rp.fScale); //use diag-matrix for scaling
|
||||
Matrix34 rt34 = Matrix34::CreateRotationXYZ( Deg2Rad(rp.vAngles),rp.vPos ); //set rotation and translation in one function call
|
||||
renderTM = rt34*diag; //optimised concatenation: m34*diag
|
||||
renderTM=GetTransposed44(renderTM); //TODO: remove this after E3 and use Matrix34 instead of Matrix44
|
||||
}
|
||||
for (unsigned int i = 0; i < m_nodes.size(); i++)
|
||||
{
|
||||
Node *node = m_nodes[i];
|
||||
if (node->m_object)
|
||||
{
|
||||
Matrix44 tm = GetNodeMatrix(node) * renderTM;
|
||||
nodeRP.pMatrix = &tm;
|
||||
nodeRP.dwFObjFlags |= FOB_TRANS_MASK;
|
||||
m_nodes[i]->m_object->Render(nodeRP,Vec3(zero),0);
|
||||
}
|
||||
}
|
||||
DrawBoundObjects( rp,renderTM,0 );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Draw the character shadow volumes into the stencil buffer using a set of specified
|
||||
//! rendering parameters ( for indoors )
|
||||
void CAnimObject::RenderShadowVolumes(const SRendParams *rParams, int nLimitLOD)
|
||||
{
|
||||
const SRendParams &rp = *rParams;
|
||||
|
||||
SRendParams nodeRP = rp;
|
||||
Matrix44 renderTM;
|
||||
if (rp.pMatrix)
|
||||
{
|
||||
renderTM = *rp.pMatrix;
|
||||
}
|
||||
else
|
||||
{
|
||||
//renderTM.Identity();
|
||||
//renderTM=GetTranslationMat(rp.vPos)*renderTM;
|
||||
//renderTM=GetRotationZYX44(-gf_DEGTORAD*rp.vAngles )*renderTM; //NOTE: angles in radians and negated
|
||||
//renderTM=GetScale33(Vec3(rp.fScale,rp.fScale,rp.fScale))*renderTM;
|
||||
|
||||
//OPTIMISED_BY_IVO
|
||||
Matrix33diag diag = Vec3(rp.fScale,rp.fScale,rp.fScale); //use diag-matrix for scaling
|
||||
Matrix34 rt34 = Matrix34::CreateRotationXYZ(Deg2Rad(rp.vAngles),rp.vPos); //set rotation and translation in one function call
|
||||
renderTM = rt34*diag; //optimised concatenation: m34*diag
|
||||
renderTM=GetTransposed44(renderTM); //TODO: remove this after E3 and use Matrix34 instead of Matrix44
|
||||
|
||||
}
|
||||
for (unsigned int i = 0; i < m_nodes.size(); i++)
|
||||
{
|
||||
Node *node = m_nodes[i];
|
||||
if (node->m_object)
|
||||
{
|
||||
Matrix44 tm = GetNodeMatrix(node) * renderTM;
|
||||
nodeRP.pMatrix = &tm;
|
||||
m_nodes[i]->m_object->RenderShadowVolumes(&nodeRP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObject::Animate( float time )
|
||||
{
|
||||
if (!m_currAnimation)
|
||||
return;
|
||||
|
||||
|
||||
if (m_currAnimation->loop)
|
||||
{
|
||||
float timelen = m_currAnimation->endTime - m_currAnimation->startTime;
|
||||
time = m_currAnimation->startTime + cry_fmod( time,timelen );
|
||||
}
|
||||
else
|
||||
{
|
||||
time = m_currAnimation->startTime + time;
|
||||
}
|
||||
if (time < 0)
|
||||
{
|
||||
// Go negative.
|
||||
time = m_currAnimation->endTime + time;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < m_nodes.size(); i++)
|
||||
{
|
||||
Node *node = m_nodes[i];
|
||||
NodeAnim *nodeAnim = GetNodeAnim(node);
|
||||
if (node->m_object && nodeAnim)
|
||||
{
|
||||
if (!m_bAllNodesValid)
|
||||
{
|
||||
node->m_pos = nodeAnim->m_pos;
|
||||
node->m_scale = nodeAnim->m_scale;
|
||||
node->m_rotate = nodeAnim->m_rotate;
|
||||
node->m_bMatrixValid = false;
|
||||
}
|
||||
if (nodeAnim->m_posTrack)
|
||||
{
|
||||
node->m_pos = nodeAnim->m_posTrack->GetPosition( time );
|
||||
node->m_bMatrixValid = false;
|
||||
// Bounding box can change.
|
||||
m_bboxValid = false;
|
||||
}
|
||||
if (nodeAnim->m_rotTrack)
|
||||
{
|
||||
node->m_rotate = nodeAnim->m_rotTrack->GetOrientation( time );
|
||||
node->m_bMatrixValid = false;
|
||||
m_bboxValid = false;
|
||||
}
|
||||
if (nodeAnim->m_scaleTrack)
|
||||
{
|
||||
node->m_scale = nodeAnim->m_scaleTrack->GetScale( time );
|
||||
node->m_bMatrixValid = false;
|
||||
m_bboxValid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
//m_bboxValid = false;
|
||||
|
||||
m_bAllNodesValid = true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObject::RecalcBBox()
|
||||
{
|
||||
AABB box;
|
||||
m_bbox[0]=SetMaxBB();
|
||||
m_bbox[1]=SetMinBB();
|
||||
// Re calc bbox.
|
||||
for (unsigned int i = 0; i < m_nodes.size(); i++)
|
||||
{
|
||||
Node *node = m_nodes[i];
|
||||
Matrix44 &tm = GetNodeMatrix(node);
|
||||
if (node->m_object)
|
||||
{
|
||||
box.min = node->m_object->GetBoxMin();
|
||||
box.max = node->m_object->GetBoxMax();
|
||||
box.Transform( tm );
|
||||
AddToBounds( box.min, m_bbox[0],m_bbox[1] );
|
||||
AddToBounds( box.max, m_bbox[0],m_bbox[1] );
|
||||
}
|
||||
}
|
||||
m_bboxValid = true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
Matrix44& CAnimObject::GetNodeMatrix( Node *node )
|
||||
{
|
||||
// fixme.
|
||||
node->m_bMatrixValid = false;
|
||||
|
||||
|
||||
assert(node);
|
||||
if (!node->m_bMatrixValid)
|
||||
{
|
||||
// Make local matrix.
|
||||
//Q2M_CHANGED_BY_IVO
|
||||
//node->m_rotate.GetMatrix(node->m_tm);
|
||||
//node->m_tm = GetTransposed44(node->m_rotate);
|
||||
node->m_tm = Matrix44(node->m_rotate);
|
||||
|
||||
|
||||
//SCALE_CHANGED_BY_IVO
|
||||
//node->m_tm.ScaleMatrix( node->m_scale.x,node->m_scale.y,node->m_scale.z );
|
||||
node->m_tm = Matrix33::CreateScale( Vec3(node->m_scale.x,node->m_scale.y,node->m_scale.z) ) * node->m_tm;
|
||||
|
||||
node->m_tm.SetTranslationOLD( node->m_pos );
|
||||
node->m_bMatrixValid = true;
|
||||
|
||||
if (node->m_parent)
|
||||
{
|
||||
// Combine with parent matrix.
|
||||
Matrix44 &parentTM = GetNodeMatrix(node->m_parent);
|
||||
node->m_tm = node->m_tm * parentTM;
|
||||
}
|
||||
}
|
||||
|
||||
return node->m_tm;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CAnimObject::NodeAnim* CAnimObject::GetNodeAnim( Node *node )
|
||||
{
|
||||
if (!m_currAnimation)
|
||||
return 0;
|
||||
if (node->m_id >= (int)m_currAnimation->nodeAnims.size())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return &(m_currAnimation->nodeAnims[node->m_id]);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObject::AddAnimation( Animation* anim )
|
||||
{
|
||||
m_animations.push_back(anim);
|
||||
if (m_animations.size() == 1)
|
||||
{
|
||||
// First anim, make current.
|
||||
m_currAnimation = anim;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObject::RemoveAnimation( Animation* anim )
|
||||
{
|
||||
m_animations.erase( std::remove(m_animations.begin(),m_animations.end(),anim),m_animations.end() );
|
||||
if (m_currAnimation = anim)
|
||||
{
|
||||
if (!m_animations.empty())
|
||||
m_currAnimation = m_animations[0];
|
||||
else
|
||||
m_currAnimation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CAnimObject::Animation* CAnimObject::FindAnimation( const char *szAnimationName )
|
||||
{
|
||||
for (unsigned int i = 0; i < m_animations.size(); i++)
|
||||
{
|
||||
if (stricmp(m_animations[i]->name.c_str(),szAnimationName)==0)
|
||||
{
|
||||
return m_animations[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
int CAnimObject::FindNodeByName( const char *szNodeName )
|
||||
{
|
||||
for (unsigned int i = 0; i < m_nodes.size(); i++)
|
||||
{
|
||||
if (stricmp(m_nodes[i]->m_name.c_str(),szNodeName)==0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
const char* CAnimObject::GetNodeName( int nodeId )
|
||||
{
|
||||
if (nodeId > 0 && nodeId < (int)m_nodes.size())
|
||||
return m_nodes[nodeId]->m_name.c_str();
|
||||
return "";
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObject::SetCurrent( Animation *anim )
|
||||
{
|
||||
if (anim != m_currAnimation)
|
||||
{
|
||||
m_time = 0;
|
||||
m_lastAnimTime = -1;
|
||||
m_bAllNodesValid = false;
|
||||
m_bboxValid = false;
|
||||
m_currAnimation = anim;
|
||||
if (m_currAnimation)
|
||||
{
|
||||
Animate(m_currAnimation->startTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Resets all animation layers ( stops all animations )
|
||||
void CAnimObject::ResetAnimations()
|
||||
{
|
||||
if (m_currAnimation)
|
||||
{
|
||||
// Animate existing animation to 0.
|
||||
Animate(m_currAnimation->startTime);
|
||||
}
|
||||
SetCurrent(0);
|
||||
UpdatePhysics( m_lastScale );
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
const CDLight* CAnimObject::GetBoundLight (int nIndex)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObject::Update( Vec3 vPos, float fRadius, unsigned uFlags)
|
||||
{
|
||||
if (!(m_nFlags & CS_FLAG_UPDATE))
|
||||
return;
|
||||
|
||||
float m_lastAnimTime = m_time;
|
||||
if (!m_bNoTimeUpdate)
|
||||
{
|
||||
float dt = g_GetTimer()->GetFrameTime();
|
||||
m_time = m_time + dt*m_animSpeed;
|
||||
|
||||
if (m_currAnimation)
|
||||
{
|
||||
if (!m_currAnimation->loop && !m_currAnimation->haveLoopingController)
|
||||
{
|
||||
if (m_time > m_currAnimation->endTime)
|
||||
m_time = m_currAnimation->endTime;
|
||||
if (m_time < m_currAnimation->startTime)
|
||||
m_time = m_currAnimation->startTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_lastAnimTime != m_time)
|
||||
Animate( m_time );
|
||||
}
|
||||
|
||||
//! Set the current time of the given layer, in seconds
|
||||
void CAnimObject::SetLayerTime (int nLayer, float fTimeSeconds)
|
||||
{
|
||||
m_time = fTimeSeconds;
|
||||
m_fAnimTime = fTimeSeconds;
|
||||
|
||||
if (m_lastAnimTime != m_time)
|
||||
Animate( m_time );
|
||||
|
||||
m_lastAnimTime = m_time;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CAnimObject::IsModelFileEqual (const char* szFileName)
|
||||
{
|
||||
string strPath = szFileName;
|
||||
UnifyFilePath(strPath);
|
||||
|
||||
return stricmp(strPath.c_str(),m_fileName.c_str()) == 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObject::CreateDecal(CryEngineDecalInfo& Decal)
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Physics.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
IPhysicalEntity* CAnimObject::CreateCharacterPhysics(IPhysicalEntity *pHost, float mass,int surface_idx,float stiffness_scale, int nLod)
|
||||
{
|
||||
//m_physic = GetPhysicalWorld()->CreatePhysicalEntity(PE_STATIC,&partpos,(IEntity*)this);
|
||||
assert( pHost );
|
||||
m_physic = pHost;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
int CAnimObject::CreateAuxilaryPhysics(IPhysicalEntity *pHost, int nLod)
|
||||
{
|
||||
//assert( pHost );
|
||||
//m_physic = pHost;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObject::BuildPhysicalEntity( IPhysicalEntity *pent,float mass,int surface_idx,float stiffness_scale,int nLod )
|
||||
{
|
||||
assert( pent );
|
||||
|
||||
m_physic = pent;
|
||||
|
||||
unsigned int i;
|
||||
float totalVolume = 0;
|
||||
|
||||
for (i = 0; i < m_nodes.size(); i++)
|
||||
{
|
||||
Node *node = m_nodes[i];
|
||||
if (!node->m_object)
|
||||
continue;
|
||||
phys_geometry *geom = node->m_object->GetPhysGeom(0);
|
||||
if (geom)
|
||||
{
|
||||
totalVolume += geom->V;
|
||||
}
|
||||
}
|
||||
float density = mass/totalVolume;
|
||||
|
||||
pe_geomparams params;
|
||||
for (i = 0; i < m_nodes.size(); i++)
|
||||
{
|
||||
Node *node = m_nodes[i];
|
||||
if (!node->m_object)
|
||||
continue;
|
||||
|
||||
params.density = density;
|
||||
|
||||
// Add collision geometry.
|
||||
params.flags = geom_collides;
|
||||
phys_geometry *geom = node->m_object->GetPhysGeom(0);
|
||||
if (geom)
|
||||
{
|
||||
m_physic->AddGeometry( geom, ¶ms, node->m_id );
|
||||
node->bPhysics = true;
|
||||
}
|
||||
|
||||
// Add obstruct geometry.
|
||||
params.flags = geom_proxy;
|
||||
geom = node->m_object->GetPhysGeom(1);
|
||||
if (geom)
|
||||
{
|
||||
m_physic->AddGeometry( geom, ¶ms, node->m_id );
|
||||
node->bPhysics = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObject::UpdatePhysics( float fScale )
|
||||
{
|
||||
if (!m_physic)
|
||||
return;
|
||||
|
||||
m_lastScale = fScale;
|
||||
|
||||
Matrix44 parentTM;
|
||||
parentTM.SetIdentity();
|
||||
|
||||
//SCALE_CHANGED_BY_IVO
|
||||
//parentTM.ScaleMatrix( fScale,fScale,fScale );
|
||||
parentTM=Matrix33::CreateScale( Vec3(fScale,fScale,fScale) )*parentTM;
|
||||
|
||||
int numNodes = (int)m_nodes.size();
|
||||
pe_params_part params;
|
||||
for (int i = 0; i < numNodes; i++)
|
||||
{
|
||||
Node *node = m_nodes[i];
|
||||
if (!node->bPhysics)
|
||||
continue;
|
||||
|
||||
params.partid = node->m_id;
|
||||
params.bRecalcBBox = true;
|
||||
//if (i == numNodes-1) // last node.
|
||||
//params.bRecalcBBox = true;
|
||||
|
||||
Matrix44 tm = GetNodeMatrix(node) * parentTM;
|
||||
params.pMtx4x4T = tm.GetData();
|
||||
|
||||
m_physic->SetParams( ¶ms );
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObject::AddImpact(int partid, vectorf point,vectorf impact)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
// Pushes the underlying tree of objects into the given Sizer object for statistics gathering
|
||||
void CAnimObject::GetMemoryUsage(class ICrySizer* pSizer)const
|
||||
{
|
||||
#if ENABLE_GET_MEMORY_USAGE
|
||||
if (!pSizer->Add (*this))
|
||||
return;
|
||||
pSizer->AddString (m_fileName);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CAnimObject::Node::GetSize (ICrySizer* pSizer)const
|
||||
{
|
||||
#if ENABLE_GET_MEMORY_USAGE
|
||||
if (!pSizer->Add (*this))
|
||||
return;
|
||||
pSizer->AddString (m_name);
|
||||
m_parent->GetSize(pSizer);
|
||||
|
||||
if (pSizer->GetFlags() & CSF_RecurseSubsystems)
|
||||
{
|
||||
SIZER_COMPONENT_NAME(pSizer, "3DEngine");
|
||||
m_object->GetMemoryUsage(pSizer);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ICryBone* CAnimObject::GetBoneByName(const char * szName)
|
||||
{
|
||||
for (unsigned int i = 0; i < m_nodes.size(); i++)
|
||||
{
|
||||
if (m_nodes[i] != NULL && stricmp(m_nodes[i]->m_name.c_str(),szName) == 0)
|
||||
return m_nodes[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CAnimObject::ObjectBindingHandle CAnimObject::AttachObjectToBone(IBindable * pWeaponModel, const char *szBoneName, bool bUseRelativeToDefPoseMatrix , unsigned nFlags )
|
||||
{
|
||||
if(!szBoneName)
|
||||
{
|
||||
// if you find this assert, this means someone passed here a model and NO bone to attach it to. What should it mean anyway??
|
||||
assert (!pWeaponModel);
|
||||
// just detach everything
|
||||
DetachAll();
|
||||
return nInvalidObjectBindingHandle;
|
||||
}
|
||||
|
||||
int nBone = FindNodeByName(szBoneName);
|
||||
if(nBone < 0)
|
||||
{
|
||||
if (!pWeaponModel)
|
||||
{
|
||||
// this is a severe bug if the bone name is invalid and someone tries to detach the model
|
||||
// if we find such a situation, we should try to detach the model, as it might be destructed already
|
||||
|
||||
// but now we just detach everything, as it's simpler
|
||||
//m_arrBoundObjects.clear();
|
||||
//m_arrBoundObjectIndices.clear();
|
||||
|
||||
g_GetLog()->LogError ("\002AttachObjectToBone is called for bone \"%s\", which is not in the model \"%s\". Ignoring, but this may cause a crash because the corresponding object won't be detached after it's destroyed", szBoneName, GetFileName() );
|
||||
#ifdef _DEBUG
|
||||
// this assert will only happen if the ca_NoAttachAssert is off
|
||||
// assert (GetCVars()->ca_NoAttachAssert());
|
||||
#endif
|
||||
}
|
||||
return nInvalidObjectBindingHandle; // bone not found, do nothing
|
||||
}
|
||||
|
||||
// detach all objects from this bone before creating a new one
|
||||
DetachAllFromBone(nBone);
|
||||
|
||||
if(pWeaponModel == NULL)
|
||||
{
|
||||
// we didn't create a new binding, so return invalid handle
|
||||
return nInvalidObjectBindingHandle;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AttachToBone(pWeaponModel, nBone, nFlags);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObject::PreloadResources ( float fDistance, float fTime, int nFlags)
|
||||
{
|
||||
CryCharInstanceBase::PreloadResources( fDistance,fTime,nFlags );
|
||||
|
||||
for (unsigned int i = 0; i < m_nodes.size(); i++)
|
||||
if (m_nodes[i] != NULL && m_nodes[i]->m_object)
|
||||
m_nodes[i]->m_object->PreloadResources(fDistance, fTime, nFlags);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObject::DrawBoundObjects(const SRendParams & rRendParams, Matrix44 &inmatTranRotMatrix, int nLOD)
|
||||
{
|
||||
static const float fColorBBoxAttached[4] = {0.5,1,1,0.75};
|
||||
|
||||
if (g_GetCVars()->ca_NoDrawBound())
|
||||
return;
|
||||
|
||||
if (m_arrBinds.empty())
|
||||
return;
|
||||
|
||||
Matrix44 matAttachedObjectMatrix;
|
||||
SRendParams rParams (rRendParams);
|
||||
// this is required to avoid the attachments using the parent character material (this is the material that overrides the default material in the attachment)
|
||||
rParams.pMaterial = NULL;
|
||||
|
||||
if (m_nFlags & CS_FLAG_DRAW_NEAR)
|
||||
{
|
||||
rParams.dwFObjFlags |= FOB_NEAREST;
|
||||
}
|
||||
|
||||
BindArray::iterator it, itEnd = m_arrBinds.end();
|
||||
for (it = m_arrBinds.begin(); it != itEnd; ++it)
|
||||
{
|
||||
IBindable* pBoundObject = (*it)->pObj;
|
||||
if (!pBoundObject)
|
||||
continue;
|
||||
|
||||
int nBone = (*it)->nBone;
|
||||
if (nBone < 0 || nBone >= (int)m_nodes.size())
|
||||
continue;
|
||||
|
||||
Matrix44 &boneTM = GetNodeMatrix(m_nodes[nBone]);
|
||||
matAttachedObjectMatrix = boneTM * inmatTranRotMatrix;
|
||||
rParams.pMatrix = &matAttachedObjectMatrix;
|
||||
|
||||
if (g_GetCVars()->ca_DrawBBox()>1){
|
||||
Matrix34 m34=Matrix34(GetTransposed44(matAttachedObjectMatrix));
|
||||
|
||||
CryAABB caabb;
|
||||
pBoundObject->GetBBox(caabb.vMin, caabb.vMax);
|
||||
|
||||
debugDrawBBox (m34, caabb, g_GetCVars()->ca_DrawBBox()-1,fColorBBoxAttached);
|
||||
}
|
||||
//pBoundObject->SetShaderTemplate( rParams.nShaderTemplate, 0] ,0 );
|
||||
pBoundObject->Render(rParams,Vec3(zero), nLOD);
|
||||
}
|
||||
}
|
||||
590
CryAnimation/AnimObject.h
Normal file
590
CryAnimation/AnimObject.h
Normal file
@@ -0,0 +1,590 @@
|
||||
#ifndef __AnimObject_h__
|
||||
#define __AnimObject_h__
|
||||
#pragma once
|
||||
|
||||
struct IAnimTrack;
|
||||
class IPhysicalEntity;
|
||||
|
||||
#include "ICryAnimation.h"
|
||||
#include "Controller.h"
|
||||
#include "CryCharInstance.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CAnimObject : public CryCharInstanceBase
|
||||
{
|
||||
public:
|
||||
// CAnimObject *obj;
|
||||
|
||||
|
||||
//! Node in animated object.
|
||||
//! Implements Bone interface.
|
||||
struct Node : public ICryBone
|
||||
{
|
||||
string m_name; // Node name.
|
||||
// Node id, (Assigned at node creation time).
|
||||
int m_id;
|
||||
//! Current node position (in object space).
|
||||
Vec3 m_pos;
|
||||
//! Current node rotation (in object space).
|
||||
CryQuat m_rotate;
|
||||
//! Original node scale (in object space).
|
||||
Vec3 m_scale;
|
||||
//! Node transformation matrix in world space.
|
||||
Matrix44 m_tm;
|
||||
//! True if current matrix is valid.
|
||||
bool m_bMatrixValid;
|
||||
//! parent node.
|
||||
Node* m_parent;
|
||||
//! Static object controlled by this node.
|
||||
IStatObj* m_object;
|
||||
//! True if have physics.
|
||||
bool bPhysics;
|
||||
|
||||
Node()
|
||||
{
|
||||
m_id = 0;
|
||||
m_tm.SetIdentity();
|
||||
m_parent = 0;
|
||||
m_object = 0;
|
||||
m_bMatrixValid = false;
|
||||
m_pos(0,0,0);
|
||||
m_rotate.SetIdentity();
|
||||
m_scale(1,1,1);
|
||||
bPhysics = false;
|
||||
}
|
||||
void GetSize (ICrySizer* pSizer)const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ICryBone interface implementation.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
virtual void DoNotCalculateBoneRelativeMatrix(bool bDoNotCalculate) {};
|
||||
virtual void FixBoneMatrix (const Matrix44& mtxBone) {};
|
||||
virtual void FixBoneOriginInWorld (const Vec3& vCharPos, const Vec3& vCharAngles, const Vec3& vTargetOrigin) {};
|
||||
virtual void SetBoneOriginInWorld (const Vec3& vCharPos, const Vec3& vCharAngles, const Vec3& vTargetOrigin) {};
|
||||
virtual void SetPlusRotation(float x, float y, float z) {};
|
||||
virtual void SetPlusRotation(const CryQuat& qRotation) {};
|
||||
virtual void ResetPlusRotation() {};
|
||||
virtual Vec3 GetBonePosition()
|
||||
{
|
||||
return m_tm.GetTranslationOLD();
|
||||
};
|
||||
virtual Vec3 GetBoneAxis(char cAxis)
|
||||
{
|
||||
switch (cAxis)
|
||||
{
|
||||
//[Timur] Not sure if its correct axises.
|
||||
case 0: return Vec3(m_tm(0,0),m_tm(1,0),m_tm(2,0));
|
||||
case 1: return Vec3(m_tm(0,1),m_tm(1,1),m_tm(2,1));
|
||||
//case 2: return Vec3(m_tm(0,2),m_tm(1,2),m_tm(2,2));
|
||||
}
|
||||
return Vec3(m_tm(0,2),m_tm(1,2),m_tm(2,2));
|
||||
};
|
||||
virtual CryQuat GetParentWQuat () {
|
||||
if (m_parent)
|
||||
m_parent->m_rotate;
|
||||
return Quat(1,0,0,0);
|
||||
}; //[Timur] Wrong implementation.
|
||||
virtual ICryBone* GetParent() { return m_parent; };
|
||||
virtual const Matrix44& GetRelativeMatrix() { return m_tm; };
|
||||
virtual const Matrix44& GetAbsoluteMatrix() { return m_tm; }; //[Timur] Wrong implementation.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
};
|
||||
|
||||
// Node animation.
|
||||
struct NodeAnim
|
||||
{
|
||||
//! Current node position (in object space).
|
||||
Vec3 m_pos;
|
||||
//! Current node rotation (in object space).
|
||||
CryQuat m_rotate;
|
||||
//! Original node scale (in object space).
|
||||
Vec3 m_scale;
|
||||
IController* m_posTrack;
|
||||
IController* m_rotTrack;
|
||||
IController* m_scaleTrack;
|
||||
|
||||
NodeAnim()
|
||||
{
|
||||
m_pos(0,0,0);
|
||||
m_rotate.SetIdentity();
|
||||
m_scale(1,1,1);
|
||||
m_posTrack = 0;
|
||||
m_rotTrack = 0;
|
||||
m_scaleTrack = 0;
|
||||
}
|
||||
|
||||
// deletes the controllers belonging ot this node
|
||||
void clearControllers()
|
||||
{
|
||||
if (m_posTrack)
|
||||
{
|
||||
delete m_posTrack;
|
||||
m_posTrack = NULL;
|
||||
}
|
||||
if (m_rotTrack)
|
||||
{
|
||||
delete m_rotTrack;
|
||||
m_rotTrack = NULL;
|
||||
}
|
||||
|
||||
if (m_scaleTrack)
|
||||
{
|
||||
delete m_scaleTrack;
|
||||
m_scaleTrack = NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
// Single animation description.
|
||||
struct Animation
|
||||
{
|
||||
string name;
|
||||
bool loop;
|
||||
bool haveLoopingController;
|
||||
float startTime; // Start time in seconds.
|
||||
float endTime; // End time in seconds.
|
||||
float secsPerFrame; // seconds per frame.
|
||||
typedef std::vector<NodeAnim> NodeAnimArray;
|
||||
NodeAnimArray nodeAnims;
|
||||
|
||||
// Ctor
|
||||
Animation()
|
||||
{
|
||||
haveLoopingController = false;
|
||||
loop = false;
|
||||
startTime = endTime = 0;
|
||||
secsPerFrame = 1;
|
||||
}
|
||||
|
||||
~Animation()
|
||||
{
|
||||
for (NodeAnimArray::iterator it = nodeAnims.begin(); it != nodeAnims.end(); ++it)
|
||||
{
|
||||
it->clearControllers();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CAnimObject();
|
||||
virtual ~CAnimObject();
|
||||
|
||||
//! Set name of file for this anmated object.
|
||||
void SetFileName( const char *szFileName );
|
||||
const char* GetFileName() { return m_fileName.c_str(); };
|
||||
|
||||
//! Creates new node.
|
||||
Node* CreateNode( const char *szNodeName );
|
||||
|
||||
//! Animate object to specified time.
|
||||
void Animate( float time );
|
||||
|
||||
//! Get matrix of node.
|
||||
Matrix44& GetNodeMatrix( Node *node );
|
||||
|
||||
//! Get node animation for current animation.
|
||||
|
||||
NodeAnim* GetNodeAnim( Node *node );
|
||||
//! Set animation of specified node.
|
||||
void AddAnimation( Animation* anim );
|
||||
//! Remove animation.
|
||||
void RemoveAnimation( Animation* anim );
|
||||
//! Sets current animation. (Can be NULL).
|
||||
void SetCurrent( Animation *anim );
|
||||
|
||||
//! Find animation by name.
|
||||
Animation* FindAnimation( const char *szAnimationName );
|
||||
//! Find Node by name, return its index.
|
||||
//! @return -1 if not found, or node index.
|
||||
int FindNodeByName( const char *szNodeName );
|
||||
//! Get name of node by node id.
|
||||
const char* GetNodeName( int nodeId );
|
||||
//! Get number of nodes.
|
||||
int GetNumNodes() const { return (int)m_nodes.size(); }
|
||||
|
||||
void RecalcBBox();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ICryCharInstance implementation.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Returns the interface for animations applicable to this model
|
||||
virtual void AddRef() { m_nRefCount++; }
|
||||
virtual void Release()
|
||||
{
|
||||
if (--m_nRefCount <= 0)
|
||||
delete this;
|
||||
}
|
||||
|
||||
// Set rendering flags like (draw/notdraw) and position offset
|
||||
virtual void SetFlags(int nFlags) { m_nFlags = nFlags; };
|
||||
virtual int GetFlags() { return m_nFlags; };
|
||||
|
||||
//! Set shader template to be used with character
|
||||
virtual bool SetShaderTemplateName(const char *TemplName, int Id, const char *ShaderName=0,IMatInfo *pCustomMaterial=0,unsigned nFlags = 0) { return true; };
|
||||
//! Sets shader template for rendering
|
||||
virtual bool SetShaderTemplate(int nTemplate, const char *TemplName, const char *ShaderName, bool bOnlyRegister, int * pnNewTemplateId=NULL)
|
||||
{
|
||||
// This gets called when character are attached recursively to each other's bones
|
||||
// What to do?
|
||||
return true;
|
||||
}
|
||||
//! Get shader template
|
||||
virtual const char * GetShaderTemplateName() { return ""; };
|
||||
//! Set refract coef. for refractive shader
|
||||
virtual void SetShaderFloat(const char *Name, float fVal, const char *ShaderName=NULL) {};
|
||||
//! Sets color parameter
|
||||
virtual void SetColor(float fR, float fG, float fB, float fA) {};
|
||||
|
||||
//! Draw the character using a set of specified rendering parameters ( for outdoors )
|
||||
virtual void Draw(const SRendParams & RendParams,const Vec3& t);
|
||||
//! Render object ( register render elements into renderer )
|
||||
void Render(const struct SRendParams & rParams,const Vec3& t, int nLodLevel);
|
||||
|
||||
//! Interface for the renderer - returns the CDLight describing the light in this character;
|
||||
//! returns NULL if there's no light with such index
|
||||
//! ICryCharInstance owns this light. This light may be destructed without notice upon next call to
|
||||
//! any other function but GetLight(). Nobody may every modify it.
|
||||
virtual const class CDLight* GetBoundLight (int nIndex);
|
||||
|
||||
//! Draw the character shadow volumes into the stencil buffer using a set of specified
|
||||
//! rendering parameters ( for indoors )
|
||||
virtual void RenderShadowVolumes(const SRendParams *rParams, int nLimitLOD);
|
||||
|
||||
//! Draw the character without shaders for shadow mapping
|
||||
//virtual void DrawForShadow(const Vec3 & vTranslationPlus = Vec3(0,0,0));
|
||||
|
||||
//! Return dynamic bbox of object
|
||||
virtual void GetBBox(Vec3& Mins, Vec3& Maxs)
|
||||
{
|
||||
if (!m_bboxValid)
|
||||
RecalcBBox();
|
||||
Mins = m_bbox[0];
|
||||
Maxs = m_bbox[1];
|
||||
}
|
||||
//! Return dynamic center of object
|
||||
virtual const Vec3 GetCenter()
|
||||
{
|
||||
return 0.5f*(m_bbox[1] + m_bbox[0]);
|
||||
}
|
||||
//! Return dynamic radius of object
|
||||
virtual const float GetRadius()
|
||||
{
|
||||
return (m_bbox[1] - m_bbox[0]).Length();
|
||||
}
|
||||
|
||||
//! 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 EnableLastIdleAnimationRestart (unsigned nLayer, bool bEnable = true) {}
|
||||
|
||||
//! Start specified animation
|
||||
bool StartAnimation (const char* szAnimName, const struct CryCharAnimationParams& Params)
|
||||
{
|
||||
SetCurrent( FindAnimation(szAnimName) );
|
||||
m_time = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StopAnimation (int nLayer)
|
||||
{
|
||||
if (!m_currAnimation)
|
||||
return false;
|
||||
SetCurrent(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//! Return current animation name ( Return 0 if animations stoped )
|
||||
const char* GetCurAnimation()
|
||||
{
|
||||
if (m_currAnimation)
|
||||
return m_currAnimation->name.c_str();
|
||||
return 0;
|
||||
};
|
||||
|
||||
int GetCurrentAnimation(unsigned nLayer)
|
||||
{
|
||||
// return fake animation id
|
||||
return m_currAnimation?0:-1;
|
||||
}
|
||||
|
||||
//! Resets all animation layers ( stops all animations )
|
||||
virtual void ResetAnimations();
|
||||
|
||||
//! Set animations speed scale
|
||||
//! This is the scale factor that affects the animation speed of the character.
|
||||
//! All the animations are played with the constant real-time speed multiplied by this factor.
|
||||
//! So, 0 means still animations (stuck at some frame), 1 - normal, 2 - twice as fast, 0.5 - twice slower than normal.
|
||||
virtual void SetAnimationSpeed(float speed)
|
||||
{
|
||||
m_animSpeed = speed;
|
||||
}
|
||||
|
||||
virtual float GetAnimationSpeed()
|
||||
{
|
||||
return m_animSpeed;
|
||||
}
|
||||
virtual void SetAnimationSpeed(int nLayer, float fSpeed)
|
||||
{
|
||||
m_animSpeed = fSpeed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//! Enable object animation time update. If the bUpdate flag is false, subsequent calls to Update will not animate the character
|
||||
virtual void EnableTimeUpdate(bool bUpdate)
|
||||
{
|
||||
m_bNoTimeUpdate = !bUpdate;
|
||||
}
|
||||
|
||||
//! Set the current time of the given layer, in seconds
|
||||
virtual void SetLayerTime(int nLayer, float fTimeSeconds);
|
||||
virtual float GetLayerTime(int nLayer) { return m_time; };
|
||||
|
||||
//! Step animation (call this function every frame to animate character)
|
||||
virtual void Update( Vec3 vPos = Vec3(0,0,0), float fRadius=0, unsigned uFlags = 0);
|
||||
|
||||
//! Synchronizes state with character physical animation; should be called after all updates (such as animation, game bones updates, etc.)
|
||||
virtual void UpdatePhysics( float fScale=1.0f );
|
||||
|
||||
//! IK (Used by physics engine)
|
||||
virtual void BuildPhysicalEntity( IPhysicalEntity *pent,float mass,int surface_idx,float stiffness_scale=1.0f,int nLod=0 );
|
||||
virtual IPhysicalEntity* CreateCharacterPhysics(IPhysicalEntity *pHost, float mass,int surface_idx,float stiffness_scale, int nLod=0);
|
||||
virtual int CreateAuxilaryPhysics(IPhysicalEntity *pHost, int nLod=0);
|
||||
virtual IPhysicalEntity *GetCharacterPhysics() { return m_physic; }
|
||||
virtual IPhysicalEntity *GetCharacterPhysics(const char *pRootBoneName) { return m_physic; }
|
||||
virtual IPhysicalEntity *GetCharacterPhysics(int iAuxPhys) { return m_physic; }
|
||||
virtual void SynchronizeWithPhysicalEntity(IPhysicalEntity *pent,const Vec3& posMaster,const Quat& qMaster) {};
|
||||
virtual void DestroyCharacterPhysics(int iMode=0) {}
|
||||
virtual void SetCharacterPhysParams(float mass,int surface_idx) {}
|
||||
virtual IPhysicalEntity *RelinquishCharacterPhysics() { return 0; };
|
||||
virtual void SetLimbIKGoal(int limbid, vectorf ptgoal=vectorf(1E10f,0,0), int ik_flags=0, float addlen=0, vectorf goal_normal=vectorf(zero)) {};
|
||||
virtual vectorf GetLimbEndPos(int limbid) { return vectorf(0,0,0); };
|
||||
virtual void AddImpact(int partid, vectorf point,vectorf impact);
|
||||
virtual int TranslatePartIdToDeadBody(int partid) { return 0; };
|
||||
virtual vectorf GetOffset() { return vectorf(0,0,0); };
|
||||
virtual void SetOffset(vectorf offset) {};
|
||||
|
||||
//! Direct access to the specified bone
|
||||
virtual ICryBone * GetBoneByName(const char * szName);
|
||||
|
||||
// Attaches a static object to the bone (given the bone name) or detaches previous object from the bone.
|
||||
virtual ObjectBindingHandle AttachObjectToBone(IBindable * pWeaponModel, const char * bone_name, bool bUseRelativeToDefPoseMatrix , unsigned nFlags = 0);
|
||||
|
||||
//! Pose character bones
|
||||
virtual bool SetAnimationFrame(const char * szString, int nFrame)
|
||||
{
|
||||
if (m_currAnimation)
|
||||
{
|
||||
m_time = m_currAnimation->secsPerFrame * nFrame;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
virtual void AddAnimationEventSink(ICharInstanceSink * pCharInstanceSink) {};
|
||||
|
||||
//! Counterpart to AddAnimationEventSink
|
||||
virtual void RemoveAnimationEventSink(ICharInstanceSink * pCharInstanceSink) {};
|
||||
|
||||
//! Enables receiving OnStart/OnEnd of specified animation from this character instance
|
||||
//! The specified sink also receives the additional animation events specified through AddAnimationEvent interface for this animation
|
||||
virtual void AddAnimationEventSink(const char* szAnimName, ICharInstanceSink * pCharInstanceSink) {};
|
||||
|
||||
//! Counterpart to the AddAnimationEventSink
|
||||
virtual void RemoveAnimationEventSink(const char* szAnimName, ICharInstanceSink * pCharInstanceSink) {};
|
||||
|
||||
//! Adds an animation event; whenever the character plays the specified frame of the specified animation,
|
||||
//! it calls back the animation event sinkreceiving OnEvent notification of specified animation for all instances using this model
|
||||
virtual bool AddAnimationEvent(const char * szAnimName, int nFrameID, AnimSinkEventData UserData) { return true; };
|
||||
|
||||
//! Deletes the animation event; from now on the sink won't be receiving the animation this event
|
||||
virtual bool RemoveAnimationEvent (const char* szAnimName, int nFrameID, AnimSinkEventData UserData) { return true; };
|
||||
|
||||
//! Callback interface
|
||||
//! Enables receiving OnStart/OnEnd of specified animation from this character instance
|
||||
virtual void SetAnimationSinkForInstance(const char * szAnimName, ICharInstanceSink * pCharInstanceSink) {};
|
||||
|
||||
//! Returns the model interface
|
||||
virtual ICryCharModel* GetModel()
|
||||
{
|
||||
return &m_characterModel;
|
||||
}
|
||||
|
||||
//! Return position of helper of the static object which is attached to animated object
|
||||
virtual Vec3 GetTPVWeaponHelper(const char * szHelperName, ObjectBindingHandle pInfo ) { return Vec3(0,0,0); };
|
||||
|
||||
//! Set the twining type. If replace - animations of second layer will overwrite first, otherwise it will sum
|
||||
virtual void SetTwiningMode(AnimTwinMode eTwinMode ) {};
|
||||
|
||||
//! Return damage zone id for specified bone id
|
||||
virtual int GetDamageTableValue (int nId) { return 0; };
|
||||
|
||||
//! Renderer calls this function to allow update the video vertex buffers right before the rendering
|
||||
virtual void ProcessSkinning(const Vec3& t,const Matrix44& mtxModel, int nTemplate, int nLod=-1, bool bForceUpdate=false) {};
|
||||
|
||||
//! returns true if the character is playing any animation now (so bbox is changing)
|
||||
virtual bool IsCharacterActive() { return m_currAnimation != 0; };
|
||||
|
||||
void SetAngles( const Vec3& angles ) { m_angles = angles; }
|
||||
Vec3& GetAngles() { return m_angles; }
|
||||
|
||||
//! Spawn decal on the walls, static objects, terrain and entities
|
||||
virtual void CreateDecal(CryEngineDecalInfo& Decal);
|
||||
|
||||
virtual bool IsModelFileEqual (const char* szFileName);
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Pushes the underlying tree of objects into the given Sizer object for statistics gathering
|
||||
void GetMemoryUsage(class ICrySizer* pSizer)const;
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
||||
void ReleaseNodes();
|
||||
void ReleaseAnims();
|
||||
|
||||
// Reference counter for this object.
|
||||
int m_nRefCount;
|
||||
|
||||
//! Name of cgf.
|
||||
string m_fileName;
|
||||
|
||||
// Animated nodes.
|
||||
std::vector<Node*> m_nodes;
|
||||
std::vector<Animation*> m_animations;
|
||||
Animation* m_currAnimation;
|
||||
|
||||
// Some general character flags.
|
||||
int m_nFlags;
|
||||
|
||||
//! Animation speed.
|
||||
float m_animSpeed;
|
||||
|
||||
//! Last time at which this character was animated.
|
||||
float m_lastAnimTime;
|
||||
|
||||
//! Current animation time.
|
||||
float m_time;
|
||||
//! Time of animation itself.
|
||||
float m_fAnimTime;
|
||||
//! True if no time step.
|
||||
bool m_bNoTimeUpdate;
|
||||
//! True if this animation was already played once.
|
||||
bool m_bAllNodesValid;
|
||||
//! True if bbox is valid.
|
||||
bool m_bboxValid;
|
||||
|
||||
// Bounding box.
|
||||
Vec3 m_bbox[2];
|
||||
|
||||
Vec3 m_angles;
|
||||
|
||||
// Physics.
|
||||
IPhysicalEntity *m_physic;
|
||||
float m_lastScale;
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
struct AnimationSet : public ICryAnimationSet
|
||||
{
|
||||
//! Returns the number of animations in this set
|
||||
virtual int Count() { return (int)obj->m_animations.size(); };
|
||||
|
||||
//! Returns the index of the animation in the set, -1 if there's no such animation
|
||||
virtual int Find (const char* szAnimationName)
|
||||
{
|
||||
for (unsigned int i = 0; i < obj->m_animations.size(); i++)
|
||||
{
|
||||
if (stricmp(obj->m_animations[i]->name.c_str(),szAnimationName)==0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//! Returns the given animation length, in seconds
|
||||
virtual float GetLength (int nAnimationId)
|
||||
{
|
||||
if (nAnimationId < 0)
|
||||
return 0;
|
||||
return obj->m_animations[nAnimationId]->endTime - obj->m_animations[nAnimationId]->startTime;
|
||||
}
|
||||
|
||||
//! Returns the given animation length, in seconds
|
||||
virtual float GetLength (const char* szAnimationName)
|
||||
{
|
||||
return GetLength(Find(szAnimationName));
|
||||
}
|
||||
|
||||
//! Returns the given animation name
|
||||
virtual const char* GetName (int nAnimationId)
|
||||
{
|
||||
if (nAnimationId < 0)
|
||||
return "";
|
||||
return obj->m_animations[nAnimationId]->name.c_str();
|
||||
}
|
||||
|
||||
//! Retrieves the animation loop flag
|
||||
virtual bool IsLoop (int nAnimationId)
|
||||
{
|
||||
if (nAnimationId < 0)
|
||||
return false;
|
||||
return obj->m_animations[nAnimationId]->loop;
|
||||
}
|
||||
|
||||
//! Retrieves the animation loop flag
|
||||
virtual bool IsLoop (const char* szAnimationName)
|
||||
{
|
||||
return IsLoop(Find(szAnimationName));
|
||||
}
|
||||
virtual void SetLoop (int nAnimationId, bool bIsLooped)
|
||||
{
|
||||
if (nAnimationId < 0)
|
||||
return;
|
||||
obj->m_animations[nAnimationId]->loop = bIsLooped;
|
||||
};
|
||||
|
||||
CAnimObject *obj;
|
||||
};
|
||||
|
||||
|
||||
friend struct AnimationSet;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
struct SCharModel : public ICryCharModel
|
||||
{
|
||||
virtual ICryAnimationSet* GetAnimationSet () { return &animSet; };
|
||||
virtual float GetScale() const { return 1; };
|
||||
|
||||
//! Return name of bone from bone table, return zero if the nId is out of range (the game gets this id from physics)
|
||||
virtual const char * GetBoneName(int nId) const { return pAnimObject->GetNodeName( nId ); }
|
||||
|
||||
//! Returns the index of the bone by its name or -1 if no such bone exists; this is Case-Sensitive
|
||||
virtual int GetBoneByName (const char* szName) { return pAnimObject->FindNodeByName( szName ); }
|
||||
|
||||
//! Returns the number of bones; all bone ids are in the range from 0 to this number exclusive; 0th bone is the root
|
||||
virtual int NumBones() const { return pAnimObject->GetNumNodes(); };
|
||||
|
||||
//! returns the file name of the character model
|
||||
virtual const char* GetFileName() { return pAnimObject->GetFileName(); }
|
||||
|
||||
AnimationSet animSet;
|
||||
CAnimObject *pAnimObject;
|
||||
};
|
||||
SCharModel m_characterModel;
|
||||
|
||||
|
||||
// sets the given aniimation to the given layer as the default
|
||||
virtual void SetDefaultIdleAnimation(unsigned nLayer, const char* szAnimName) {}
|
||||
|
||||
// preload textures
|
||||
virtual void PreloadResources ( float fDistance, float fTime, int nFlags);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Darw bound objects.
|
||||
void DrawBoundObjects(const SRendParams & rRendParams, Matrix44 &inmatTranRotMatrix, int nLOD);
|
||||
};
|
||||
|
||||
#endif // __AnimObject_h__
|
||||
345
CryAnimation/AnimObjectLoader.cpp
Normal file
345
CryAnimation/AnimObjectLoader.cpp
Normal file
@@ -0,0 +1,345 @@
|
||||
#include "StdAfx.h"
|
||||
#include "AnimObjectLoader.h"
|
||||
|
||||
#include "ChunkFileReader.h"
|
||||
#include "ControllerTCB.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
#include <ICryPak.h>
|
||||
|
||||
#include "makepath.h"
|
||||
#include "splitpath.h"
|
||||
|
||||
#define ANIMATION_EXT "anm"
|
||||
|
||||
using namespace CryStringUtils;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Loads animation object.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CAnimObjectLoader::Load( CAnimObject* animObject,const char *geomName,const char *animFile )
|
||||
{
|
||||
m_animObject = animObject;
|
||||
|
||||
// Load chunk file.
|
||||
// try to read the file
|
||||
CChunkFileReader_AutoPtr pReader = new CChunkFileReader ();
|
||||
if (!pReader->open (geomName))
|
||||
{
|
||||
// NOTE: see the SIDE EFFECT NOTES
|
||||
//GetLog()->LogError ("Error: CControllerManager::LoadAnimation: file loading %s", strFileName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// check the file header for validity
|
||||
const FILE_HEADER& fh = pReader->getFileHeader();
|
||||
|
||||
if(fh.Version != GeomFileVersion || fh.FileType != FileType_Geom)
|
||||
{
|
||||
g_GetLog()->LogError ("CAnimObjectLoader::Load: file version error or not an geometry file: %s", geomName );
|
||||
return false;
|
||||
}
|
||||
|
||||
m_animStart = 0;
|
||||
m_animEnd = 0;
|
||||
m_secsPerTick = 1;
|
||||
m_ticksPerFrame = 1;
|
||||
|
||||
m_currAnimation = new CAnimObject::Animation;
|
||||
m_currAnimation->name = "Default";
|
||||
animObject->AddAnimation(m_currAnimation);
|
||||
|
||||
LoadChunks( pReader,true );
|
||||
|
||||
LoadAnimations( geomName );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CAnimObjectLoader::LoadAnimation( const char *animFile )
|
||||
{
|
||||
// Load chunk file.
|
||||
// try to read the file
|
||||
CChunkFileReader_AutoPtr pReader = new CChunkFileReader ();
|
||||
if (!pReader->open (animFile))
|
||||
{
|
||||
g_GetLog()->LogError ("CAnimObjectLoader::LoadAnimation: file loading %s", animFile );
|
||||
return false;
|
||||
}
|
||||
|
||||
// check the file header for validity
|
||||
const FILE_HEADER& fh = pReader->getFileHeader();
|
||||
|
||||
//if(fh.Version != AnimFileVersion || fh.FileType != FileType_Anim)
|
||||
if(fh.Version != GeomFileVersion || fh.FileType != FileType_Geom)
|
||||
{
|
||||
g_GetLog()->LogError ("CAnimObjectLoader::LoadAnimation: file version error or not an animation file: %s", animFile );
|
||||
return false;
|
||||
}
|
||||
|
||||
m_nodeMap.clear();
|
||||
|
||||
m_animStart = 0;
|
||||
m_animEnd = 0;
|
||||
m_secsPerTick = 1;
|
||||
m_ticksPerFrame = 1;
|
||||
|
||||
// Get file name, this is a name of application.
|
||||
char fname[_MAX_PATH];
|
||||
strcpy( fname,animFile );
|
||||
StripFileExtension(fname);
|
||||
const char *sAnimName = FindFileNameInPath(fname);
|
||||
|
||||
const char *sName = strchr(sAnimName,'_');
|
||||
if (sName)
|
||||
{
|
||||
sName += 1;
|
||||
}
|
||||
else
|
||||
sName = sAnimName;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
m_currAnimation = new CAnimObject::Animation;
|
||||
m_currAnimation->name = sName;
|
||||
m_animObject->AddAnimation(m_currAnimation);
|
||||
|
||||
LoadChunks( pReader,false );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObjectLoader::LoadChunks( CChunkFileReader* pReader,bool bMakeNodes )
|
||||
{
|
||||
m_numChunks = pReader->numChunks();
|
||||
m_controllers = new IController*[m_numChunks];
|
||||
memset( m_controllers,0,sizeof(IController*)*m_numChunks );
|
||||
|
||||
// scan the chunks and load all controllers and time data into the animation structure
|
||||
for (int nChunk = 0; nChunk < pReader->numChunks (); ++nChunk)
|
||||
{
|
||||
// this is the chunk header in the chunk table at the end of the file
|
||||
const CHUNK_HEADER& chunkHeader = pReader->getChunkHeader(nChunk);
|
||||
// this is the chunk raw data, starts with the chunk header/descriptor structure
|
||||
const void* pChunk = pReader->getChunkData (nChunk);
|
||||
unsigned nChunkSize = pReader->getChunkSize(nChunk);
|
||||
|
||||
switch (chunkHeader.ChunkType)
|
||||
{
|
||||
case ChunkType_Node:
|
||||
LoadNodeChunk( chunkHeader.ChunkID,pChunk,bMakeNodes );
|
||||
break;
|
||||
|
||||
case ChunkType_Controller:
|
||||
if (chunkHeader.ChunkVersion == CONTROLLER_CHUNK_DESC_0826::VERSION)
|
||||
{
|
||||
// load and add a controller constructed from the controller chunk
|
||||
LoadControllerChunk( chunkHeader.ChunkID,pChunk );
|
||||
}
|
||||
break;
|
||||
/*
|
||||
default:
|
||||
GetLog()->LogError ("\003Unsupported controller chunk 0x%08X version 0x%08X in file %s. Please re-export the file.", chunkHeader.ChunkID, chunkHeader.ChunkVersion, strFileName.c_str());
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
case ChunkType_Timing:
|
||||
{
|
||||
// memorize the timing info
|
||||
const TIMING_CHUNK_DESC* pTimingChunk = static_cast<const TIMING_CHUNK_DESC*> (pChunk);
|
||||
m_ticksPerFrame = pTimingChunk->TicksPerFrame;
|
||||
m_secsPerTick = pTimingChunk->SecsPerTick;
|
||||
m_animStart = m_secsPerTick * m_ticksPerFrame * pTimingChunk->global_range.start;
|
||||
m_animEnd = m_secsPerTick * m_ticksPerFrame * pTimingChunk->global_range.end;
|
||||
|
||||
m_currAnimation->secsPerFrame = m_ticksPerFrame*m_secsPerTick;
|
||||
m_currAnimation->startTime = m_animStart;
|
||||
m_currAnimation->endTime = m_animEnd;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
InitNodes();
|
||||
|
||||
delete []m_controllers;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObjectLoader::LoadNodeChunk( int chunkID,const void* pChunk,bool bMakeNode )
|
||||
{
|
||||
const NODE_CHUNK_DESC *pNodeChunk = static_cast<const NODE_CHUNK_DESC*>(pChunk);
|
||||
|
||||
NodeDesc nd;
|
||||
nd.parentID = pNodeChunk->ParentID;
|
||||
nd.pos_cont_id = pNodeChunk->pos_cont_id;
|
||||
nd.rot_cont_id = pNodeChunk->rot_cont_id;
|
||||
nd.scl_cont_id = pNodeChunk->scl_cont_id;
|
||||
|
||||
nd.pos = pNodeChunk->pos * (1.0f/100.0f);
|
||||
nd.rotate = pNodeChunk->rot;
|
||||
nd.scale = pNodeChunk->scl;
|
||||
|
||||
CAnimObject::Node *node = 0;
|
||||
if (bMakeNode)
|
||||
{
|
||||
node = m_animObject->CreateNode( pNodeChunk->name );
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
// Fill node params.
|
||||
// Position must be scaled down by 100.
|
||||
node->m_tm = pNodeChunk->tm;
|
||||
node->m_tm.SetTranslationOLD( node->m_tm.GetTranslationOLD()*(1.0f/100.0f) );
|
||||
node->m_pos = nd.pos;
|
||||
node->m_rotate = nd.rotate;
|
||||
node->m_scale = nd.scale;
|
||||
m_nodeNameMap[node->m_name] = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load node from map.
|
||||
NodeNamesMap::iterator it = m_nodeNameMap.find(pNodeChunk->name);
|
||||
if (it == m_nodeNameMap.end())
|
||||
{
|
||||
// No such node.
|
||||
return;
|
||||
}
|
||||
node = it->second;
|
||||
}
|
||||
|
||||
// Add this node description.
|
||||
nd.node = node;
|
||||
m_nodeMap[chunkID] = nd;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObjectLoader::LoadControllerChunk( int chunkID,const void* pChunk )
|
||||
{
|
||||
assert( chunkID >= 0 && chunkID < m_numChunks );
|
||||
const CONTROLLER_CHUNK_DESC_0826* pCtrlChunk = static_cast<const CONTROLLER_CHUNK_DESC_0826*>(pChunk);
|
||||
IController *ctrl = 0;
|
||||
switch (pCtrlChunk->type)
|
||||
{
|
||||
case CTRL_TCB3:
|
||||
{
|
||||
CControllerTCBVec3 *ctrl = new CControllerTCBVec3;
|
||||
ctrl->Load( pCtrlChunk,m_secsPerTick );
|
||||
m_controllers[chunkID] = ctrl;
|
||||
}
|
||||
break;
|
||||
case CTRL_TCBQ:
|
||||
{
|
||||
CControllerTCBQuat *ctrl = new CControllerTCBQuat;
|
||||
ctrl->Load( pCtrlChunk,m_secsPerTick );
|
||||
m_controllers[chunkID] = ctrl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObjectLoader::InitNodes()
|
||||
{
|
||||
m_currAnimation->nodeAnims.resize( m_nodeMap.size() );
|
||||
|
||||
NodeDescMap::iterator nit;
|
||||
// Iterate over all loaded nodes and initialize parent links, and assign controllers.
|
||||
for (nit = m_nodeMap.begin(); nit != m_nodeMap.end(); ++nit)
|
||||
{
|
||||
const NodeDesc &nd = nit->second;
|
||||
|
||||
if (nd.node->m_id < 0 || nd.node->m_id >= (int)m_currAnimation->nodeAnims.size())
|
||||
continue;
|
||||
|
||||
CAnimObject::NodeAnim *nodeAnim = &m_currAnimation->nodeAnims[nd.node->m_id];
|
||||
|
||||
// find parent node.
|
||||
if (nd.parentID != 0)
|
||||
{
|
||||
NodeDescMap::iterator it = m_nodeMap.find(nd.parentID);
|
||||
if (it != m_nodeMap.end())
|
||||
nd.node->m_parent = it->second.node;
|
||||
}
|
||||
|
||||
nodeAnim->m_pos = nd.pos;
|
||||
nodeAnim->m_rotate = nd.rotate;
|
||||
nodeAnim->m_scale = nd.scale;
|
||||
|
||||
// find controllers.
|
||||
if (nd.pos_cont_id >= 0)
|
||||
{
|
||||
assert( nd.pos_cont_id >= 0 && nd.pos_cont_id < m_numChunks );
|
||||
nodeAnim->m_posTrack = m_controllers[nd.pos_cont_id];
|
||||
if (nodeAnim->m_posTrack)
|
||||
{
|
||||
if (nodeAnim->m_posTrack->IsLooping())
|
||||
m_currAnimation->haveLoopingController = true;
|
||||
}
|
||||
}
|
||||
if (nd.rot_cont_id >= 0)
|
||||
{
|
||||
assert( nd.rot_cont_id >= 0 && nd.rot_cont_id < m_numChunks );
|
||||
nodeAnim->m_rotTrack = m_controllers[nd.rot_cont_id];
|
||||
if (nodeAnim->m_rotTrack)
|
||||
{
|
||||
if (nodeAnim->m_rotTrack->IsLooping())
|
||||
m_currAnimation->haveLoopingController = true;
|
||||
}
|
||||
}
|
||||
if (nd.scl_cont_id >= 0)
|
||||
{
|
||||
assert( nd.scl_cont_id >= 0 && nd.scl_cont_id < m_numChunks );
|
||||
nodeAnim->m_scaleTrack = m_controllers[nd.scl_cont_id];
|
||||
if (nodeAnim->m_scaleTrack)
|
||||
{
|
||||
if (nodeAnim->m_scaleTrack->IsLooping())
|
||||
m_currAnimation->haveLoopingController = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObjectLoader::LoadAnimations( const char *cgaFile )
|
||||
{
|
||||
// Load all filename_***.anm files.
|
||||
char filter[_MAX_PATH];
|
||||
char drive[_MAX_DRIVE];
|
||||
char dir[_MAX_DIR];
|
||||
char fname[_MAX_FNAME];
|
||||
char ext[_MAX_EXT];
|
||||
|
||||
portable_splitpath( cgaFile,drive,dir,fname,ext );
|
||||
strcat( fname,"_*");
|
||||
portable_makepath( filter, drive,dir,fname,"anm" );
|
||||
|
||||
char fullpath[_MAX_PATH];
|
||||
char filename[_MAX_PATH];
|
||||
portable_makepath( fullpath, drive,dir,NULL,NULL );
|
||||
|
||||
ICryPak *pack = g_GetISystem()->GetIPak();
|
||||
|
||||
// Search files that match filter specification.
|
||||
_finddata_t fd;
|
||||
int res;
|
||||
intptr_t handle;
|
||||
if ((handle = pack->FindFirst( filter,&fd )) != -1)
|
||||
if (handle != -1)
|
||||
{
|
||||
do
|
||||
{
|
||||
// Animation file found, load it.
|
||||
strcpy( filename,fullpath );
|
||||
strcat( filename,fd.name );
|
||||
LoadAnimation( filename );
|
||||
|
||||
res = pack->FindNext( handle,&fd );
|
||||
} while (res >= 0);
|
||||
pack->FindClose(handle);
|
||||
}
|
||||
}
|
||||
73
CryAnimation/AnimObjectLoader.h
Normal file
73
CryAnimation/AnimObjectLoader.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#ifndef __AnimObjectLoader_h__
|
||||
#define __AnimObjectLoader_h__
|
||||
#pragma once
|
||||
|
||||
#include "AnimObject.h"
|
||||
#include "Controller.h"
|
||||
|
||||
class CChunkFileReader;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Loads AnimObject from CGF/CAF files.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CAnimObjectLoader
|
||||
{
|
||||
public:
|
||||
// Load animation object from cgf or caf.
|
||||
bool Load( CAnimObject* animObject,const char *geomName,const char *animFile );
|
||||
|
||||
private:
|
||||
void LoadChunks( CChunkFileReader* pReader,bool bMakeNodes );
|
||||
void LoadNodeChunk( int chunkID,const void* pChunk,bool bMakeNode );
|
||||
void LoadControllerChunk( int chunkID,const void* pChunk );
|
||||
void InitNodes();
|
||||
|
||||
// Load all animations for this object.
|
||||
void LoadAnimations( const char *cgaFile );
|
||||
bool LoadAnimation( const char *animFile );
|
||||
|
||||
// Convert controller ticks to time in seconds.
|
||||
float TickToTime( int ticks )
|
||||
{
|
||||
return (float)ticks * m_secsPerTick;
|
||||
}
|
||||
|
||||
// Internal description of node.
|
||||
struct NodeDesc
|
||||
{
|
||||
int parentID;
|
||||
int pos_cont_id; // position controller chunk id
|
||||
int rot_cont_id; // rotation controller chunk id
|
||||
int scl_cont_id; // scale controller chunk id
|
||||
Vec3 pos;
|
||||
CryQuat rotate;
|
||||
Vec3 scale;
|
||||
CAnimObject::Node* node;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
typedef std::map<int,NodeDesc> NodeDescMap;
|
||||
NodeDescMap m_nodeMap;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
typedef std::map<string,CAnimObject::Node*> NodeNamesMap;
|
||||
NodeNamesMap m_nodeNameMap;
|
||||
|
||||
|
||||
// Array of controllers.
|
||||
IController** m_controllers;
|
||||
int m_numChunks;
|
||||
|
||||
// ticks per one max frame.
|
||||
int m_ticksPerFrame;
|
||||
//! controller ticks per second.
|
||||
float m_secsPerTick;
|
||||
float m_animStart;
|
||||
float m_animEnd;
|
||||
|
||||
// Created animation object
|
||||
CAnimObject* m_animObject;
|
||||
CAnimObject::Animation* m_currAnimation;
|
||||
};
|
||||
|
||||
#endif //__AnimObjectLoader_h__
|
||||
84
CryAnimation/AnimObjectManager.cpp
Normal file
84
CryAnimation/AnimObjectManager.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Engine Source File.
|
||||
// Copyright (C), Crytek Studios, 2002.
|
||||
// -------------------------------------------------------------------------
|
||||
// File name: animobjectmanager.cpp
|
||||
// Version: v1.00
|
||||
// Created: 14/11/2002 by Timur.
|
||||
// Compilers: Visual Studio.NET
|
||||
// Description:
|
||||
// -------------------------------------------------------------------------
|
||||
// History:
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "AnimObjectManager.h"
|
||||
|
||||
#include "AnimObject.h"
|
||||
#include "AnimObjectLoader.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CAnimObjectManager::CAnimObjectManager()
|
||||
{
|
||||
m_bResourcesLocked = false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ICryCharInstance* CAnimObjectManager::MakeAnimObject( const char *animFile )
|
||||
{
|
||||
CAnimObject* obj = new CAnimObject;
|
||||
obj->SetFileName(animFile);
|
||||
|
||||
CAnimObjectLoader loader;
|
||||
if (!loader.Load( obj,animFile,animFile ))
|
||||
{
|
||||
// loading failed.
|
||||
delete obj;
|
||||
return 0;
|
||||
}
|
||||
m_objects.insert(obj);
|
||||
m_animObjects.insert(obj);
|
||||
if (m_bResourcesLocked)
|
||||
m_lockArray.push_back( obj );
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool CAnimObjectManager::RemoveCharacter( ICryCharInstance* obj )
|
||||
{
|
||||
ObjectsSet::iterator it = m_objects.find(obj);
|
||||
if (it != m_objects.end())
|
||||
{
|
||||
// This is our character.
|
||||
m_objects.erase( it );
|
||||
m_animObjects.erase( (CAnimObject*)obj );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// puts the size of the whole subsystem into this sizer object, classified,
|
||||
// according to the flags set in the sizer
|
||||
void CAnimObjectManager::GetMemoryUsage(class ICrySizer* pSizer)const
|
||||
{
|
||||
// TODO:
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObjectManager::LockResources()
|
||||
{
|
||||
m_bResourcesLocked = true;
|
||||
m_lockArray.reserve( m_animObjects.size() );
|
||||
for (AnimObjectsSet::iterator it = m_animObjects.begin(); it != m_animObjects.end(); ++it)
|
||||
{
|
||||
m_lockArray.push_back( *it );
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CAnimObjectManager::UnlockResources()
|
||||
{
|
||||
m_lockArray.clear();
|
||||
m_bResourcesLocked = false;
|
||||
}
|
||||
54
CryAnimation/AnimObjectManager.h
Normal file
54
CryAnimation/AnimObjectManager.h
Normal file
@@ -0,0 +1,54 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Engine Source File.
|
||||
// Copyright (C), Crytek Studios, 2002.
|
||||
// -------------------------------------------------------------------------
|
||||
// File name: animobjectmanager.h
|
||||
// Version: v1.00
|
||||
// Created: 14/11/2002 by Timur.
|
||||
// Compilers: Visual Studio.NET
|
||||
// Description: Manages collection of AnimObjects.
|
||||
// -------------------------------------------------------------------------
|
||||
// History:
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef __animobjectmanager_h__
|
||||
#define __animobjectmanager_h__
|
||||
#pragma once
|
||||
|
||||
#include "AnimObject.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Manages collection of AnimObjects.
|
||||
// Controls creation/destruction of AnimObjects.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CAnimObjectManager
|
||||
{
|
||||
public:
|
||||
CAnimObjectManager();
|
||||
|
||||
//! Creates anim object, or get already created one.
|
||||
ICryCharInstance* MakeAnimObject( const char *animFile );
|
||||
bool RemoveCharacter( ICryCharInstance* obj );
|
||||
|
||||
// puts the size of the whole subsystem into this sizer object, classified,
|
||||
// according to the flags set in the sizer
|
||||
void GetMemoryUsage(class ICrySizer* pSizer)const;
|
||||
|
||||
unsigned NumObjects () {return (unsigned)m_objects.size();}
|
||||
|
||||
void LockResources();
|
||||
void UnlockResources();
|
||||
private:
|
||||
typedef std::set<ICryCharInstance*> ObjectsSet;
|
||||
ObjectsSet m_objects;
|
||||
typedef std::set<_smart_ptr<CAnimObject> > AnimObjectsSet;
|
||||
AnimObjectsSet m_animObjects;
|
||||
|
||||
std::vector<_smart_ptr<CAnimObject> > m_lockArray;
|
||||
bool m_bResourcesLocked;
|
||||
};
|
||||
|
||||
|
||||
#endif // __animobjectmanager_h__
|
||||
23
CryAnimation/AnimationLayerInfo.h
Normal file
23
CryAnimation/AnimationLayerInfo.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef _ANIMATION_LAYER_INFO_HDR_
|
||||
#define _ANIMATION_LAYER_INFO_HDR_
|
||||
|
||||
// This is used to pass the information about animation that needs to be blended between
|
||||
// several animations to the bone that calculates the actual position / rotation out of it
|
||||
struct CAnimationLayerInfo
|
||||
{
|
||||
int nAnimId;
|
||||
float fTime; // this is time suitable for passing to controllers, i.e. ticks
|
||||
float fBlending;
|
||||
CAnimationLayerInfo (int animid, float time, float blending): fBlending(blending), fTime(time), nAnimId(animid) {}
|
||||
CAnimationLayerInfo(){}
|
||||
};
|
||||
|
||||
typedef std::vector<CAnimationLayerInfo> CAnimationLayerInfoArray;
|
||||
/*
|
||||
// This is used to pass the information about morphs that need to be blended
|
||||
struct CMorphLayerInfo
|
||||
{
|
||||
|
||||
};
|
||||
*/
|
||||
#endif
|
||||
199
CryAnimation/BSplineKnots.cpp
Normal file
199
CryAnimation/BSplineKnots.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
#include "StdAfx.h"
|
||||
#include "BSplineKnots.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// allocates the array of the given number of knots, does NOT initialize the knots
|
||||
BSplineKnots::BSplineKnots(int numKnots):
|
||||
m_numKnots (numKnots)
|
||||
{
|
||||
m_pKnots = new Time [numKnots];
|
||||
}
|
||||
|
||||
BSplineKnots::~BSplineKnots(void)
|
||||
{
|
||||
delete []m_pKnots;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// searches and returns the interval to which the given time belongs.
|
||||
// each pair of knots defines interval [k1,k2)
|
||||
// -1 is before the 0-th knot, numKnots() is after the last knot
|
||||
int BSplineKnots::findInterval (Time fTime)const
|
||||
{
|
||||
return std::upper_bound (m_pKnots, m_pKnots + m_numKnots, fTime) - m_pKnots - 1;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// returns the i-th basis function of degree d, given the time t
|
||||
BSplineKnots::Value BSplineKnots::getBasis (int i, int d, Time t) const
|
||||
{
|
||||
// the requested basis must have defined supporting knots
|
||||
assert (i>= 0 && i + d + 1 < m_numKnots);
|
||||
|
||||
// starting and ending knots - they demark the support interval: [*begin,*(end-1)]
|
||||
const Time* pKnotBegin = m_pKnots + i;
|
||||
const Time* pKnotEnd = pKnotBegin + d + 2;
|
||||
// find the interval where the t is, among the supporting intervals
|
||||
// the upper bound of that interval is searched for
|
||||
const Time* pKnotAfterT = std::upper_bound (pKnotBegin, pKnotEnd, t);
|
||||
if (pKnotAfterT == pKnotBegin)
|
||||
{
|
||||
assert (t < pKnotBegin[0]);
|
||||
return 0; // the time t is before the supporting interval of the basis function
|
||||
}
|
||||
|
||||
if (pKnotAfterT == pKnotEnd)
|
||||
{
|
||||
if (t > pKnotEnd[-1])
|
||||
return 0; // the time t is after the supporting interval
|
||||
|
||||
assert (t == pKnotEnd[-1]);
|
||||
// scan down multiple knots
|
||||
while (t == pKnotAfterT[-1])
|
||||
--pKnotAfterT;
|
||||
}
|
||||
|
||||
return getBasis (pKnotBegin,d,t,pKnotAfterT - 1);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// returns the i-th basis function of degree d, given the time t and a hint, in which interval to search for it
|
||||
// the interval may be outside the supporting interval for the basis function
|
||||
BSplineKnots::Value BSplineKnots::getBasis (int i, int d, Time t, int nIntervalT) const
|
||||
{
|
||||
if (nIntervalT < 0 || nIntervalT >= m_numKnots)
|
||||
return 0;
|
||||
|
||||
assert (t >= m_pKnots[nIntervalT] && t < m_pKnots[nIntervalT+1]);
|
||||
|
||||
// the requested basis must have defined supporting knots
|
||||
if (i < 0 || i + d + 1 >= m_numKnots)
|
||||
return 0;
|
||||
|
||||
if (nIntervalT < i || nIntervalT > i + d)
|
||||
return 0; // the time is outside supporting base
|
||||
|
||||
return getBasis (m_pKnots + i, d,t,m_pKnots + nIntervalT);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// returns the value of the basis function of degree d, given the time t
|
||||
// the knots of the basis function start at pKnotBegin, and the first knot before t is pKnotBeforeT
|
||||
BSplineKnots::Value BSplineKnots::getBasis (const Time* pKnotBegin, int d, Time t, const Time* pKnotBeforeT)const
|
||||
{
|
||||
assert (t >= pKnotBeforeT[0] && t <= pKnotBeforeT[1]);
|
||||
assert (pKnotBegin >= m_pKnots && pKnotBegin + d + 1 < m_pKnots + m_numKnots);
|
||||
assert (pKnotBeforeT >= pKnotBegin && pKnotBeforeT <= pKnotBegin + d);
|
||||
|
||||
switch (d)
|
||||
{
|
||||
case 0:
|
||||
// trivial case
|
||||
return 1;
|
||||
case 1:
|
||||
if (pKnotBeforeT == pKnotBegin)
|
||||
{
|
||||
// the t is in the first interval
|
||||
return (t - pKnotBegin[0]) / (pKnotBegin[1] - pKnotBegin[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert (pKnotBeforeT == pKnotBegin + 1);
|
||||
return (pKnotBegin[2] - t) / (pKnotBegin[2] - pKnotBegin[1]);
|
||||
}
|
||||
default:
|
||||
{
|
||||
Value fResult = 0;
|
||||
if (pKnotBeforeT < pKnotBegin + d)
|
||||
fResult += getBasis (pKnotBegin, d-1, t, pKnotBeforeT) * (t - pKnotBegin[0]) / (pKnotBegin[d] - pKnotBegin[0]);
|
||||
if (pKnotBeforeT > pKnotBegin)
|
||||
fResult += getBasis (pKnotBegin+1, d-1, t, pKnotBeforeT) * (pKnotBegin[d+1] - t) / (pKnotBegin[d+1] - pKnotBegin[1]);
|
||||
return fResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// returns the time where the i-th given basis reaches its maximum
|
||||
BSplineKnots::Time BSplineKnots::getBasisPeak (int i/*nStartKnot*/, int d/*nDegree*/)
|
||||
{
|
||||
assert (i >= 0 && i < m_numKnots-d-1);
|
||||
if (d == 1)
|
||||
return m_pKnots[i+1];
|
||||
|
||||
// plain search
|
||||
float fLeft = m_pKnots[i], fRight = m_pKnots[i+d+1], fStep = (fRight-fLeft)/128.0f;
|
||||
float fBestTime = (fLeft + fRight)/2, fBestValue = getBasis (i, d, fBestTime);
|
||||
for (float t = fLeft; t < fRight; t += fStep)
|
||||
{
|
||||
Value fValue = getBasis (i, d, t);
|
||||
if (fValue > fBestValue)
|
||||
{
|
||||
fBestValue = fValue;
|
||||
fBestTime = t;
|
||||
}
|
||||
}
|
||||
return fBestTime;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Returns the P (product) penalty for knot closeness, as defined by Mary J. Lindstrom "Penalized Estimation of Free Knot Splines" 1999,
|
||||
// logarithmic form the end knot must exist
|
||||
double BSplineKnots::getKnotProductPenalty (int nStartKnot, int nEndKnot)
|
||||
{
|
||||
if (nEndKnot - nStartKnot == 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
double fSum = (log(double(m_pKnots[nEndKnot]-m_pKnots[nStartKnot])) - log(double(nEndKnot-nStartKnot)))*(nEndKnot-nStartKnot);
|
||||
for (int nKnotInterval = nStartKnot; nKnotInterval < nEndKnot; ++nKnotInterval)
|
||||
{
|
||||
fSum -= log_tpl (m_pKnots[nKnotInterval + 1] - m_pKnots[nKnotInterval]);
|
||||
}
|
||||
|
||||
assert (fSum > -1e-10);
|
||||
|
||||
return max(0.0,fSum);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// returns the i-th basis d-th derivative discontinuity at knot k
|
||||
// (amplitude of the delta function)
|
||||
// The result is undefined for non-existing delta functions
|
||||
BSplineKnots::Value BSplineKnots::getDelta (int i, int d, int k) const
|
||||
{
|
||||
// the support interval is [i..i+d+1]
|
||||
assert (k >= i && k <= i+d+1);
|
||||
|
||||
double dResult = 1;
|
||||
int j;
|
||||
int nMultiplicity = 1;
|
||||
|
||||
for (j = i; j < k; ++j)
|
||||
{
|
||||
double dt = m_pKnots[k] - m_pKnots[j];
|
||||
if (fabs(dt) > 1e-10)
|
||||
dResult *= dt;
|
||||
else
|
||||
++nMultiplicity;
|
||||
}
|
||||
// skip the k-th knot itself
|
||||
for (++j; j <= i+d+1; ++j)
|
||||
{
|
||||
double dt = m_pKnots[k] - m_pKnots[j];
|
||||
if (fabs(dt) > 1e-10)
|
||||
dResult *= dt;
|
||||
else
|
||||
++nMultiplicity;
|
||||
}
|
||||
|
||||
// de facto, we can return something larger if there was a >1 multiplicity
|
||||
return /*nMultiplicity **/ Value((m_pKnots[i+d+1] - m_pKnots[i]) / dResult);
|
||||
}
|
||||
91
CryAnimation/BSplineKnots.h
Normal file
91
CryAnimation/BSplineKnots.h
Normal file
@@ -0,0 +1,91 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CryEngine Source code
|
||||
//
|
||||
// File:BSplineknots.h
|
||||
// Declaration of class BSplineKnots
|
||||
//
|
||||
// History:
|
||||
// 06/17/2002 :Created by Sergiy Migdalskiy <sergiy@crytek.de>
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// class BSplineKnots
|
||||
//
|
||||
// This class represents the array of knots used to calculate
|
||||
// basis functions of the given degree over the range
|
||||
// [knot[degree] .. knot [N-degree]] , N being the total number of knots
|
||||
//
|
||||
// NOTE:
|
||||
// order == degree+1
|
||||
// There are always N-degree-1 control points
|
||||
// This class incorporates neither control point nor degree info
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
class BSplineKnots
|
||||
{
|
||||
public:
|
||||
// the abscissae and ordinates
|
||||
typedef float Time;
|
||||
typedef float Value;
|
||||
|
||||
// standard scaling between Max and BSpline knot times: max time = g_nStdTicksPerSecond * bspline time
|
||||
//enum {g_nStdTicksPerSecond = 160};
|
||||
|
||||
// allocates the array of the given number of knots, does NOT initialize the knots
|
||||
BSplineKnots(int numKnots);
|
||||
~BSplineKnots(void);
|
||||
|
||||
Time& operator [] (int nKnot)
|
||||
{
|
||||
assert (nKnot >= 0 && nKnot < m_numKnots);
|
||||
return m_pKnots[nKnot];
|
||||
}
|
||||
|
||||
Time operator [] (int nKnot) const
|
||||
{
|
||||
assert (nKnot >= 0 && nKnot < m_numKnots);
|
||||
return m_pKnots[nKnot];
|
||||
}
|
||||
|
||||
// searches and returns the interval to which the given time belongs.
|
||||
// each pair of knots defines interval [k1,k2)
|
||||
// -1 is before the 0-th knot, numKnots() is after the last knot
|
||||
int findInterval (Time fTime)const;
|
||||
|
||||
// number of knots
|
||||
int numKnots() const
|
||||
{
|
||||
return m_numKnots;
|
||||
}
|
||||
|
||||
// returns the i-th basis function of degree d, given the time t
|
||||
Value getBasis (int i, int d, Time t) const;
|
||||
|
||||
// returns the i-th basis function of degree d, given the time t and a hint, in which interval to search for it
|
||||
Value getBasis (int i, int d, Time t, int nIntervalT) const;
|
||||
|
||||
// returns the time where the i-th given basis reaches its maximum
|
||||
Time getBasisPeak (int i/*nStartKnot*/, int d/*nDegree*/);
|
||||
|
||||
// returns the i-th basis d-th derivative discontinuity at knot k
|
||||
// (amplitude of the delta function)
|
||||
Value getDelta (int i, int d, int k) const;
|
||||
|
||||
// Returns the P (product) penalty for knot closeness, as defined by Mary J. Lindstrom "Penalized Estimation of Free Knot Splines" 1999,
|
||||
// logarithmic form
|
||||
double getKnotProductPenalty(int nStartKnot, int nEndKnot);
|
||||
protected:
|
||||
// returns the value of the basis function of degree d, given the time t
|
||||
// the knots of the basis function start at pKnotBegin, and the first knot before t is pKnotBeforeT
|
||||
Value getBasis (const Time* pKnotBegin, int d, Time t, const Time* pKnotBeforeT)const;
|
||||
|
||||
// number of knots
|
||||
int m_numKnots;
|
||||
|
||||
// the array of knots
|
||||
// no assumption is made about the multiplicity of knots
|
||||
Time *m_pKnots;
|
||||
};
|
||||
207
CryAnimation/BSplineOpen.cpp
Normal file
207
CryAnimation/BSplineOpen.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
#include "StdAfx.h"
|
||||
#include "bsplineopen.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// retrives the boundaries of the influence of the given control point, in time:
|
||||
// pTime[0] is the starting knot, pTime[1] is the end knot
|
||||
// NOTE: end knot may be < start knot; it means the support interval is cyclic
|
||||
void BSplineVec3d::getInfluenceInterval (int nControlPoint, int pTime[2])const
|
||||
{
|
||||
assert (nControlPoint >= 0 && nControlPoint < numCPs());
|
||||
if (m_isOpen)
|
||||
{
|
||||
pTime[0] = nControlPoint - m_nDegree;
|
||||
pTime[1] = nControlPoint + 1;
|
||||
}
|
||||
else
|
||||
if (numKnots() - m_nDegree < 2)
|
||||
{
|
||||
pTime[0] = 0;
|
||||
pTime[1] = numKnots()-1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pTime[0] = nControlPoint;
|
||||
pTime[1] = (nControlPoint + m_nDegree + 1) % (numKnots()-1);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// get the d+1 derivative (delta) amplitude at knot k (so-called "comb" delta amplitude)
|
||||
BSplineVec3d::Value BSplineVec3d::getDelta (int nKnot)
|
||||
{
|
||||
assert (nKnot >= 1 && nKnot < numKnots()-1);
|
||||
|
||||
Value ptResult = m_arrCPs[nKnot-1] * m_Knots.getDelta (nKnot-1, m_nDegree, nKnot+m_nDegree);
|
||||
for (int i = 0; i <= m_nDegree; ++i)
|
||||
ptResult += m_arrCPs[(nKnot+i)%numCPs()] * m_Knots.getDelta (nKnot + i, m_nDegree, nKnot+m_nDegree);
|
||||
|
||||
return ptResult;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// number of unique knots passed, and the degree
|
||||
// the array of control points is NOT initialized
|
||||
BSplineVec3d::BSplineVec3d (int numKnots, int nDegree, bool isOpen):
|
||||
m_nDegree (nDegree),
|
||||
m_Knots (numKnots + 2 * nDegree),
|
||||
m_isOpen(isOpen),
|
||||
m_arrCPs ("BSplineVec3d.CPs")
|
||||
{
|
||||
m_arrCPs.reinit(numCPs());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
BSplineVec3d::~BSplineVec3d()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Calculates spline value at the given time
|
||||
BSplineVec3d::Value BSplineVec3d::getValue (Time fTime)const
|
||||
{
|
||||
fixupTime(fTime);
|
||||
int nInterval = m_Knots.findInterval (fTime);
|
||||
|
||||
if (m_isOpen)
|
||||
{
|
||||
if (nInterval < m_nDegree)
|
||||
return m_arrCPs[0];
|
||||
|
||||
// NOTE: in the case of open spline, we guarantee here nInterval >= m_nDegree
|
||||
|
||||
if (nInterval - m_nDegree >= numCPs())
|
||||
return m_arrCPs[numCPs()-1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// as the result of clamping, ..
|
||||
assert (nInterval >= m_nDegree);
|
||||
assert (nInterval - m_nDegree <= numCPs()); // == only if the time is at the end of the loop
|
||||
if (nInterval - m_nDegree >= numCPs()) // cycle to the start of the loop
|
||||
{
|
||||
nInterval = m_nDegree;
|
||||
fTime = getKnotTime(0);
|
||||
}
|
||||
}
|
||||
|
||||
// the index of the CP corresponding to the interval starting at nInterval knot
|
||||
int idxCP = m_isOpen ? nInterval : nInterval - m_nDegree;
|
||||
|
||||
Value vCP = m_arrCPs[idxCP];
|
||||
BlendValue fBasis = m_Knots.getBasis (nInterval,m_nDegree, fTime, nInterval);
|
||||
Value ptResult = vCP * fBasis;
|
||||
for (int i = -1; i >= -m_nDegree; --i)
|
||||
{
|
||||
int nCurrentCP = idxCP+i;
|
||||
if (!m_isOpen)
|
||||
{ // the CP is cyclic in closed splines; in case the number of CPs is less than the degree,
|
||||
// we have to run this cycle (otherwise we could just make (nCurrentCP + numCPs()) % numCPs()
|
||||
assert (numCPs() > 0);
|
||||
while (nCurrentCP < 0)
|
||||
nCurrentCP += numCPs();
|
||||
nCurrentCP %= numCPs();
|
||||
}
|
||||
vCP = m_arrCPs[nCurrentCP];
|
||||
fBasis = m_Knots.getBasis (nInterval + i, m_nDegree, fTime, nInterval);
|
||||
ptResult += vCP * fBasis;
|
||||
}
|
||||
|
||||
return ptResult;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Sets the given knot time, maintaining boundary knot multiplicity
|
||||
void BSplineVec3d::setKnotTime (int nKnot, Time fTime)
|
||||
{
|
||||
assert (nKnot >= 0 && nKnot < numKnots());
|
||||
|
||||
m_Knots[nKnot + m_nDegree] = fTime;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// call this after setting all knot times
|
||||
void BSplineVec3d::finalizeKnotTimes()
|
||||
{
|
||||
Time fStart = getKnotTime(0);
|
||||
Time fEnd = getKnotTime(numKnots()-1);
|
||||
if (m_isOpen)
|
||||
{
|
||||
for (int i = 0; i < m_nDegree; ++i)
|
||||
{
|
||||
// multiplicity of the first knot is D
|
||||
m_Knots [i] = fStart;
|
||||
// .. and the last one, too
|
||||
m_Knots [m_nDegree + numKnots() + i] = fEnd;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// this algorithm of filling in the extra knots also takes into account
|
||||
// the possibility of underdetermined knot vector
|
||||
for (int i = 1; i <= m_nDegree; ++i)
|
||||
{
|
||||
m_Knots[m_nDegree-i] = fStart - (fEnd - getKnotTime(numKnots()-1 - i));
|
||||
m_Knots[m_nDegree+numKnots()-1 + i] = fEnd + (getKnotTime(i)- fStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Retrieves the given knot time, maintaining boundary knot multiplicity
|
||||
BSplineVec3d::Time BSplineVec3d::getKnotTime (int nKnot)const
|
||||
{
|
||||
return m_Knots[nKnot + m_nDegree];
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// retrives the basis function value for the given control point at the given time
|
||||
BSplineVec3d::BlendValue BSplineVec3d::getBasis (int nControlPoint, Time fTime)const
|
||||
{
|
||||
if (m_isOpen)
|
||||
return m_Knots.getBasis(nControlPoint, m_nDegree, fTime);
|
||||
else
|
||||
{
|
||||
fixupTime(fTime);
|
||||
return getBasis(nControlPoint+m_nDegree, fTime, m_Knots.findInterval(fTime));
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Returns the basis function starting at knot i (m_Knot internal numeration)
|
||||
// The function is calculated at time t, on the interval nIntervalT (m_Knot numeration)
|
||||
BSplineVec3d::BlendValue BSplineVec3d::getBasis (int i, Time t, int nIntervalT) const
|
||||
{
|
||||
if (m_isOpen)
|
||||
return m_Knots.getBasis (i,m_nDegree,t,nIntervalT);
|
||||
else
|
||||
{
|
||||
BlendValue fResult = m_Knots.getBasis (i,m_nDegree,t,nIntervalT);
|
||||
while (i+1 >= numKnots()) // the i is i+d actually
|
||||
{
|
||||
i = m_nDegree - (numKnots()-1 - (i - m_nDegree));
|
||||
if (i < 0)
|
||||
break;
|
||||
fResult += m_Knots.getBasis (i,m_nDegree,t,nIntervalT);
|
||||
}
|
||||
return fResult;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Modifies the given time so that it fits inside the spline boundaries.
|
||||
void BSplineVec3d::fixupTime (Time& fTime)const
|
||||
{
|
||||
if (!m_isOpen)
|
||||
{
|
||||
float fTimeStart = getKnotTime (0);
|
||||
float fTimeEnd = getKnotTime (numKnots()-1);
|
||||
if (fTime >= fTimeStart)
|
||||
fTime = fTimeStart + cry_fmod (fTime - fTimeStart, fTimeEnd-fTimeStart);
|
||||
else
|
||||
fTime = fTimeEnd - cry_fmod (fTimeStart - fTime, fTimeEnd - fTimeStart);
|
||||
}
|
||||
}
|
||||
154
CryAnimation/BSplineOpen.h
Normal file
154
CryAnimation/BSplineOpen.h
Normal file
@@ -0,0 +1,154 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CryEngine Source code
|
||||
//
|
||||
// File:BSplineOpen
|
||||
// Declaration of class BSplineVec3d (originally only open, but now
|
||||
// capable of being a closed spline)
|
||||
//
|
||||
// History:
|
||||
// 06/17/2002 :Created by Sergiy Migdalskiy <sergiy@crytek.de>
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
|
||||
#include "BSplineKnots.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// This is a general B-Spline that can interpolate any values (like vectors or scalars)
|
||||
// that support linear combination operations + and *
|
||||
// Upon construction, you specify the number of unique knots (the extra knots are
|
||||
// automatically added to maintain inclusive boundary condition) and the degree
|
||||
// The number of control ponints you are to supply afterwards through the []
|
||||
// operator is:
|
||||
// numCPs() == nNumKnots + d - 1 // for open spline
|
||||
// numCPs() == nNumKnots - 1 // for closed spline
|
||||
// This is natural spline without derivative boundary conditions.
|
||||
// The total number of extra knots added is 2*d, d for each boundary
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
// Value is the value that's interpolated by this BSpline
|
||||
// Desc is the descriptor class with some static members describing
|
||||
// how to treat the spline: as a closed or open one
|
||||
class BSplineVec3d:
|
||||
public _reference_target_t
|
||||
{
|
||||
public:
|
||||
typedef Vec3 Value;
|
||||
typedef BSplineKnots::Time Time;
|
||||
typedef BSplineKnots::Value BlendValue;
|
||||
|
||||
// number of unique knots passed, and the degree
|
||||
// the array of control points is NOT initialized
|
||||
BSplineVec3d (int numKnots, int nDegree, bool isOpen);
|
||||
~BSplineVec3d(void);
|
||||
|
||||
bool isOpen() {return m_isOpen;}
|
||||
|
||||
// retrieves the number of basis functions (and thus unique control points) involved
|
||||
int numCPs()const
|
||||
{
|
||||
return m_isOpen ? numKnots() + m_nDegree - 1 : numKnots() - 1;
|
||||
}
|
||||
// number of unique knots maintained
|
||||
int numKnots()const
|
||||
{
|
||||
return m_Knots.numKnots() - 2 * m_nDegree;
|
||||
}
|
||||
|
||||
// retrieves a reference to the i-th control point
|
||||
Value& operator [] (int i)
|
||||
{
|
||||
assert (i >= 0 && i < numCPs());
|
||||
return m_arrCPs[i];
|
||||
}
|
||||
|
||||
// retrieves a reference to the i-th control point
|
||||
const Value& operator [] (int i)const
|
||||
{
|
||||
return getCP(i);
|
||||
}
|
||||
|
||||
// retrieves a reference to the i-th control point
|
||||
const Value& getCP (int i) const
|
||||
{
|
||||
assert (i >= 0 && i < numCPs());
|
||||
return m_arrCPs[i];
|
||||
}
|
||||
|
||||
// sets the given control point
|
||||
void setCP (int i, const Value& pt)
|
||||
{
|
||||
assert (i >= 0 && i < numCPs());
|
||||
m_arrCPs[i] = pt;
|
||||
}
|
||||
|
||||
// computes the value of the spline at the given time
|
||||
Value getValue (Time fTime)const;
|
||||
|
||||
// computes the value of the spline at the given time t
|
||||
Value operator () (Time fTime) const
|
||||
{
|
||||
return getValue (fTime);
|
||||
}
|
||||
|
||||
// sets the given unique knot time
|
||||
void setKnotTime (int nKnot, Time fTime);
|
||||
|
||||
// call this after setting all knot times
|
||||
void finalizeKnotTimes();
|
||||
|
||||
// retrieves the knot time, knot index is in the unique knot indexation (i.e. normally 0th != 1st)
|
||||
Time getKnotTime (int nKnot)const;
|
||||
|
||||
// retrives the basis function value for the given control point at the given time
|
||||
BlendValue getBasis (int nControlPoint, Time fTime) const;
|
||||
|
||||
// Returns the basis function starting at knot i (m_Knot internal numeration)
|
||||
// The function is calculated at time t, on the interval nIntervalT (m_Knot numeration)
|
||||
BlendValue getBasis (int i, Time t, int nIntervalT) const;
|
||||
|
||||
// get the d+1 derivative (delta) amplitude at knot k
|
||||
Value getDelta (int k);
|
||||
|
||||
// return the spline degree
|
||||
int getDegree()const
|
||||
{
|
||||
return m_nDegree;
|
||||
}
|
||||
|
||||
// retrives the boundaries of the influence of the given control point, in time:
|
||||
// pTime[0] is the starting knot, pTime[1] is the end knot
|
||||
// NOTE: end knot may be < start knot; it means the support interval is cyclic
|
||||
void getInfluenceInterval (int nControlPoint, int pTime[2]) const;
|
||||
|
||||
// Returns the P (product) penalty for knot closeness, as defined by Mary J. Lindstrom "Penalized Estimation of Free Knot Splines" 1999,
|
||||
// logarithmic form
|
||||
double getKnotProductPenalty()
|
||||
{
|
||||
return m_Knots.getKnotProductPenalty(m_nDegree, m_isOpen?numCPs() : m_nDegree+numKnots()-1);
|
||||
}
|
||||
|
||||
// returns the time at which the given CP has the maximum influence at the curve
|
||||
BSplineKnots::Time getCPPeak (int nControlPoint)
|
||||
{
|
||||
return m_Knots.getBasisPeak (nControlPoint + (m_isOpen?0:m_nDegree), m_nDegree);
|
||||
}
|
||||
protected:
|
||||
// Modifies the given time so that it fits inside the spline boundaries.
|
||||
void fixupTime(Time& fTime)const;
|
||||
|
||||
// degree of the spline
|
||||
const int m_nDegree;
|
||||
|
||||
// array of control ponits
|
||||
TElementaryArray<Value> m_arrCPs;
|
||||
|
||||
// knot array
|
||||
BSplineKnots m_Knots;
|
||||
|
||||
// is the spline open?
|
||||
bool m_isOpen;
|
||||
};
|
||||
|
||||
TYPEDEF_AUTOPTR(BSplineVec3d);
|
||||
15
CryAnimation/BSplineVec3dPacked.cpp
Normal file
15
CryAnimation/BSplineVec3dPacked.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "StdAfx.h"
|
||||
#include "bsplinevec3dpacked.h"
|
||||
|
||||
// returns the normalized knot and the base - how many cycles the knot rolled back or forth
|
||||
int PackedSplineClosedGetKnotTime(int &nKnot, int numKnots)
|
||||
{
|
||||
int nBase = nKnot / (numKnots-1);
|
||||
nKnot = nKnot % (numKnots-1);
|
||||
if (nKnot < 0)
|
||||
{
|
||||
--nBase;
|
||||
nKnot += numKnots-1;
|
||||
}
|
||||
return nBase;
|
||||
}
|
||||
697
CryAnimation/BSplineVec3dPacked.h
Normal file
697
CryAnimation/BSplineVec3dPacked.h
Normal file
@@ -0,0 +1,697 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CryEngine Source code
|
||||
//
|
||||
// File:BSplineOpen
|
||||
// Declaration of
|
||||
// interface IBSpline3Packed
|
||||
// template <> struct TBSplineVec3dPackedBase
|
||||
// template <> struct TBSplineVec3dPackedDesc
|
||||
// template <> struct TBSplineVec3dPacked
|
||||
//
|
||||
// History:
|
||||
// 06/21/2002 :Created by Sergiy Migdalskiy <sergiy@crytek.de>
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
|
||||
#include "BSplineOpen.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Packed (minimal memory footprint) representation of BSpline.
|
||||
// Keeps the data in 1- or 2-byte fixed-point scaled numbers
|
||||
// CAUTION: Avoid copying it too much, the data gets copied upon
|
||||
// each invokation of the copy constructor.
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// interface to all representations of the packed spline
|
||||
class IBSpline3Packed:
|
||||
public _reference_target<unsigned short> // we don't need the alignment of 4 bytes here
|
||||
{
|
||||
public:
|
||||
// apply the given scale to the spline (multiply the spline by scale
|
||||
virtual void scale (float fScale) = 0;
|
||||
|
||||
// returns the size of the buffer required to pack in the spline in the current state
|
||||
virtual int getPackedSize()const = 0; // used
|
||||
|
||||
// returns the size of the array that follows the descriptor structure
|
||||
virtual int getRawDataSize()const = 0;
|
||||
|
||||
// returns the min corner of the bounding box for CPs
|
||||
virtual Vec3 getCPMin ()const = 0;
|
||||
// returns the max corner of the bounding box for CPs
|
||||
virtual Vec3 getCPMax ()const = 0;
|
||||
|
||||
// returns the min time
|
||||
virtual float getTimeMin() const = 0;
|
||||
// returns the max time
|
||||
virtual float getTimeMax () const = 0;
|
||||
// returns the timespan range
|
||||
virtual float getTimeRange() const = 0;
|
||||
|
||||
virtual int numKnots()const = 0;
|
||||
virtual int numCPs()const = 0;
|
||||
|
||||
// unpacks this object out of the given storage;
|
||||
// returns the size of storage used for unpacking; 0 means unpack failed
|
||||
virtual int unpack(void* pBuffer, int nSize) = 0; // used
|
||||
|
||||
// clears the contents of the spline
|
||||
virtual void clear() = 0;
|
||||
|
||||
// constructs a packed version of the given unpacked bspline
|
||||
virtual void copy (BSplineVec3d* pSpline) = 0; // used
|
||||
|
||||
// packs the spline into the given buffer
|
||||
virtual void pack (void* pBuffer) = 0; // used
|
||||
|
||||
// calculates the value of the spline at the specified point
|
||||
virtual Vec3 getValue (float t) = 0;
|
||||
|
||||
// returns the CP from the array of value triplets
|
||||
virtual Vec3 getCP (int nCP) = 0;
|
||||
|
||||
// returns the time
|
||||
virtual float getKnotTime (int nKnot)const = 0;
|
||||
|
||||
virtual unsigned sizeofThis()const = 0;
|
||||
};
|
||||
|
||||
TYPEDEF_AUTOPTR(IBSpline3Packed);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
struct TBSplineVec3dPackedBase
|
||||
{
|
||||
public:
|
||||
// fixed-point values.
|
||||
// may be unsigned short or unsigned char ONLY
|
||||
typedef FixedPointType fixed;
|
||||
|
||||
enum
|
||||
{
|
||||
// total number of fixed values - may be overflow in the case of fixed == int
|
||||
//numFixedValues = 1<<(sizeof(fixed)*8)),
|
||||
// maximum fixed value representible
|
||||
nMaxFixedValue = ((fixed)~0)
|
||||
};
|
||||
protected:
|
||||
TBSplineVec3dPackedBase():
|
||||
m_nDegree(1),
|
||||
m_numKnots(0)
|
||||
{
|
||||
}
|
||||
|
||||
// degree of the spline
|
||||
short m_nDegree;
|
||||
// number of knots
|
||||
short m_numKnots;
|
||||
|
||||
// the scale of the time values
|
||||
struct Scale
|
||||
{
|
||||
// the default scale is make all fixed values represent the interval [0,1)
|
||||
Scale():
|
||||
fBase(0),
|
||||
fScale(1.0f / (float)nMaxFixedValue)
|
||||
{
|
||||
}
|
||||
|
||||
float fBase;
|
||||
float fScale;
|
||||
|
||||
inline float unpack (fixed fVal)const
|
||||
{
|
||||
return fBase + fScale * fVal;
|
||||
}
|
||||
|
||||
fixed pack (float fVal)const
|
||||
{
|
||||
float f=fScale;
|
||||
int i=nMaxFixedValue;
|
||||
|
||||
if (fVal <= fBase)
|
||||
return 0;
|
||||
if (fVal >= fBase + f*i)
|
||||
return nMaxFixedValue;
|
||||
|
||||
return (fixed)((fVal-fBase) / fScale + 0.5);
|
||||
}
|
||||
|
||||
// unpacks the values, assuming that there are 2 more Scale structures after this
|
||||
Vec3 unpack (fixed*pVec)
|
||||
{
|
||||
return Vec3 (this[0].unpack(pVec[0]), this[1].unpack(pVec[1]), this[2].unpack(pVec[2]));
|
||||
}
|
||||
|
||||
// initialize the structure base and scale so that the resulting fixed value range
|
||||
// maps effectively the [min,max] closed interval
|
||||
void initFromMinMax (float fMin, float fMax)
|
||||
{
|
||||
fBase = fMin;
|
||||
fScale = (fMax-fMin) / nMaxFixedValue;
|
||||
}
|
||||
|
||||
// returns the min representable value
|
||||
float getMin () const
|
||||
{
|
||||
return fBase;
|
||||
}
|
||||
|
||||
// rehtrns the max reprsentable value
|
||||
float getMax () const
|
||||
{
|
||||
return fBase + getRange();
|
||||
}
|
||||
|
||||
float getRange() const
|
||||
{
|
||||
float f=fScale;
|
||||
int i=nMaxFixedValue;
|
||||
return f * i;
|
||||
}
|
||||
|
||||
void scale (float _scale)
|
||||
{
|
||||
fBase *= _scale;
|
||||
fScale *= _scale;
|
||||
}
|
||||
};
|
||||
// scale for each
|
||||
Scale m_TimeScale;
|
||||
Scale m_PosScale[3];
|
||||
|
||||
public:
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// This is the descriptor of class TBSplineVec3dPacked.
|
||||
// it's used to plainly pack/unpack the spline and reuse this structure
|
||||
template <bool isOpen = true, typename FixedPointType = unsigned char>
|
||||
struct TBSplineVec3dPackedDesc:
|
||||
public IBSpline3Packed,
|
||||
public TBSplineVec3dPackedBase<isOpen,FixedPointType>
|
||||
{
|
||||
typedef TBSplineVec3dPackedBase<isOpen, FixedPointType> Base;
|
||||
|
||||
// apply the given scale to the spline (multiply the spline by scale
|
||||
void scale (float fScale);
|
||||
|
||||
// returns the size of the buffer required to pack in the spline in the current state
|
||||
int getPackedSize()const
|
||||
{
|
||||
return sizeof (Base) + this->getRawDataSize();
|
||||
}
|
||||
|
||||
unsigned sizeofThis()const
|
||||
{
|
||||
return getPackedSize();
|
||||
}
|
||||
|
||||
int numKnots()const
|
||||
{
|
||||
assert ((this->m_numKnots >= 2 && this->m_numKnots < 10000) || (this->m_numKnots==0 && this->m_nDegree==1));// boundary check
|
||||
return this->m_numKnots;
|
||||
}
|
||||
int numCPs()const
|
||||
{
|
||||
assert (this->m_nDegree >= 1 && this->m_nDegree < 5);// boundary check
|
||||
return this->m_numKnots + (isOpen? this->m_nDegree - 1 : -1);
|
||||
}
|
||||
// returns the number of raw data elements
|
||||
int numRawDataElements()const
|
||||
{
|
||||
return (numKnots() - 2 + 3*numCPs());
|
||||
}
|
||||
// returns the size of the array that follows the descriptor structure
|
||||
int getRawDataSize()const
|
||||
{
|
||||
return numRawDataElements()*sizeof(typename TBSplineVec3dPackedBase<isOpen,FixedPointType>::fixed);
|
||||
}
|
||||
|
||||
// returns the min corner of the bounding box for CPs
|
||||
Vec3 getCPMin ()const;
|
||||
// returns the max corner of the bounding box for CPs
|
||||
Vec3 getCPMax ()const;
|
||||
|
||||
// returns the min time
|
||||
float getTimeMin() const;
|
||||
// returns the max time
|
||||
float getTimeMax () const;
|
||||
// returns the timespan range
|
||||
float getTimeRange() const;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// The spline packed implementation
|
||||
template <bool isOpen = true, typename FixedPointType = unsigned char>
|
||||
class TBSplineVec3dPacked:
|
||||
public TBSplineVec3dPackedDesc<isOpen, FixedPointType>
|
||||
{
|
||||
public:
|
||||
typedef TBSplineVec3dPackedDesc<isOpen, FixedPointType> Desc;
|
||||
|
||||
// constructs an empty spline (no knots, no keys)
|
||||
// you can unpack or copy the
|
||||
TBSplineVec3dPacked ();
|
||||
|
||||
// destructs
|
||||
~TBSplineVec3dPacked (void);
|
||||
|
||||
// copies the packed version of the BSpline
|
||||
TBSplineVec3dPacked (const TBSplineVec3dPacked<isOpen, FixedPointType>& that);
|
||||
|
||||
// unpacks this object out of the given storage;
|
||||
// returns the size of storage used for unpacking; 0 means unpack failed
|
||||
int unpack(void* pBuffer, int nSize);
|
||||
|
||||
// clears the contents of the spline
|
||||
void clear();
|
||||
|
||||
// constructs a packed version of the given unpacked bspline
|
||||
void copy (BSplineVec3d* pSpline);
|
||||
|
||||
// packs the spline into the given buffer
|
||||
void pack (void* pBuffer);
|
||||
|
||||
// calculates the value of the spline at the specified point
|
||||
Vec3 getValue (float t);
|
||||
|
||||
// returns the CP from the array of value triplets
|
||||
Vec3 getCP (int nCP);
|
||||
|
||||
// returns the time
|
||||
float getKnotTime (int nKnot)const;
|
||||
|
||||
// searches and returns the interval to which the given time belongs.
|
||||
// each pair of knots defines interval [k1,k2)
|
||||
// -1 is before the 0-th knot, numKnots()-1 is after the last knot
|
||||
int findInterval (float fTime)const;
|
||||
|
||||
// returns the i-th basis function of degree d, given the time t
|
||||
float getBasis (int i, int d, float t) const;
|
||||
|
||||
// returns the i-th basis function of degree d, given the time t and a hint, in which interval to search for it
|
||||
float getBasis (int i, int d, float t, int nIntervalT) const;
|
||||
protected:
|
||||
// returns the value of the basis function of degree d, given the time t
|
||||
// the knots of the basis function start at pKnotBegin, and the first knot before t is pKnotBeforeT
|
||||
float getBasisUnsafe (int nKnotBegin, int d, float t, int nKnotBeforeT) const;
|
||||
|
||||
// plain array of fixed-point values scaled between the min and max time and position
|
||||
// there are m_numKnots knot time values, immediately followed by m_numKnots+m_nDegree-1
|
||||
// control point values, each control point value is 3 fixed values
|
||||
FixedPointType* m_pData;
|
||||
};
|
||||
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
TBSplineVec3dPacked<isOpen, FixedPointType>::TBSplineVec3dPacked(void):
|
||||
m_pData(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
TBSplineVec3dPacked<isOpen, FixedPointType>::~TBSplineVec3dPacked(void)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
// copies the packed version of the BSpline
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
TBSplineVec3dPacked<isOpen, FixedPointType>::TBSplineVec3dPacked (const TBSplineVec3dPacked<isOpen,FixedPointType>& that):
|
||||
Desc(that)
|
||||
{
|
||||
if (that.numKnots()) // if not an empty one
|
||||
{
|
||||
m_pData = new typename TBSplineVec3dPackedBase<isOpen,FixedPointType>::fixed[that.numRawDataElements()];
|
||||
memcpy (m_pData, that.m_pData, that.getRawDataSize());
|
||||
}
|
||||
else
|
||||
m_pData = NULL;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// unpacks this object out of the given storage;
|
||||
// returns the size of storage used for unpacking; 0 means unpack failed
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
int TBSplineVec3dPacked<isOpen, FixedPointType>::unpack(void* pBuffer, int nSize)
|
||||
{
|
||||
clear();
|
||||
|
||||
if (nSize < sizeof(typename TBSplineVec3dPackedDesc<isOpen, FixedPointType>::Base))
|
||||
return 0; // failed
|
||||
|
||||
memcpy (static_cast<typename TBSplineVec3dPackedDesc<isOpen, FixedPointType>::Base*>(this), pBuffer, sizeof(typename TBSplineVec3dPackedDesc<isOpen, FixedPointType>::Base));
|
||||
|
||||
if (nSize < (int)sizeof(typename TBSplineVec3dPackedDesc<isOpen, FixedPointType>::Base) + this->getRawDataSize())
|
||||
{
|
||||
clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_pData = (new typename TBSplineVec3dPackedBase<isOpen,FixedPointType>::fixed[this->numRawDataElements()]);
|
||||
memcpy (m_pData, ((const typename TBSplineVec3dPackedDesc<isOpen, FixedPointType>::Base*)pBuffer)+1, this->getRawDataSize());
|
||||
return sizeof(typename TBSplineVec3dPackedDesc<isOpen, FixedPointType>::Base) + this->getRawDataSize();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// clears the contents of the spline
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
void TBSplineVec3dPacked<isOpen, FixedPointType>::clear()
|
||||
{
|
||||
if (m_pData)
|
||||
{
|
||||
delete[]m_pData;
|
||||
m_pData = NULL;
|
||||
}
|
||||
|
||||
this->m_nDegree = 1;
|
||||
this->m_numKnots = 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// constructs a packed version of the given unpacked bspline
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
void TBSplineVec3dPacked<isOpen, FixedPointType>::copy (BSplineVec3d* pSpline)
|
||||
{
|
||||
int i, nCoord;
|
||||
assert (isOpen == pSpline->isOpen());
|
||||
|
||||
clear();
|
||||
this->m_nDegree = pSpline->getDegree();
|
||||
this->m_numKnots = pSpline->numKnots();
|
||||
|
||||
assert (this->numCPs() == pSpline->numCPs());
|
||||
|
||||
// find the time scale
|
||||
this->m_TimeScale.initFromMinMax (pSpline->getKnotTime(0), pSpline->getKnotTime(this->m_numKnots-1));
|
||||
|
||||
// find the spacial scale for the CPs - min/max CP coordinates
|
||||
Vec3 vMinCP, vMaxCP;
|
||||
vMinCP = vMaxCP = pSpline->getCP(0);
|
||||
|
||||
for (i = 1; i < pSpline->numCPs(); ++i)
|
||||
{
|
||||
const Vec3& vCP = pSpline->getCP(i);
|
||||
for (nCoord = 0; nCoord < 3; ++nCoord)
|
||||
{
|
||||
if (vMinCP[nCoord] > vCP[nCoord])
|
||||
vMinCP[nCoord] = vCP[nCoord];
|
||||
else
|
||||
if (vMaxCP[nCoord] < vCP[nCoord])
|
||||
vMaxCP[nCoord] = vCP[nCoord];
|
||||
}
|
||||
}
|
||||
|
||||
// map the min/max coordinates to the scales
|
||||
for (nCoord = 0; nCoord < 3; ++nCoord)
|
||||
{
|
||||
this->m_PosScale[nCoord].initFromMinMax(vMinCP[nCoord], vMaxCP[nCoord]);
|
||||
}
|
||||
|
||||
m_pData = new typename TBSplineVec3dPackedBase<isOpen,FixedPointType>::fixed[this->numRawDataElements()];
|
||||
|
||||
// copy the data (knots and CPs)
|
||||
// We ignore the first and last knot, because information about them is already present in the scale structure
|
||||
for (i = 0; i < pSpline->numKnots()-2; ++i)
|
||||
m_pData[i] = this->m_TimeScale.pack(pSpline->getKnotTime(i+1));
|
||||
|
||||
// each CP has 3 coordinates, each mapped with different pos scale
|
||||
for (i = 0; i < pSpline->numCPs(); ++i)
|
||||
for (nCoord = 0; nCoord < 3; ++nCoord)
|
||||
m_pData[i*3+nCoord+this->numKnots()-2] = this->m_PosScale[nCoord].pack (pSpline->getCP(i)[nCoord]);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// packs the spline into the given buffer
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
void TBSplineVec3dPacked<isOpen, FixedPointType>::pack (void* pBuffer)
|
||||
{
|
||||
memcpy (pBuffer, static_cast<const typename TBSplineVec3dPackedDesc<isOpen, FixedPointType>::Base*>(this), sizeof(typename TBSplineVec3dPackedDesc<isOpen, FixedPointType>::Base));
|
||||
memcpy (static_cast<typename TBSplineVec3dPackedDesc<isOpen, FixedPointType>::Base*>(pBuffer) + 1, m_pData, this->getRawDataSize());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// returns the CP from the array of value triplets
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
Vec3 TBSplineVec3dPacked<isOpen, FixedPointType>::getCP (int nCP)
|
||||
{
|
||||
|
||||
if (isOpen)
|
||||
{
|
||||
if (nCP < 0)
|
||||
nCP = 0;
|
||||
else
|
||||
if (nCP >= this->numCPs())
|
||||
nCP = this->numCPs()-1;
|
||||
}
|
||||
else
|
||||
{
|
||||
nCP = nCP % this->numCPs();
|
||||
if (nCP < 0)
|
||||
nCP += this->numCPs();
|
||||
}
|
||||
|
||||
assert (nCP >= 0 && nCP < this->numCPs());
|
||||
return this->m_PosScale->unpack(m_pData + this->numKnots()-2 + 3 * nCP);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// returns the normalized knot and the base - how many cycles the knot rolled back or forth
|
||||
extern int PackedSplineClosedGetKnotTime(int &nKnot, int numKnots);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// returns the time
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
float TBSplineVec3dPacked<isOpen, FixedPointType>::getKnotTime (int nKnot)const
|
||||
{
|
||||
if (!isOpen)
|
||||
{
|
||||
int nBase = PackedSplineClosedGetKnotTime(nKnot, this->numKnots());
|
||||
|
||||
if (nKnot == 0)
|
||||
return this->m_TimeScale.getMin() + this->m_TimeScale.getRange()*nBase;
|
||||
else
|
||||
{
|
||||
assert (nKnot < this->numKnots()-1);
|
||||
return this->m_TimeScale.getRange()*nBase + this->m_TimeScale.unpack (m_pData[nKnot-1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nKnot <= 0)
|
||||
return this->m_TimeScale.getMin();
|
||||
else
|
||||
if (nKnot >= this->numKnots()-1)
|
||||
return this->m_TimeScale.getMax();
|
||||
else
|
||||
return this->m_TimeScale.unpack (m_pData[nKnot-1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// returns the max corner of the bounding box for CPs
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
Vec3 TBSplineVec3dPackedDesc<isOpen, FixedPointType>::getCPMax ()const
|
||||
{
|
||||
return Vec3(this->m_PosScale[0].getMax(), this->m_PosScale[1].getMax(), this->m_PosScale[2].getMax());
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// returns the min corner of the bounding box for CPs
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
Vec3 TBSplineVec3dPackedDesc<isOpen, FixedPointType>::getCPMin ()const
|
||||
{
|
||||
return Vec3(this->m_PosScale[0].getMin(), this->m_PosScale[1].getMin(), this->m_PosScale[2].getMin());
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// returns the min time
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
float TBSplineVec3dPackedDesc<isOpen, FixedPointType>::getTimeMin() const
|
||||
{
|
||||
return this->m_TimeScale.getMin();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// returns the max time
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
float TBSplineVec3dPackedDesc<isOpen, FixedPointType>::getTimeMax () const
|
||||
{
|
||||
return this->m_TimeScale.getMax();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// returns the max-min time
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
float TBSplineVec3dPackedDesc<isOpen, FixedPointType>::getTimeRange () const
|
||||
{
|
||||
return this->m_TimeScale.getRange();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// calculates the value of the spline at the specified point
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
Vec3 TBSplineVec3dPacked<isOpen, FixedPointType>::getValue (float fTime)
|
||||
{
|
||||
int nInterval = findInterval (fTime);
|
||||
assert (nInterval < 0 || nInterval >= this->numKnots()-1 || (getKnotTime (nInterval) <= fTime && fTime <= getKnotTime(nInterval+1)));
|
||||
|
||||
if (nInterval < 0)
|
||||
return getCP(0);
|
||||
|
||||
if (nInterval >= this->numKnots()-1)
|
||||
return getCP(this->numCPs()-1);
|
||||
|
||||
Vec3 vResult(0,0,0);
|
||||
for (int i = 0; i >= -this->m_nDegree; --i)
|
||||
{
|
||||
Vec3 vCP = getCP(nInterval + (isOpen?this->m_nDegree:0) + i);
|
||||
float fBasis = getBasis (nInterval + i, this->m_nDegree, fTime, nInterval);
|
||||
vResult += vCP * fBasis;
|
||||
}
|
||||
|
||||
return vResult;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// searches and returns the interval to which the given time belongs.
|
||||
// each pair of knots defines interval [k1,k2)
|
||||
// -1 is before the 0-th knot, numKnots()-1 is after the last knot
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
int TBSplineVec3dPacked<isOpen, FixedPointType>::findInterval (float fTime)const
|
||||
{
|
||||
if (fTime < this->m_TimeScale.getMin())
|
||||
return -1;
|
||||
if (fTime < getKnotTime (1))
|
||||
return 0;
|
||||
if (fTime >= this->m_TimeScale.getMax())
|
||||
return this->numKnots()-1;
|
||||
if (fTime >= getKnotTime (this->numKnots()-2))
|
||||
return this->numKnots()-2;
|
||||
|
||||
typename TBSplineVec3dPackedBase<isOpen,FixedPointType>::fixed fTimePacked = this->m_TimeScale.pack(fTime);
|
||||
// depending on how we rounded the fTIme value, we seek either the bigger value or bigger or equal
|
||||
if (fTime > this->m_TimeScale.unpack(fTimePacked))
|
||||
// now, the time is clamped and can be safely packed
|
||||
// search for the value that's bigger than fTimePacked
|
||||
return std::upper_bound (m_pData, m_pData + this->numKnots()-2, fTimePacked) - m_pData;
|
||||
else
|
||||
// search for the fTimePacked or bigger value
|
||||
return std::lower_bound (m_pData, m_pData + this->numKnots()-2, fTimePacked) - m_pData;
|
||||
}
|
||||
|
||||
// returns the i-th basis function of degree d, given the time t
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
float TBSplineVec3dPacked<isOpen, FixedPointType>::getBasis (int i, int d, float t) const
|
||||
{
|
||||
// the requested basis must have defined supporting knots
|
||||
assert (i>= -d && i < this->numKnots()-1);
|
||||
|
||||
// starting and ending knots - they demark the support interval: [*begin,*(end-1)]
|
||||
int nKnotBegin = i;
|
||||
int nKnotEnd = nKnotBegin + d + 2;
|
||||
// find the interval where the t is, among the supporting intervals
|
||||
// the upper bound of that interval is searched for
|
||||
int nKnotAfterT( nKnotBegin );
|
||||
for ( ; nKnotAfterT < nKnotEnd && getKnotTime (nKnotAfterT) < t; ++nKnotAfterT)
|
||||
continue;
|
||||
|
||||
if (nKnotAfterT == nKnotBegin)
|
||||
{
|
||||
assert (t < getKnotTime(nKnotBegin));
|
||||
return 0; // the time t is before the supporting interval of the basis function
|
||||
}
|
||||
|
||||
if (nKnotAfterT == nKnotEnd)
|
||||
{
|
||||
if (t > getKnotTime(nKnotEnd - 1))
|
||||
return 0; // the time t is after the supporting interval
|
||||
|
||||
assert (t == getKnotTime (nKnotEnd - 1));
|
||||
// scan down multiple knots
|
||||
while (t == getKnotTime (nKnotAfterT - 1));
|
||||
--nKnotAfterT;
|
||||
}
|
||||
|
||||
return getBasis (nKnotBegin, d, t, nKnotAfterT - 1);
|
||||
}
|
||||
|
||||
// returns the i-th basis function of degree d, given the time t and a hint, in which interval to search for it
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
float TBSplineVec3dPacked<isOpen, FixedPointType>::getBasis (int i, int d, float t, int nIntervalT) const
|
||||
{
|
||||
if (nIntervalT < -d || nIntervalT >= this->numKnots()-1)
|
||||
return 0;
|
||||
|
||||
assert (t >= getKnotTime(nIntervalT) && (t < getKnotTime(nIntervalT+1) || (t==getKnotTime(nIntervalT+1)&&getKnotTime(nIntervalT+1)==getKnotTime(nIntervalT))));
|
||||
|
||||
// the requested basis must have defined supporting knots
|
||||
if (i < -d || i >= this->numKnots()-1)
|
||||
return 0;
|
||||
|
||||
if (nIntervalT < i || nIntervalT > i + d)
|
||||
return 0; // the time is outside supporting base
|
||||
|
||||
return getBasisUnsafe (i, d, t, nIntervalT);
|
||||
}
|
||||
|
||||
// returns the value of the basis function of degree d, given the time t
|
||||
// the knots of the basis function start at pKnotBegin, and the first knot before t is pKnotBeforeT
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
float TBSplineVec3dPacked<isOpen, FixedPointType>::getBasisUnsafe (int nKnotBegin, int d, float t, int nKnotBeforeT)const
|
||||
{
|
||||
assert (t >= getKnotTime(nKnotBeforeT) && t <= getKnotTime(nKnotBeforeT+1));
|
||||
assert (nKnotBegin >= -d && nKnotBegin < this->numKnots()-1);
|
||||
assert (nKnotBeforeT >= nKnotBegin && nKnotBeforeT <= nKnotBegin + d);
|
||||
|
||||
switch (d)
|
||||
{
|
||||
case 0:
|
||||
// trivial case
|
||||
return 1;
|
||||
case 1:
|
||||
if (nKnotBeforeT == nKnotBegin)
|
||||
{
|
||||
// the t is in the first interval
|
||||
return (t - getKnotTime(nKnotBegin)) / (getKnotTime(nKnotBegin+1) - getKnotTime(nKnotBegin));
|
||||
}
|
||||
else
|
||||
{
|
||||
assert (nKnotBeforeT == nKnotBegin + 1);
|
||||
return (getKnotTime(nKnotBegin+2) - t) / (getKnotTime(nKnotBegin+2) - getKnotTime(nKnotBegin+1));
|
||||
}
|
||||
default:
|
||||
{
|
||||
float fResult = 0;
|
||||
if (nKnotBeforeT < nKnotBegin + d)
|
||||
fResult += getBasis (nKnotBegin, d-1, t, nKnotBeforeT) * (t - getKnotTime(nKnotBegin)) / (getKnotTime(nKnotBegin+d) - getKnotTime(nKnotBegin));
|
||||
if (nKnotBeforeT > nKnotBegin)
|
||||
fResult += getBasis (nKnotBegin+1, d-1, t, nKnotBeforeT) * (getKnotTime(nKnotBegin+d+1) - t) / (getKnotTime(nKnotBegin+d+1) - getKnotTime(nKnotBegin+1));
|
||||
return fResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply the given scale to the spline (multiply the spline by scale
|
||||
template <bool isOpen, typename FixedPointType>
|
||||
void TBSplineVec3dPackedDesc<isOpen,FixedPointType>::scale (float fScale)
|
||||
{
|
||||
this->m_PosScale[0].scale (fScale);
|
||||
this->m_PosScale[1].scale (fScale);
|
||||
this->m_PosScale[2].scale (fScale);
|
||||
}
|
||||
|
||||
typedef TBSplineVec3dPacked<true,unsigned char> BSplineVec3dPackedOpen;
|
||||
typedef TBSplineVec3dPacked<false,unsigned char> BSplineVec3dPackedClosed;
|
||||
200
CryAnimation/BoneLightBindInfo.cpp
Normal file
200
CryAnimation/BoneLightBindInfo.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
#include "stdafx.h"
|
||||
#ifdef _CRY_ANIMATION_BASE_HEADER_
|
||||
#include "CVars.h"
|
||||
#endif
|
||||
#include "BoneLightBindInfo.h"
|
||||
|
||||
// initializes this from the structures found in the cgf file
|
||||
void CBoneLightBindInfo::load (const SBoneLightBind& bind, const LIGHT_CHUNK_DESC& light, const char* szLightName, float fScale)
|
||||
{
|
||||
// set up the flags that will be set to the DLight
|
||||
|
||||
m_nDLightFlags = 0;
|
||||
|
||||
// the light source must have a name with substrings _ls and/or _hs
|
||||
// _ls means a light source, _hs means a heat source
|
||||
// if none is present, then the heat source flag is automatically set up
|
||||
if (strstr(szLightName,"_ls"))
|
||||
m_nDLightFlags |= DLF_LIGHTSOURCE;
|
||||
if (strstr(szLightName, "_hs"))
|
||||
m_nDLightFlags |= DLF_HEATSOURCE;
|
||||
if (!(m_nDLightFlags & (DLF_HEATSOURCE|DLF_LIGHTSOURCE)))
|
||||
m_nDLightFlags |= DLF_HEATSOURCE;
|
||||
|
||||
if (strstr(szLightName, "local"))
|
||||
m_nDLightFlags |= DLF_LOCAL;
|
||||
|
||||
if (light.szLightImage[0])
|
||||
m_nDLightFlags |= DLF_PROJECT;
|
||||
else
|
||||
m_nDLightFlags |= DLF_POINT;
|
||||
|
||||
|
||||
m_nBone = bind.nBoneId;
|
||||
m_vPos = bind.vLightOffset;
|
||||
|
||||
m_qRot = exp( quaternionf(0,bind.vRotLightOrientation) );
|
||||
|
||||
constructMatLight();
|
||||
|
||||
m_nType = light.type;
|
||||
|
||||
m_rgbColor.r = light.color.r / 255.0f;
|
||||
m_rgbColor.g = light.color.g / 255.0f;
|
||||
m_rgbColor.b = light.color.b / 255.0f;
|
||||
m_rgbColor.a = 1.0f;
|
||||
|
||||
m_fIntensity = light.intens;
|
||||
m_fHotSpotSize = light.hotsize;
|
||||
m_fFalloffSize = light.fallsize;
|
||||
m_fNearAttenStart = light.nearAttenStart;
|
||||
m_fNearAttenEnd = light.nearAttenEnd;
|
||||
m_fFarAttenStart = light.attenStart;
|
||||
m_fFarAttenEnd = light.attenEnd;
|
||||
m_vDirection = light.vDirection;
|
||||
m_strLightImage = light.szLightImage;
|
||||
|
||||
m_bOn = light.on;
|
||||
m_bUseNearAtten = light.useNearAtten;
|
||||
m_bUseFarAtten = light.useAtten;
|
||||
m_bShadow = light.shadow;
|
||||
|
||||
if (!m_bUseFarAtten)
|
||||
{
|
||||
#ifdef _CRY_ANIMATION_BASE_HEADER_
|
||||
g_GetLog()->LogWarning ("\003no far attenuation in the heat source (bone) light, using default");
|
||||
#endif
|
||||
m_fFarAttenEnd = 40;
|
||||
}
|
||||
scale (fScale);
|
||||
}
|
||||
|
||||
void CBoneLightBindInfo::scale (float fScale)
|
||||
{
|
||||
m_vPos *= fScale;
|
||||
m_matLight.SetTranslationOLD (m_matLight.GetTranslationOLD()*fScale);
|
||||
|
||||
m_fNearAttenStart *= fScale;
|
||||
m_fNearAttenEnd *= fScale;
|
||||
m_fFarAttenStart *= fScale;
|
||||
m_fFarAttenEnd *= fScale;
|
||||
|
||||
if (!m_bUseFarAtten)
|
||||
m_fFarAttenEnd *= fScale;
|
||||
}
|
||||
|
||||
void CBoneLightBindInfo::constructMatLight()
|
||||
{
|
||||
m_matLight=Matrix44(m_qRot);
|
||||
m_matLight.SetTranslationOLD(m_vPos);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// initializes the given DLight structure out of the given bone instance
|
||||
// this is one-time call that is only required after construction of the DLight
|
||||
// to initialize its constant parameters
|
||||
void CBoneLightBindInfo::initDLight (CDLight& rDLight)
|
||||
{
|
||||
rDLight.m_Flags &= ~(DLF_LIGHTSOURCE|DLF_HEATSOURCE|DLF_PROJECT|DLF_POINT);
|
||||
rDLight.m_Flags |= m_nDLightFlags;
|
||||
rDLight.m_Flags |= DLF_LOCAL;
|
||||
rDLight.m_fDirectFactor = 0;
|
||||
rDLight.m_Color = m_rgbColor;
|
||||
rDLight.m_fRadius = m_fFarAttenEnd;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// per-frame update of the DLight structure. Updates the light position and radius
|
||||
// PARAMETERS:
|
||||
// matParentBone - the partent coordinate frame
|
||||
// fRadiusFactor - the multiplier for the original radius of the light
|
||||
// rDLight - the light to update
|
||||
void CBoneLightBindInfo::updateDLight (const Matrix44& matParentBone, float fRadiusMultiplier, CDLight& DLight)
|
||||
{
|
||||
DLight.m_Origin = matParentBone.TransformPointOLD(m_vPos);
|
||||
DLight.m_fRadius = m_fFarAttenEnd * fRadiusMultiplier;
|
||||
}
|
||||
|
||||
// returns true if this light source is local (affects only the character)
|
||||
bool CBoneLightBindInfo::isLocal()const
|
||||
{
|
||||
return (m_nDLightFlags & DLF_LOCAL) != 0;
|
||||
}
|
||||
|
||||
bool CBoneLightBindInfo::isHeatSource()const
|
||||
{
|
||||
return (m_nDLightFlags & DLF_HEATSOURCE) != 0;
|
||||
}
|
||||
|
||||
bool CBoneLightBindInfo::isLightSource()const
|
||||
{
|
||||
return (m_nDLightFlags & DLF_LIGHTSOURCE) != 0;
|
||||
}
|
||||
|
||||
|
||||
// returns the priority to sort
|
||||
int CBoneLightBindInfo::getPriority()const
|
||||
{
|
||||
int nPriority = isLocal()?0:1;
|
||||
nPriority <<= 1;
|
||||
nPriority |= isHeatSource()?0:1;
|
||||
nPriority <<= 1;
|
||||
return nPriority;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Serialization to/from memory buffer.
|
||||
// if the buffer is NULL, and bSave is true, returns the required buffer size
|
||||
// otherwise, tries to save/load from the buffer, returns the number of bytes written/read
|
||||
// or 0 if the buffer is too small or some error occured
|
||||
unsigned CBoneLightBindInfo::Serialize (bool bSave, void* pBuffer, unsigned nSize)
|
||||
{
|
||||
const int nVersion = 1;
|
||||
|
||||
typedef CBoneLightBindDesc Desc;
|
||||
if (bSave)
|
||||
{
|
||||
// calculate the required size
|
||||
unsigned nRequiredSize = unsigned(sizeof(nVersion) + sizeof(Desc) + m_strLightImage.length()+1);
|
||||
if (!pBuffer)
|
||||
return nRequiredSize;
|
||||
|
||||
if (nSize < nRequiredSize)
|
||||
return 0;
|
||||
|
||||
*(int*)pBuffer = 1;
|
||||
pBuffer = (int*)pBuffer+1;
|
||||
|
||||
*((Desc*)pBuffer) = *(Desc*)this;
|
||||
pBuffer = ((Desc*)pBuffer)+1;
|
||||
|
||||
memcpy (pBuffer, m_strLightImage.c_str(), m_strLightImage.length()+1);
|
||||
return nRequiredSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nSize < sizeof(nVersion) +sizeof(Desc))
|
||||
return 0;
|
||||
|
||||
const char* pBufEnd = (const char*)pBuffer + nSize;
|
||||
|
||||
int nRealVersion = *(int*)pBuffer;
|
||||
if (nRealVersion != nVersion)
|
||||
return 0;
|
||||
void* pRawData = (int*)pBuffer + 1;
|
||||
|
||||
*(Desc*)this = *(const Desc*)pRawData;
|
||||
pRawData = ((Desc*)pRawData)+1;
|
||||
|
||||
const char* pName = (const char*)pRawData;
|
||||
|
||||
m_strLightImage = "";
|
||||
for(;*pName && pName < pBufEnd;++pName)
|
||||
m_strLightImage += *pName;
|
||||
|
||||
constructMatLight();
|
||||
return (unsigned)(pName+1 - (const char*)pBuffer);
|
||||
}
|
||||
}
|
||||
89
CryAnimation/BoneLightBindInfo.h
Normal file
89
CryAnimation/BoneLightBindInfo.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#ifndef _BONELIGHTBIND_HDR_
|
||||
#define _BONELIGHTBIND_HDR_
|
||||
|
||||
// the storable in file structure for permanent data in CBoneLightBindInfo
|
||||
struct CBoneLightBindDesc
|
||||
{
|
||||
unsigned m_nDLightFlags; // additional flags that DLight receives (maybe light or heatsource)
|
||||
|
||||
unsigned m_nBone; // the bone to which the binding is made
|
||||
Vec3 m_vPos; // position of the light in the bone CS
|
||||
CryQuat m_qRot; // orientation of the light within the bone CS
|
||||
|
||||
LightTypes m_nType; // one of the LightTypes values
|
||||
CFColor m_rgbColor; // RGB color
|
||||
float m_fIntensity; // multiplier value
|
||||
|
||||
float m_fHotSpotSize; // for spot and direct lights hotspot value
|
||||
float m_fFalloffSize; // for spot and direct lights falloff value
|
||||
float m_fNearAttenStart; // near attenuation ranges
|
||||
float m_fNearAttenEnd;
|
||||
float m_fFarAttenStart; // far attenuation ranges
|
||||
float m_fFarAttenEnd;
|
||||
|
||||
Vec3 m_vDirection; //spot light direction
|
||||
|
||||
// for better alignment, these flags are at the end of the structure
|
||||
bool m_bOn; // light is on
|
||||
bool m_bUseNearAtten; // near atten. on
|
||||
bool m_bUseFarAtten; // far atteniation
|
||||
bool m_bShadow; // shadow is on
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// the structure describing binding of a light to a bone.
|
||||
// refers to the bone by the id, and contains the whole info about the ligtht
|
||||
class CBoneLightBindInfo: public CBoneLightBindDesc
|
||||
{
|
||||
public:
|
||||
CBoneLightBindInfo()
|
||||
{m_nBone = 0;}
|
||||
|
||||
// initializes this from the structures found in the cgf file
|
||||
void load (const SBoneLightBind&, const LIGHT_CHUNK_DESC&, const char* szLightName, float fScale);
|
||||
|
||||
void scale (float fScale);
|
||||
|
||||
// returns the index of the bone to which the light source is bound
|
||||
unsigned getBone() const {return m_nBone;}
|
||||
|
||||
// initializes the given DLight structure out of the given bone instance
|
||||
// this is one-time call that is only required after construction of the DLight to initialize its constant parameters
|
||||
void initDLight (CDLight& DLight);
|
||||
|
||||
// per-frame update of the DLight structure. Updates the light position and radius
|
||||
void updateDLight (const Matrix44& matParentBone, float fRadiusMultiplier, CDLight& DLight);
|
||||
|
||||
// returns true if this light source is local (affects only the character)
|
||||
bool isLocal()const;
|
||||
|
||||
bool isHeatSource()const;
|
||||
bool isLightSource()const;
|
||||
|
||||
// returns the priority to sort
|
||||
int getPriority()const;
|
||||
|
||||
bool operator < (const CBoneLightBindInfo& rRight)const
|
||||
{
|
||||
return getPriority() < rRight.getPriority();
|
||||
}
|
||||
|
||||
// Serialization to/from memory buffer.
|
||||
// if the buffer is NULL, and bSave is true, returns the required buffer size
|
||||
// otherwise, tries to save/load from the buffer, returns the number of bytes written/read
|
||||
// or 0 if the buffer is too small or some error occured
|
||||
unsigned Serialize (bool bSave, void* pBuffer, unsigned nSize);
|
||||
|
||||
const char* getLightImageCStr()const {return m_strLightImage.c_str();}
|
||||
protected:
|
||||
string m_strLightImage; //spot light texture
|
||||
|
||||
// the following members are not serialized:
|
||||
Matrix44 m_matLight; // transform matrix of the light in the bone CS
|
||||
|
||||
void constructMatLight();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
93
CryAnimation/BoneLightDynamicBind.cpp
Normal file
93
CryAnimation/BoneLightDynamicBind.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include "stdafx.h"
|
||||
#include "cvars.h"
|
||||
#include "DebugUtils.h"
|
||||
#include "MathUtils.h"
|
||||
#include "BoneLightDynamicBind.h"
|
||||
|
||||
/*
|
||||
* Here is the sample code from Renderer.cpp, line 3000:
|
||||
dl->m_Orientation.m_vForward = Vec3d(1,0,0);
|
||||
dl->m_Orientation.m_vUp = Vec3d(0,1,0);
|
||||
dl->m_Orientation.m_vRight = Vec3d(0,0,1);
|
||||
dl->m_Orientation.rotate(Vec3d(1,0,0), dl->m_ProjAngles.x);
|
||||
dl->m_Orientation.rotate(Vec3d(0,1,0), dl->m_ProjAngles.y);
|
||||
dl->m_Orientation.rotate(Vec3d(0,0,1), dl->m_ProjAngles.z);
|
||||
* it converts the light angles into the matrix
|
||||
*/
|
||||
|
||||
// initializes this object out of the given DLight structure and bone index
|
||||
// this is one-time call that is only required for construction out of the DLight
|
||||
// to initialize the constant parameters
|
||||
void CBoneLightDynamicBind::init (ICryCharInstance* pParent, CDLight* pDLight, unsigned nBone, const Matrix44& matBone, bool bCopyLight)
|
||||
{
|
||||
m_pDLight = bCopyLight?new CDLight(*pDLight):pDLight;
|
||||
m_bDeleteLight = bCopyLight;
|
||||
m_nBone = nBone;
|
||||
m_fRadius = pDLight->m_fRadius;
|
||||
m_nDLightFlags = pDLight->m_Flags;
|
||||
m_matLight.SetRotationXYZ(DEG2RAD(pDLight->m_ProjAngles));
|
||||
m_matLight.SetTranslationOLD(pDLight->m_vObjectSpacePos);
|
||||
m_pDLight->m_pCharInstance = pParent;
|
||||
|
||||
// at this point, we have the m_matLight in character object coordinates
|
||||
// now let's transform it into the bone coordinates
|
||||
m_matLight = m_matLight * OrthoUniformGetInverted(matBone);
|
||||
/*
|
||||
pDLight->m_Orientation.m_vForward = Vec3d(1,0,0);
|
||||
pDLight->m_Orientation.m_vUp = Vec3d(0,1,0);
|
||||
pDLight->m_Orientation.m_vRight = Vec3d(0,0,1);
|
||||
pDLight->m_Orientation.rotate(Vec3d(1,0,0), pDLight->m_ProjAngles.x);
|
||||
pDLight->m_Orientation.rotate(Vec3d(0,1,0), pDLight->m_ProjAngles.y);
|
||||
pDLight->m_Orientation.rotate(Vec3d(0,0,1), pDLight->m_ProjAngles.z);
|
||||
*/
|
||||
}
|
||||
|
||||
// deletes the light object, if needed
|
||||
void CBoneLightDynamicBind::done()
|
||||
{
|
||||
m_pDLight->m_pCharInstance = NULL;
|
||||
if (m_bDeleteLight)
|
||||
delete m_pDLight;
|
||||
}
|
||||
|
||||
// per-frame update of the DLight structure. Updates the light position and radius
|
||||
void CBoneLightDynamicBind::updateDLight (const Matrix44& matBone, const Matrix44& matModel, float fRadiusMultiplier)
|
||||
{
|
||||
Matrix44 matLightObject = m_matLight * matBone;
|
||||
Matrix44 matLightWorld = matLightObject * matModel;
|
||||
m_pDLight->m_Origin = matLightWorld.GetTranslationOLD();
|
||||
m_pDLight->m_vObjectSpacePos = matLightObject.GetTranslationOLD();
|
||||
m_pDLight->m_ProjAngles = RAD2DEG(Ang3::GetAnglesXYZ(Matrix33(matLightWorld)));
|
||||
m_pDLight->m_fRadius = m_fRadius * fRadiusMultiplier;
|
||||
|
||||
static const float fColor[3][4] =
|
||||
{
|
||||
{1,0.8f,0.8f,1},
|
||||
{0.8f,1,0.8f,1},
|
||||
{0.8f,0.8f,1,1}
|
||||
};
|
||||
if (g_GetCVars()->ca_DrawLHelpers())
|
||||
{
|
||||
float fTripodScale = 1;
|
||||
debugDrawLine (m_pDLight->m_Origin, matLightWorld.TransformPointOLD(Vec3d(fTripodScale,0,0)), fColor[0]);
|
||||
debugDrawLine (m_pDLight->m_Origin, matLightWorld.TransformPointOLD(Vec3d(0,fTripodScale,0)), fColor[1]);
|
||||
debugDrawLine (m_pDLight->m_Origin, matLightWorld.TransformPointOLD(Vec3d(0,0,fTripodScale)), fColor[2]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// returns true if this light source is local (affects only the character)
|
||||
bool CBoneLightDynamicBind::isLocal()const
|
||||
{
|
||||
return (m_nDLightFlags & DLF_LOCAL) != 0;
|
||||
}
|
||||
|
||||
bool CBoneLightDynamicBind::isHeatSource()const
|
||||
{
|
||||
return (m_nDLightFlags & DLF_HEATSOURCE) != 0;
|
||||
}
|
||||
|
||||
bool CBoneLightDynamicBind::isLightSource()const
|
||||
{
|
||||
return (m_nDLightFlags & DLF_LIGHTSOURCE) != 0;
|
||||
}
|
||||
49
CryAnimation/BoneLightDynamicBind.h
Normal file
49
CryAnimation/BoneLightDynamicBind.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef _BONE_LIGHT_DYNAMIC_BIND_HDR_
|
||||
#define _BONE_LIGHT_DYNAMIC_BIND_HDR_
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// this is used to bind the light sources dynamically.
|
||||
// it can update the light source's position and rotation
|
||||
// based on the relative position and rotation and the bone matrix
|
||||
class CBoneLightDynamicBind
|
||||
{
|
||||
public:
|
||||
|
||||
// returns the index of the bone to which the light source is bound
|
||||
unsigned getBone() const {return m_nBone;}
|
||||
|
||||
// initializes this object out of the given DLight structure and bone index
|
||||
// this is one-time call that is only required for construction out of the DLight
|
||||
// to initialize the constant parameters
|
||||
void init (ICryCharInstance* pParent, CDLight* pDLight, unsigned nBone, const Matrix44& matBone, bool bCopyLight);
|
||||
// deletes the light object, if needed
|
||||
void done();
|
||||
|
||||
// per-frame update of the DLight structure. Updates the light position and radius
|
||||
void updateDLight (const Matrix44& matBone, const Matrix44& matModel, float fRadiusMultiplier);
|
||||
|
||||
void debugDraw (const Matrix44& matBone, const Matrix44& matModel);
|
||||
|
||||
// returns true if this light source is local (affects only the character)
|
||||
bool isLocal()const;
|
||||
|
||||
bool isHeatSource()const;
|
||||
bool isLightSource()const;
|
||||
|
||||
CDLight* getDLight() {return m_pDLight;}
|
||||
|
||||
protected:
|
||||
CDLight* m_pDLight;
|
||||
// the bone to which this light is attached
|
||||
unsigned m_nBone;
|
||||
// the original DLight radius
|
||||
float m_fRadius;
|
||||
unsigned m_nDLightFlags;
|
||||
// the matrix in bone's coordinates
|
||||
Matrix44 m_matLight; // transform matrix of the light in the bone CS
|
||||
// should we delete the light upon final destruction of this object?
|
||||
bool m_bDeleteLight;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
542
CryAnimation/CgfUtils.cpp
Normal file
542
CryAnimation/CgfUtils.cpp
Normal file
@@ -0,0 +1,542 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Sergiy Migdalskiy
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "stdafx.h"
|
||||
#include "StringUtils.h"
|
||||
#include "CgfUtils.h"
|
||||
|
||||
using namespace CryStringUtils;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// given the chunk, parses it and returns the array of pointers to the strings with the bone names
|
||||
// in the supplied array. Returns true when successful
|
||||
// PARAMETERS:
|
||||
// chunkHeader - the header of the chunk in the chunk table
|
||||
// pChunk - the raw chunk data in the file
|
||||
// nChunkSize - the size of the raw data piece in the file
|
||||
// arrNames - the array that will be resized according to the number of entries, and each element of this array
|
||||
// will be the pointer to the corresponding 0-terminated name string.
|
||||
bool LoadBoneNameList (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize, std::vector<const char*>& arrNames)
|
||||
{
|
||||
switch (chunkHeader.ChunkVersion)
|
||||
{
|
||||
case BONENAMELIST_CHUNK_DESC_0744::VERSION:
|
||||
{
|
||||
// the old chunk version - fixed-size name list
|
||||
const BONENAMELIST_CHUNK_DESC_0744* pNameChunk = (const BONENAMELIST_CHUNK_DESC_0744*)(pChunk);
|
||||
|
||||
int nGeomBones = pNameChunk->nEntities;
|
||||
|
||||
// just fill in the pointers to the fixed-size string buffers
|
||||
const NAME_ENTITY* pGeomBoneNameTable = (const NAME_ENTITY*)(pNameChunk+1);
|
||||
if(nGeomBones < 0 || nGeomBones > 0x800)
|
||||
return false;
|
||||
arrNames.resize(nGeomBones);
|
||||
for (int i = 0; i < nGeomBones; ++i)
|
||||
arrNames[i] = pGeomBoneNameTable[i].name;
|
||||
}
|
||||
break;
|
||||
|
||||
case BONENAMELIST_CHUNK_DESC_0745::VERSION:
|
||||
{
|
||||
// the new memory-economizing chunk with variable-length packed strings following tightly each other
|
||||
const BONENAMELIST_CHUNK_DESC_0745* pNameChunk = (const BONENAMELIST_CHUNK_DESC_0745*)(pChunk);
|
||||
int nGeomBones = pNameChunk->numEntities;
|
||||
|
||||
// we know how many strings there are there; the whole bunch of strings may
|
||||
// be followed by pad zeros, to enable alignment
|
||||
arrNames.resize(nGeomBones, "");
|
||||
|
||||
// scan through all the strings, each string following immediately the 0 terminator of the previous one
|
||||
const char* pNameListEnd = ((const char*)pNameChunk) + nChunkSize;
|
||||
const char* pName = (const char*)(pNameChunk+1);
|
||||
int nName = 0;
|
||||
while (*pName && pName < pNameListEnd && nName < nGeomBones)
|
||||
{
|
||||
arrNames[nName] = pName;
|
||||
pName += strnlen(pName, pNameListEnd) + 1;
|
||||
++nName;
|
||||
}
|
||||
if (nName < nGeomBones)
|
||||
{
|
||||
// the chunk is truncated
|
||||
#ifdef _CRY_ANIMATION_BASE_HEADER_
|
||||
// if this is in the engine, log the error
|
||||
g_GetLog()->LogWarning ("\003inconsistent bone name list chunk: only %d out of %d bone names have been read.", nName, nGeomBones);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Attempts to load the material from the given material chunk to the "me" structure
|
||||
MatChunkLoadErrorEnum LoadMatEntity (const CHUNK_HEADER& chunkHeader, const void* pChunkData, unsigned nChunkSize, MAT_ENTITY& me)
|
||||
{
|
||||
memset(&me, 0, sizeof(MAT_ENTITY));
|
||||
|
||||
switch (chunkHeader.ChunkVersion)
|
||||
{
|
||||
case MTL_CHUNK_DESC_0746::VERSION:
|
||||
{
|
||||
const MTL_CHUNK_DESC_0746* pMatChunk = (const MTL_CHUNK_DESC_0746*)pChunkData;
|
||||
|
||||
me.m_New = 2;
|
||||
strcpy(me.name, pMatChunk->name);
|
||||
switch (pMatChunk->MtlType)
|
||||
{
|
||||
case MTL_STANDARD:
|
||||
{
|
||||
me.IsStdMat = true;
|
||||
me.col_d = pMatChunk->col_d;
|
||||
me.col_a = pMatChunk->col_a;
|
||||
//me.col_a.g=0;
|
||||
//me.col_a.b=0;
|
||||
me.col_s = pMatChunk->col_s;
|
||||
|
||||
me.specLevel = pMatChunk->specLevel;
|
||||
me.specShininess = pMatChunk->specShininess*100;
|
||||
me.opacity = pMatChunk->opacity;
|
||||
me.selfIllum = pMatChunk->selfIllum;
|
||||
me.flags = pMatChunk->flags;
|
||||
|
||||
me.Dyn_Bounce = pMatChunk->Dyn_Bounce;
|
||||
me.Dyn_StaticFriction = pMatChunk->Dyn_StaticFriction;
|
||||
me.Dyn_SlidingFriction = pMatChunk->Dyn_SlidingFriction;
|
||||
/* //Timur[10/24/2001]
|
||||
strcpy(me.map_a, pMatChunk->tex_a.name);
|
||||
strcpy(me.map_d, pMatChunk->tex_d.name);
|
||||
strcpy(me.map_o, pMatChunk->tex_o.name);
|
||||
strcpy(me.map_b, pMatChunk->tex_b.name);
|
||||
strcpy(me.map_s, pMatChunk->tex_s.name);
|
||||
strcpy(me.map_g, pMatChunk->tex_g.name);
|
||||
strcpy(me.map_c, pMatChunk->tex_c.name);
|
||||
strcpy(me.map_e, pMatChunk->tex_rl.name);
|
||||
strcpy(me.map_rr, pMatChunk->tex_rr.name);
|
||||
strcpy(me.map_det, pMatChunk->tex_det.name);
|
||||
*/
|
||||
me.map_a = pMatChunk->tex_a;
|
||||
me.map_d = pMatChunk->tex_d;
|
||||
me.map_o = pMatChunk->tex_o;
|
||||
me.map_b = pMatChunk->tex_b;
|
||||
me.map_s = pMatChunk->tex_s;
|
||||
me.map_g = pMatChunk->tex_g;
|
||||
me.map_detail = pMatChunk->tex_fl;
|
||||
me.map_e = pMatChunk->tex_rl;
|
||||
me.map_subsurf = pMatChunk->tex_subsurf;
|
||||
me.map_displ = pMatChunk->tex_det;
|
||||
|
||||
me.nChildren = pMatChunk->nChildren;
|
||||
|
||||
me.alpharef = pMatChunk->alphaTest;
|
||||
}
|
||||
return MCLE_Success;
|
||||
|
||||
default:
|
||||
return MCLE_IgnoredType;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MTL_CHUNK_DESC_0745::VERSION:
|
||||
{
|
||||
const MTL_CHUNK_DESC_0745* pMatChunk = (const MTL_CHUNK_DESC_0745*)pChunkData;
|
||||
|
||||
me.m_New = 1;
|
||||
strcpy(me.name, pMatChunk->name);
|
||||
switch (pMatChunk->MtlType)
|
||||
{
|
||||
case MTL_STANDARD:
|
||||
me.IsStdMat = true;
|
||||
me.col_d = pMatChunk->col_d;
|
||||
me.col_a = pMatChunk->col_a;
|
||||
//me.col_a.g=0;
|
||||
//me.col_a.b=0;
|
||||
me.col_s = pMatChunk->col_s;
|
||||
|
||||
me.specLevel = pMatChunk->specLevel;
|
||||
me.specShininess = pMatChunk->specShininess*100;
|
||||
me.opacity = pMatChunk->opacity;
|
||||
me.selfIllum = pMatChunk->selfIllum;
|
||||
me.flags = pMatChunk->flags;
|
||||
|
||||
me.Dyn_Bounce = pMatChunk->Dyn_Bounce;
|
||||
me.Dyn_StaticFriction = pMatChunk->Dyn_StaticFriction;
|
||||
me.Dyn_SlidingFriction = pMatChunk->Dyn_SlidingFriction;
|
||||
me.map_a = pMatChunk->tex_a;
|
||||
me.map_d = pMatChunk->tex_d;
|
||||
me.map_o = pMatChunk->tex_o;
|
||||
me.map_b = pMatChunk->tex_b;
|
||||
me.map_s = pMatChunk->tex_s;
|
||||
me.map_g = pMatChunk->tex_g;
|
||||
me.map_detail = pMatChunk->tex_c;
|
||||
me.map_e = pMatChunk->tex_rl;
|
||||
me.map_subsurf = pMatChunk->tex_subsurf;
|
||||
me.map_displ = pMatChunk->tex_det;
|
||||
|
||||
me.nChildren = pMatChunk->nChildren;
|
||||
|
||||
return MCLE_Success;
|
||||
|
||||
default:
|
||||
return MCLE_IgnoredType;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return MCLE_UnknownVersion;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if the given string is null, assigns the "" to the pointer
|
||||
void chkNullString (const char*&pszString)
|
||||
{
|
||||
if (!pszString)
|
||||
pszString = "";
|
||||
}
|
||||
|
||||
static void rtrim (char* szString)
|
||||
{
|
||||
for (char* p = szString + strlen (szString) - 1; p >= szString && isspace(*p); --p)
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
static void ltrim (const char*& szString)
|
||||
{
|
||||
while (*szString && isspace(*szString))
|
||||
++szString;
|
||||
}
|
||||
|
||||
|
||||
CMatEntityNameTokenizer::CMatEntityNameTokenizer ():
|
||||
m_szMtlName (NULL),
|
||||
szName (""),
|
||||
szTemplate (""),
|
||||
szPhysMtl (""),
|
||||
nSortValue (0),
|
||||
bInvert (false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void CMatEntityNameTokenizer::tokenize (const char* szMtlFullName)
|
||||
{
|
||||
if (m_szMtlName)
|
||||
{
|
||||
free (m_szMtlName);
|
||||
m_szMtlName = NULL;
|
||||
}
|
||||
if (!szMtlFullName)
|
||||
return;
|
||||
|
||||
int nLen = (int)strlen(szMtlFullName);
|
||||
m_szMtlName = (char*)malloc (nLen+1);
|
||||
memcpy (m_szMtlName, szMtlFullName, nLen + 1);
|
||||
|
||||
szName = NULL;
|
||||
szTemplate = NULL;
|
||||
szPhysMtl = NULL;
|
||||
nSortValue = 0;
|
||||
bInvert = false;
|
||||
|
||||
// the state machine will parse the whole string
|
||||
enum StateEnum
|
||||
{
|
||||
kUnknown,
|
||||
kName,
|
||||
kTemplate,
|
||||
kPhysMtl,
|
||||
kIndex
|
||||
};
|
||||
|
||||
StateEnum nState = kName; // by default, the string begins with name
|
||||
this->szName = m_szMtlName;
|
||||
|
||||
for (char* p = m_szMtlName; *p; ++p)
|
||||
{
|
||||
switch (*p)
|
||||
{
|
||||
case '(': // template name begins
|
||||
this->szTemplate = p+1;
|
||||
nState = kTemplate;
|
||||
*p = '\0';
|
||||
break;
|
||||
|
||||
case '[': // render priority number begins
|
||||
*p = '\0';
|
||||
nState = kIndex;
|
||||
break;
|
||||
|
||||
case '/':
|
||||
this->szPhysMtl = p+1;
|
||||
*p = '\0';
|
||||
nState = kPhysMtl;
|
||||
break;
|
||||
|
||||
default:
|
||||
switch (nState)
|
||||
{
|
||||
case kName:
|
||||
switch (*p)
|
||||
{
|
||||
/*case ' ': // there are no spaces in the name
|
||||
*p = '\0';
|
||||
break;*/
|
||||
|
||||
case ')':
|
||||
case ']':
|
||||
#ifdef _CRY_ANIMATION_BASE_HEADER_
|
||||
g_GetLog()->LogError ("Invalid material name (unexpected closing bracket) \"%s\"", szMtlFullName);
|
||||
#endif
|
||||
break;
|
||||
};
|
||||
break;
|
||||
|
||||
case kTemplate:
|
||||
switch (*p)
|
||||
{
|
||||
case ')':
|
||||
nState = kUnknown;
|
||||
*p = '\0';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case kIndex:
|
||||
{
|
||||
switch (*p)
|
||||
{
|
||||
case ']':
|
||||
nState = kUnknown;
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
break;
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
this->nSortValue *= 10;
|
||||
this->nSortValue += *p - '0';
|
||||
break;
|
||||
|
||||
default:
|
||||
nState = kUnknown;
|
||||
#ifdef _CRY_ANIMATION_BASE_HEADER_
|
||||
g_GetLog()->LogError ("Invalid material name (unexpected symbol in index field) \"%s\"", szMtlFullName);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// take into account hte old form $s_... of setting the template name
|
||||
// if there was no () template, then use this one (after _)
|
||||
if ((!this->szTemplate || !this->szTemplate[0]) &&
|
||||
this->szName[0] == '$' && tolower(this->szName[1]) == 's' && this->szName[2] == '_')
|
||||
{
|
||||
this->szTemplate = this->szName + 3;
|
||||
}
|
||||
|
||||
// make sure all the strings get their pointers - if there's no name, then it will be an empty name
|
||||
chkNullString(this->szName);
|
||||
chkNullString(this->szTemplate);
|
||||
chkNullString(this->szPhysMtl);
|
||||
|
||||
// a special case (one more) - template name preceded by # means (or meant) the inverted template
|
||||
if (this->szTemplate[0] == '#')
|
||||
{
|
||||
this->szTemplate++;
|
||||
this->bInvert = true;
|
||||
}
|
||||
|
||||
// trim unneeded left and right leading spaces
|
||||
rtrim ((char*)this->szName);
|
||||
rtrim ((char*)this->szTemplate);
|
||||
rtrim ((char*)this->szPhysMtl);
|
||||
|
||||
ltrim (this->szName);
|
||||
ltrim (this->szTemplate);
|
||||
ltrim (this->szPhysMtl);
|
||||
}
|
||||
|
||||
CMatEntityNameTokenizer::~CMatEntityNameTokenizer ()
|
||||
{
|
||||
if (m_szMtlName)
|
||||
free (m_szMtlName);
|
||||
}
|
||||
|
||||
// operator that sorts the materials for rendering
|
||||
bool CMatEntityNameTokenizer::operator < (const CMatEntityNameTokenizer& right)const
|
||||
{
|
||||
if (this->nSortValue < right.nSortValue)
|
||||
return true;
|
||||
|
||||
if (this->nSortValue > right.nSortValue)
|
||||
return false;
|
||||
|
||||
int nComp = stricmp (this->szTemplate, right.szTemplate);
|
||||
|
||||
if (nComp < 0)
|
||||
return true;
|
||||
if (nComp > 0)
|
||||
return false;
|
||||
|
||||
nComp = stricmp (this->szName, right.szName);
|
||||
|
||||
if (nComp < 0)
|
||||
return true;
|
||||
if (nComp > 0)
|
||||
return false;
|
||||
|
||||
return false; // they're equal
|
||||
}
|
||||
|
||||
// given the in-permutation, constructs the inverse out-permutation, so that:
|
||||
// pOut[pIn[i]] == i
|
||||
// pIn[pOut[i]] == i
|
||||
void ConstructReversePermutation (const unsigned* pIn, unsigned* pOut, unsigned num)
|
||||
{
|
||||
unsigned i;
|
||||
#ifdef _DEBUG
|
||||
// we'll check the correctness of permutation by checking if there are duplicate entries in pIn
|
||||
// if there are no duplicate entries, according to Dirichle principle, there are no missed entries in pOut
|
||||
for (i = 0; i < num; ++i)
|
||||
pOut[i] = (unsigned)-1;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < num; ++i)
|
||||
{
|
||||
assert (pIn[i] < num);
|
||||
assert ((int)pOut[pIn[i]] ==(unsigned)-1);
|
||||
pOut[pIn[i]] = i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Remaps the materials according to the given permutation
|
||||
// the permutation is perm[new index] == old index
|
||||
void RemapMatEntities (MAT_ENTITY* pMtls, unsigned numMtls, unsigned* pPerm)
|
||||
{
|
||||
MAT_ENTITY* pOldMtls = new MAT_ENTITY[numMtls];
|
||||
memcpy (pOldMtls, pMtls, sizeof(MAT_ENTITY)*numMtls);
|
||||
|
||||
for (unsigned nNewMtl = 0; nNewMtl < numMtls; ++nNewMtl)
|
||||
memcpy (pMtls + nNewMtl, pOldMtls + pPerm[nNewMtl], sizeof(MAT_ENTITY));
|
||||
|
||||
delete[]pOldMtls;
|
||||
}
|
||||
|
||||
// copies the matrix from SBoneInitPosMatrix to Matrix
|
||||
void copyMatrix (Matrix44& matDst, const SBoneInitPosMatrix& matSrc)
|
||||
{
|
||||
for (unsigned i = 0; i < 4; ++i)
|
||||
{
|
||||
matDst(i,0) = matSrc[i][0];
|
||||
matDst(i,1) = matSrc[i][1];
|
||||
matDst(i,2) = matSrc[i][2];
|
||||
}
|
||||
matDst(0,3) = 0;
|
||||
matDst(1,3) = 0;
|
||||
matDst(2,3) = 0;
|
||||
matDst(3,3) = 1;
|
||||
}
|
||||
|
||||
const char* getMtlType (unsigned nMtlType)
|
||||
{
|
||||
switch (nMtlType)
|
||||
{
|
||||
case MTL_UNKNOWN:
|
||||
return "UNKNOWN";
|
||||
case MTL_STANDARD:
|
||||
return "STANDARD";
|
||||
case MTL_MULTI:
|
||||
return "MULTI";
|
||||
case MTL_2SIDED:
|
||||
return "2SIDED";
|
||||
default:
|
||||
return "#Unknown#";
|
||||
}
|
||||
}
|
||||
|
||||
const char* getTexType (unsigned char nTexType)
|
||||
{
|
||||
switch (nTexType)
|
||||
{
|
||||
case TEXMAP_AUTOCUBIC:
|
||||
return "TEXMAP_AUTOCUBIC";
|
||||
case TEXMAP_CUBIC:
|
||||
return "TEXMAP_CUBIC";
|
||||
case TEXMAP_ENVIRONMENT:
|
||||
return "TEXMAP_ENVIRONMENT";
|
||||
case TEXMAP_SCREENENVIRONMENT:
|
||||
return "!TEXMAP_SCREENENVIRONMENT(unsupported)!";
|
||||
default:
|
||||
return "#Unknown#";
|
||||
}
|
||||
}
|
||||
|
||||
string getLightType (LightTypes nType)
|
||||
{
|
||||
switch (nType)
|
||||
{
|
||||
case LT_OMNI:
|
||||
return "Omni";
|
||||
case LT_SPOT:
|
||||
return "Spot";
|
||||
case LT_DIRECT:
|
||||
return "Direct";
|
||||
case LT_AMBIENT:
|
||||
return "Ambient";
|
||||
default:
|
||||
{
|
||||
char szBuffer[32];
|
||||
printf (szBuffer, "Unknown(%d)", nType);
|
||||
return szBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string getMtlFlags (int nFlags)
|
||||
{
|
||||
string strResult;
|
||||
if (nFlags & MTLFLAG_WIRE)
|
||||
strResult += "MTLFLAG_WIRE|";
|
||||
if (nFlags & MTLFLAG_2SIDED)
|
||||
strResult += "MTLFLAG_2SIDED|";
|
||||
if (nFlags & MTLFLAG_FACEMAP)
|
||||
strResult += "MTLFLAG_FACEMAP|";
|
||||
if (nFlags & MTLFLAG_FACETED)
|
||||
strResult += "MTLFLAG_FACETED|";
|
||||
if (nFlags & MTLFLAG_ADDITIVE)
|
||||
strResult += "MTLFLAG_ADDITIVE|";
|
||||
if (nFlags & MTLFLAG_SUBTRACTIVE)
|
||||
strResult += "MTLFLAG_SUBTRACTIVE|";
|
||||
|
||||
if (!strResult.empty ())
|
||||
strResult.resize (strResult.length ()-1);
|
||||
|
||||
return strResult;
|
||||
}
|
||||
183
CryAnimation/CgfUtils.h
Normal file
183
CryAnimation/CgfUtils.h
Normal file
@@ -0,0 +1,183 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Sergiy Migdalskiy
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef _CGF_UTILS_HDR_
|
||||
#define _CGF_UTILS_HDR_
|
||||
|
||||
// prerequisities
|
||||
//#include <CryHeaders.h>
|
||||
//#include <StlDbgAlloc.h>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
#endif
|
||||
|
||||
// given the chunk, parses it and returns the array of pointers to the strings with the bone names
|
||||
// in the supplied array. Returns true when successful
|
||||
extern bool LoadBoneNameList (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize, std::vector<const char*>& arrNames);
|
||||
|
||||
|
||||
|
||||
enum MatChunkLoadErrorEnum
|
||||
{
|
||||
MCLE_Success,
|
||||
MCLE_Truncated,
|
||||
MCLE_UnknownVersion,
|
||||
MCLE_IgnoredType // this material type should be ignored (multimaterial)
|
||||
};
|
||||
|
||||
|
||||
// Attempts to load the material from the given material chunk to the "me" structure
|
||||
extern MatChunkLoadErrorEnum LoadMatEntity (const CHUNK_HEADER& chunkHeader, const void* pChunkData, unsigned nChunkSize, MAT_ENTITY& me);
|
||||
|
||||
// material name parser.
|
||||
// In the CGF, the material has to have a complex name:
|
||||
// name_spec template_spec phys_mat_spec render_index_spec
|
||||
//
|
||||
// name_spec:
|
||||
// $s_* -- * means the template name; in this case, material name remains $s_*
|
||||
// * -- * means just the material name
|
||||
//
|
||||
// template_spec: -- not necessary
|
||||
// (*) -- * means the template; closing bracket may be absent
|
||||
//
|
||||
// phys_mat_spec: -- not necessary
|
||||
// /name
|
||||
//
|
||||
// render_index_spec: -- not necessary
|
||||
// [id]
|
||||
class CMatEntityNameTokenizer {
|
||||
|
||||
public:
|
||||
CMatEntityNameTokenizer ();
|
||||
~CMatEntityNameTokenizer ();
|
||||
|
||||
void tokenize(const char* szName);
|
||||
|
||||
// material name
|
||||
const char *szName;
|
||||
|
||||
// template name (NULL if no template)
|
||||
const char *szTemplate;
|
||||
|
||||
// physic material name
|
||||
const char *szPhysMtl;
|
||||
|
||||
// render index (0 if not specified)
|
||||
int nSortValue;
|
||||
|
||||
// true, if the template is to be inverted.. (# before the template name
|
||||
bool bInvert;
|
||||
|
||||
// operator that sorts the materials for rendering
|
||||
bool operator < (const CMatEntityNameTokenizer& right)const;
|
||||
protected:
|
||||
char* m_szMtlName;
|
||||
};
|
||||
|
||||
// this is sorting predicate that helps form a mapping of old-new mat ids
|
||||
// it's given an array of tokenizers to index into
|
||||
class CMatEntityIndexSort
|
||||
{
|
||||
public:
|
||||
// accepts the array of initialized tokenizers and their number
|
||||
CMatEntityIndexSort (const CMatEntityNameTokenizer* pTokenizers, unsigned numSize):
|
||||
m_pTokenizers (pTokenizers),
|
||||
m_numSize (numSize)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator () (unsigned nLeft, unsigned nRight) const
|
||||
{
|
||||
assert (nLeft >= 0 && nLeft < m_numSize);
|
||||
assert (nRight >= 0 && nRight < m_numSize);
|
||||
return m_pTokenizers[nLeft ]<m_pTokenizers[nRight];
|
||||
}
|
||||
|
||||
protected:
|
||||
const CMatEntityNameTokenizer* m_pTokenizers;
|
||||
unsigned m_numSize;
|
||||
};
|
||||
|
||||
// given the in-permutation, constructs the inverse out-permutation, so that:
|
||||
// pOut[pIn[i]] == i
|
||||
// pIn[pOut[i]] == i
|
||||
void ConstructReversePermutation (const unsigned* pIn, unsigned* pOut, unsigned num);
|
||||
|
||||
// Remaps the materials according to the given permutation
|
||||
// the permutation is perm[new index] == old index
|
||||
void RemapMatEntities (MAT_ENTITY* pMtls, unsigned numMtls, unsigned* pPerm);
|
||||
|
||||
// copies the matrix from SBoneInitPosMatrix to Matrix
|
||||
extern void copyMatrix (Matrix44& matDst, const SBoneInitPosMatrix& matSrc);
|
||||
|
||||
struct SBasisProperties
|
||||
{
|
||||
#ifdef _LEAFBUFFER_H_
|
||||
SBasisProperties (const TangData& td)
|
||||
{
|
||||
init (td.tangent, td.binormal, td.tnormal);
|
||||
}
|
||||
#endif
|
||||
|
||||
SBasisProperties(const Vec3& vX, const Vec3& vY, const Vec3& vZ)
|
||||
{
|
||||
init (vX,vY,vZ);
|
||||
}
|
||||
|
||||
static double maxCosToErrorDeg(double f)
|
||||
{
|
||||
return 90 - fabs(acos(f)*180/M_PI);
|
||||
}
|
||||
|
||||
void init (const Vec3& vX, const Vec3& vY, const Vec3& vZ)
|
||||
{
|
||||
bMatrixDegraded = false;
|
||||
bLeftHanded = false;
|
||||
fErrorDeg = 0;
|
||||
// max of cosine between two vectors
|
||||
double fMaxCos = 0;
|
||||
float fXLength = vX.Length(), fYLength = vY.Length(), fZLength = vZ.Length();
|
||||
|
||||
if (fXLength < 1e-3 || fYLength < 1e-3 || fZLength < 1e-3)
|
||||
bMatrixDegraded = true;
|
||||
else
|
||||
{
|
||||
double fMaxCosXY = fabs(vX*vY)/(fXLength*fYLength);
|
||||
fMaxCos = max (fMaxCos, fMaxCosXY);
|
||||
double fMaxCosXZ = fabs(vX*vZ)/(fXLength*fZLength);
|
||||
fMaxCos = max (fMaxCos, fMaxCosXZ);
|
||||
double fMaxCosYZ = fabs(vY*vZ)/(fYLength*fZLength);
|
||||
fMaxCos = max (fMaxCos, fMaxCosYZ);
|
||||
|
||||
fErrorDeg = maxCosToErrorDeg(fMaxCos);
|
||||
fErrorDegTB = (float)maxCosToErrorDeg(fMaxCosXY);
|
||||
fErrorDegTN = (float)maxCosToErrorDeg(fMaxCosXZ);
|
||||
fErrorDegBN = (float)maxCosToErrorDeg(fMaxCosYZ);
|
||||
|
||||
bLeftHanded = (vX^vY)*vZ < 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
double fErrorDeg;
|
||||
// the error degrees between the tangent, binormal and normal by pair
|
||||
float fErrorDegTB, fErrorDegTN, fErrorDegBN;
|
||||
bool bMatrixDegraded;
|
||||
bool bLeftHanded;
|
||||
|
||||
bool isOrthogonal () const {return fErrorDeg < 0.5;}
|
||||
double getLeftHandednessPercentage () const {return (100-fErrorDeg*100/90);}
|
||||
};
|
||||
|
||||
extern const char* getMtlType (unsigned nMtlType);
|
||||
extern const char* getTexType (unsigned char nTexType);
|
||||
extern string getLightType (LightTypes nType);
|
||||
extern string getMtlFlags (int nFlags);
|
||||
|
||||
#endif
|
||||
48
CryAnimation/Changes.txt
Normal file
48
CryAnimation/Changes.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
[2/6/03]
|
||||
Made CryBone::GetParentWQuat: returns the parent world coordinate system rotation as a quaternion
|
||||
[2/10/03]
|
||||
Made ICryCharInstance::GetCurrentAnimation ()
|
||||
Got rid of cry:: debug namespace in animation system. In the future, if needed, std:: namespace will be used for debug-allocator-containers, together with DEBUG_STD_CONTAINERS macro
|
||||
[2/14/03]
|
||||
Added support for initial position chunk in cgf (In CryModelGeometryLoader), reorganized CryBoneHierarchyLoader for that a bit.
|
||||
[2/25/03]
|
||||
The Layer-0 phase synchronization has been corrected: previously, the secondary layers always synchronized with themselves.
|
||||
The corresponding checkbox for Phase SYnchronization has been added to the Model View of the editor.
|
||||
Sublayer blending has been changed. Previously, stack of animations being faded out has been frozen (when an animation on the layer started fading out, it did so with the constant phase). This caused smooth but unpleasant effects in the cases when many synchronized animations needed to be started/stopped in many layers. Currently, all the looped animations continue playing even during fading out.
|
||||
[2/26/03]
|
||||
Since we often have complains about animation quality, including too "robotic" motions, I introduced a new experimental blending function for animations. It's designed to make the blending motions less robotic and more human-like. My visual experiments show that it improves the quality of blended animations, but the artists must say their opinion. The change is very very slight. The feature is turned off by default, to turn it on set ca_EnableCubicBlending variable to 1 and try to see the multiple blended motions on the same character.
|
||||
|
||||
Currently, all animations are blended linearly in time. This looks perceptibly unnatural, though all the motions seem to be smooth (continuous). But in the real world, all motions are driven by acceleration, and though have continuous velocity. I would say, acceleration is also continuous, but the eye hardly spots it.
|
||||
So, now, the linear blending value x (linear relative to the time) is replaced with smooth:
|
||||
|
||||
Notebook[{
|
||||
Cell[BoxData[
|
||||
\(Plot[{x, 1\/2 - 2 \((x - 1\/2)\)\^3 + \(3\/2\) \((x - 1\/2)\)}, {x, 0,
|
||||
1}]\)], "Input"]
|
||||
},
|
||||
FrontEndVersion->"4.0 for Microsoft Windows",
|
||||
ScreenRectangle->{{0, 1280}, {0, 951}},
|
||||
WindowSize->{1272, 924},
|
||||
WindowMargins->{{0, Automatic}, {Automatic, 0}}
|
||||
]
|
||||
|
||||
[3/3/03]
|
||||
Changed to the new Matrix
|
||||
Made StartMorph() function
|
||||
Implemented support for morphing of smooth-skinned vertices (was broken)
|
||||
[3/6/03]
|
||||
Made infrastructure for the CCG format
|
||||
Made enumeration for the morph targets
|
||||
[3/7/03]
|
||||
SetMorphTime()
|
||||
Now the face info is stored in an array GeomFace and array of unsigned char for materials instead of array of CryFace, for better memory utilization
|
||||
[3/10/03]
|
||||
Stopping routines for morph
|
||||
[3/17/03]
|
||||
Attach/Detach light functionality
|
||||
[3/19/03]
|
||||
Attach/Detach multiple objects to bone functionality
|
||||
[3/20/03]
|
||||
Got rid of the timer parameter in AUTO_PROFILE_SECTION macro for easier profiling of load times
|
||||
[4/9/03]
|
||||
Made support for character-tocharacter attachments
|
||||
205
CryAnimation/ChunkFileReader.cpp
Normal file
205
CryAnimation/ChunkFileReader.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
#include "StdAfx.h"
|
||||
#include "FileMapping.h"
|
||||
#include "chunkfilereader.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#undef THIS_FILE
|
||||
static char THIS_FILE[] = __FILE__;
|
||||
#endif
|
||||
|
||||
const char* CChunkFileReader::gLastError = "";
|
||||
|
||||
CChunkFileReader::CChunkFileReader():
|
||||
m_pChunks(NULL)
|
||||
//,m_arrChunkSize ("CChunkFileReader.ChunkSize")
|
||||
{
|
||||
}
|
||||
|
||||
CChunkFileReader::~CChunkFileReader()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
bool CChunkFileReader::open (const string& strFileName, unsigned nFlags)
|
||||
{
|
||||
return open (strFileName.c_str(), nFlags);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// attaches the file mapping object to this file reader and checks
|
||||
// whether the file is a valid chunked file
|
||||
bool CChunkFileReader::open(CFileMapping* pFile)
|
||||
{
|
||||
close();
|
||||
m_pFile = pFile;
|
||||
|
||||
bool bSuccess = false;
|
||||
|
||||
if ( (m_pFile != (CFileMapping*)NULL) && (m_pFile->getData() != NULL) )
|
||||
{
|
||||
if (m_pFile->getSize() >= sizeof(FileHeader))
|
||||
{// the file must contain the header
|
||||
const FileHeader& fileHeader = getFileHeader();
|
||||
if (m_pFile->getSize() >= fileHeader.ChunkTableOffset + sizeof(int)
|
||||
&& (int)fileHeader.ChunkTableOffset > (int)sizeof(fileHeader)
|
||||
)
|
||||
{// there must be room for the chunk table header
|
||||
unsigned numChunks = *static_cast<const unsigned*>(m_pFile->getData(fileHeader.ChunkTableOffset));
|
||||
unsigned nChunk;
|
||||
if (m_pFile->getSize() >= fileHeader.ChunkTableOffset + sizeof(int) + numChunks*sizeof(ChunkHeader)
|
||||
&& numChunks <= (pFile->getSize () - fileHeader.ChunkTableOffset - sizeof(int)) / sizeof(ChunkHeader))
|
||||
{
|
||||
// the file must contain the full chunk table
|
||||
m_pChunks = (const ChunkHeader*)m_pFile->getData(fileHeader.ChunkTableOffset + sizeof(int));
|
||||
|
||||
bool bInvalidChunkOffsetsFound = false; // sets to true if there are chunks pointing outside the raw data section of the file
|
||||
|
||||
// step through all the chunks to fetch file offsets
|
||||
std::set<int> setOffsets;
|
||||
for (nChunk = 0; nChunk < numChunks; ++nChunk)
|
||||
{
|
||||
int nOffset = m_pChunks[nChunk].FileOffset;
|
||||
|
||||
if (nOffset < sizeof(FileHeader) || nOffset >= fileHeader.ChunkTableOffset)
|
||||
{
|
||||
gLastError = "CryFile is corrupted: chunk table is corrupted (invalid chunk offsets found)";
|
||||
bInvalidChunkOffsetsFound = true;
|
||||
}
|
||||
|
||||
setOffsets.insert(nOffset);
|
||||
}
|
||||
m_arrChunkSize.clear();
|
||||
m_arrChunkSize.resize (numChunks);
|
||||
for (nChunk = 0; nChunk < numChunks; ++nChunk)
|
||||
{
|
||||
int nOffset = m_pChunks[nChunk].FileOffset;
|
||||
int nSize = 0; // the size for invalid chunks (with invalid offsets)
|
||||
if (nOffset >= sizeof(FileHeader) && nOffset < fileHeader.ChunkTableOffset)
|
||||
{
|
||||
// find the next offset
|
||||
std::set<int>::const_iterator it = setOffsets.find (nOffset);
|
||||
assert (it != setOffsets.end());
|
||||
assert (*it == nOffset);
|
||||
++it;
|
||||
nSize = (it==setOffsets.end()?fileHeader.ChunkTableOffset:*it) - nOffset;
|
||||
}
|
||||
|
||||
assert (nSize >= 0);
|
||||
m_arrChunkSize[nChunk] = nSize;
|
||||
}
|
||||
|
||||
bSuccess = true;
|
||||
// don't let the files with invalid chunks..
|
||||
//bSuccess = !bInvalidChunkOffsetsFound;
|
||||
}
|
||||
else
|
||||
gLastError = "CryFile is corrupted: chunk table is corrupted or truncated (file too small)";
|
||||
}
|
||||
else
|
||||
gLastError = "CryFile is corrupted: chunk table is trucated (file header is probably corrupted)";
|
||||
}
|
||||
else
|
||||
gLastError = "CryFile is corrupted: header is truncated (file too small)";
|
||||
}
|
||||
else
|
||||
gLastError = "Invalid file mapping passed to CChunkFileReader::open";
|
||||
|
||||
|
||||
if (!bSuccess)
|
||||
close();
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// attaches a new file mapping object to this file reader and checks
|
||||
// whether the file is a valid chunked file
|
||||
bool CChunkFileReader::open(const char* szFileName, unsigned nFlags)
|
||||
{
|
||||
CFileMapping_AutoPtr pFileMapping = new CFileMapping(szFileName, nFlags);
|
||||
if (!pFileMapping->getData())
|
||||
{
|
||||
gLastError = "Cannot open file";
|
||||
return false;
|
||||
}
|
||||
return open (pFileMapping);
|
||||
}
|
||||
|
||||
void CChunkFileReader::close()
|
||||
{
|
||||
m_arrChunkSize.clear();
|
||||
m_pFile = NULL;
|
||||
m_pChunks = NULL;
|
||||
}
|
||||
|
||||
// returns the raw data of the file from the given offset
|
||||
const void* CChunkFileReader::getRawData(unsigned nOffset)const
|
||||
{
|
||||
if ((m_pFile != (CFileMapping*)NULL) && m_pFile->getData())
|
||||
return ((char*)m_pFile->getData())+nOffset;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// retrieves the raw chunk header, as it appears in the file
|
||||
const CChunkFileReader::ChunkHeader& CChunkFileReader::getChunkHeader(int nChunkIdx)const
|
||||
{
|
||||
return m_pChunks[nChunkIdx];
|
||||
}
|
||||
|
||||
|
||||
// returns the raw data of the i-th chunk
|
||||
const void* CChunkFileReader::getChunkData(int nChunkIdx)const
|
||||
{
|
||||
if (nChunkIdx>= 0 && nChunkIdx < numChunks())
|
||||
{
|
||||
int nOffset = m_pChunks[nChunkIdx].FileOffset;
|
||||
if (nOffset < sizeof(FileHeader) || nOffset >= getFileHeader().ChunkTableOffset)
|
||||
return 0;
|
||||
else
|
||||
return m_pFile->getData(nOffset);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// number of chunks
|
||||
int CChunkFileReader::numChunks()const
|
||||
{
|
||||
return (int)m_arrChunkSize.size();
|
||||
}
|
||||
|
||||
// number of chunks of the specified type
|
||||
int CChunkFileReader::numChunksOfType (ChunkTypes nChunkType)const
|
||||
{
|
||||
int nResult = 0;
|
||||
for (int i = 0; i < numChunks(); ++i)
|
||||
{
|
||||
if (m_pChunks[i].ChunkType == nChunkType)
|
||||
++nResult;
|
||||
}
|
||||
return nResult;
|
||||
}
|
||||
|
||||
|
||||
// returns the file headers
|
||||
const CChunkFileReader::FileHeader& CChunkFileReader::getFileHeader() const
|
||||
{
|
||||
return m_pFile?*((const FileHeader*)(m_pFile->getData())):*(const FileHeader*)NULL;
|
||||
}
|
||||
|
||||
bool CChunkFileReader::isValid () const
|
||||
{
|
||||
return m_pFile != (CFileMapping*)NULL;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// calculates the chunk size, based on the very next chunk with greater offset
|
||||
// or the end of the raw data portion of the file
|
||||
int CChunkFileReader::getChunkSize(int nChunkIdx) const
|
||||
{
|
||||
assert (nChunkIdx >= 0 && nChunkIdx < numChunks());
|
||||
return m_arrChunkSize[nChunkIdx];
|
||||
}
|
||||
|
||||
186
CryAnimation/ChunkFileReader.h
Normal file
186
CryAnimation/ChunkFileReader.h
Normal file
@@ -0,0 +1,186 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CryEngine Source code
|
||||
//
|
||||
// File:ChunkFileReader.h
|
||||
// Declaration of class CChunkFileReader
|
||||
//
|
||||
// History:
|
||||
// 06/26/2002 :Created by Sergiy Migdalskiy <sergiy@crytek.de>
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#ifndef _CHUNK_FILE_READER_HDR_
|
||||
#define _CHUNK_FILE_READER_HDR_
|
||||
|
||||
#include "CryHeaders.h"
|
||||
#include "smartptr.h"
|
||||
|
||||
class CFileMapping;
|
||||
TYPEDEF_AUTOPTR(CFileMapping);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Chunk file reader.
|
||||
// Accesses a chunked file structure through file mapping object.
|
||||
// Opens a chunk file and checks for its validity.
|
||||
// If it's invalid, closes it as if there was no open operation.
|
||||
// Error handling is performed through the return value of open: it must
|
||||
// be true for successfully open files
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CChunkFileReader:
|
||||
public _reference_target_t
|
||||
{
|
||||
public:
|
||||
typedef FILE_HEADER FileHeader;
|
||||
typedef CHUNK_HEADER ChunkHeader;
|
||||
|
||||
CChunkFileReader();
|
||||
~CChunkFileReader();
|
||||
|
||||
// attaches the file mapping object to this file reader and checks
|
||||
// whether the file is a valid chunked file
|
||||
bool open(CFileMapping* pFile);
|
||||
|
||||
// attaches a new file mapping object to this file reader and checks
|
||||
// whether the file is a valid chunked file
|
||||
bool open(const char* szFileName, unsigned nFlags = 0);
|
||||
|
||||
bool open (const string& strFileName, unsigned nFlags = 0);
|
||||
|
||||
// closes the file mapping object and thus detaches the file from this reader
|
||||
void close();
|
||||
|
||||
// returns the raw data of the file from the given offset
|
||||
const void* getRawData(unsigned nOffset)const;
|
||||
|
||||
// returns the raw data of the i-th chunk
|
||||
const void* getChunkData(int nChunkIdx)const;
|
||||
|
||||
// retrieves the raw chunk header, as it appears in the file
|
||||
const ChunkHeader& getChunkHeader(int nChunkIdx)const;
|
||||
|
||||
// calculates the chunk size, based on the very next chunk with greater offset
|
||||
// or the end of the raw data portion of the file
|
||||
int getChunkSize(int nChunkIdx) const;
|
||||
|
||||
// number of chunks
|
||||
int numChunks()const;
|
||||
|
||||
// number of chunks of the specified type
|
||||
int numChunksOfType (ChunkTypes nChunkType) const;
|
||||
|
||||
// returns the file headers
|
||||
const FileHeader& getFileHeader() const;
|
||||
|
||||
bool isValid () const;
|
||||
|
||||
const char* getLastError()const {return gLastError;}
|
||||
protected:
|
||||
// this variable contains the last error occured in this class
|
||||
static const char* gLastError;
|
||||
|
||||
CFileMapping_AutoPtr m_pFile;
|
||||
// array of offsets used by the chunks
|
||||
typedef std::vector<int> ChunkSizeArray;
|
||||
ChunkSizeArray m_arrChunkSize;
|
||||
// pointer to the array of chunks in the m_pFile
|
||||
const ChunkHeader* m_pChunks;
|
||||
};
|
||||
|
||||
TYPEDEF_AUTOPTR(CChunkFileReader);
|
||||
|
||||
// this function eats the given number of elements from the raw data pointer pRawData
|
||||
// and increments the pRawData to point to the end of data just eaten
|
||||
template <typename T>
|
||||
void EatRawData (T*pArray, unsigned nSize, const void*&pRawData)
|
||||
{
|
||||
memcpy (pArray, pRawData, sizeof(T)*nSize);
|
||||
pRawData = static_cast<const T*>(pRawData) + nSize;
|
||||
}
|
||||
|
||||
// this function eats the given number of elements from the raw data pointer pRawData
|
||||
// and increments the pRawData to point to the end of data just eaten
|
||||
// RETURNS:
|
||||
// false if failed to read the data
|
||||
template <typename T>
|
||||
bool EatRawData (T*pArray, unsigned nSize, const void*&pRawData, unsigned& nDataSize)
|
||||
{
|
||||
if (sizeof(T)*nSize <= nDataSize)
|
||||
{
|
||||
memcpy (pArray, pRawData, sizeof(T)*nSize);
|
||||
pRawData = static_cast <const T*> (pRawData) + nSize;
|
||||
nDataSize -= sizeof(T)*nSize;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool EatRawData (T*pArray, unsigned nSize, const void*&pRawData, const void* pRawDataEnd)
|
||||
{
|
||||
if ((const char*)pRawData + sizeof(T)*nSize <= (const char*)pRawDataEnd)
|
||||
{
|
||||
memcpy (pArray, pRawData, sizeof(T)*nSize);
|
||||
pRawData = static_cast <const T*> (pRawData) + nSize;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// this function puts the pointer to the data to the given pointer, and moves
|
||||
// the raw data pointer further; if fails, nothing happens and false is returned
|
||||
// PARAMETERS:
|
||||
// pArray - reference to the (pointer) variable to which the pointer to the actual data will be stored
|
||||
// nSize - number of elements in the array (depending on this, the raw data pointer will be moved)
|
||||
// pRawData - reference to the actual raw data pointer, this will be incremented
|
||||
// nDataSize - reference to the data size counter, this will be decremented upon success
|
||||
// RETURNS:
|
||||
// false if failed to read the data
|
||||
template <typename T>
|
||||
bool EatRawDataPtr(const T*&pArray, unsigned nSize, const void*&pRawData, unsigned& nDataSize)
|
||||
{
|
||||
if (sizeof(T)*nSize <= nDataSize)
|
||||
{
|
||||
pArray = (const T*)pRawData;
|
||||
pRawData = static_cast <const T*> (pRawData) + nSize;
|
||||
nDataSize -= sizeof(T)*nSize;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool EatRawDataPtr(const T*&pArray, unsigned nSize, const void*&pRawData, const void* pRawDataEnd)
|
||||
{
|
||||
if ((const char*)pRawData + sizeof(T)*nSize <= (const char*)pRawDataEnd)
|
||||
{
|
||||
pArray = (const T*)pRawData;
|
||||
pRawData = static_cast <const T*> (pRawData) + nSize;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// ... non-const version, this will hardly be ever needed
|
||||
/*
|
||||
template <typename T>
|
||||
bool EatRawDataPtr(T*&pArray, unsigned nSize, void*&pRawData, unsigned& nDataSize)
|
||||
{
|
||||
if (sizeof(T)*nSize <= nDataSize)
|
||||
{
|
||||
pArray = (const T*)pRawData;
|
||||
pRawData = static_cast <const T*> (pRawData) + nSize;
|
||||
nDataSize -= sizeof(T)*nSize;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
#endif
|
||||
203
CryAnimation/Controller.cpp
Normal file
203
CryAnimation/Controller.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
// Controller.cpp: implementation of the utilities declared with the
|
||||
// IController interface
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "CryBone.h"
|
||||
#include "CryModEffector.h"
|
||||
#include "CryModEffIKSolver.h"
|
||||
#include "CryModelState.h"
|
||||
#include "Controller.h"
|
||||
#include "ControllerManager.h"
|
||||
#include "CryKeyInterpolation.h"
|
||||
#include "MathUtils.h"
|
||||
|
||||
// returns the rotation quaternion
|
||||
CryQuat IController::PQLog::getOrientation()const
|
||||
{
|
||||
return exp( quaternionf(0,vRotLog) );
|
||||
}
|
||||
|
||||
// resets the state to zero
|
||||
void IController::PQLog::reset ()
|
||||
{
|
||||
vPos(0,0,0);
|
||||
vRotLog(0,0,0);
|
||||
}
|
||||
|
||||
// blends the pqSource[0] and pqSource[1] with weights 1-fBlend and fBlend into pqResult
|
||||
void IController::PQLog::blendPQ (const PQLog* pqSource, float fBlend)
|
||||
{
|
||||
blendPQ (pqSource[0], pqSource[1], fBlend);
|
||||
}
|
||||
|
||||
// returns the equivalent rotation in logarithmic space (the quaternion of which is negative the original)
|
||||
Vec3 IController::PQLog::getComplementaryRotLog() const
|
||||
{
|
||||
// assuming |vRotLog| < gPi
|
||||
Vec3 vResult = vRotLog * float(1 - gPi/vRotLog.Length());
|
||||
#ifdef _DEBUG
|
||||
//CryQuat qOrig = exp(vRotLog);
|
||||
//CryQuat qNew = exp(vResult);
|
||||
//assert (qOrig.Dot(qNew) < -0.99);
|
||||
#endif
|
||||
return vResult;
|
||||
}
|
||||
|
||||
// blends the pqFrom and pqTo with weights 1-fBlend and fBlend into pqResult
|
||||
void IController::PQLog::blendPQ (const PQLog& pqFrom, const PQLog& pqTo, float fBlend)
|
||||
{
|
||||
assert (fBlend >= 0 && fBlend <=1);
|
||||
vPos = pqFrom.vPos * (1-fBlend) + pqTo.vPos * fBlend;
|
||||
vRotLog = pqFrom.vRotLog * (1-fBlend) + pqTo.vRotLog * fBlend;
|
||||
}
|
||||
|
||||
// builds the matrix out of the position and orientation stored in this PQLog
|
||||
void IController::PQLog::buildMatrix(Matrix44& matDest)const
|
||||
{
|
||||
CryQuat qRot;
|
||||
quaternionExponentOptimized(vRotLog, qRot);
|
||||
//BuildMatrixFromQP(matDest, qRot, vPos);
|
||||
matDest=Matrix44(qRot);
|
||||
matDest.SetTranslationOLD(vPos);
|
||||
}
|
||||
|
||||
void IController::PQLog::assignFromMatrix (const Matrix44& mat)
|
||||
{
|
||||
this->vPos = mat.GetTranslationOLD();
|
||||
|
||||
Matrix33 m(mat);
|
||||
m.SetRow (0, mat.GetRow(0).normalized());
|
||||
m.SetRow (1, mat.GetRow(1).normalized());
|
||||
m.SetRow (2, mat.GetRow(2).normalized());
|
||||
|
||||
// check for parity
|
||||
if ((m.GetRow(0)^m.GetRow(1))*m.GetRow(2) < 0)
|
||||
m.SetRow (2, -m.GetRow(2));
|
||||
|
||||
this->vRotLog = log(CryQuat(m)).v;
|
||||
|
||||
#ifdef _DEBUG
|
||||
Matrix44 t;
|
||||
buildMatrix(t);
|
||||
for (int i = 0; i < 3; ++i)
|
||||
for (int j = 0; j < 3; ++j)
|
||||
assert (fabs(m(i,j)-t(i,j)) < 0.5);
|
||||
#endif
|
||||
}
|
||||
|
||||
// a special version of the buildMatrix that adds a rotation to the rotation of this PQLog
|
||||
void IController::PQLog::buildMatrixPlusRot(Matrix44& matDest, const CryQuat& qRotPlus)const
|
||||
{
|
||||
CryQuat qRot;
|
||||
quaternionExponentOptimized(vRotLog, qRot);
|
||||
//BuildMatrixFromQP(matDest, qRot*qRotPlus, vPos);
|
||||
matDest=Matrix44(qRot*qRotPlus); matDest.SetTranslationOLD(vPos);
|
||||
}
|
||||
|
||||
Vec3& operator *= (Vec3& v, double d)
|
||||
{
|
||||
v.x = float(v.x*d);
|
||||
v.y = float(v.y*d);
|
||||
v.z = float(v.z*d);
|
||||
return v;
|
||||
}
|
||||
|
||||
// adjusts the rotation of these PQs: if necessary, flips them or one of them (effectively NOT changing the whole rotation,but
|
||||
// changing the rotation angle to Pi-X and flipping the rotation axis simultaneously)
|
||||
// this is needed for blending between animations represented by quaternions rotated by ~PI in quaternion space
|
||||
// (and thus ~2*PI in real space)
|
||||
void AdjustLogRotations (Vec3& vRotLog1, Vec3& vRotLog2)
|
||||
{
|
||||
double dLen1 = DLength(vRotLog1);
|
||||
if (dLen1 > gPi/2)
|
||||
{
|
||||
vRotLog1 *= 1 - gPi/dLen1;
|
||||
// now the length of the first vector is actually gPi - dLen1,
|
||||
// and it's closer to the origin than its complementary
|
||||
|
||||
// but we won't need the dLen1 any more
|
||||
}
|
||||
|
||||
double dLen2 = DLength(vRotLog2);
|
||||
// if the flipped 2nd rotation vector is closer to the first rotation vector,
|
||||
// then flip the second vector
|
||||
if (vRotLog1 * vRotLog2 < (dLen2 - gPi/2) * dLen2)
|
||||
{
|
||||
// flip the second rotation also
|
||||
vRotLog2 *= 1 - gPi / dLen2;
|
||||
}
|
||||
}
|
||||
|
||||
void AdjustLogRotationTo (const Vec3& vRotLog1, Vec3& vRotLog2)
|
||||
{
|
||||
double dLen2 = DLength(vRotLog2);
|
||||
|
||||
if (dLen2 > gPi/2)
|
||||
{
|
||||
if (dLen2 > gPi)
|
||||
{
|
||||
double dReminder = fmod (dLen2, gPi);
|
||||
if (dReminder <= gPi/2)
|
||||
{
|
||||
// we don't need to flip the axis
|
||||
vRotLog2 *= dReminder / dLen2;
|
||||
dLen2 = dReminder;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we need to flip the axis
|
||||
vRotLog2 *= (dReminder - gPi)/dLen2;
|
||||
dLen2 = fabs(dReminder - gPi);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vRotLog2 *= 1 - gPi/dLen2; // (dLen2-gPi)/dLen2
|
||||
dLen2 = fabs(dLen2 - gPi);
|
||||
}
|
||||
// now the length of the first vector is actually gPi - dLen1,
|
||||
// and it's closer to the origin than its complementary
|
||||
|
||||
// but we won't need the dLen1 any more
|
||||
}
|
||||
|
||||
// if the flipped 2nd rotation vector is closer to the first rotation vector,
|
||||
// then flip the second vector
|
||||
if (vRotLog1 * vRotLog2 < (dLen2 - gPi/2) * dLen2)
|
||||
{
|
||||
// flip the second rotation also
|
||||
vRotLog2 *= 1 - gPi / dLen2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AdjustLogRotation(Vec3& vRotLog)
|
||||
{
|
||||
double dLen = DLength(vRotLog);
|
||||
if (dLen > gPi/2)
|
||||
{
|
||||
if (dLen > gPi)
|
||||
{
|
||||
double dReminder = fmod (dLen, gPi);
|
||||
if (dReminder <= gPi/2)
|
||||
{
|
||||
// we don't need to flip the axis
|
||||
vRotLog *= dReminder / dLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we need to flip the axis
|
||||
vRotLog *= (dReminder - gPi)/dLen;
|
||||
}
|
||||
}
|
||||
else
|
||||
vRotLog *= 1 - gPi/dLen; // (dLen-gPi)/dLen
|
||||
// now the length of the first vector is actually gPi - dLen1,
|
||||
// and it's closer to the origin than its complementary
|
||||
|
||||
// but we won't need the dLen1 any more
|
||||
}
|
||||
|
||||
}
|
||||
129
CryAnimation/Controller.h
Normal file
129
CryAnimation/Controller.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Sergiy Migdalskiy
|
||||
//
|
||||
// Notes:
|
||||
// IController interface declaration
|
||||
// See the IController comment for more info
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _CRYTEK_CONTROLLER_HEADER_
|
||||
#define _CRYTEK_CONTROLLER_HEADER_
|
||||
|
||||
#include "StringUtils.h"
|
||||
#include "CryHeaders.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// interface IController
|
||||
// Describes the position and orientation of an object, changing in time.
|
||||
// Responsible for loading itself from a binary file, calculations
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
class IController: public _reference_target_t
|
||||
{
|
||||
public:
|
||||
|
||||
// each controller has an ID, by which it is identifiable
|
||||
virtual unsigned GetID () const = 0;
|
||||
|
||||
// returns the orientation of the controller at the given time
|
||||
virtual CryQuat GetOrientation (float t) = 0;
|
||||
|
||||
// returns the orientation of the controller at the given time, in logarithmic space
|
||||
virtual Vec3 GetOrientation2 (float t) = 0;
|
||||
|
||||
// returns position of the controller at the given time
|
||||
virtual Vec3 GetPosition (float t) = 0;
|
||||
|
||||
// returns scale of the controller at the given time
|
||||
virtual Vec3 GetScale (float t) = 0;
|
||||
|
||||
// retrieves the position and orientation within one call
|
||||
// may be optimal in some applications
|
||||
virtual void GetValue (float t, CryQuat& q, Vec3 &p) = 0;
|
||||
|
||||
// retrieves the position and orientation (in the logarithmic space, i.e. instead of quaternion, its logarithm is returned)
|
||||
// may be optimal for motion interpolation
|
||||
struct PQLog
|
||||
{
|
||||
Vec3 vPos;
|
||||
Vec3 vRotLog; // logarithm of the rotation
|
||||
|
||||
string toString()const {return "{pos:" + CryStringUtils::toString(vPos)+", rot="+CryStringUtils::toString (vRotLog) + "}";}
|
||||
|
||||
// returns the rotation quaternion
|
||||
CryQuat getOrientation()const;
|
||||
// resets the state to zero
|
||||
void reset ();
|
||||
// blends the pqSource[0] and pqSource[1] with weights 1-fBlend and fBlend into pqResult
|
||||
void blendPQ (const PQLog* pqSource, float fBlend);
|
||||
// blends the pqFrom and pqTo with weights 1-fBlend and fBlend into pqResult
|
||||
void blendPQ (const PQLog& pqFrom, const PQLog& pqTo, float fBlend);
|
||||
// builds the matrix out of the position and orientation stored in this PQLog
|
||||
void buildMatrix(Matrix44& mat)const;
|
||||
// a special version of the buildMatrix that adds a rotation to the rotation of this PQLog
|
||||
void buildMatrixPlusRot(Matrix44& mat, const CryQuat& qRotPlus) const;
|
||||
// returns the equivalent rotation in logarithmic space (the quaternion of which is negative the original)
|
||||
Vec3 getComplementaryRotLog() const;
|
||||
// constructs the position/rotation from the given matrix
|
||||
void assignFromMatrix (const Matrix44& mat);
|
||||
|
||||
PQLog operator * (float f)const
|
||||
{
|
||||
PQLog res;
|
||||
res.vPos = this->vPos * f;
|
||||
res.vRotLog = this->vRotLog * f;
|
||||
return res;
|
||||
}
|
||||
|
||||
const PQLog& operator += (const PQLog& pq)
|
||||
{
|
||||
this->vPos += pq.vPos;
|
||||
this->vRotLog += pq.vRotLog;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
virtual void GetValue2 (float t, PQLog& pq) = 0;
|
||||
|
||||
// returns the start time
|
||||
virtual float GetTimeStart () = 0;
|
||||
|
||||
// returns the end time
|
||||
virtual float GetTimeEnd() = 0;
|
||||
|
||||
virtual size_t sizeofThis () const = 0;
|
||||
|
||||
virtual bool IsLooping() const { return false; };
|
||||
};
|
||||
|
||||
TYPEDEF_AUTOPTR(IController);
|
||||
//typedef IController*IController_AutoPtr;
|
||||
|
||||
// adjusts the rotation of these PQs: if necessary, flips them or one of them (effectively NOT changing the whole rotation,but
|
||||
// changing the rotation angle to Pi-X and flipping the rotation axis simultaneously)
|
||||
// this is needed for blending between animations represented by quaternions rotated by ~PI in quaternion space
|
||||
// (and thus ~2*PI in real space)
|
||||
extern void AdjustLogRotations (Vec3& vRotLog1, Vec3& vRotLog2);
|
||||
extern void AdjustLogRotationTo (const Vec3& vRotLog1, Vec3& vRotLog2);
|
||||
extern void AdjustLogRotation(Vec3& vRotLog);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// This class is a non-trivial predicate used for sorting an
|
||||
// ARRAY OF smart POINTERS to IController's. That's why I need a separate
|
||||
// predicate class for that. Also, to avoid multiplying predicate classes,
|
||||
// there are a couple of functions that are also used to find a IController
|
||||
// in a sorted by ID array of IController* pointers, passing only ID to the
|
||||
// lower_bound function instead of creating and passing a dummy IController*
|
||||
class AnimCtrlSortPred
|
||||
{
|
||||
public:
|
||||
bool operator() (const IController_AutoPtr& a, const IController_AutoPtr& b) {assert (a!=(IController*)NULL && b != (IController*)NULL); return a->GetID() < b->GetID();}
|
||||
bool operator() (const IController_AutoPtr& a, unsigned nID) {assert (a != (IController*)NULL);return a->GetID() < nID;}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
167
CryAnimation/ControllerCryBone.cpp
Normal file
167
CryAnimation/ControllerCryBone.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
#include "stdafx.h"
|
||||
#include "Controller.h"
|
||||
#include "ControllerCryBone.h"
|
||||
#include "CryKeyInterpolation.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#undef THIS_FILE
|
||||
static char THIS_FILE[] = __FILE__;
|
||||
#endif
|
||||
|
||||
static const bool g_bOptimizeAndLogKeys = false;
|
||||
|
||||
CControllerCryBone::CControllerCryBone():
|
||||
m_nControllerId(0),
|
||||
m_arrTimes ("CControllerCryBone.Keys"),
|
||||
m_arrKeys ("CControllerCryBone.Keys")
|
||||
{
|
||||
}
|
||||
|
||||
CControllerCryBone::~CControllerCryBone()
|
||||
{
|
||||
}
|
||||
|
||||
// Loads (initializes) controller from the given chunk. The chunk descriptor is followed by the
|
||||
// chunk data immediately, Returns true if successful.
|
||||
bool CControllerCryBone::Load (const CONTROLLER_CHUNK_DESC_0826* pChunk, float fScale)
|
||||
{
|
||||
//m_Chunk = *pChunk;
|
||||
m_nControllerId = pChunk->nControllerId;
|
||||
|
||||
if(pChunk->chdr.ChunkType != ChunkType_Controller || pChunk->chdr.ChunkVersion != CONTROLLER_CHUNK_DESC_0826::VERSION)
|
||||
{
|
||||
m_nControllerId = 0;
|
||||
GetLog()->LogToFile("CControllerCryBone::Load: File version error");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pChunk->type != CTRL_CRYBONE)
|
||||
return false;
|
||||
|
||||
unsigned i, numKeys = pChunk->nKeys;
|
||||
m_arrKeys.reinit(numKeys);
|
||||
m_arrTimes.reinit (numKeys);
|
||||
{
|
||||
const CryBoneKey * pCryBoneKey = (const CryBoneKey*)(pChunk+1); // iterator through the original keys
|
||||
int *pTime = m_arrTimes.begin();
|
||||
PQLog* pKey = m_arrKeys.begin();
|
||||
CryQuat qLast; // the last orientation
|
||||
for (i = 0; i < numKeys; ++i)
|
||||
{
|
||||
m_arrTimes[i] = pCryBoneKey->time;
|
||||
m_arrKeys[i].vPos = pCryBoneKey->relpos * fScale;
|
||||
|
||||
// make sure we don't make >PI rotations per keyframe
|
||||
if ( (qLast|(pCryBoneKey->relquat)) >= 0)
|
||||
qLast = pCryBoneKey->relquat;
|
||||
else
|
||||
qLast = -pCryBoneKey->relquat;
|
||||
|
||||
m_arrKeys[i].vRotLog = log(qLast).v;
|
||||
|
||||
++pCryBoneKey, ++pTime, ++pKey;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Loads (initializes) controller from the given chunk. The chunk descriptor is followed by the
|
||||
// chunk data immediately. Returns true if successful
|
||||
bool CControllerCryBone::Load (const CONTROLLER_CHUNK_DESC_0827* pChunk, unsigned nSize, float fScale)
|
||||
{
|
||||
m_nControllerId = pChunk->nControllerId;
|
||||
unsigned numKeys = pChunk->numKeys;
|
||||
|
||||
const CryKeyPQLog* pCryKey = (const CryKeyPQLog*)(pChunk+1);
|
||||
|
||||
if ((const char*)(pCryKey + numKeys) > ((const char*)pChunk) + nSize)
|
||||
{
|
||||
GetLog()->LogError ("\002CControllerCryBone::Load: cannot load controller 0x%08X, chunk is truncated",
|
||||
m_nControllerId);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_arrKeys.reinit (numKeys);
|
||||
m_arrTimes.reinit (numKeys);
|
||||
int *pTime = m_arrTimes.begin();
|
||||
PQLog* pKey = m_arrKeys.begin();
|
||||
|
||||
for (unsigned i = 0; i < numKeys; ++i)
|
||||
{
|
||||
m_arrTimes[i] = pCryKey->nTime;
|
||||
m_arrKeys[i].vPos = pCryKey->vPos * fScale;
|
||||
m_arrKeys[i].vRotLog = pCryKey->vRotLog;
|
||||
|
||||
++pCryKey, ++pTime, ++pKey;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// retrieves the position and orientation (in the logarithmic space,
|
||||
// i.e. instead of quaternion, its logarithm is returned)
|
||||
// may be optimal for motion interpolation
|
||||
void CControllerCryBone::GetValue2 (float fTime, PQLog& pq)
|
||||
{
|
||||
assert(numKeys());
|
||||
|
||||
if(!(m_arrTimes[0] < fTime))
|
||||
{
|
||||
pq = m_arrKeys[0];
|
||||
return ;
|
||||
}
|
||||
|
||||
if(m_arrTimes[numKeys()-1] <= fTime)
|
||||
{
|
||||
pq = m_arrKeys[numKeys()-1];
|
||||
return ;
|
||||
}
|
||||
|
||||
assert(numKeys()>1);
|
||||
|
||||
int nPos = numKeys()>>1;
|
||||
int nStep = numKeys()>>2;
|
||||
|
||||
// use binary search
|
||||
while(nStep)
|
||||
{
|
||||
if(fTime < m_arrTimes[nPos])
|
||||
nPos = nPos - nStep;
|
||||
else
|
||||
if(fTime > m_arrTimes[nPos])
|
||||
nPos = nPos + nStep;
|
||||
else
|
||||
break;
|
||||
|
||||
nStep = nStep>>1;
|
||||
}
|
||||
|
||||
// finetuning needed since time is not linear
|
||||
while(fTime > m_arrTimes[nPos])
|
||||
nPos++;
|
||||
|
||||
while(fTime < m_arrTimes[nPos-1])
|
||||
nPos--;
|
||||
|
||||
assert(nPos > 0 && nPos < (int)numKeys());
|
||||
assert(m_arrTimes[nPos] != m_arrTimes[nPos-1]);
|
||||
|
||||
float t = (float(fTime-m_arrTimes[nPos-1]))/(m_arrTimes[nPos] - m_arrTimes[nPos-1]);
|
||||
PQLog pKeys[2] = {m_arrKeys[nPos-1], m_arrKeys[nPos]};
|
||||
AdjustLogRotations (pKeys[0].vRotLog, pKeys[1].vRotLog);
|
||||
pq.blendPQ (pKeys, t);
|
||||
}
|
||||
|
||||
ILog* CControllerCryBone::GetLog() const
|
||||
{
|
||||
return g_GetLog();
|
||||
}
|
||||
|
||||
|
||||
size_t CControllerCryBone::sizeofThis ()const
|
||||
{
|
||||
return sizeof(*this) + m_arrKeys.size() * (sizeof(m_arrKeys[0])+sizeof(m_arrTimes[0]));
|
||||
}
|
||||
113
CryAnimation/ControllerCryBone.h
Normal file
113
CryAnimation/ControllerCryBone.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Sergiy Migdalskiy
|
||||
//
|
||||
// Notes:
|
||||
// CControllerCryBone class declaration
|
||||
// CControllerCryBone is implementation of IController which is compatible with
|
||||
// the old (before 6/27/02) caf file format that contained only CryBoneKey keys.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef _CRYTEK_CONTROLLER_CRY_BONE_HEADER_
|
||||
#define _CRYTEK_CONTROLLER_CRY_BONE_HEADER_
|
||||
|
||||
// #include "Controller.h" // must be included for the declaration of IController
|
||||
|
||||
// old motion format cry bone controller
|
||||
class CControllerCryBone: public IController
|
||||
{
|
||||
public:
|
||||
unsigned numKeys()const
|
||||
{
|
||||
return (unsigned)m_arrKeys.size();
|
||||
}
|
||||
|
||||
unsigned GetID () const {return m_nControllerId;}
|
||||
|
||||
CControllerCryBone();
|
||||
~CControllerCryBone();
|
||||
|
||||
// Loads (initializes) controller from the given chunk. The chunk descriptor is followed by the
|
||||
// chunk data immediately. Returns true if successful
|
||||
bool Load(const CONTROLLER_CHUNK_DESC_0826* pChunk, float scale);
|
||||
|
||||
// Loads (initializes) controller from the given chunk. The chunk descriptor is followed by the
|
||||
// chunk data immediately. Returns true if successful
|
||||
bool Load (const CONTROLLER_CHUNK_DESC_0827* pChunk, unsigned nSize, float fScale);
|
||||
|
||||
// retrieves the position and orientation within one call
|
||||
// may be optimal in some applications
|
||||
void GetValue(float t, CryQuat& q, Vec3 &p)
|
||||
{
|
||||
PQLog pq;
|
||||
GetValue2(t, pq);
|
||||
|
||||
q = exp( quaternionf(0,pq.vRotLog) );
|
||||
|
||||
p = pq.vPos;
|
||||
}
|
||||
|
||||
// retrieves the position and orientation (in the logarithmic space,
|
||||
// i.e. instead of quaternion, its logarithm is returned)
|
||||
// may be optimal for motion interpolation
|
||||
void GetValue2 (float t, PQLog& pq);
|
||||
|
||||
void LogKeys(const char* szFileName, const char* szVarName);
|
||||
|
||||
CryQuat GetOrientation (float fTime)
|
||||
{
|
||||
PQLog pq;
|
||||
GetValue2(fTime, pq);
|
||||
return exp( quaternionf(0,pq.vRotLog) );
|
||||
}
|
||||
|
||||
// returns the orientation of the controller at the given time, in logarithmic space
|
||||
Vec3 GetOrientation2 (float t)
|
||||
{
|
||||
PQLog pq;
|
||||
GetValue2(t, pq);
|
||||
return pq.vRotLog;
|
||||
}
|
||||
|
||||
Vec3 GetPosition (float fTime)
|
||||
{
|
||||
PQLog pq;
|
||||
GetValue2(fTime, pq);
|
||||
return pq.vPos;
|
||||
}
|
||||
|
||||
virtual Vec3 GetScale (float t)
|
||||
{
|
||||
return Vec3(1,1,1);
|
||||
}
|
||||
|
||||
// returns the start time
|
||||
float GetTimeStart ()
|
||||
{
|
||||
return float(m_arrTimes[0]);
|
||||
}
|
||||
|
||||
// returns the end time
|
||||
float GetTimeEnd()
|
||||
{
|
||||
assert (numKeys() > 0);
|
||||
return float(m_arrTimes[numKeys()-1]);
|
||||
}
|
||||
|
||||
ILog* GetLog() const;
|
||||
|
||||
size_t sizeofThis ()const;
|
||||
protected:
|
||||
TFixedArray<PQLog> m_arrKeys;
|
||||
TElementaryArray<int> m_arrTimes;
|
||||
|
||||
unsigned m_nControllerId;
|
||||
};
|
||||
|
||||
TYPEDEF_AUTOPTR(CControllerCryBone);
|
||||
|
||||
#endif
|
||||
|
||||
646
CryAnimation/ControllerManager.cpp
Normal file
646
CryAnimation/ControllerManager.cpp
Normal file
@@ -0,0 +1,646 @@
|
||||
// method definitions extracted from CryModel.*
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <StlUtils.h>
|
||||
#include <CryCompiledFile.h>
|
||||
#include "ControllerManager.h"
|
||||
#include "Controller.h"
|
||||
#include "ControllerCryBone.h"
|
||||
#include "ControllerPackedBSpline.h"
|
||||
#include "FileMapping.h"
|
||||
#include "ChunkFileReader.h"
|
||||
#include "CVars.h"
|
||||
#include "CryModelAnimationContainer.h"
|
||||
|
||||
#if defined(LINUX)
|
||||
# include "CryAnimationInfo.h"
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
#undef THIS_FILE
|
||||
static char THIS_FILE[] = __FILE__;
|
||||
#endif
|
||||
|
||||
CControllerManager::CControllerManager():
|
||||
#ifdef DEBUG_STD_CONTAINERS
|
||||
m_arrAnims ("CControllerManager.Anims"),
|
||||
m_arrAnimByFile ("CControllerManager.AnimByFile"),
|
||||
#endif
|
||||
m_nCachedSizeofThis (0),
|
||||
m_nLastCheckedUnloadCandidate(0)
|
||||
{
|
||||
// we reserve the place for future animations. The best performance will be
|
||||
// archived when this number is >= actual number of animations that can be used,
|
||||
// and not much greater
|
||||
m_arrAnims.reserve (0x100);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// finds the animation by name. Returns -1 if no animation was found
|
||||
// Returns the animation ID if it was found
|
||||
int CControllerManager::FindAnimationByFile (const string& sAnimFileName)
|
||||
{
|
||||
std::vector<int>::iterator it = std::lower_bound(m_arrAnimByFile.begin(), m_arrAnimByFile.end(), sAnimFileName.c_str(), AnimationIdPred(m_arrAnims));
|
||||
if (it != m_arrAnimByFile.end() && !stricmp(m_arrAnims[*it].strFileName.c_str(),sAnimFileName.c_str()))
|
||||
return *it;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
CControllerManager::~CControllerManager()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
for (AnimationArray::iterator it = m_arrAnims.begin(); it!= m_arrAnims.end(); ++it)
|
||||
assert (it->nRefCount == 0);
|
||||
#endif
|
||||
for(int nAttempt = 0; nAttempt < 40 && !m_setPendingAnimLoads.empty(); ++nAttempt)
|
||||
{
|
||||
g_GetLog()->LogToFile("\003%d asynch loading are pending, waiting...", m_setPendingAnimLoads.size());
|
||||
g_GetStreamEngine()->Wait(200,IStreamEngine::FLAGS_DISABLE_CALLBACK_TIME_QUOTA);
|
||||
}
|
||||
if (!m_setPendingAnimLoads.empty())
|
||||
g_GetLog()->LogWarning ("\002%d asynchronous operations are left; destructing the controller manager anyway, because they seem to be dangling.If a crash happens after this, perhaps this is because some async operation took _very_ long to complete.", m_setPendingAnimLoads.size());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// loads the animation with the specified name; if the animation is already loaded,
|
||||
// then just returns its id
|
||||
// The caller MUST TAKE CARE to bind the animation if it's already loaded before it has registered itself within this manager
|
||||
// RETURNS:
|
||||
// The global animation id.
|
||||
// -1 if the animation couldn't be loaded
|
||||
// SIDE EFFECT NOTES:
|
||||
// This function does not put up a warning in the case the file was not found.
|
||||
// The caller must do so. But if the file was found and was corrupted, more detailed
|
||||
// error information (like where the error occured etc.) may be put into the log
|
||||
int CControllerManager::StartLoadAnimation (const string& strFileName, float fScale, unsigned nFlags)
|
||||
{
|
||||
int nAnimId = FindAnimationByFile (strFileName);
|
||||
bool bRecordExists = (nAnimId >= 0);
|
||||
if (bRecordExists && m_arrAnims[nAnimId].IsLoaded())
|
||||
{
|
||||
// we have already such file loaded
|
||||
return nAnimId;
|
||||
}
|
||||
else
|
||||
{
|
||||
selfValidate();
|
||||
if (!bRecordExists)
|
||||
{
|
||||
// add a new animation structure that will hold the info about the new animation.
|
||||
// the new animation id is the index of this structure in the array
|
||||
nAnimId = (int)m_arrAnims.size();
|
||||
m_arrAnims.resize (nAnimId + 1);
|
||||
}
|
||||
|
||||
Animation& Anim = m_arrAnims[nAnimId];
|
||||
Anim.nFlags = nFlags;
|
||||
|
||||
if (!bRecordExists)
|
||||
Anim.strFileName = strFileName;
|
||||
|
||||
Anim.fScale = fScale;
|
||||
|
||||
if (!bRecordExists)// no need to insert if we reload the file
|
||||
{
|
||||
m_arrAnimByFile.insert (std::lower_bound(m_arrAnimByFile.begin(), m_arrAnimByFile.end(), nAnimId, AnimationIdPred(m_arrAnims)), nAnimId);
|
||||
}
|
||||
selfValidate();
|
||||
}
|
||||
|
||||
return nAnimId;
|
||||
}
|
||||
|
||||
// loads the animation info, if not loaded yet
|
||||
bool CControllerManager::LoadAnimationInfo(int nGlobalAnimId)
|
||||
{
|
||||
if ((unsigned)nGlobalAnimId < m_arrAnims.size())
|
||||
{
|
||||
if (m_arrAnims[nGlobalAnimId].IsInfoLoaded())
|
||||
return true;
|
||||
return LoadAnimation(nGlobalAnimId);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Loads the animation controllers into the existing animation record.
|
||||
// May be used to load the animation the first time, or reload it upon request.
|
||||
// Does nothing if there are already controls in the existing animation record.
|
||||
bool CControllerManager::LoadAnimation(int nAnimId)
|
||||
{
|
||||
FUNCTION_PROFILER( g_GetISystem(),PROFILE_ANIMATION );
|
||||
|
||||
Animation& Anim = m_arrAnims[nAnimId];
|
||||
if (Anim.IsLoaded())
|
||||
return true;
|
||||
|
||||
if (Anim.nFlags & Animation::FLAGS_LOAD_PENDING)
|
||||
{
|
||||
// we already have this animation being loaded
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned nFileSize = g_GetStreamEngine()->GetFileSize(Anim.strFileName.c_str());
|
||||
|
||||
if (!nFileSize) // no such file
|
||||
{
|
||||
if (!(Anim.nFlags & GlobalAnimation::FLAGS_DISABLE_LOAD_ERROR_LOG))
|
||||
g_GetLog()->LogError ("\003CControllerManager::LoadAnimation: file loading %s file not found", Anim.strFileName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Anim.IsInfoLoaded())
|
||||
{ // we can start loading asynchronously
|
||||
// this structure will keep the info until the completion routine is called
|
||||
PendingAnimLoad_AutoPtr pAnimLoad = new PendingAnimLoad;
|
||||
pAnimLoad->nAnimId = nAnimId;
|
||||
pAnimLoad->pFile = new CFileMapping;
|
||||
pAnimLoad->pFile->attach (malloc (nFileSize), nFileSize);
|
||||
pAnimLoad->nFrameId = g_nFrameID;
|
||||
|
||||
// these parameters tell that the file must be read directly into the file mapping object
|
||||
// the file mapping object will be passed to the chunk reader that will parse the data
|
||||
StreamReadParams params;
|
||||
params.dwUserData = (DWORD_PTR)(PendingAnimLoad*)pAnimLoad;
|
||||
params.nSize = nFileSize;
|
||||
params.pBuffer = pAnimLoad->pFile->getData();
|
||||
|
||||
// now we're prepared to read, register the pending operation and start reading
|
||||
m_setPendingAnimLoads.insert (pAnimLoad);
|
||||
// mark the animation as being loaded
|
||||
Anim.nFlags |= Animation::FLAGS_LOAD_PENDING;
|
||||
// we're not interested in the pointer to the read stream - we only need to know when it's finished
|
||||
pAnimLoad->pStream = g_GetStreamEngine()->StartRead("Animation", Anim.strFileName.c_str(), this, ¶ms);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the info hasn't still been loaded, we need to load synchronously
|
||||
// try to read the file
|
||||
CChunkFileReader Reader;
|
||||
if (!Reader.open (Anim.strFileName))
|
||||
{
|
||||
if (!(Anim.nFlags & GlobalAnimation::FLAGS_DISABLE_LOAD_ERROR_LOG))
|
||||
g_GetLog()->LogError ("\003CControllerManager::LoadAnimation: file loading %s, last error is: %s", Anim.strFileName.c_str(), Reader.getLastError());
|
||||
return false;
|
||||
}
|
||||
return LoadAnimation(nAnimId, &Reader);
|
||||
}
|
||||
}
|
||||
|
||||
void CControllerManager::StreamOnComplete (IReadStream* pStream, unsigned nError)
|
||||
{
|
||||
PendingAnimLoad_AutoPtr pAnimLoad = (PendingAnimLoad*)pStream->GetUserData();
|
||||
assert (m_setPendingAnimLoads.find (pAnimLoad) != m_setPendingAnimLoads.end());
|
||||
Animation& Anim = m_arrAnims[pAnimLoad->nAnimId];
|
||||
m_setPendingAnimLoads.erase(pAnimLoad);
|
||||
// flag the animation as being loaded
|
||||
Anim.nFlags &= ~Animation::FLAGS_LOAD_PENDING;
|
||||
if (nError)
|
||||
{
|
||||
#if defined(LINUX)
|
||||
g_GetLog()->LogError ("\003Asynchronous load of file %s has failed, error code 0x%X", Anim.strFileName.c_str(), nError);
|
||||
#else
|
||||
g_GetLog()->LogError ("\003Asynchronous load of file %s has failed (%d of %d bytes read), error code 0x%X", Anim.strFileName.c_str(), pStream->GetBytesRead(), pAnimLoad->pFile->getSize(), nError);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
CChunkFileReader Reader;
|
||||
if (!Reader.open (pAnimLoad->pFile))
|
||||
#if defined(LINUX)
|
||||
g_GetLog()->LogError ("\003Error: Asynchronous load of file %s has completed successfully, but the data can't be parsed. The last \"%s\".", Anim.strFileName.c_str(), Reader.getLastError());
|
||||
#else
|
||||
g_GetLog()->LogError ("\003Error: Asynchronous load of file %s has completed successfully (%d of expected %d bytes read), but the data can't be parsed. The last \"%s\".", Anim.strFileName.c_str(), pStream->GetBytesRead(), pAnimLoad->pFile->getSize(), Reader.getLastError());
|
||||
#endif
|
||||
else
|
||||
{
|
||||
if (LoadAnimation (pAnimLoad->nAnimId, &Reader))
|
||||
{
|
||||
++g_nAsyncAnimCounter;
|
||||
g_nAsyncAnimFrameDelays += g_nFrameID - pAnimLoad->nFrameId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// immediately load the given animation from the already opened reader
|
||||
bool CControllerManager::LoadAnimation (int nAnimId, CChunkFileReader* pReader)
|
||||
{
|
||||
FUNCTION_PROFILER( g_GetISystem(),PROFILE_ANIMATION );
|
||||
|
||||
Animation& Anim = m_arrAnims[nAnimId];
|
||||
|
||||
// check the file header for validity
|
||||
const FILE_HEADER& fh = pReader->getFileHeader();
|
||||
|
||||
if(fh.Version != AnimFileVersion || fh.FileType != FileType_Anim)
|
||||
{
|
||||
g_GetLog()->LogError ("\003CControllerManager::LoadAnimation: file version error or not an animation file: %s", Anim.strFileName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// prepare the array of controllers and the counter for them
|
||||
Anim.arrCtrls.resize(pReader->numChunksOfType (ChunkType_Controller));
|
||||
unsigned nController = 0;
|
||||
|
||||
m_nCachedSizeofThis = 0;
|
||||
// scan the chunks and load all controllers and time data into the animation structure
|
||||
for (int nChunk = 0; nChunk < pReader->numChunks (); ++nChunk)
|
||||
{
|
||||
// this is the chunk header in the chunk table at the end of the file
|
||||
const CHUNK_HEADER& chunkHeader = pReader->getChunkHeader(nChunk);
|
||||
// this is the chunk raw data, starts with the chunk header/descriptor structure
|
||||
const void* pChunk = pReader->getChunkData (nChunk);
|
||||
unsigned nChunkSize = pReader->getChunkSize(nChunk);
|
||||
|
||||
switch (chunkHeader.ChunkType)
|
||||
{
|
||||
case ChunkType_Controller:
|
||||
switch (chunkHeader.ChunkVersion)
|
||||
{
|
||||
case CONTROLLER_CHUNK_DESC_0826::VERSION:
|
||||
{
|
||||
// load and add a controller constructed from the controller chunk
|
||||
const CONTROLLER_CHUNK_DESC_0826* pCtrlChunk = static_cast<const CONTROLLER_CHUNK_DESC_0826*>(pChunk);
|
||||
IController_AutoPtr pController = LoadController (Anim.fScale, pCtrlChunk, nChunkSize);
|
||||
if (!pController)
|
||||
{
|
||||
g_GetLog()->LogError ("\002CControllerManager::LoadAnimation: error loading v826 controller: %s", Anim.strFileName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
assert (nController < Anim.arrCtrls.size());
|
||||
Anim.arrCtrls[nController++] = pController;
|
||||
}
|
||||
break;
|
||||
|
||||
case CONTROLLER_CHUNK_DESC_0827::VERSION:
|
||||
{
|
||||
const CONTROLLER_CHUNK_DESC_0827* pCtrlChunk = static_cast<const CONTROLLER_CHUNK_DESC_0827*>(pChunk);
|
||||
CControllerCryBone_AutoPtr pController = new CControllerCryBone ();
|
||||
if (pController->Load (pCtrlChunk, nChunkSize, Anim.fScale))
|
||||
{
|
||||
assert (nController < Anim.arrCtrls.size());
|
||||
Anim.arrCtrls[nController++] = static_cast<IController*>(pController);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_GetLog()->LogError ("\002CControllerManager::LoadAnimation: error loading v827 controller: %s", Anim.strFileName.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_GetLog()->LogError ("\003Unsupported controller chunk 0x%08X version 0x%08X in file %s. Please re-export the file.", chunkHeader.ChunkID, chunkHeader.ChunkVersion, Anim.strFileName.c_str());
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ChunkType_Timing:
|
||||
{
|
||||
// memorize the timing info
|
||||
const TIMING_CHUNK_DESC* pTimingChunk = static_cast<const TIMING_CHUNK_DESC*> (pChunk);
|
||||
Anim.nTicksPerFrame = pTimingChunk->TicksPerFrame;
|
||||
Anim.fSecsPerTick = pTimingChunk->SecsPerTick;
|
||||
Anim.rangeGlobal = pTimingChunk->global_range;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nController != Anim.arrCtrls.size())
|
||||
{
|
||||
g_GetLog()->LogError ("\003%d controllers (%d expected) loaded from file %s. Please re-export the file. The animations will be discarded.",
|
||||
nController, Anim.arrCtrls.size(), Anim.strFileName.c_str());
|
||||
}
|
||||
|
||||
std::sort(
|
||||
Anim.arrCtrls.begin(),
|
||||
Anim.arrCtrls.end(),
|
||||
AnimCtrlSortPred()
|
||||
);
|
||||
|
||||
Anim.OnInfoLoaded();
|
||||
FireAnimationGlobalLoad(nAnimId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// updates the animation from the chunk of AnimInfo
|
||||
bool CControllerManager::UpdateAnimation (int nGlobalAnimId, const CCFAnimInfo* pAnimInfo)
|
||||
{
|
||||
if ((unsigned)nGlobalAnimId >= m_arrAnims.size())
|
||||
return false;
|
||||
|
||||
GlobalAnimation& Anim = m_arrAnims[nGlobalAnimId];
|
||||
|
||||
#if !defined(LINUX)
|
||||
/* if (Anim.IsInfoLoaded())
|
||||
{
|
||||
assert (Anim.fSecsPerTick == pAnimInfo->fSecsPerTick);
|
||||
assert (Anim.nTicksPerFrame == pAnimInfo->nTicksPerFrame);
|
||||
assert (Anim.rangeGlobal.start == pAnimInfo->nRangeStart);
|
||||
assert (Anim.rangeGlobal.end == pAnimInfo->nRangeEnd);
|
||||
return true;
|
||||
}*/
|
||||
#endif
|
||||
|
||||
Anim.fSecsPerTick = pAnimInfo->fSecsPerTick;
|
||||
Anim.nTicksPerFrame = pAnimInfo->nTicksPerFrame;
|
||||
Anim.rangeGlobal.start = pAnimInfo->nRangeStart;
|
||||
Anim.rangeGlobal.end = pAnimInfo->nRangeEnd;
|
||||
Anim.nFlags |= pAnimInfo->nAnimFlags;
|
||||
Anim.OnInfoLoaded();
|
||||
|
||||
FireAnimationGlobalLoad(nGlobalAnimId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// notify everybody that the given animation OR its info has been just loaded
|
||||
void CControllerManager::FireAnimationGlobalLoad (int nAnimId)
|
||||
{
|
||||
for (std::vector<CryModelAnimationContainer*>::iterator it = m_arrClients.begin(); it != m_arrClients.end(); ++it)
|
||||
(*it)->OnAnimationGlobalLoad(nAnimId);
|
||||
}
|
||||
|
||||
// notifies the controller manager that another client uses the given animation.
|
||||
// these calls must be balanced with AnimationRelease() calls
|
||||
void CControllerManager::AnimationAddRef (int nGlobalAnimId, CryModelAnimationContainer* pClient)
|
||||
{
|
||||
assert ((unsigned)nGlobalAnimId< m_arrAnims.size());
|
||||
// the new client, though it might have received "OnLoad" events from this global animation,
|
||||
// might have missed it because it wasn't interested in them; now we re-send this event if needed
|
||||
m_arrAnims[nGlobalAnimId].AddRef();
|
||||
if (m_arrAnims[nGlobalAnimId].IsLoaded())
|
||||
pClient->OnAnimationGlobalLoad(nGlobalAnimId);
|
||||
}
|
||||
|
||||
// notifies the controller manager that this client doesn't use the given animation any more.
|
||||
// these calls must be balanced with AnimationAddRef() calls
|
||||
void CControllerManager::AnimationRelease (int nGlobalAnimId, CryModelAnimationContainer* pClient)
|
||||
{
|
||||
assert ((unsigned)nGlobalAnimId< m_arrAnims.size());
|
||||
// if the given client is still interested in unload events, it will receive them all anyway,
|
||||
// so we don't force anything but pure release. Normally during this call the client doesn't need
|
||||
// any information about the animation being released
|
||||
m_arrAnims[nGlobalAnimId].Release();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// returns the total number of animations hold in memory (for statistics)
|
||||
unsigned CControllerManager::NumAnimations()
|
||||
{
|
||||
return (unsigned)m_arrAnims.size();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Loads the controller from the chunk, returns the result in the autopointer
|
||||
// It is important that the return type is autoptr: it lets the procedure gracefully destruct
|
||||
// half-constructed animation in case of an error.
|
||||
IController_AutoPtr CControllerManager::LoadController (float fScale, const CONTROLLER_CHUNK_DESC_0826* pChunk, int nSize)
|
||||
{
|
||||
// different controllers are constructed depending on the representation of the raw data:
|
||||
// spline or original crybone
|
||||
IController_AutoPtr pController;
|
||||
switch (pChunk->type)
|
||||
{
|
||||
case CTRL_BSPLINE_2O:
|
||||
case CTRL_BSPLINE_1O:
|
||||
case CTRL_BSPLINE_2C:
|
||||
case CTRL_BSPLINE_1C:
|
||||
{
|
||||
// one of the fixed-point spline formats
|
||||
CControllerPackedBSpline_AutoPtr pCtrl = new CControllerPackedBSpline();
|
||||
if (pCtrl->Load(pChunk, nSize, fScale))
|
||||
pController = static_cast<IController*>(static_cast<CControllerPackedBSpline*>(pCtrl));
|
||||
}
|
||||
break;
|
||||
|
||||
case CTRL_CRYBONE:
|
||||
{
|
||||
// the old bone format
|
||||
CControllerCryBone_AutoPtr pCtrl = new CControllerCryBone ();
|
||||
if (pCtrl->Load(pChunk, fScale))
|
||||
pController = static_cast<IController*>(static_cast<CControllerCryBone*>(pCtrl));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return pController;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// finds controller with the given nControllerID among controller in the animation
|
||||
// identified by nGlobalAnimID
|
||||
IController* CControllerManager::GetController (int nGlobalAnimID, unsigned nControllerID)
|
||||
{
|
||||
Animation& Anim = m_arrAnims[nGlobalAnimID];
|
||||
return Anim.GetController(nControllerID);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// logs controller usage statistics. Used for debugging only
|
||||
void CControllerManager::LogUsageStats()
|
||||
{
|
||||
size_t nCtrlUnused = 0;
|
||||
size_t nCtrlTotal = 0;
|
||||
size_t nCtrlUsage = 0;
|
||||
AnimationArray::const_iterator it = m_arrAnims.begin(), itEnd = it + m_arrAnims.size();
|
||||
for (; it != itEnd; ++it)
|
||||
{
|
||||
const Animation& Anim = *it;
|
||||
nCtrlTotal += Anim.arrCtrls.size();
|
||||
Animation::ControllerArray::const_iterator itCtrl = Anim.arrCtrls.begin(), itCtrlEnd = itCtrl + Anim.arrCtrls.size();
|
||||
for (; itCtrl != itCtrlEnd; ++itCtrl)
|
||||
{
|
||||
IController* pCtrl = *itCtrl;
|
||||
if (pCtrl->NumRefs() == 1)
|
||||
++nCtrlUnused;
|
||||
else
|
||||
nCtrlUsage += pCtrl->NumRefs()-1;
|
||||
}
|
||||
}
|
||||
|
||||
g_GetLog()->LogToFile ("%u controllers, %u (%.1f percent) unused, %.2f average refs per used controller",
|
||||
nCtrlTotal, nCtrlUnused, (nCtrlUnused*100.0f/nCtrlTotal),
|
||||
float(nCtrlUsage)/(nCtrlTotal-nCtrlUnused));
|
||||
}
|
||||
|
||||
|
||||
// returns the structure describing the animation data, given the global anim id
|
||||
CControllerManager::Animation& CControllerManager::GetAnimation (int nAnimID)
|
||||
{
|
||||
if ((unsigned)nAnimID < m_arrAnims.size())
|
||||
return m_arrAnims[nAnimID];
|
||||
else
|
||||
{
|
||||
assert (0);
|
||||
static Animation dummy;
|
||||
return dummy;
|
||||
}
|
||||
}
|
||||
|
||||
void CControllerManager::DumpAnims()
|
||||
{
|
||||
// approximately calculating the size of the map
|
||||
std::vector<int>::const_iterator it;
|
||||
const std::vector<int>::const_iterator itBegin = m_arrAnimByFile.begin(), itEnd = m_arrAnimByFile.end();
|
||||
unsigned nMaxNameLength = 0;
|
||||
for (it=itBegin; it != itEnd; ++it)
|
||||
{
|
||||
unsigned nNameLength = (unsigned)m_arrAnims[*it].strFileName.length();
|
||||
if (nNameLength > nMaxNameLength)
|
||||
nMaxNameLength = nNameLength;
|
||||
}
|
||||
|
||||
nMaxNameLength += 2;
|
||||
|
||||
g_GetLog()->LogToFile ("\001%*s kbytes started ticks", nMaxNameLength, "Animation Memory Usage Dump");
|
||||
g_GetLog()->LogToConsole ("\001%*s kbytes started ticks", nMaxNameLength, "Animation Memory Usage Dump");
|
||||
|
||||
// the size of the array of controllers
|
||||
size_t nSize = 0;
|
||||
|
||||
typedef std::multimap<size_t, int> SizeMap;
|
||||
SizeMap mapSizes;
|
||||
for (int nAnim = 0; nAnim < (int)m_arrAnims.size(); ++nAnim)
|
||||
{
|
||||
size_t nSizeAnimation = m_arrAnims[nAnim].sizeofThis();
|
||||
nSize += nSizeAnimation;
|
||||
mapSizes.insert (SizeMap::value_type(nSizeAnimation, nAnim));
|
||||
}
|
||||
|
||||
for (SizeMap::reverse_iterator itSize = mapSizes.rbegin(); itSize != mapSizes.rend(); ++itSize)
|
||||
{
|
||||
int nAnim = itSize->second;
|
||||
Animation& anim = m_arrAnims[nAnim];
|
||||
|
||||
g_GetLog()->LogToFile ("\001%*s : %6.1f%10u%10u%s%s %d refs", nMaxNameLength, anim.strFileName.c_str(), itSize->first/1024.0f, anim.nTickCount, anim.nStartCount, anim.IsLoaded()?"":anim.IsInfoLoaded()?",Unloaded":",Not Loaded",anim.nRefCount?"":",Not Used:", anim.nRefCount);
|
||||
g_GetLog()->LogToConsole ("\001%*s : %6.1f%10u%10u%s%s %d refs", nMaxNameLength, anim.strFileName.c_str(), itSize->first/1024.0f, anim.nTickCount, anim.nStartCount, anim.IsLoaded()?"":anim.IsInfoLoaded()?",Unloaded":",Not Loaded",anim.nRefCount?"":",Not Used:", anim.nRefCount);
|
||||
}
|
||||
|
||||
g_GetLog()->LogToFile ("\001%*s : %6.1f", nMaxNameLength, "TOTAL", nSize / 1024.0f);
|
||||
g_GetLog()->LogToConsole ("\001%*s : %6.1f", nMaxNameLength, "TOTAL", nSize / 1024.0f);
|
||||
}
|
||||
|
||||
// unreferences the controllers and makes the animation unloaded
|
||||
// before this operation, all bones must be unbound
|
||||
// returns true if the animation was really unloaded (if it had been unloaded before, returns false)
|
||||
bool CControllerManager::UnloadAnimation (int nGlobAnimId)
|
||||
{
|
||||
FUNCTION_PROFILER( g_GetISystem(),PROFILE_ANIMATION );
|
||||
|
||||
if ((unsigned)nGlobAnimId>= m_arrAnims.size())
|
||||
return false;
|
||||
|
||||
if (m_arrAnims[nGlobAnimId].arrCtrls.empty())
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_nCachedSizeofThis = 0;
|
||||
for (std::vector<CryModelAnimationContainer*>::iterator it = m_arrClients.begin(); it!= m_arrClients.end(); ++it)
|
||||
(*it)->OnAnimationGlobalUnload (nGlobAnimId);
|
||||
#if !defined(LINUX)
|
||||
assert (m_arrAnims[nGlobAnimId].MaxControllerRefCount()==1);
|
||||
#endif
|
||||
m_arrAnims[nGlobAnimId].arrCtrls.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CControllerManager::OnStartAnimation (int nGlobalAnimId)
|
||||
{
|
||||
assert ((unsigned)nGlobalAnimId < m_arrAnims.size());
|
||||
LoadAnimation(nGlobalAnimId);
|
||||
m_arrAnims[nGlobalAnimId].OnStart();
|
||||
}
|
||||
|
||||
void CControllerManager::OnTickAnimation(int nGlobalAnimId)
|
||||
{
|
||||
assert ((unsigned)nGlobalAnimId < m_arrAnims.size());
|
||||
LoadAnimation(nGlobalAnimId);
|
||||
m_arrAnims[nGlobalAnimId].OnTick();
|
||||
}
|
||||
|
||||
void CControllerManager::OnApplyAnimation(int nGlobalAnimId)
|
||||
{
|
||||
assert ((unsigned)nGlobalAnimId < m_arrAnims.size());
|
||||
LoadAnimation(nGlobalAnimId);
|
||||
m_arrAnims[nGlobalAnimId].OnApply();
|
||||
}
|
||||
|
||||
// puts the size of the whole subsystem into this sizer object, classified,
|
||||
// according to the flags set in the sizer
|
||||
void CControllerManager::GetSize(class ICrySizer* pSizer)
|
||||
{
|
||||
#if ENABLE_GET_MEMORY_USAGE
|
||||
SIZER_SUBCOMPONENT_NAME(pSizer, "Keys");
|
||||
|
||||
size_t nSize = sizeof(*this);
|
||||
if (m_nCachedSizeofThis)
|
||||
nSize = m_nCachedSizeofThis;
|
||||
else
|
||||
{
|
||||
// approximately calculating the size of the map
|
||||
unsigned nMaxNameLength = 0;
|
||||
nSize += sizeofArray (m_arrAnimByFile);
|
||||
for (AnimationArray::const_iterator it = m_arrAnims.begin(); it != m_arrAnims.end(); ++it)
|
||||
nSize += it->sizeofThis();
|
||||
|
||||
m_nCachedSizeofThis = nSize;
|
||||
}
|
||||
pSizer->AddObject(this, nSize);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t CControllerManager::Animation::sizeofThis ()const
|
||||
{
|
||||
size_t nSize = sizeof(*this) + sizeofArray (arrCtrls)+ strFileName.capacity() + 1;
|
||||
ControllerArray::const_iterator it = arrCtrls.begin(), itEnd = it + arrCtrls.size();
|
||||
for (; it != itEnd; ++it)
|
||||
nSize += (*it)->sizeofThis();
|
||||
return nSize;
|
||||
}
|
||||
|
||||
void CControllerManager::selfValidate()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
assert (m_arrAnimByFile.size()==m_arrAnims.size());
|
||||
for (int i = 0; i < (int)m_arrAnimByFile.size()-1; ++i)
|
||||
assert (stricmp(m_arrAnims[m_arrAnimByFile[i]].strFileName.c_str(), m_arrAnims[m_arrAnimByFile[i+1]].strFileName.c_str()) < 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CControllerManager::Update()
|
||||
{
|
||||
if (g_GetCVars()->ca_AnimationUnloadDelay() < 30)
|
||||
return;
|
||||
|
||||
if (m_nLastCheckedUnloadCandidate < m_arrAnims.size())
|
||||
{
|
||||
Animation &GlobalAnim = m_arrAnims[m_nLastCheckedUnloadCandidate];
|
||||
if (
|
||||
GlobalAnim.IsAutoUnload() &&
|
||||
GlobalAnim.nLastAccessFrameId + g_GetCVars()->ca_AnimationUnloadDelay() < g_nFrameID
|
||||
)
|
||||
{
|
||||
if (UnloadAnimation(m_nLastCheckedUnloadCandidate))
|
||||
if (g_GetCVars()->ca_Debug())
|
||||
g_GetLog()->LogToFile ("\004Unloaded animation %s", GlobalAnim.strFileName.c_str());
|
||||
}
|
||||
++m_nLastCheckedUnloadCandidate;
|
||||
}
|
||||
else
|
||||
m_nLastCheckedUnloadCandidate = 0;
|
||||
}
|
||||
189
CryAnimation/ControllerManager.h
Normal file
189
CryAnimation/ControllerManager.h
Normal file
@@ -0,0 +1,189 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Sergiy Migdalskiy
|
||||
//
|
||||
// Notes:
|
||||
// CControllerManager class declaration extracted from File:CryModelState.h
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _CRYTEK_CONTROLLER_MANAGER_HEADER_
|
||||
#define _CRYTEK_CONTROLLER_MANAGER_HEADER_
|
||||
|
||||
#include "Controller.h"
|
||||
#include "CryAnimationInfo.h"
|
||||
#include "FileMapping.h"
|
||||
#include "IStreamEngine.h"
|
||||
|
||||
class CryModelAnimationContainer;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// class CControllerManager
|
||||
// Responsible for creation of multiple animations, subsequently bind to the character bones
|
||||
// There is one instance of this class per a game.
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
class CControllerManager : public IStreamCallback
|
||||
{
|
||||
public:
|
||||
// array of animations, each animation having its name and id
|
||||
// Animation is an array of controllers. Animation id is an index in the array
|
||||
|
||||
typedef GlobalAnimation Animation;
|
||||
|
||||
struct AnimationIdPred
|
||||
{
|
||||
const std::vector<Animation>& m_arrAnims;
|
||||
AnimationIdPred(const std::vector<Animation>& arrAnims):
|
||||
m_arrAnims(arrAnims)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator () (int left, int right)const
|
||||
{
|
||||
return stricmp(m_arrAnims[left].strFileName.c_str(), m_arrAnims[right].strFileName.c_str()) < 0;
|
||||
}
|
||||
bool operator () (int left, const char* right)const
|
||||
{
|
||||
return stricmp(m_arrAnims[left].strFileName.c_str(), right)<0;
|
||||
}
|
||||
bool operator () (const char* left, int right)const
|
||||
{
|
||||
return stricmp(left, m_arrAnims[right].strFileName.c_str())<0;
|
||||
}
|
||||
};
|
||||
|
||||
// returns the structure describing the animation data, given the global anim id
|
||||
Animation& GetAnimation (int nAnimID);
|
||||
|
||||
CControllerManager();
|
||||
|
||||
// loads the animation with the specified name; if the animation is already loaded,
|
||||
// then just returns its id
|
||||
// The caller MUST TAKE CARE to bind the animation if it's already loaded before it has registered itself within this manager
|
||||
int StartLoadAnimation (const string& strFileName, float fScale, unsigned nFlags = Animation::FLAGS_DEFAULT_FLAGS);
|
||||
|
||||
// unreferences the controllers and makes the animation unloaded
|
||||
// before this operation, all bones must be unbound
|
||||
bool UnloadAnimation (int nGlobAnimId);
|
||||
|
||||
// loads existing animation record, returns false on error
|
||||
bool LoadAnimation(int nGlobalAnimId);
|
||||
|
||||
// loads the animation info, if not loaded yet
|
||||
bool LoadAnimationInfo(int nGlobalAnimId);
|
||||
|
||||
// updates the animation from the chunk of AnimInfo
|
||||
bool UpdateAnimation (int nGlobalAnimId, const struct CCFAnimInfo* pAnimInfo);
|
||||
|
||||
// notifies the controller manager that another client uses the given animation.
|
||||
// these calls must be balanced with AnimationRelease() calls
|
||||
void AnimationAddRef (int nGlobalAnimId, CryModelAnimationContainer* pClient);
|
||||
|
||||
|
||||
// notifies the controller manager that this client doesn't use the given animation any more.
|
||||
// these calls must be balanced with AnimationAddRef() calls
|
||||
void AnimationRelease (int nGlobalAnimId, CryModelAnimationContainer* pClient);
|
||||
|
||||
void OnStartAnimation (int nGlobalAnimId);
|
||||
void OnTickAnimation(int nGlobalAnimId);
|
||||
void OnApplyAnimation(int nGlobalAnimId);
|
||||
|
||||
// returns the total number of animations hold in memory (for statistics)
|
||||
unsigned NumAnimations();
|
||||
|
||||
// finds controller with the given nControllerID among controller in the animation
|
||||
// identified by nGlobalAnimID
|
||||
IController* GetController (int nGlobalAnimID, unsigned nControllerID);
|
||||
|
||||
~CControllerManager();
|
||||
|
||||
// logs controller usage statistics
|
||||
void LogUsageStats();
|
||||
|
||||
// puts the size of the whole subsystem into this sizer object, classified,
|
||||
// according to the flags set in the sizer
|
||||
void GetSize(class ICrySizer* pSizer);
|
||||
|
||||
// dumps the used animations
|
||||
void DumpAnims();
|
||||
|
||||
void Update();
|
||||
|
||||
void Register (CryModelAnimationContainer* pClient)
|
||||
{
|
||||
m_arrClients.insert (std::lower_bound(m_arrClients.begin(), m_arrClients.end(), pClient), pClient);
|
||||
}
|
||||
void Unregister (CryModelAnimationContainer* pClient)
|
||||
{
|
||||
std::vector<CryModelAnimationContainer*>::iterator it = std::lower_bound(m_arrClients.begin(), m_arrClients.end(), pClient);
|
||||
if(it != m_arrClients.end() && *it == pClient)
|
||||
m_arrClients.erase (it);
|
||||
else
|
||||
assert (0); // the unregistered client tries to unregister
|
||||
}
|
||||
|
||||
// finds the animation by name. Returns -1 if no animation was found
|
||||
// Returns the animation ID if it was found
|
||||
int FindAnimationByFile(const string& sAnimFileName);
|
||||
|
||||
protected:
|
||||
// notifies all clients that the animation or its info has been loaded
|
||||
void FireAnimationGlobalLoad (int nAnimId);
|
||||
|
||||
// immediately load the given animation from the already opened reader
|
||||
bool LoadAnimation(int nAnimId, class CChunkFileReader* pReader);
|
||||
|
||||
// these two are called back when an asynchronous IO operation has finished
|
||||
void StreamOnComplete (IReadStream* pStream, unsigned nError);
|
||||
|
||||
// this structure describes the pending animation load request: it exists as long as the animation
|
||||
// is being loaded (asynchronously)
|
||||
struct PendingAnimLoad:public _reference_target_t
|
||||
{
|
||||
public:
|
||||
PendingAnimLoad (int animId){nAnimId = animId;}
|
||||
PendingAnimLoad (){}
|
||||
// the data that's being loaded
|
||||
CFileMapping_AutoPtr pFile;
|
||||
// the animation for which the data is loaded
|
||||
int nAnimId;
|
||||
// the frame at which the animation load was started
|
||||
int nFrameId;
|
||||
// the stream that loads the data
|
||||
IReadStreamPtr pStream;
|
||||
};
|
||||
TYPEDEF_AUTOPTR(PendingAnimLoad);
|
||||
|
||||
// the set of pending animation load requests; they get deleted as soon as the IO operation is completed
|
||||
std::set<PendingAnimLoad_AutoPtr> m_setPendingAnimLoads;
|
||||
|
||||
// Loads the controller from the chunk, returns the result in the autopointer
|
||||
// It is important that the return type is autoptr: it lets the procedure gracefully destruct
|
||||
// half-constructed animation in case of an error.
|
||||
IController_AutoPtr LoadController(float fScale, const CONTROLLER_CHUNK_DESC_0826*pChunk, int nSize);
|
||||
|
||||
// array of animations
|
||||
// since the number of animations dynamically changes and STL vector has good balanced
|
||||
// reallocation properties, we use it here
|
||||
typedef std::vector<Animation> AnimationArray;
|
||||
AnimationArray m_arrAnims;
|
||||
|
||||
// index array for the animation indices sorted by file name
|
||||
std::vector<int>m_arrAnimByFile;
|
||||
|
||||
void selfValidate();
|
||||
|
||||
size_t m_nCachedSizeofThis;
|
||||
|
||||
// the sorted list of pointers to the clients that use the services of this manager
|
||||
std::vector<CryModelAnimationContainer*> m_arrClients;
|
||||
|
||||
// this scans through all animations cyclicly each frame and checks which ones should be unloaded
|
||||
// due to not being used
|
||||
unsigned m_nLastCheckedUnloadCandidate;
|
||||
};
|
||||
|
||||
#endif
|
||||
138
CryAnimation/ControllerPackedBSpline.cpp
Normal file
138
CryAnimation/ControllerPackedBSpline.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
// Implementation of Packed BSpline Controller
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "Controller.h"
|
||||
#include "ControllerPackedBSpline.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Loads (initializes) controller from the given chunk. The chunk descriptor is followed by the
|
||||
// chunk data immediately. Returns true if successful
|
||||
// PARAMETERS:
|
||||
// pChunk - the header of the chunk to load data from. This is the start of the chunk in memory-mapped file normally.
|
||||
// nSize - size of the chunk in bytes, including the header structure
|
||||
// scale - factor to scale the positional data after successful load
|
||||
// pILog - ILog
|
||||
// RETURNS:
|
||||
// true if successfully loaded the controller, false otherwise (controller is not ready)
|
||||
bool CControllerPackedBSpline::Load (const CONTROLLER_CHUNK_DESC_0826* pChunk, int nSize, float scale)
|
||||
{
|
||||
// create one of the possible representations of packed spline, depending on the
|
||||
// format of the chunk: either 1- or 2-byte fixed point numbers and either closed or open spline
|
||||
switch (pChunk->type)
|
||||
{
|
||||
case CTRL_BSPLINE_1C:
|
||||
m_pRot = new TBSplineVec3dPacked<false,unsigned char>();
|
||||
m_pPos = new TBSplineVec3dPacked<false,unsigned char>();
|
||||
break;
|
||||
case CTRL_BSPLINE_2C:
|
||||
m_pRot = new TBSplineVec3dPacked<false,unsigned short>();
|
||||
m_pPos = new TBSplineVec3dPacked<false,unsigned short>();
|
||||
break;
|
||||
case CTRL_BSPLINE_1O:
|
||||
m_pRot = new TBSplineVec3dPacked<true,unsigned char>();
|
||||
m_pPos = new TBSplineVec3dPacked<true,unsigned char>();
|
||||
break;
|
||||
case CTRL_BSPLINE_2O:
|
||||
m_pRot = new TBSplineVec3dPacked<true,unsigned short>();
|
||||
m_pPos = new TBSplineVec3dPacked<true,unsigned short>();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
|
||||
// The chunk must be at least big enough to contain the header structure
|
||||
if (nSize < sizeof (CONTROLLER_CHUNK_DESC_0826))
|
||||
return false;
|
||||
|
||||
// controller identification will be subsequently used for bone binding
|
||||
m_nControllerId = pChunk->nControllerId;
|
||||
|
||||
//
|
||||
if(pChunk->chdr.ChunkType != ChunkType_Controller || pChunk->chdr.ChunkVersion != CONTROLLER_CHUNK_DESC_0826::VERSION)
|
||||
{
|
||||
GetLog()->LogToFile("CControllerPackedBSpline::Load: File version error");
|
||||
return false;
|
||||
}
|
||||
|
||||
// go to the positional raw data subchunk
|
||||
char* pRawData = (char*)(pChunk+1);
|
||||
// remaining chunk piece size
|
||||
int nRawDataSize = nSize - sizeof (CONTROLLER_CHUNK_DESC_0826);
|
||||
|
||||
// try to construct the position spline from the raw data
|
||||
int nPosDataSize = m_pPos->unpack (pRawData, nRawDataSize);
|
||||
if (!nPosDataSize)
|
||||
return false; // couldn't construct
|
||||
|
||||
// the positional data has been successfully constructed
|
||||
// now scale it by the given factor
|
||||
m_pPos->scale (scale);
|
||||
|
||||
// proceed with the rotation subchunk
|
||||
nRawDataSize -= nPosDataSize;
|
||||
pRawData += nPosDataSize;
|
||||
|
||||
int nRotDataSize = m_pRot->unpack (pRawData, nRawDataSize);
|
||||
if (!nRotDataSize)
|
||||
return false;
|
||||
|
||||
nRawDataSize -= nRotDataSize;
|
||||
pRawData += nRotDataSize;
|
||||
|
||||
if (nRawDataSize != 0)
|
||||
{
|
||||
// the chunk loaded ok, but not all of its data has been used
|
||||
GetLog()->LogToFile("CControllerPackedBSpline::Load: %d extra bytes at hte end of the chunk", nRawDataSize );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// returns position of the controller at the given time
|
||||
CryQuat CControllerPackedBSpline::GetOrientation (float t)
|
||||
{
|
||||
|
||||
return exp ( quaternionf(0,CControllerPackedBSpline::GetOrientation2(t)) );
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// returns position of the controller at the given time
|
||||
Vec3 CControllerPackedBSpline::GetPosition (float t)
|
||||
{
|
||||
return m_pPos->getValue (t/*/BSplineKnots::g_nStdTicksPerSecond*/);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// retrieves the position and orientation within one call
|
||||
// may be optimal in some applications
|
||||
void CControllerPackedBSpline::GetValue (float t, CryQuat& q, Vec3 &p)
|
||||
{
|
||||
q = GetOrientation(t);
|
||||
p = GetPosition(t);
|
||||
}
|
||||
|
||||
// retrieves the position and orientation (in the logarithmic space, i.e. instead of quaternion, its logarithm is returned)
|
||||
// may be optimal for motion interpolation
|
||||
void CControllerPackedBSpline::GetValue2 (float t, PQLog& pq)
|
||||
{
|
||||
pq.vPos = GetPosition(t);
|
||||
pq.vRotLog = GetOrientation2(t);
|
||||
}
|
||||
|
||||
// returns the orientation of the controller at the given time, in logarithmic space
|
||||
Vec3 CControllerPackedBSpline::GetOrientation2(float t)
|
||||
{
|
||||
return m_pRot->getValue (t/*/BSplineKnots::g_nStdTicksPerSecond*/);
|
||||
}
|
||||
|
||||
ILog* CControllerPackedBSpline::GetLog()const
|
||||
{
|
||||
return g_GetLog();
|
||||
}
|
||||
|
||||
|
||||
size_t CControllerPackedBSpline::sizeofThis ()const
|
||||
{
|
||||
return sizeof(*this) + m_pPos->sizeofThis() + m_pRot->sizeofThis();
|
||||
}
|
||||
85
CryAnimation/ControllerPackedBSpline.h
Normal file
85
CryAnimation/ControllerPackedBSpline.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Sergiy Migdalskiy
|
||||
//
|
||||
// Notes:
|
||||
// CControllerPackedBSpline class declaration
|
||||
// See the CControllerPackedBSpline comment for more info
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _CRYTEK_CONTROLLER_PACKED_BSPLINE_HEADER_
|
||||
#define _CRYTEK_CONTROLLER_PACKED_BSPLINE_HEADER_
|
||||
|
||||
#include "BSplineVec3dPacked.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// class CControllerPackedBSpline
|
||||
// Implementation of IController interface (see File:Controller.h)
|
||||
// Controller implementing the packed representation of BSpline, exported
|
||||
// from the Motion Optimizer utility
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
class CControllerPackedBSpline: public IController
|
||||
{
|
||||
public:
|
||||
// Loads (initializes) controller from the given chunk. The chunk descriptor is followed by the
|
||||
// chunk data immediately. Returns true if successful
|
||||
bool Load(const CONTROLLER_CHUNK_DESC_0826* pChunk, int nSize, float scale);
|
||||
|
||||
// each controller has an ID, by which it is identifiable
|
||||
unsigned GetID () const {return m_nControllerId;}
|
||||
|
||||
// returns orientation of the controller at the given time
|
||||
CryQuat GetOrientation (float t);
|
||||
|
||||
// returns the orientation of the controller at the given time, in logarithmic space
|
||||
Vec3 GetOrientation2(float t);
|
||||
|
||||
// returns position of the controller at the given time
|
||||
Vec3 GetPosition (float t);
|
||||
|
||||
// returns scale of the controller at the given time
|
||||
Vec3 GetScale (float t)
|
||||
{
|
||||
return Vec3(1,1,1);
|
||||
}
|
||||
|
||||
// retrieves the position and orientation within one call
|
||||
// may be optimal in some applications
|
||||
void GetValue (float t, CryQuat& q, Vec3 &p);
|
||||
|
||||
// retrieves the position and orientation (in the logarithmic space, i.e. instead of quaternion, its logarithm is returned)
|
||||
// may be optimal for motion interpolation
|
||||
void GetValue2 (float t, PQLog& pq);
|
||||
|
||||
// returns the start time
|
||||
virtual float GetTimeStart ()
|
||||
{
|
||||
return min(m_pPos->getTimeMin(), m_pRot->getTimeMin());
|
||||
}
|
||||
|
||||
// returns the end time
|
||||
virtual float GetTimeEnd()
|
||||
{
|
||||
return max(m_pPos->getTimeMax(), m_pRot->getTimeMax());
|
||||
}
|
||||
|
||||
ILog* GetLog()const;
|
||||
|
||||
size_t sizeofThis ()const;
|
||||
protected:
|
||||
// Controller ID, used for identification purposes (bones are bound to controllers using their IDs
|
||||
unsigned m_nControllerId;
|
||||
|
||||
// packed splines for position and orientation.
|
||||
// orientational data is represented by logarithmic mapping of the quaternion.
|
||||
// may be one of 4 formats of packed splines.
|
||||
IBSpline3Packed_AutoPtr m_pPos, m_pRot;
|
||||
};
|
||||
|
||||
TYPEDEF_AUTOPTR(CControllerPackedBSpline);
|
||||
|
||||
#endif
|
||||
171
CryAnimation/ControllerTCB.cpp
Normal file
171
CryAnimation/ControllerTCB.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Engine Source File.
|
||||
// Copyright (C), Crytek Studios, 2002.
|
||||
// -------------------------------------------------------------------------
|
||||
// File name: controllertcb.cpp
|
||||
// Version: v1.00
|
||||
// Created: 12/11/2002 by Timur.
|
||||
// Compilers: Visual Studio.NET
|
||||
// Description:
|
||||
// -------------------------------------------------------------------------
|
||||
// History:
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "ControllerTCB.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// returns position of the controller at the given time
|
||||
CryQuat CControllerTCB::GetOrientation (float t)
|
||||
{
|
||||
return CryQuat(1,0,0,0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// returns position of the controller at the given time
|
||||
Vec3d CControllerTCB::GetPosition (float t)
|
||||
{
|
||||
return Vec3d(0,0,0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// returns scale of the controller at the given time
|
||||
Vec3d CControllerTCB::GetScale (float t)
|
||||
{
|
||||
return Vec3d(1,1,1);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// retrieves the position and orientation within one call
|
||||
// may be optimal in some applications
|
||||
void CControllerTCB::GetValue (float t, CryQuat& q, Vec3d &p)
|
||||
{
|
||||
q = GetOrientation(t);
|
||||
p = GetPosition(t);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ILog* CControllerTCB::GetLog()const
|
||||
{
|
||||
return g_GetLog();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CControllerTCBVec3 spline implementation.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CControllerTCBVec3::Load( const CONTROLLER_CHUNK_DESC_0826* pChunk,float secsPerTick )
|
||||
{
|
||||
CryTCB3Key *pKeys = (CryTCB3Key*)(pChunk+1);
|
||||
|
||||
int nkeys = pChunk->nKeys;
|
||||
m_spline.resize(nkeys);
|
||||
for (int i = 0; i < nkeys; i++)
|
||||
{
|
||||
CryTCB3Key &ck = pKeys[i];
|
||||
TCBSpline<Vec3d>::key_type &key = m_spline.key(i);
|
||||
key.flags = 0;
|
||||
key.time = ck.time * secsPerTick;
|
||||
key.value = ck.val;
|
||||
key.tens = ck.t;
|
||||
key.cont = ck.c;
|
||||
key.bias = ck.b;
|
||||
key.easefrom = ck.eout;
|
||||
key.easeto = ck.ein;
|
||||
}
|
||||
|
||||
if (pChunk->nFlags & CTRL_ORT_CYCLE)
|
||||
m_spline.ORT( TCBSpline<Vec3d>::ORT_CYCLE );
|
||||
else if (pChunk->nFlags & CTRL_ORT_LOOP)
|
||||
m_spline.ORT( TCBSpline<Vec3d>::ORT_LOOP );
|
||||
|
||||
// Precompute spline tangents.
|
||||
m_spline.comp_deriv();
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
Vec3d CControllerTCBVec3::GetPosition( float t )
|
||||
{
|
||||
Vec3d val;
|
||||
m_spline.interpolate( t,val );
|
||||
// Position controller from Max must be scalled 100 times down.
|
||||
return val * (1.0f/100.0f);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
Vec3d CControllerTCBVec3::GetScale( float t )
|
||||
{
|
||||
// equialent to position.
|
||||
Vec3d val;
|
||||
m_spline.interpolate( t,val );
|
||||
return val;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CControllerTCBVec3::IsLooping() const
|
||||
{
|
||||
if (m_spline.ORT() > TCBSpline<Vec3d>::ORT_CONSTANT)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CControllerTCBQuat spline implementation.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CControllerTCBQuat::Load( const CONTROLLER_CHUNK_DESC_0826* pChunk,float secsPerTick )
|
||||
{
|
||||
CryTCBQKey *pKeys = (CryTCBQKey*)(pChunk+1);
|
||||
|
||||
int nkeys = pChunk->nKeys;
|
||||
m_spline.resize(nkeys);
|
||||
for (int i = 0; i < nkeys; i++)
|
||||
{
|
||||
CryTCBQKey &ck = pKeys[i];
|
||||
TCBAngAxisKey &key = m_spline.key(i);
|
||||
key.flags = 0;
|
||||
key.time = ck.time * secsPerTick;
|
||||
|
||||
// TCBAngAxisSpline stores relative rotation angle-axis.
|
||||
//@FIXME rotation direction somehow differ from Max.
|
||||
// also invert direction of rotation by negating axis component.
|
||||
key.angle = ck.val.w;
|
||||
// key.axis = -Vec3d(ck.val.v.x,ck.val.v.y,ck.val.v.z);
|
||||
key.axis = Vec3d(ck.val.v.x,ck.val.v.y,ck.val.v.z);
|
||||
|
||||
key.tens = ck.t;
|
||||
key.cont = ck.c;
|
||||
key.bias = ck.b;
|
||||
key.easefrom = ck.eout;
|
||||
key.easeto = ck.ein;
|
||||
}
|
||||
|
||||
if (pChunk->nFlags & CTRL_ORT_CYCLE)
|
||||
m_spline.ORT( TCBAngleAxisSpline::ORT_CYCLE );
|
||||
else if (pChunk->nFlags & CTRL_ORT_LOOP)
|
||||
m_spline.ORT( TCBAngleAxisSpline::ORT_LOOP );
|
||||
// Precompute spline tangents.
|
||||
m_spline.comp_deriv();
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CryQuat CControllerTCBQuat::GetOrientation( float t )
|
||||
{
|
||||
CryQuat q;
|
||||
m_spline.interpolate( t,q );
|
||||
return q;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CControllerTCBQuat::IsLooping() const
|
||||
{
|
||||
if (m_spline.ORT() > TCBAngleAxisSpline::ORT_CONSTANT)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
131
CryAnimation/ControllerTCB.h
Normal file
131
CryAnimation/ControllerTCB.h
Normal file
@@ -0,0 +1,131 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Engine Source File.
|
||||
// Copyright (C), Crytek Studios, 2002.
|
||||
// -------------------------------------------------------------------------
|
||||
// File name: controllertcb.h
|
||||
// Version: v1.00
|
||||
// Created: 12/11/2002 by Timur.
|
||||
// Compilers: Visual Studio.NET
|
||||
// Description: TCB controller implementation.
|
||||
// -------------------------------------------------------------------------
|
||||
// History:
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef __controllertcb_h__
|
||||
#define __controllertcb_h__
|
||||
#pragma once
|
||||
|
||||
#include "Controller.h"
|
||||
#include "TCBSpline.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// class CControllerTCB
|
||||
// Implementation of IController interface (see File:Controller.h)
|
||||
// Controller implementing the TCB (Kochanek-Bartles Hermit spline).
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
class CControllerTCB: public IController
|
||||
{
|
||||
public:
|
||||
// each controller has an ID, by which it is identifiable
|
||||
unsigned GetID () const {return m_nControllerId;}
|
||||
|
||||
// returns orientation of the controller at the given time
|
||||
CryQuat GetOrientation (float t);
|
||||
|
||||
// returns the orientation of the controller at the given time, in logarithmic space
|
||||
Vec3 GetOrientation2(float t) { return Vec3(0,0,0); };
|
||||
|
||||
// returns position of the controller at the given time
|
||||
Vec3 GetPosition (float t);
|
||||
|
||||
// returns scale of the controller at the given time
|
||||
Vec3 GetScale (float t);
|
||||
|
||||
// retrieves the position and orientation within one call
|
||||
// may be optimal in some applications
|
||||
void GetValue (float t, CryQuat& q, Vec3 &p);
|
||||
|
||||
// ignore.
|
||||
void GetValue2 (float t, PQLog& pq) {};
|
||||
|
||||
// returns the start time
|
||||
virtual float GetTimeStart ()
|
||||
{
|
||||
return m_timeStart;
|
||||
}
|
||||
|
||||
// returns the end time
|
||||
virtual float GetTimeEnd()
|
||||
{
|
||||
return m_timeEnd;
|
||||
}
|
||||
|
||||
ILog* GetLog()const;
|
||||
|
||||
size_t sizeofThis() const { return sizeof(*this); }
|
||||
protected:
|
||||
// Controller ID, used for identification purposes (bones are bound to controllers using their IDs
|
||||
unsigned m_nControllerId;
|
||||
|
||||
float m_timeStart;
|
||||
float m_timeEnd;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TCB Controller implementation for vector.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CControllerTCBVec3 : public CControllerTCB
|
||||
{
|
||||
public:
|
||||
// Loads (initializes) controller from the given chunk. The chunk descriptor is followed by the
|
||||
// chunk data immediately. Returns true if successful
|
||||
bool Load( const CONTROLLER_CHUNK_DESC_0826* pChunk,float secsPerTick );
|
||||
|
||||
// returns position of the controller at the given time
|
||||
Vec3 GetPosition (float t);
|
||||
|
||||
// returns scale of the controller at the given time
|
||||
Vec3 GetScale (float t);
|
||||
|
||||
virtual bool IsLooping() const;
|
||||
|
||||
size_t sizeofThis()const
|
||||
{
|
||||
return sizeof(*this) + m_spline.sizeofThis();
|
||||
}
|
||||
|
||||
protected:
|
||||
// TCB Splines.
|
||||
TCBSpline<Vec3> m_spline;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TCB Controller implementation for quaternion.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CControllerTCBQuat : public CControllerTCB
|
||||
{
|
||||
public:
|
||||
// Loads (initializes) controller from the given chunk. The chunk descriptor is followed by the
|
||||
// chunk data immediately. Returns true if successful
|
||||
bool Load( const CONTROLLER_CHUNK_DESC_0826* pChunk,float secsPerTick );
|
||||
|
||||
// returns orientation of the controller at the given time
|
||||
CryQuat GetOrientation(float t);
|
||||
|
||||
virtual bool IsLooping() const;
|
||||
|
||||
size_t sizeofThis()const
|
||||
{
|
||||
return sizeof(*this) + m_spline.sizeofThis();
|
||||
}
|
||||
protected:
|
||||
// TCB Splines.
|
||||
TCBAngleAxisSpline m_spline;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
TYPEDEF_AUTOPTR(CControllerTCB);
|
||||
|
||||
#endif // __controllertcb_h__
|
||||
972
CryAnimation/CryAnimation.vcproj
Normal file
972
CryAnimation/CryAnimation.vcproj
Normal file
@@ -0,0 +1,972 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="7.10"
|
||||
Name="CryAnimation"
|
||||
ProjectGUID="{7BB11400-AFC9-4439-89B3-A00122B44850}"
|
||||
SccProjectName="Perforce Project"
|
||||
SccAuxPath=""
|
||||
SccLocalPath="."
|
||||
SccProvider="MSSCCI:Perforce SCM">
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"/>
|
||||
</Platforms>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="D:\Games\FC\Bin32"
|
||||
IntermediateDirectory="Release"
|
||||
ConfigurationType="2"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2"
|
||||
WholeProgramOptimization="TRUE">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="3"
|
||||
GlobalOptimizations="TRUE"
|
||||
InlineFunctionExpansion="2"
|
||||
EnableIntrinsicFunctions="TRUE"
|
||||
ImproveFloatingPointConsistency="FALSE"
|
||||
FavorSizeOrSpeed="2"
|
||||
OmitFramePointers="TRUE"
|
||||
EnableFiberSafeOptimizations="FALSE"
|
||||
OptimizeForProcessor="2"
|
||||
AdditionalIncludeDirectories="..\CryCommon"
|
||||
PreprocessorDefinitions="WIN32;_RELEASE;COMP_VC;DO_ASM;OS_WIN32;PROC_INTEL;WIN32;NDEBUG;_WINDOWS;_USRDLL;CRYANIMATION_EXPORTS"
|
||||
StringPooling="TRUE"
|
||||
RuntimeLibrary="2"
|
||||
BufferSecurityCheck="FALSE"
|
||||
EnableFunctionLevelLinking="FALSE"
|
||||
EnableEnhancedInstructionSet="0"
|
||||
UsePrecompiledHeader="3"
|
||||
PrecompiledHeaderThrough="stdafx.h"
|
||||
PrecompiledHeaderFile=".\Release/CryAnimation.pch"
|
||||
AssemblerListingLocation=".\Release/"
|
||||
ObjectFile=".\Release/"
|
||||
ProgramDataBaseFileName=".\Release/"
|
||||
BrowseInformation="0"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalOptions="/MACHINE:I386"
|
||||
AdditionalDependencies="Glu32.lib Winmm.lib odbc32.lib odbccp32.lib"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
ProgramDatabaseFile=".\Release/CryAnimation.pdb"
|
||||
ImportLibrary="$(IntDir)/$(TargetName).lib"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
PreprocessorDefinitions="NDEBUG"
|
||||
MkTypLibCompatible="TRUE"
|
||||
SuppressStartupBanner="TRUE"
|
||||
TargetEnvironment="1"
|
||||
TypeLibraryName=".\Release/CryAnimation.tlb"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="NDEBUG"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Profile|Win32"
|
||||
OutputDirectory="D:\Games\FC\Bin32"
|
||||
IntermediateDirectory="Profile"
|
||||
ConfigurationType="2"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2"
|
||||
WholeProgramOptimization="TRUE">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="3"
|
||||
GlobalOptimizations="TRUE"
|
||||
InlineFunctionExpansion="2"
|
||||
EnableIntrinsicFunctions="TRUE"
|
||||
FavorSizeOrSpeed="2"
|
||||
OmitFramePointers="TRUE"
|
||||
EnableFiberSafeOptimizations="FALSE"
|
||||
OptimizeForProcessor="2"
|
||||
AdditionalIncludeDirectories="..\CryCommon"
|
||||
PreprocessorDefinitions="WIN32;COMP_VC;DO_ASM;OS_WIN32;PROC_INTEL;WIN32;NDEBUG;_WINDOWS;_USRDLL;CRYANIMATION_EXPORTS"
|
||||
StringPooling="TRUE"
|
||||
RuntimeLibrary="2"
|
||||
BufferSecurityCheck="FALSE"
|
||||
EnableFunctionLevelLinking="FALSE"
|
||||
EnableEnhancedInstructionSet="0"
|
||||
UsePrecompiledHeader="3"
|
||||
PrecompiledHeaderThrough="stdafx.h"
|
||||
AssemblerListingLocation=".\Profile/"
|
||||
ObjectFile=".\Profile/"
|
||||
ProgramDataBaseFileName=".\Profile/"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalOptions="/MACHINE:I386"
|
||||
AdditionalDependencies="glu32.lib Winmm.lib odbc32.lib odbccp32.lib"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
GenerateDebugInformation="TRUE"
|
||||
BaseAddress="0x31500000"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
PreprocessorDefinitions="NDEBUG"
|
||||
MkTypLibCompatible="TRUE"
|
||||
SuppressStartupBanner="TRUE"
|
||||
TargetEnvironment="1"
|
||||
TypeLibraryName="C:\MasterCD/CryAnimation.tlb"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="NDEBUG"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="D:\Games\FC\Bin32"
|
||||
IntermediateDirectory=".\Debug"
|
||||
ConfigurationType="2"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
OptimizeForProcessor="0"
|
||||
AdditionalIncludeDirectories="..\CryCommon"
|
||||
PreprocessorDefinitions="WIN32;COMP_VC;DO_ASM;OS_WIN32;PROC_INTEL;WIN32;_DEBUG;_WINDOWS;_USRDLL;CRYANIMATION_EXPORTS"
|
||||
MinimalRebuild="TRUE"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
BufferSecurityCheck="TRUE"
|
||||
EnableFunctionLevelLinking="TRUE"
|
||||
UsePrecompiledHeader="3"
|
||||
PrecompiledHeaderThrough="stdafx.h"
|
||||
PrecompiledHeaderFile=".\Debug/CryAnimation.pch"
|
||||
AssemblerListingLocation=".\Debug/"
|
||||
ObjectFile=".\Debug/"
|
||||
ProgramDataBaseFileName=".\Debug/"
|
||||
BrowseInformation="1"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="4"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalOptions="/MACHINE:I386"
|
||||
AdditionalDependencies="glu32.lib Winmm.lib odbc32.lib odbccp32.lib"
|
||||
LinkIncremental="2"
|
||||
SuppressStartupBanner="TRUE"
|
||||
GenerateDebugInformation="TRUE"
|
||||
ImportLibrary="$(IntDir)/$(TargetName).lib"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
PreprocessorDefinitions="_DEBUG"
|
||||
MkTypLibCompatible="TRUE"
|
||||
SuppressStartupBanner="TRUE"
|
||||
TargetEnvironment="1"
|
||||
TypeLibraryName=".\Debug/CryAnimation.tlb"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="_DEBUG"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Debug64|Win32"
|
||||
OutputDirectory="D:\Games\FC\Bin32"
|
||||
IntermediateDirectory="Debug64"
|
||||
ConfigurationType="2"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\CryCommon"
|
||||
PreprocessorDefinitions="WIN64;COMP_VC;OS_WIN64;PROC_AMD64;_AMD64_;_DEBUG;_WINDOWS;_USRDLL;CRYANIMATION_EXPORTS"
|
||||
BasicRuntimeChecks="0"
|
||||
RuntimeLibrary="1"
|
||||
BufferSecurityCheck="FALSE"
|
||||
UsePrecompiledHeader="3"
|
||||
PrecompiledHeaderThrough="stdafx.h"
|
||||
PrecompiledHeaderFile="$(IntDir)/CryAnimation.pch"
|
||||
AssemblerListingLocation="$(IntDir)/"
|
||||
ObjectFile="$(IntDir)/"
|
||||
ProgramDataBaseFileName="$(IntDir)/$(ProjectName).pdb"
|
||||
BrowseInformation="0"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
Detect64BitPortabilityProblems="TRUE"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalOptions="/MACHINE:AMD64"
|
||||
AdditionalDependencies="../CryCommon/fSinCos64.lib"
|
||||
ShowProgress="1"
|
||||
LinkIncremental="0"
|
||||
SuppressStartupBanner="FALSE"
|
||||
IgnoreDefaultLibraryNames=""
|
||||
GenerateDebugInformation="TRUE"
|
||||
ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"
|
||||
LargeAddressAware="2"
|
||||
ImportLibrary="$(OutDir)/$(ProjectName).lib"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
PreprocessorDefinitions="_DEBUG"
|
||||
MkTypLibCompatible="TRUE"
|
||||
SuppressStartupBanner="TRUE"
|
||||
TargetEnvironment="1"
|
||||
TypeLibraryName=".\Debug/CryAnimation.tlb"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="_DEBUG"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release64|Win32"
|
||||
OutputDirectory="D:\Games\FC\Bin32"
|
||||
IntermediateDirectory="Release64"
|
||||
ConfigurationType="2"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2"
|
||||
WholeProgramOptimization="TRUE">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalOptions="/fp:fast /GL"
|
||||
Optimization="2"
|
||||
GlobalOptimizations="TRUE"
|
||||
InlineFunctionExpansion="2"
|
||||
EnableIntrinsicFunctions="TRUE"
|
||||
FavorSizeOrSpeed="1"
|
||||
OmitFramePointers="TRUE"
|
||||
AdditionalIncludeDirectories="..\CryCommon"
|
||||
PreprocessorDefinitions="_RELEASE;NDEBUG;WIN64;WIN32;_AMD64_;COMP_VC;OS_WIN64;PROC_AMD64;NDEBUG;_WINDOWS;_USRDLL;CRYANIMATION_EXPORTS"
|
||||
BasicRuntimeChecks="0"
|
||||
RuntimeLibrary="2"
|
||||
BufferSecurityCheck="FALSE"
|
||||
UsePrecompiledHeader="3"
|
||||
PrecompiledHeaderThrough="stdafx.h"
|
||||
PrecompiledHeaderFile="$(IntDir)/CryAnimation.pch"
|
||||
AssemblerListingLocation="$(IntDir)/"
|
||||
ObjectFile="$(IntDir)/"
|
||||
ProgramDataBaseFileName="$(IntDir)/$(ProjectName).pdb"
|
||||
BrowseInformation="0"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
Detect64BitPortabilityProblems="TRUE"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="../CryCommon/fSinCos64.lib"
|
||||
ShowProgress="0"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="FALSE"
|
||||
IgnoreDefaultLibraryNames=""
|
||||
GenerateDebugInformation="TRUE"
|
||||
ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"
|
||||
LargeAddressAware="2"
|
||||
ImportLibrary="$(OutDir)/$(ProjectName).lib"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
PreprocessorDefinitions="_DEBUG"
|
||||
MkTypLibCompatible="TRUE"
|
||||
SuppressStartupBanner="TRUE"
|
||||
TargetEnvironment="1"
|
||||
TypeLibraryName=".\Debug/CryAnimation.tlb"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="_DEBUG"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Model"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="CryGeometryInfo.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryGeometryInfo.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryGeomMorphTarget.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryGeomMorphTarget.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModel-Data.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModel.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModel.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelAnimationContainer.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelAnimationContainer.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelArrays.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelGeometryLoader.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelGeometryLoader.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelLoader.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelLoader.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelShadowVolume.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelState.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelState.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelStatePhys.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\CryModelSubmesh.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\CryModelSubmesh.h">
|
||||
</File>
|
||||
<Filter
|
||||
Name="Skin"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="CrySkinAMD64.asm">
|
||||
<FileConfiguration
|
||||
Name="Release|Win32"
|
||||
ExcludedFromBuild="TRUE">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Profile|Win32"
|
||||
ExcludedFromBuild="TRUE">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32"
|
||||
ExcludedFromBuild="TRUE">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug64|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description="Compiling $(InputName)"
|
||||
CommandLine="ml64 /nologo /c /Fl"$(OutDir)\$(InputName).cod" /Fo"$(OutDir)\$(InputName).obj" /Zi $(InputPath)
|
||||
"
|
||||
Outputs="$(OutDir)\$(InputName).obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release64|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description="Compiling $(InputName)"
|
||||
CommandLine="ml64 /nologo /c /Fl"$(OutDir)\$(InputName).cod" /Fo"$(OutDir)\$(InputName).obj" /Zi $(InputPath)
|
||||
"
|
||||
Outputs="$(OutDir)\$(InputName).obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinBase.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinBase.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinBasisBuilder.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinBasisBuilder.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinBuilder.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinBuilder.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinBuilderBase.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinBuilderBase.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinFull.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinFull.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinMorph.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinMorph.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinMorphBuilder.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinMorphBuilder.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinRigidBasis.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinRigidBasis.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinTypes.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Shadow Volume"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="CryCharReShadowManager.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharReShadowManager.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharReShadowVolume.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharReShadowVolume.h">
|
||||
</File>
|
||||
</Filter>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Bones"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="BoneLightBindInfo.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="BoneLightBindInfo.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="BoneLightDynamicBind.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="BoneLightDynamicBind.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryBone.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryBone.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryBoneDesc.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryBoneDesc.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryBoneHierarchyLoader.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryBoneHierarchyLoader.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryBoneInfo.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryBoneInfo.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Front-End"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="CryAnimationBase.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryAnimationBase.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharBody.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharBody.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharInstance.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharInstance.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharManager.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharManager.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="stdafx.cpp">
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Profile|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug64|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release64|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="stdafx.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="TCB-Spline"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="ControllerTCB.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ControllerTCB.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="TCBSpline.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="B-Splines"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="BSplineKnots.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="BSplineKnots.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="BSplineOpen.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="BSplineOpen.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="BSplineVec3dPacked.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="BSplineVec3dPacked.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="CVars"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="cvars-list.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="cvars.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="cvars.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Decals"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="CryCharDecal.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharDecal.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharDecalBuilder.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharDecalBuilder.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharDecalCommon.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharDecalManager.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharDecalManager.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Chunk Files"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="ChunkFileReader.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ChunkFileReader.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="FileMapping.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="FileMapping.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Utils"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="CgfUtils.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CgfUtils.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharRenderElement.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharRenderElement.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryKeyInterpolation.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryKeyInterpolation.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="DebugUtils.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="DebugUtils.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="drand.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="drand.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="GeomCommon.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="IncContHeap.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="IncContHeap.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="makepath.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="makepath.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="MathUtils.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="MathUtils.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="QuaternionExponentX87.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="QuaternionExponentX87.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="RenderUtils.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="RenderUtils.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="SparseArrayDriver.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="splitpath.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="splitpath.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="SSEUtils.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="SSEUtils.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="TFace.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="Vec3a16.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="VertexBufferArrayDrivers.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="VertexBufferArrayDrivers.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Controllers"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="Controller.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="Controller.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ControllerCryBone.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ControllerCryBone.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ControllerManager.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ControllerManager.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ControllerPackedBSpline.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ControllerPackedBSpline.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Animation"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="AnimationLayerInfo.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryAnimationInfo.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModEffAnimation.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModEffAnimation.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModEffector.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModEffIKSolver.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModEffIKSolver.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModEffMorph.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModEffMorph.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryVertexBinding.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryVertexBinding.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="AnimObject"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="AnimObject.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="AnimObject.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="AnimObjectLoader.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="AnimObjectLoader.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="AnimObjectManager.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="AnimObjectManager.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Other"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="Changes.txt">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="Notes.txt">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="XBoxExport.cpp">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Particles"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="CryCharParticleManager.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharParticleManager.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="fx"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath=".\CryCharFxTrail.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\CryCharFxTrail.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\CryCharInstanceRenderParams.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\CryCharInstanceRenderParams.h">
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
10
CryAnimation/CryAnimation.vcproj.vspscc
Normal file
10
CryAnimation/CryAnimation.vcproj.vspscc
Normal file
@@ -0,0 +1,10 @@
|
||||
""
|
||||
{
|
||||
"FILE_VERSION" = "9237"
|
||||
"ENLISTMENT_CHOICE" = "NEVER"
|
||||
"PROJECT_FILE_RELATIVE_PATH" = ""
|
||||
"NUMBER_OF_EXCLUDED_FILES" = "0"
|
||||
"ORIGINAL_PROJECT_FILE_PATH" = "file:C:\\Programming\\GAME01\\CryAnimation\\CryAnimation.vcproj"
|
||||
"NUMBER_OF_NESTED_PROJECTS" = "0"
|
||||
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT"
|
||||
}
|
||||
82
CryAnimation/CryAnimationBase.cpp
Normal file
82
CryAnimation/CryAnimationBase.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Animation DLL source code
|
||||
//
|
||||
// File: CryAnimation.cpp
|
||||
// Description :
|
||||
// Defines the entry point for the DLL application.
|
||||
// Implements the base class for major CryAnimation classes
|
||||
//
|
||||
// History:
|
||||
// - September 10, 2001: Created by Vladimir Kajalin
|
||||
// - August 15, 2002: Taken over by Sergiy Migdalskiy
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <stdarg.h>
|
||||
#include "CVars.h"
|
||||
#include "CryCharManager.h"
|
||||
#include "CryAnimationBase.h"
|
||||
//#include "CryAnimation.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef _DEBUG
|
||||
#undef THIS_FILE
|
||||
static char THIS_FILE[] = __FILE__;
|
||||
#endif
|
||||
|
||||
// cached interfaces - valid during the whole session, when the character manager is alive; then get erased
|
||||
ISystem* g_pISystem = NULL;
|
||||
ITimer* g_pITimer = NULL;
|
||||
ILog* g_pILog = NULL;
|
||||
IConsole* g_pIConsole = NULL;
|
||||
ICryPak* g_pIPak = NULL;
|
||||
IStreamEngine* g_pIStreamEngine = NULL;;
|
||||
|
||||
IRenderer* g_pIRenderer = NULL;
|
||||
IPhysicalWorld* g_pIPhysicalWorld = NULL;
|
||||
I3DEngine* g_pI3DEngine = NULL;
|
||||
|
||||
|
||||
|
||||
// this is current frame id PLUS OR MINUS a few frames.
|
||||
// can be used in places where it's really not significant for functionality but speed is a must.
|
||||
int g_nFrameID = 0;
|
||||
|
||||
// this is true when the game runs in such a mode that requires all bones be updated every frame
|
||||
bool g_bUpdateBonesAlways = false;
|
||||
|
||||
bool g_bProfilerOn = false;
|
||||
|
||||
// the cached console variable interfaces that are valid when the CryCharManager singleton is alive
|
||||
CryAnimVars* g_pCVariables = NULL;
|
||||
|
||||
|
||||
double g_dTimeAnimLoadBind;
|
||||
double g_dTimeAnimLoadBindPreallocate;
|
||||
double g_dTimeAnimLoadBindNoCal;
|
||||
double g_dTimeAnimLoadBindWithCal;
|
||||
double g_dTimeGeomLoad;
|
||||
double g_dTimeGeomPostInit;
|
||||
double g_dTimeShaderLoad;
|
||||
double g_dTimeGeomChunkLoad;
|
||||
double g_dTimeGeomChunkLoadFileIO;
|
||||
double g_dTimeGenRenderArrays;
|
||||
double g_dTimeAnimBindControllers;
|
||||
double g_dTimeAnimLoadFile;
|
||||
double g_dTimeTest1;
|
||||
double g_dTimeTest2;
|
||||
double g_dTimeTest3;
|
||||
double g_dTimeTest4;
|
||||
|
||||
int g_CpuFlags;
|
||||
double g_SecondsPerCycle;
|
||||
|
||||
// the number of animations that were loaded asynchronously
|
||||
// (one animation can be counted several times if it has been loaded/unloaded)
|
||||
unsigned g_nAsyncAnimCounter = 0;
|
||||
// this is the sum of all delays between animation load and animation load finish, in frames
|
||||
unsigned g_nAsyncAnimFrameDelays = 0;
|
||||
|
||||
273
CryAnimation/CryAnimationBase.h
Normal file
273
CryAnimation/CryAnimationBase.h
Normal file
@@ -0,0 +1,273 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CryEngine Source code
|
||||
//
|
||||
// File:CryAnimationBase.h
|
||||
// Declaration of CryAnimationBase class
|
||||
// Access to external stuff used by 3d engine
|
||||
// Most classes are derived from this base class to access the external interfaces
|
||||
//
|
||||
// History:
|
||||
// -:Created by Vladimir Kajalin
|
||||
// -:Taken over by Sergiy Migdalskiy
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _CRY_ANIMATION_BASE_HEADER_
|
||||
#define _CRY_ANIMATION_BASE_HEADER_
|
||||
|
||||
#include "FrameProfiler.h"
|
||||
#include "CVars.h"
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// There's only one ISystem in the process, just like there is one CryCharManager.
|
||||
// So this ISystem is kept in the global pointer and is initialized upon creation
|
||||
// of the CryCharManager and is valid until its destruction.
|
||||
// Upon destruction, it's NULLed. In any case, there must be no object needing it
|
||||
// after that since the animation system is only active when the Manager object is alive
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extern ISystem* g_pISystem;
|
||||
extern IConsole* g_pIConsole;
|
||||
extern ITimer* g_pITimer;
|
||||
extern ILog* g_pILog;
|
||||
extern ICryPak* g_pIPak;
|
||||
extern IStreamEngine* g_pIStreamEngine;
|
||||
|
||||
extern IRenderer* g_pIRenderer;
|
||||
extern IPhysicalWorld* g_pIPhysicalWorld;
|
||||
extern I3DEngine* g_pI3DEngine;
|
||||
|
||||
extern bool g_bProfilerOn;
|
||||
extern CryAnimVars* g_pCVariables;
|
||||
|
||||
|
||||
|
||||
|
||||
// initializes the global values - just remembers the pointer to the system that will
|
||||
// be kept valid until deinitialization of the class (that happens upon destruction of the
|
||||
// CryCharManager instance). Also initializes the console variables
|
||||
__forceinline void g_InitInterfaces(ISystem* pISystem)
|
||||
{
|
||||
assert (pISystem);
|
||||
g_pISystem = pISystem;
|
||||
g_pIConsole = pISystem->GetIConsole();
|
||||
g_pITimer = pISystem->GetITimer();
|
||||
g_pILog = pISystem->GetILog();
|
||||
g_pIPak = pISystem->GetIPak();
|
||||
g_pIStreamEngine = pISystem->GetStreamEngine();
|
||||
|
||||
//we initialize this pointers in CryCharManager::Update()
|
||||
g_pIRenderer = NULL; //pISystem->GetIRenderer();
|
||||
g_pIPhysicalWorld = NULL; //pISystem->GetIPhysicalWorld();
|
||||
g_pI3DEngine = NULL; //pISystem->GetI3DEngine();
|
||||
|
||||
//---------------------------------------------------
|
||||
|
||||
#ifdef _DEBUG
|
||||
enum {numTests = 2};
|
||||
for (int i = 0; i < numTests; ++i)
|
||||
{
|
||||
CryAnimVars* p = new CryAnimVars();
|
||||
delete p;
|
||||
}
|
||||
#endif
|
||||
|
||||
g_pCVariables = new CryAnimVars();
|
||||
}
|
||||
|
||||
|
||||
// deinitializes the Class - actually just NULLs the system pointer and deletes the variables
|
||||
__forceinline void g_DeleteInterfaces()
|
||||
{
|
||||
delete g_pCVariables;
|
||||
g_pCVariables = NULL;
|
||||
|
||||
g_pISystem = NULL;
|
||||
g_pITimer = NULL;
|
||||
g_pILog = NULL;
|
||||
g_pIConsole = NULL;
|
||||
g_pIPak = NULL;
|
||||
g_pIStreamEngine = NULL;;
|
||||
|
||||
g_pIRenderer = NULL;
|
||||
g_pIPhysicalWorld = NULL;
|
||||
g_pI3DEngine = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
__forceinline CCamera& GetViewCamera() { return g_pISystem->GetViewCamera(); }
|
||||
__forceinline ISystem* GetISystem() { return g_pISystem; } //we need this one just for the profiler
|
||||
|
||||
__forceinline ISystem* g_GetISystem() { return g_pISystem; }
|
||||
__forceinline ITimer* g_GetTimer() {return g_pITimer;}
|
||||
__forceinline ILog* g_GetLog() {return g_pILog;}
|
||||
__forceinline IConsole* g_GetConsole() { return g_pIConsole; }
|
||||
__forceinline ICryPak* g_GetPak() { return g_pIPak; }
|
||||
__forceinline IStreamEngine* g_GetStreamEngine() { return g_pIStreamEngine;}
|
||||
|
||||
__forceinline IPhysicalWorld* GetPhysicalWorld() { return g_pIPhysicalWorld; }
|
||||
__forceinline I3DEngine* Get3DEngine() { return g_pI3DEngine; }
|
||||
__forceinline IRenderer* g_GetIRenderer() { return g_pIRenderer; }
|
||||
|
||||
__forceinline bool IsProfilerOn() { return g_bProfilerOn; }
|
||||
__forceinline CryAnimVars* g_GetCVars() { return g_pCVariables; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
inline void g_LogToFile (const char* szFormat, ...)
|
||||
{
|
||||
char szBuffer[0x800];
|
||||
va_list args;
|
||||
va_start(args,szFormat);
|
||||
_vsnprintf (szBuffer, sizeof(szBuffer), szFormat, args);
|
||||
va_end(args);
|
||||
g_GetLog()->LogToFile ("%s", szBuffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
// this is an alternate log, that will do nothing in non-debug builds
|
||||
inline void g_Info (const char* szFormat, ...)
|
||||
{
|
||||
if (!g_GetCVars()->ca_EnableAnimationLog())
|
||||
return;
|
||||
|
||||
FILE* f = fopen ("Animation.log", "at");
|
||||
if (f)
|
||||
{
|
||||
va_list arg;
|
||||
va_start (arg, szFormat);
|
||||
|
||||
fprintf (f, "%5d ", g_GetIRenderer()->GetFrameID());
|
||||
vfprintf (f, szFormat, arg);
|
||||
fprintf (f, "\n");
|
||||
|
||||
va_end(arg);
|
||||
fclose (f);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define g_Info while(0)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
// collector profilers: collect the total time spent on something
|
||||
extern double g_dTimeAnimLoadBind;
|
||||
extern double g_dTimeAnimLoadBindNoCal;
|
||||
extern double g_dTimeAnimLoadBindWithCal;
|
||||
extern double g_dTimeAnimLoadBindPreallocate;
|
||||
extern double g_dTimeGeomLoad;
|
||||
extern double g_dTimeGeomPostInit;
|
||||
extern double g_dTimeShaderLoad;
|
||||
extern double g_dTimeGeomChunkLoad;
|
||||
extern double g_dTimeGeomChunkLoadFileIO;
|
||||
extern double g_dTimeGenRenderArrays;
|
||||
extern double g_dTimeAnimLoadFile;
|
||||
extern double g_dTimeAnimBindControllers;
|
||||
extern double g_dTimeTest1;
|
||||
extern double g_dTimeTest2;
|
||||
extern double g_dTimeTest3;
|
||||
extern double g_dTimeTest4;
|
||||
|
||||
// the number of animations that were loaded asynchronously
|
||||
// (one animation can be counted several times if it has been loaded/unloaded)
|
||||
extern unsigned g_nAsyncAnimCounter;
|
||||
// this is the sum of all delays between animation load and animation load finish, in frames
|
||||
extern unsigned g_nAsyncAnimFrameDelays;
|
||||
|
||||
extern int g_CpuFlags;
|
||||
extern double g_SecondsPerCycle;
|
||||
|
||||
#define ENABLE_GET_MEMORY_USAGE 1
|
||||
|
||||
const float g_fDefaultAnimationScale = 0.01f;
|
||||
|
||||
|
||||
// this is current frame id PLUS OR MINUS a few frames.
|
||||
// can be used in places where it's really not significant for functionality but speed is a must.
|
||||
extern int g_nFrameID;
|
||||
|
||||
// this is true when the game runs in such a mode that requires all bones be updated every frame
|
||||
extern bool g_bUpdateBonesAlways;
|
||||
|
||||
#ifndef AUTO_PROFILE_SECTION
|
||||
#pragma message ("Warning: ITimer not included")
|
||||
#else
|
||||
#undef AUTO_PROFILE_SECTION
|
||||
#endif
|
||||
|
||||
#define AUTO_PROFILE_SECTION(g_fTimer) CITimerAutoProfiler<double> __section_auto_profiler(g_GetTimer(), g_fTimer)
|
||||
|
||||
#define DEFINE_PROFILER_FUNCTION() FUNCTION_PROFILER_FAST(g_GetISystem(), PROFILE_ANIMATION, IsProfilerOn())
|
||||
#define DEFINE_PROFILER_SECTION(NAME) FRAME_PROFILER_FAST(NAME, g_GetISystem(), PROFILE_ANIMATION, IsProfilerOn())
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
inline void g_UpdateLoadingScreen(const char *command,...)
|
||||
{
|
||||
if(command)
|
||||
{
|
||||
va_list arglist;
|
||||
char buf[512];
|
||||
va_start(arglist, command);
|
||||
vsprintf(buf, command, arglist);
|
||||
va_end(arglist);
|
||||
g_GetLog()->UpdateLoadingScreen(buf);
|
||||
}
|
||||
else
|
||||
g_GetLog()->UpdateLoadingScreen(0);
|
||||
}
|
||||
|
||||
inline void UpdateLoadingScreenPlus(const char *command,...)
|
||||
{
|
||||
va_list arglist;
|
||||
char buf[512];
|
||||
va_start(arglist, command);
|
||||
vsprintf(buf, command, arglist);
|
||||
va_end(arglist);
|
||||
g_GetLog()->UpdateLoadingScreenPlus(buf);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline T g_GetConsoleVariable (const char * szVarName, const char * szFileName, const T tDefaultValue)
|
||||
{
|
||||
return GetConsole()->GetVariable(szVarName, szFileName, tDefaultValue);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T g_GetConsoleVariable (const char * szVarName, const string& strFileName, const T szDefaultValue)
|
||||
{
|
||||
return GetConsoleVariable (szVarName, strFileName.c_str(), szDefaultValue);
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // _CryAnimationBase_h_
|
||||
207
CryAnimation/CryAnimationInfo.h
Normal file
207
CryAnimation/CryAnimationInfo.h
Normal file
@@ -0,0 +1,207 @@
|
||||
#ifndef _CRY_ANIMATION_CRY_ANIMATION_INFO_HDR_
|
||||
#define _CRY_ANIMATION_CRY_ANIMATION_INFO_HDR_
|
||||
|
||||
struct AnimEvent { AnimSinkEventData UserData; float fTime; };
|
||||
inline bool operator < (const AnimEvent& left, const AnimEvent& right)
|
||||
{
|
||||
return left.fTime < right.fTime;
|
||||
}
|
||||
// returns true if the given events are equal with the given time tolerance
|
||||
inline bool isEqual (const AnimEvent& left, const AnimEvent& right, float fTolerance = 1e-3f)
|
||||
{
|
||||
return left.UserData == right.UserData
|
||||
&& fabs(left.fTime - right.fTime) < fTolerance;
|
||||
}
|
||||
|
||||
//! this structure contains info about loaded animations
|
||||
struct AnimData
|
||||
{
|
||||
string strName; // the name of the animation (not the name of the file) - unique per-model
|
||||
float fStart, fStop; // start and stop time, in seconds
|
||||
int nGlobalAnimId;
|
||||
bool bLoop;
|
||||
|
||||
AnimData():
|
||||
fStart(0), fStop(0), bLoop(false),
|
||||
nGlobalAnimId (-1)
|
||||
{
|
||||
}
|
||||
bool isStatic () const {return fStart == fStop;}
|
||||
float getLength() const {return fStop - fStart;}
|
||||
|
||||
size_t sizeofThis()const
|
||||
{
|
||||
return sizeof(*this) + strName.capacity();
|
||||
}
|
||||
};
|
||||
|
||||
struct MiniRangeEntity {
|
||||
int start;
|
||||
int end;
|
||||
MiniRangeEntity()
|
||||
{
|
||||
start = end = 0;
|
||||
}
|
||||
void operator = (const RANGE_ENTITY& right)
|
||||
{
|
||||
this->start = right.start;
|
||||
this->end = right.end;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// this is the animation information on the module level (not on the per-model level)
|
||||
// it doesn't know the name of the animation (which is model-specific), but does know the file name
|
||||
// Implements some services for bone binding and ref counting
|
||||
struct GlobalAnimation
|
||||
{
|
||||
// Since we know the number of controllers per animation from the beginning and don't
|
||||
// change it, we could use more econimical TFixedArray here instead of STL or other array.
|
||||
typedef IController_AutoArray ControllerArray;
|
||||
|
||||
// the flags used in the nFlags member
|
||||
enum
|
||||
{
|
||||
// if this is true, then the animation has valid info data (has been loaded at least once)
|
||||
FLAGS_INFO_LOADED = 1,
|
||||
// this doesn't allow the auto-unloading to happen
|
||||
FLAGS_DISABLE_AUTO_UNLOAD = 1 << 1,
|
||||
// this forces the animation to be loaded immediately
|
||||
FLAGS_DISABLE_DELAY_LOAD = 1 << 2,
|
||||
// this disables the error log in LoadAnimation() in case the animation wasn't found on disk
|
||||
FLAGS_DISABLE_LOAD_ERROR_LOG = 1 << 3,
|
||||
|
||||
// if this flag is set, it means that the animation is being loaded right now via asynchronous operation
|
||||
// and shouldn't be attempted to be played back or loaded
|
||||
FLAGS_LOAD_PENDING = 1 << 4,
|
||||
|
||||
// this is the flag combination that should be applied only to default animations
|
||||
FLAGS_DEFAULT_ANIMATION = FLAGS_DISABLE_DELAY_LOAD|FLAGS_DISABLE_LOAD_ERROR_LOG,
|
||||
|
||||
// combination of all possible flags
|
||||
FLAGS_ALL_FLAGS = (1<<5) - 1,
|
||||
|
||||
// the flags by default
|
||||
FLAGS_DEFAULT_FLAGS = 0
|
||||
};
|
||||
|
||||
GlobalAnimation ()
|
||||
{
|
||||
// some standard values to fill in before the animation will be loaded
|
||||
nRefCount = 0;
|
||||
nFlags = FLAGS_DEFAULT_FLAGS;
|
||||
fSecsPerTick = 0.000208f;
|
||||
nLastAccessFrameId = g_nFrameID;
|
||||
nTickCount = nStartCount = nApplyCount = 0;
|
||||
nTicksPerFrame = 160;
|
||||
// the defaults that are anyway immediately overridden
|
||||
fScale = 0.01f;
|
||||
rangeGlobal.end = 900; // this is in ticks, means 30 frames
|
||||
}
|
||||
|
||||
IController*GetController(unsigned nControllerID)
|
||||
{
|
||||
ControllerArray::iterator it = std::lower_bound(arrCtrls.begin(), arrCtrls.end(), nControllerID, AnimCtrlSortPred());
|
||||
if (it != arrCtrls.end() && (*it)->GetID() == nControllerID)
|
||||
{
|
||||
IController *c = *it;
|
||||
#ifdef _DEBUG
|
||||
// set this to true in the debugger to obtain the 0th frame p and q
|
||||
bool bCheck = false;
|
||||
if (bCheck)
|
||||
{
|
||||
CryQuat q; Vec3d p;
|
||||
c->GetValue(0, q, p);
|
||||
}
|
||||
#endif
|
||||
return c;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool IsLoaded()const {return !arrCtrls.empty();}
|
||||
|
||||
bool IsInfoLoaded()const {return (nFlags&FLAGS_INFO_LOADED) != 0;}
|
||||
void OnInfoLoaded() {nFlags |= FLAGS_INFO_LOADED;}
|
||||
bool IsAutoUnload()const {return (nFlags&FLAGS_DISABLE_AUTO_UNLOAD)== 0;}
|
||||
|
||||
void OnTick ()
|
||||
{
|
||||
++nTickCount;
|
||||
nLastAccessFrameId = g_nFrameID;
|
||||
}
|
||||
void OnStart()
|
||||
{
|
||||
++nStartCount;
|
||||
nLastAccessFrameId = g_nFrameID;
|
||||
}
|
||||
void OnApply()
|
||||
{
|
||||
++nApplyCount;
|
||||
nLastAccessFrameId = g_nFrameID;
|
||||
}
|
||||
|
||||
void AddRef()
|
||||
{
|
||||
++nRefCount;
|
||||
}
|
||||
|
||||
void Release()
|
||||
{
|
||||
if (!--nRefCount)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
for (ControllerArray::iterator it = arrCtrls.begin(); it!= arrCtrls.end(); ++it)
|
||||
assert ((*it)->NumRefs()==1); // only this object references the controllers now
|
||||
#endif
|
||||
arrCtrls.clear(); // nobody uses the controllers; clean them up. This makes the animation effectively unloaded
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// returns the maximum reference counter from all controllers. 1 means that nobody but this animation
|
||||
// structure refers to them
|
||||
int MaxControllerRefCount()
|
||||
{
|
||||
if (arrCtrls.empty())
|
||||
return 0;
|
||||
int nMax = arrCtrls[0]->NumRefs();
|
||||
for (ControllerArray::iterator it = arrCtrls.begin()+1; it!= arrCtrls.end(); ++it)
|
||||
if((*it)->NumRefs() > nMax)
|
||||
nMax = (*it)->NumRefs();
|
||||
return nMax;
|
||||
}
|
||||
#endif
|
||||
size_t sizeofThis ()const;
|
||||
|
||||
|
||||
|
||||
// controllers comprising the animation; within the animation, they're sorted by ids
|
||||
ControllerArray arrCtrls;
|
||||
// timing data, retrieved from the timing_chunk_desc
|
||||
int nTicksPerFrame;
|
||||
float fSecsPerTick;
|
||||
float fScale; // the parameter from the initial load animation, used for reloadin
|
||||
|
||||
MiniRangeEntity rangeGlobal;
|
||||
|
||||
// the file name of the animation
|
||||
string strFileName;
|
||||
|
||||
// the number of times this animation has been accessed
|
||||
unsigned nTickCount;
|
||||
// the number of times this animation has been started
|
||||
unsigned nStartCount;
|
||||
// the number of times this animation has been applied to bones
|
||||
unsigned nApplyCount;
|
||||
// the last time the animation has been accessed
|
||||
int nLastAccessFrameId;
|
||||
// the number of referrers to this global animation record (doesn't matter if the controllers are currently loaded)
|
||||
int nRefCount;
|
||||
// the flags (see the enum in the top of the declaration)
|
||||
unsigned nFlags;
|
||||
};
|
||||
|
||||
#endif
|
||||
773
CryAnimation/CryAnimation_XBox.vcproj
Normal file
773
CryAnimation/CryAnimation_XBox.vcproj
Normal file
@@ -0,0 +1,773 @@
|
||||
<?xml version="1.0" encoding = "windows-1251"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="7.00"
|
||||
Name="CryAnimation_XBox"
|
||||
ProjectGUID="{7BB11400-AFC9-4439-89B3-A00122B44850}"
|
||||
SccProjectName=""$/Game01/CryAnimation", WNFBAAAA"
|
||||
SccAuxPath=""
|
||||
SccLocalPath="."
|
||||
SccProvider="MSSCCI:Microsoft Visual SourceSafe">
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Xbox"/>
|
||||
</Platforms>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Release|Xbox"
|
||||
OutputDirectory="Release_XBox"
|
||||
IntermediateDirectory="Release_XBox"
|
||||
ConfigurationType="4"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2"
|
||||
WholeProgramOptimization="TRUE">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
GlobalOptimizations="TRUE"
|
||||
InlineFunctionExpansion="2"
|
||||
EnableIntrinsicFunctions="TRUE"
|
||||
ImproveFloatingPointConsistency="FALSE"
|
||||
FavorSizeOrSpeed="1"
|
||||
OmitFramePointers="TRUE"
|
||||
EnableFiberSafeOptimizations="FALSE"
|
||||
OptimizeForProcessor="2"
|
||||
OptimizeForWindowsApplication="FALSE"
|
||||
AdditionalIncludeDirectories="..\CryCommon"
|
||||
PreprocessorDefinitions="_XBOX;_LIB;COMP_VC;DO_ASM;ROC_INTEL;NDEBUG"
|
||||
StringPooling="TRUE"
|
||||
RuntimeLibrary="4"
|
||||
BufferSecurityCheck="FALSE"
|
||||
EnableFunctionLevelLinking="FALSE"
|
||||
UsePrecompiledHeader="3"
|
||||
PrecompiledHeaderThrough="stdafx.h"
|
||||
PrecompiledHeaderFile="$(IntDir)/CryAnimation.pch"
|
||||
AssemblerListingLocation="$(IntDir)/"
|
||||
ObjectFile="$(IntDir)/"
|
||||
ProgramDataBaseFileName="$(IntDir)/"
|
||||
BrowseInformation="0"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="2"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
OutputFile="$(OutDir)/CryAnimation.lib"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Profile|Xbox"
|
||||
OutputDirectory="Profile"
|
||||
IntermediateDirectory="Profile"
|
||||
ConfigurationType="4"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
GlobalOptimizations="TRUE"
|
||||
InlineFunctionExpansion="2"
|
||||
EnableIntrinsicFunctions="TRUE"
|
||||
FavorSizeOrSpeed="1"
|
||||
OmitFramePointers="TRUE"
|
||||
OptimizeForProcessor="2"
|
||||
AdditionalIncludeDirectories="..\CryCommon"
|
||||
PreprocessorDefinitions="_XBOX;_LIB;COMP_VC;DO_ASM;ROC_INTEL;NDEBUG"
|
||||
StringPooling="TRUE"
|
||||
BufferSecurityCheck="FALSE"
|
||||
UsePrecompiledHeader="3"
|
||||
PrecompiledHeaderThrough="stdafx.h"
|
||||
AssemblerListingLocation=".\Profile/"
|
||||
ObjectFile=".\Profile/"
|
||||
ProgramDataBaseFileName=".\Profile/"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
OutputFile="$(OutDir)/CryAnimation.lib"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Debug|Xbox"
|
||||
OutputDirectory="Debug_XBox"
|
||||
IntermediateDirectory="Debug_XBox"
|
||||
ConfigurationType="4"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
OptimizeForProcessor="2"
|
||||
AdditionalIncludeDirectories="..\CryCommon"
|
||||
PreprocessorDefinitions="_DEBUG;_XBOX;_LIB;COMP_VC;DO_ASM;PROC_INTEL"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="5"
|
||||
BufferSecurityCheck="TRUE"
|
||||
EnableFunctionLevelLinking="TRUE"
|
||||
UsePrecompiledHeader="3"
|
||||
PrecompiledHeaderThrough="stdafx.h"
|
||||
PrecompiledHeaderFile=".\Debug_XBox/CryAnimation.pch"
|
||||
AssemblerListingLocation=".\Debug_XBox/"
|
||||
ObjectFile=".\Debug_XBox/"
|
||||
ProgramDataBaseFileName=".\Debug_XBox/"
|
||||
BrowseInformation="1"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="4"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
OutputFile="$(OutDir)/CryAnimation.lib"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Hybrid|Xbox"
|
||||
OutputDirectory="Hybrid"
|
||||
IntermediateDirectory="Hybrid"
|
||||
ConfigurationType="4"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
GlobalOptimizations="FALSE"
|
||||
InlineFunctionExpansion="0"
|
||||
EnableIntrinsicFunctions="FALSE"
|
||||
OmitFramePointers="FALSE"
|
||||
OptimizeForProcessor="2"
|
||||
AdditionalIncludeDirectories="..\CryCommon"
|
||||
PreprocessorDefinitions="COMP_VC;DO_ASM;OPENGL;OS_WIN32;PROC_INTEL;WIN32;NDEBUG;_WINDOWS;_USRDLL;CRYANIMATION_EXPORTS;_HYBRID"
|
||||
StringPooling="TRUE"
|
||||
RuntimeLibrary="4"
|
||||
EnableFunctionLevelLinking="TRUE"
|
||||
UsePrecompiledHeader="3"
|
||||
PrecompiledHeaderThrough="stdafx.h"
|
||||
AssemblerListingLocation=".\Profile/"
|
||||
ObjectFile=".\Profile/"
|
||||
ProgramDataBaseFileName=".\Profile/"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
OutputFile="$(OutDir)/CryAnimation.lib"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Model"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="CryGeomMorphTarget.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryGeomMorphTarget.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryGeometryInfo.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryGeometryInfo.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModel-Data.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModel.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModel.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelAnimationContainer.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelAnimationContainer.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelArrays.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelGeometryLoader.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelGeometryLoader.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelLoader.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelLoader.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelState.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelState.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelStatePhys.cpp">
|
||||
</File>
|
||||
<Filter
|
||||
Name="Skin"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="CrySkinBase.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinBase.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinBasisBuilder.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinBasisBuilder.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinBuilder.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinBuilder.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinBuilderBase.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinBuilderBase.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinFull.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinFull.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinMorph.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinMorph.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinMorphBuilder.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinMorphBuilder.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinRigidBasis.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinRigidBasis.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CrySkinTypes.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Shadow Volume"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="CryCharReShadowManager.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharReShadowManager.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharReShadowVolume.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharReShadowVolume.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModelShadowVolume.cpp">
|
||||
</File>
|
||||
</Filter>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Bones"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="BoneLightBindInfo.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="BoneLightBindInfo.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="BoneLightDynamicBind.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryBone.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryBone.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryBoneHierarchyLoader.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryBoneHierarchyLoader.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryBoneInfo.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryBoneInfo.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Front-End"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="CryAnimation.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryAnimation.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryAnimationBase.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryAnimationBase.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharBody.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharBody.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharInstance.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharInstance.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharManager.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharManager.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\crycommon\ICryAnimation.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="stdafx.cpp">
|
||||
<FileConfiguration
|
||||
Name="Release|Xbox">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Profile|Xbox">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug|Xbox">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Hybrid|Xbox">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="stdafx.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="TCB-Spline"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="ControllerTCB.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ControllerTCB.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="TCBSpline.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="B-Splines"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="BSplineKnots.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="BSplineKnots.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="BSplineOpen.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="BSplineOpen.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="BSplineVec3dPacked.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="BSplineVec3dPacked.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="CVars"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="cvars-list.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="cvars.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="cvars.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Decals"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="CryCharDecal.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharDecal.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharDecalBuilder.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharDecalBuilder.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharDecalCommon.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharDecalManager.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharDecalManager.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Chunk Files"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="ChunkFileReader.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ChunkFileReader.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="FileMapping.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="FileMapping.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Utils"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="CryCharRenderElement.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharRenderElement.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryKeyInterpolation.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryKeyInterpolation.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="DebugUtils.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="DebugUtils.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="FrameProfilers-list.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="IncContHeap.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="IncContHeap.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="MathUtils.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="MathUtils.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="QuaternionExponentX87.c">
|
||||
<FileConfiguration
|
||||
Name="Release|Xbox">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Profile|Xbox">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug|Xbox">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Hybrid|Xbox">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="QuaternionExponentX87.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="RenderUtils.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="RenderUtils.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="SSEUtils.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="SSEUtils.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="SimpleFrameProfiler.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="SimpleFrameProfiler.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="SparseArrayDriver.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="Vec3a16.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="VertexBufferArrayDrivers.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="VertexBufferArrayDrivers.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Controllers"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="Controller.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="Controller.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ControllerCryBone.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ControllerCryBone.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ControllerManager.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ControllerManager.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ControllerPackedBSpline.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ControllerPackedBSpline.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Animation"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="AnimationLayerInfo.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryAnimationInfo.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModEffAnimation.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModEffAnimation.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModEffIKSolver.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModEffIKSolver.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModEffMorph.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModEffMorph.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryModEffector.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryVertexBinding.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryVertexBinding.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="AnimObject"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="AnimObject.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="AnimObject.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="AnimObjectLoader.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="AnimObjectLoader.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="AnimObjectManager.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="AnimObjectManager.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Other"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="CgfUtils.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="Notes.txt">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="XBoxExport.cpp">
|
||||
</File>
|
||||
</Filter>
|
||||
<File
|
||||
RelativePath="CryBoneDesc.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryBoneDesc.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharParticleManager.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="CryCharParticleManager.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="drand.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="drand.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="makepath.c">
|
||||
<FileConfiguration
|
||||
Name="Release|Xbox">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Profile|Xbox">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug|Xbox">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="makepath.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="splitpath.c">
|
||||
<FileConfiguration
|
||||
Name="Release|Xbox">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Profile|Xbox">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug|Xbox">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="splitpath.h">
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
10
CryAnimation/CryAnimation_XBox.vcproj.vspscc
Normal file
10
CryAnimation/CryAnimation_XBox.vcproj.vspscc
Normal file
@@ -0,0 +1,10 @@
|
||||
""
|
||||
{
|
||||
"FILE_VERSION" = "9237"
|
||||
"ENLISTMENT_CHOICE" = "NEVER"
|
||||
"PROJECT_FILE_RELATIVE_PATH" = ""
|
||||
"NUMBER_OF_EXCLUDED_FILES" = "0"
|
||||
"ORIGINAL_PROJECT_FILE_PATH" = "file:F:\\Crytek\\CryAnimation\\CryAnimation_XBox.vcproj"
|
||||
"NUMBER_OF_NESTED_PROJECTS" = "0"
|
||||
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT"
|
||||
}
|
||||
368
CryAnimation/CryBone.cpp
Normal file
368
CryAnimation/CryBone.cpp
Normal file
@@ -0,0 +1,368 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Sergiy Migdalskiy <sergiy@crytek.de>
|
||||
//
|
||||
// Content:
|
||||
// Single bone implementation
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <float.h>
|
||||
#include <StlUtils.h>
|
||||
#include "CryModel.h"
|
||||
#include "CryBoneInfo.h"
|
||||
#include "CryModelState.h"
|
||||
#include "ControllerManager.h"
|
||||
#include "ChunkFileReader.h"
|
||||
#include "STringUtils.h"
|
||||
#include "CVars.h"
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
#undef THIS_FILE
|
||||
static char THIS_FILE[] = __FILE__;
|
||||
#endif
|
||||
|
||||
CryBone::CryBone():
|
||||
m_bUseMatPlus (false),
|
||||
m_bUseReadyRelativeToParentMatrix (false),
|
||||
m_pParent(NULL)
|
||||
{
|
||||
//m_MatPlus.Identity();
|
||||
m_matRelativeToParent.SetIdentity();; // current result of animation
|
||||
|
||||
// zero orientation and rotation
|
||||
m_pqTransform.reset();
|
||||
}
|
||||
|
||||
void CryBone::setParent (CryModelState*pParent)
|
||||
{
|
||||
m_pParent = pParent;
|
||||
}
|
||||
|
||||
static inline bool isone (float x)
|
||||
{
|
||||
return x >= 0.98f && x < 1.02f;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// builds the relative to parent matrix
|
||||
void CryBone::BuildRelToParentFromQP (const IController::PQLog& pqNew)
|
||||
{
|
||||
if (m_bUseMatPlus)
|
||||
pqNew.buildMatrixPlusRot (m_matRelativeToParent, m_qRotPlus);
|
||||
else
|
||||
pqNew.buildMatrix (m_matRelativeToParent);
|
||||
|
||||
if (_isnan(m_matRelativeToParent[0][0]))
|
||||
g_GetLog()->LogWarning ("\001CryBone::BuildRelToParentFromQP: Invalid QP(q=%g,%g,%g;p=%g,%g,%g)", pqNew.vRotLog.x,pqNew.vRotLog.y,pqNew.vRotLog.z,pqNew.vPos.x,pqNew.vPos.y,pqNew.vPos.z);
|
||||
#ifdef _DEBUG
|
||||
if (g_GetCVars()->ca_Debug())
|
||||
{
|
||||
// we don't have translations that are millions of kilometers away
|
||||
assert (GetLengthSquared(m_matRelativeToParent.GetTranslationOLD()) < 1e22);
|
||||
// we should have the normal orthogonal and unitary matrix
|
||||
assert (isone(GetLengthSquared(m_matRelativeToParent.GetRow(0))));
|
||||
assert (isone(GetLengthSquared(m_matRelativeToParent.GetRow(1))));
|
||||
assert (isone(GetLengthSquared(m_matRelativeToParent.GetRow(2))));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// builds the m_pqTransform from the internal relative to parent matrix
|
||||
void CryBone::BuildQPFromRelToParent ()
|
||||
{
|
||||
m_pqTransform.assignFromMatrix(m_matRelativeToParent);
|
||||
}
|
||||
|
||||
// adds an offset to the bone relative to parent matrix
|
||||
void CryBone::AddOffsetRelToParent (const Vec3d& vOffset)
|
||||
{
|
||||
m_matRelativeToParent.AddTranslationOLD(vOffset);
|
||||
}
|
||||
|
||||
void CryBone::ScaleRelToParent (const Vec3d& vScale)
|
||||
{
|
||||
//m_matRelativeToParent *= fScale;
|
||||
Vec3d vPos = m_matRelativeToParent.GetRow(3);
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
m_matRelativeToParent.SetRow(i, vScale[i]*m_matRelativeToParent.GetRow(i));
|
||||
vPos[i] = vScale[i]*vPos[i];
|
||||
}
|
||||
m_matRelativeToParent.SetRow(3,vPos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vec3d CryBone::GetBonePosition()
|
||||
{
|
||||
return *(Vec3d*) (getMatrixGlobal()[3]);
|
||||
}
|
||||
|
||||
|
||||
Vec3d CryBone::GetBoneAxis(char cAxis)
|
||||
{
|
||||
assert (cAxis == 'x' || cAxis == 'y' || cAxis == 'z');
|
||||
switch(cAxis)
|
||||
{
|
||||
case 'x':
|
||||
case 'y':
|
||||
case 'z':
|
||||
return Vec3d(getMatrixGlobal()[cAxis - 'x']);
|
||||
break;
|
||||
}
|
||||
|
||||
return Vec3d(0,0,0);
|
||||
}
|
||||
|
||||
// checks for possible memory corruptions in this object and its children
|
||||
void CryBone::SelfValidate ()const
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
CryQuat quatAxis (int nAxis, float fDegrees)
|
||||
{
|
||||
struct {
|
||||
double cos;
|
||||
double sin;
|
||||
} x;
|
||||
cry_sincos( DEG2RAD(fDegrees)/2, &x.cos);
|
||||
CryQuat q;
|
||||
q.w = float(x.cos);
|
||||
switch (nAxis)
|
||||
{
|
||||
case 0: //x
|
||||
q.v.x = float(x.sin); q.v.y = q.v.z = 0;
|
||||
break;
|
||||
case 1: //y
|
||||
q.v.y = float(x.sin); q.v.x = q.v.z = 0;
|
||||
break;
|
||||
case 2: //z
|
||||
q.v.z = float(x.sin); q.v.x = q.v.y = 0;
|
||||
break;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// sets the plus-matrix rotation components.
|
||||
// the plus-matrix is used to rotate the upper body in response
|
||||
// to small rotation of the head (in order to avoid to frequent
|
||||
// rotation of the whole body) and is set by the game code
|
||||
//
|
||||
void CryBone::SetPlusRotation(float dX, float dY, float dZ)
|
||||
{
|
||||
if (g_GetCVars()->ca_NoMatPlus() || (dX == 0 && dY == 0 && dZ == 0))
|
||||
{
|
||||
m_bUseMatPlus = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isSane(dX) || !isSane(dY) || !isSane(dZ))
|
||||
{
|
||||
m_bUseMatPlus = false;
|
||||
g_GetLog()->LogError ("\003CryBone::SetPlusRotation: insane value passed as an angle(%g,%g,%g); ignoring.",dX, dY,dZ);
|
||||
return;
|
||||
}
|
||||
|
||||
m_bUseMatPlus = true;
|
||||
m_qRotPlus =
|
||||
quatAxis (0, -dX) * quatAxis(1, -dY) * quatAxis(2, -dZ);
|
||||
|
||||
if (g_GetCVars()->ca_MatPlusDebug())
|
||||
{
|
||||
if (fabs(dX) > 5 || fabs(dY) > 5 || fabs(dZ) > 5)
|
||||
g_GetLog()->LogToFile ("\005 MatPlus(%s, %s)(%.1f,%.1f,%.1f)", getBoneInfo()->getNameCStr(), m_bUseReadyRelativeToParentMatrix?"TM<-outside":"normal TM", dX,dY,dZ);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// sets the plus-matrix rotation components.
|
||||
// the plus-matrix is used to rotate the upper body in response
|
||||
// to small rotation of the head (in order to avoid to frequent
|
||||
// rotation of the whole body) and is set by the game code
|
||||
//
|
||||
void CryBone::SetPlusRotation(const CryQuat& qRot)
|
||||
{
|
||||
if (g_GetCVars()->ca_NoMatPlus() || (qRot.v.x == 0 && qRot.v.y == 0 && qRot.v.z == 0))
|
||||
{
|
||||
m_bUseMatPlus = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isSane(qRot.v) || !isSane(qRot.w))
|
||||
{
|
||||
g_GetLog()->LogError ("\001CryBone::SetPlusRotation: insane quaternion{%g,%g,%g,%g}",qRot.w,qRot.v.x,qRot.v.y,qRot.v.z);
|
||||
return;
|
||||
}
|
||||
|
||||
float fUnit = qRot|qRot;
|
||||
if (fUnit < 0.9f || fUnit > 1.1f)
|
||||
{
|
||||
m_bUseMatPlus = false;
|
||||
g_GetLog()->LogError ("\003CryBone::SetPlusRotation(CryQuat): non-normalized quaternion {%g,%g,%g,%g} passed; ignoring.",qRot.w,qRot.v.x,qRot.v.y,qRot.v.z);
|
||||
return;
|
||||
}
|
||||
|
||||
m_bUseMatPlus = true;
|
||||
m_qRotPlus = qRot;
|
||||
}
|
||||
|
||||
// returns the parent world coordinate system rotation as a quaternion
|
||||
CryQuat CryBone::GetParentWQuat ()
|
||||
{
|
||||
if (!getBoneInfo()->hasParent())
|
||||
return CryQuat (1,0,0,0);
|
||||
|
||||
const Matrix44& matParent = getParent()->getMatrixGlobal();
|
||||
//M2Q_CHANGED_BY_IVO
|
||||
//CryQuat qResult = CovertMatToQuat<float>(matParent);
|
||||
CryQuat qResult = Quat(matParent);
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (g_GetCVars()->ca_DebugGetParentWQuat())
|
||||
{
|
||||
Matrix44 matTest;
|
||||
|
||||
//Q2M_CHANGED_BY_IVO
|
||||
//qResult.GetMatrix(matTest);
|
||||
matTest=GetTransposed44(Matrix33(qResult));
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
for (int j = 0; j < 3; ++j)
|
||||
assert (fabs(matTest(i,j) - matParent(i,j)) < 0.02f);
|
||||
}
|
||||
#endif
|
||||
|
||||
return qResult;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// resets the plus-matrix to identity, so that the body is not rotated
|
||||
// additionally to the currently played animation.
|
||||
void CryBone::ResetPlusRotation()
|
||||
{
|
||||
m_bUseMatPlus = false;
|
||||
//m_MatPlus.Identity();
|
||||
m_qRotPlus = CryQuat(1, 0,0,0);
|
||||
}
|
||||
|
||||
Matrix44& CryBone::getMatrixGlobal ()
|
||||
{
|
||||
return m_pParent->getBoneMatrixGlobal (this);
|
||||
}
|
||||
|
||||
const Matrix44& CryBone::getMatrixGlobal ()const
|
||||
{
|
||||
return m_pParent->getBoneMatrixGlobal (this);
|
||||
}
|
||||
|
||||
CryBoneInfo* CryBone::getBoneInfo()
|
||||
{
|
||||
return m_pParent->getBoneInfo(this);
|
||||
}
|
||||
const CryBoneInfo* CryBone::getBoneInfo()const
|
||||
{
|
||||
return m_pParent->getBoneInfo(this);
|
||||
}
|
||||
|
||||
// fixes the bone matrix to the given position in world coordinates,
|
||||
// assuming the character position and orientation are given by the vCharPos and vCharAngles
|
||||
// vCharAngles are the same as in the entity and in the Draw call to ICryCharInstance
|
||||
void CryBone::FixBoneOriginInWorld (const Vec3d& vCharPos, const Vec3d& vCharAngles, const Vec3d& vTargetOrigin)
|
||||
{
|
||||
using namespace CryStringUtils;
|
||||
m_bUseReadyRelativeToParentMatrix = false; // assume the safety checks won't pass
|
||||
if (!isSane(vCharPos) || !isSane(vCharAngles) || !isSane(vTargetOrigin))
|
||||
{
|
||||
g_GetLog()->LogError ("\001CryBone::FixBoneOriginInWorld: insane input vCharPos = %s, vCharAngles = %s, vTargetOrigin = %s", toString(vCharPos).c_str(), toString(vCharAngles).c_str(), toString(vTargetOrigin).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// make tran&rot matrix
|
||||
//Matrix44 matChar;
|
||||
//matChar.Identity();
|
||||
//matChar = GetTranslationMat(vCharPos)*matChar;
|
||||
//matChar = GetRotationZYX44(-gf_DEGTORAD*vCharAngles )*matChar; //NOTE: angles in radians and negated
|
||||
|
||||
//OPTIMIZED_BY_IVO
|
||||
Matrix44 matChar=Matrix34::CreateRotationXYZ( Deg2Rad(vCharAngles),vCharPos );
|
||||
matChar = GetTransposed44(matChar); //TODO: remove this after E3 and use Matrix34 instead of Matrix44
|
||||
|
||||
Matrix44 matParentBone;
|
||||
matParentBone.SetIdentity();
|
||||
for (CryBone* pParent = getParent(); pParent; pParent = pParent->getParent())
|
||||
matParentBone = matParentBone * pParent->m_matRelativeToParent;
|
||||
|
||||
Matrix44 matInvParentBone = OrthoUniformGetInverted(matParentBone/*getParent()->getMatrixGlobal()*/*matChar);
|
||||
m_matRelativeToParent.SetTranslationOLD(matInvParentBone.TransformPointOLD(vTargetOrigin));
|
||||
m_bUseReadyRelativeToParentMatrix = true;
|
||||
}
|
||||
|
||||
// Sets the bone matrix to the given position in world coordinates, only for this frame
|
||||
// assuming the character position and orientation are given by the vCharPos and vCharAngles
|
||||
// vCharAngles are the same as in the entity and in the Draw call to ICryCharInstance
|
||||
void CryBone::SetBoneOriginInWorld (const Vec3d& vCharPos, const Vec3d& vCharAngles, const Vec3d& vTargetOrigin)
|
||||
{
|
||||
using namespace CryStringUtils;
|
||||
if (!isSane(vCharPos) || !isSane(vCharAngles) || !isSane(vTargetOrigin))
|
||||
{
|
||||
g_GetLog()->LogError ("\001CryBone::SetBoneOriginInWorld: insane input vCharPos = %s, vCharAngles = %s, vTargetOrigin = %s", toString(vCharPos).c_str(), toString(vCharAngles).c_str(), toString(vTargetOrigin).c_str());
|
||||
return;
|
||||
}
|
||||
// make tran&rot matrix
|
||||
// Matrix44 matChar;
|
||||
// matChar.Identity();
|
||||
// matChar = GetTranslationMat(vCharPos)*matChar;
|
||||
// matChar = GetRotationZYX44(-gf_DEGTORAD*vCharAngles )*matChar; //NOTE: angles in radians and negated
|
||||
|
||||
//OPTIMIZED_BY_IVO
|
||||
Matrix44 matChar=Matrix34::CreateRotationXYZ( Deg2Rad(vCharAngles),vCharPos );
|
||||
matChar = GetTransposed44(matChar); //TODO: remove this after E3 and use Matrix34 instead of Matrix44
|
||||
|
||||
Matrix44 matInvChar = OrthoUniformGetInverted(matChar);
|
||||
getMatrixGlobal().SetTranslationOLD(matInvChar.TransformPointOLD(vTargetOrigin));
|
||||
}
|
||||
|
||||
|
||||
void CryBone::FixBoneMatrix (const Matrix44& mtxBone)
|
||||
{
|
||||
using namespace CryStringUtils;
|
||||
// suppose we won't accept this matrix
|
||||
m_bUseReadyRelativeToParentMatrix = false;
|
||||
if (!(mtxBone(0,3)==0 && mtxBone(1,3)==0 && mtxBone(2,3)==0 && mtxBone(3,3)==1))
|
||||
{
|
||||
g_GetLog()->LogError ("\001CryBone::FixBoneMatrix: invalid matrix last column {%g,%g,%g,%g}; expected{0,0,0,1}",mtxBone(0,3),mtxBone(1,3),mtxBone(2,3),mtxBone(3,3));
|
||||
return;
|
||||
}
|
||||
|
||||
Vec3d vTrans = mtxBone.GetRow(3);
|
||||
if (!isSane(vTrans))
|
||||
{
|
||||
g_GetLog()->LogError ("\001CryBone::FixBoneMatrix: insane translation vector %s",toString(vTrans).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
Vec3d v = mtxBone.GetRow(i);
|
||||
if (!isUnit(v,0.5f))
|
||||
{
|
||||
g_GetLog()->LogError ("\001CryBone::FixBoneMatrix: matrix is not orthonormal, row %d :%s", i, toString(v).c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_matRelativeToParent = mtxBone;
|
||||
m_bUseReadyRelativeToParentMatrix = true;
|
||||
}
|
||||
115
CryAnimation/CryBone.h
Normal file
115
CryAnimation/CryBone.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Oscar Blasco
|
||||
// Taken over by Vladimir Kajalin, Andrey Honich
|
||||
// Taken over by Sergiy Migdalskiy <sergiy@crytek.de>
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _BONE_H
|
||||
#define _BONE_H
|
||||
|
||||
#include "CryHeaders.h"
|
||||
#include "Controller.h"
|
||||
#include "AnimationLayerInfo.h"
|
||||
#include "CryBoneInfo.h"
|
||||
|
||||
class CryModel;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// The bone class contains bone matrices and pointers to it parent and child<6C>s.
|
||||
// Also every bone has pointer to array of controllers for this bone.
|
||||
// Bone uses controller to get position and orientation of bone for current animation frame.
|
||||
class CryBone : public ICryBone
|
||||
{
|
||||
friend class CryModel;
|
||||
public:
|
||||
CryBone();
|
||||
|
||||
void setParent (CryModelState*pParent);
|
||||
|
||||
// builds the relative to parent matrix
|
||||
void BuildRelToParentFromQP (const IController::PQLog& pqNew);
|
||||
|
||||
// builds the m_pqTransform from the internal relative to parent matrix
|
||||
void BuildQPFromRelToParent ();
|
||||
|
||||
// adds an offset to the bone relative to parent matrix
|
||||
void AddOffsetRelToParent (const Vec3& vOffset);
|
||||
void ScaleRelToParent (const Vec3& fScale);
|
||||
|
||||
unsigned numChildren ()const {return getBoneInfo()->numChildren();}
|
||||
CryBone* getChild (unsigned i) {assert(i < numChildren()); return this + getBoneInfo()->m_nOffsetChildren + i;}
|
||||
const CryBone* getChild (unsigned i) const {assert(i < numChildren()); return this + getBoneInfo()->m_nOffsetChildren + i;}
|
||||
CryBone* getParent () {return getBoneInfo()->m_nOffsetParent ? this + getBoneInfo()->m_nOffsetParent : NULL;}
|
||||
int getParentIndexOffset()const {return getBoneInfo()->m_nOffsetParent;}
|
||||
const CryBone* getParent () const {return getBoneInfo()->m_nOffsetParent ? this + getBoneInfo()->m_nOffsetParent : NULL;}
|
||||
|
||||
ICryBone* GetParent() {return getParent();}
|
||||
// returns the matrix relative to parent
|
||||
virtual const Matrix44& GetRelativeMatrix() {return m_matRelativeToParent;}
|
||||
// returns the matrix in object coordinates
|
||||
virtual const Matrix44& GetAbsoluteMatrix() {return getMatrixGlobal();}
|
||||
// fixes the bone matrix to the given position in world coordinates,
|
||||
// assuming the character position and orientation are given by the vCharPos and vCharAngles
|
||||
// vCharAngles are the same as in the entity and in the Draw call to ICryCharInstance
|
||||
virtual void FixBoneOriginInWorld (const Vec3& vCharPos, const Vec3& vCharAngles, const Vec3& vTargetOrigin);
|
||||
virtual void SetBoneOriginInWorld (const Vec3& vCharPos, const Vec3& vCharAngles, const Vec3& vTargetOrigin);
|
||||
private:
|
||||
// only for debugging/logging purposes
|
||||
CryBoneInfo* getBoneInfo();
|
||||
const CryBoneInfo* getBoneInfo()const;
|
||||
|
||||
CryModelState* m_pParent;
|
||||
public:
|
||||
// ICryBone imlpementation
|
||||
const Matrix44& GetGlobalMatrix() const {return getMatrixGlobal();}
|
||||
|
||||
// sets the plus-matrix rotation components.
|
||||
// the plus-matrix is used to rotate the upper body in response to small rotation of the head
|
||||
// (in order to avoid to frequent rotation of the whole body) and is set by the game code
|
||||
void SetPlusRotation(float x, float y, float z);
|
||||
void SetPlusRotation(const CryQuat& qRotation);
|
||||
|
||||
// resets the plus-matrix to identity, so that the body is not rotated additionally to the currently played animation.
|
||||
void ResetPlusRotation() ;
|
||||
|
||||
virtual Vec3 GetBonePosition();
|
||||
virtual Vec3 GetBoneAxis(char cAxis);
|
||||
// returns the parent world coordinate system rotation as a quaternion
|
||||
virtual CryQuat GetParentWQuat ();
|
||||
|
||||
// todo: clear this up
|
||||
virtual void DoNotCalculateBoneRelativeMatrix(bool bDoNotCalculate)
|
||||
{ m_bUseReadyRelativeToParentMatrix = bDoNotCalculate; };
|
||||
|
||||
// remembers the given matrix as the bone's matrix (relative to parent) and from now on
|
||||
// doesn't change it. To reset this effect, call DoNotCalculateBoneRelativeMatrix(false)
|
||||
void FixBoneMatrix (const Matrix44& mtxBone);
|
||||
|
||||
bool
|
||||
// if this is true, then the relative to parent matrix will be set from outside (by the lipsync at the moment)
|
||||
m_bUseReadyRelativeToParentMatrix
|
||||
// if this is false, we don't have to use the matPlus which is set from the outside, and can assume it's identity
|
||||
,m_bUseMatPlus
|
||||
;
|
||||
|
||||
// relative to parent position and orientation of the bone
|
||||
IController::PQLog m_pqTransform;
|
||||
|
||||
Matrix44 m_matRelativeToParent; // current result of animation
|
||||
//Matrix m_matGlobal; // used by physics
|
||||
Matrix44& getMatrixGlobal ();
|
||||
const Matrix44& getMatrixGlobal ()const;
|
||||
|
||||
CryQuat m_qRotPlus; // additional rotation set by the game; this is the logarithm of the quaternion representing the rotation
|
||||
|
||||
// checks for possible memory corruptions in this object and its children
|
||||
void SelfValidate ()const;
|
||||
};
|
||||
|
||||
|
||||
#endif // _BONE_H
|
||||
186
CryAnimation/CryBoneDesc.cpp
Normal file
186
CryAnimation/CryBoneDesc.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#include "stdafx.h"
|
||||
#include "MathUtils.h"
|
||||
#include "CryBoneDesc.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
using namespace CryStringUtils;
|
||||
|
||||
CryBoneDesc::CryBoneDesc()
|
||||
{
|
||||
m_PhysInfo[0].pPhysGeom = m_PhysInfo[1].pPhysGeom = 0;
|
||||
m_matInvDefGlobal.SetIdentity();; // allows to get difference to def pose matrices
|
||||
}
|
||||
|
||||
CryBoneDesc::~CryBoneDesc()
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// loads the bone from a raw chunk data (no header)
|
||||
// PARAMETERS:
|
||||
// pEntity - the chunk data to load the bone info from
|
||||
// RETURNS:
|
||||
// false if the bone couldn't be loaded
|
||||
bool CryBoneDesc::LoadRaw (const BONE_ENTITY* pEntity)
|
||||
{
|
||||
//read bone info
|
||||
assert(pEntity->nChildren<200);
|
||||
|
||||
// update the lod 0 physics of this bone
|
||||
UpdatePhysics (*pEntity, 0);
|
||||
|
||||
//get bone info
|
||||
m_nControllerID = pEntity->ControllerID;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// updates this bone physics, from the given entity descriptor, and of the given lod
|
||||
void CryBoneDesc::UpdatePhysics (const BONE_ENTITY& entity, int nLod)
|
||||
{
|
||||
assert (nLod >= 0 && nLod < SIZEOF_ARRAY(m_PhysInfo));
|
||||
CopyPhysInfo(m_PhysInfo[nLod], entity.phys);
|
||||
|
||||
int nFlags = 0;
|
||||
if (entity.prop[0])
|
||||
{
|
||||
nFlags = joint_no_gravity|joint_isolated_accelerations;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!strnstr(entity.prop,"gravity", sizeof(entity.prop)))
|
||||
nFlags |= joint_no_gravity;
|
||||
|
||||
if (!strnstr(entity.prop,"physical",sizeof(entity.prop)))
|
||||
nFlags |= joint_isolated_accelerations;
|
||||
}
|
||||
|
||||
m_PhysInfo[nLod].flags |= nFlags;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//! Performs post-initialization. This step is requred to initialize the pPhysGeom of the bones
|
||||
//! After the bone has been loaded but before it is first used. When the bone is first loaded, pPhysGeom
|
||||
//! is set to the value equal to the chunk id in the file where the physical geometry (BoneMesh) chunk is kept.
|
||||
//! After those chunks are loaded, and chunk ids are mapped to the registered physical geometry objects,
|
||||
//! call this function to replace pPhysGeom chunk ids with the actual physical geometry object pointers.
|
||||
//! RETURNS:
|
||||
// true if the corresponding physical geometry object has been found
|
||||
//! NOTE:
|
||||
//! The entries of the map that were used are deleted
|
||||
bool CryBoneDesc::PostInitPhysGeom (ChunkIdToPhysGeomMap& mapChunkIdToPhysGeom, int nLodLevel)
|
||||
{
|
||||
phys_geometry*& pPhysGeom = m_PhysInfo[nLodLevel].pPhysGeom;
|
||||
ChunkIdToPhysGeomMap::iterator it = mapChunkIdToPhysGeom.find ((INT_PTR)pPhysGeom);
|
||||
if (it != mapChunkIdToPhysGeom.end())
|
||||
{
|
||||
// remap the chunk id to the actual pointer to the geometry
|
||||
pPhysGeom = it->second;
|
||||
mapChunkIdToPhysGeom.erase (it);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pPhysGeom = NULL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// sets the name of the bone out of the given buffer of the given max size
|
||||
void CryBoneDesc::setName (const char* szName)
|
||||
{
|
||||
m_strName.assign (szName);
|
||||
|
||||
static const char *g_arrLimbNames[4] = { "L UpperArm","R UpperArm","L Thigh","R Thigh" };
|
||||
m_nLimbId = -1;
|
||||
for (int j=0; j < SIZEOF_ARRAY(g_arrLimbNames) ;j++)
|
||||
if (strstr(szName,g_arrLimbNames[j]))
|
||||
{
|
||||
m_nLimbId = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// returns the bone name, if available
|
||||
const char* CryBoneDesc::getNameCStr()const
|
||||
{
|
||||
return m_strName.c_str();
|
||||
}
|
||||
|
||||
const string& CryBoneDesc::getName()const
|
||||
{
|
||||
return m_strName;
|
||||
}
|
||||
|
||||
// compares two bone descriptions and returns true if they're the same bone
|
||||
// (the same name and the same position in the hierarchy)
|
||||
bool CryBoneDesc::isEqual (const CryBoneDesc& desc)const
|
||||
{
|
||||
return m_strName == desc.m_strName
|
||||
&& m_nControllerID == desc.m_nControllerID
|
||||
&& m_nOffsetParent == desc.m_nOffsetParent
|
||||
&& m_numChildren == desc.m_numChildren
|
||||
&& m_nOffsetChildren == desc.m_nOffsetChildren;
|
||||
}
|
||||
|
||||
|
||||
inline size_t align4 (size_t x)
|
||||
{
|
||||
return (x + 3)&~3;
|
||||
}
|
||||
|
||||
// Serializes the description:
|
||||
// returns the number of required bytes for serialization, if the data pointer is NULL
|
||||
// returns 0 (if the buffer size is not enough) or the number of bytes written, if the data pointer is given
|
||||
unsigned CryBoneDesc::Serialize (bool bSave, void *pStream, unsigned nSize)
|
||||
{
|
||||
if (bSave)
|
||||
{
|
||||
unsigned nSizeRequired = (unsigned)(sizeof(CryBoneDescData_Comp) + align4 (m_strName.length()+1));
|
||||
if (!pStream)
|
||||
return nSizeRequired;
|
||||
if (nSize < nSizeRequired)
|
||||
return 0;
|
||||
|
||||
CopyCryBone(*(CryBoneDescData_Comp*)pStream, *this);
|
||||
memcpy ((CryBoneDescData_Comp*)pStream+1,m_strName.c_str(),m_strName.length()+1);
|
||||
return nSizeRequired;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!pStream)
|
||||
return 0;
|
||||
if (nSize < sizeof(CryBoneDescData_Comp)+1)
|
||||
return 0;
|
||||
if (nSize & 3)
|
||||
return 0; // alignment error
|
||||
CopyCryBone(*this, *(CryBoneDescData_Comp*)pStream);
|
||||
// end of the stream
|
||||
const char* pEnd = (const char*)pStream + nSize;
|
||||
// the start byte of the bone name
|
||||
const char* pName = (const char*)((CryBoneDescData_Comp*)pStream+1);
|
||||
|
||||
// scan until the end of the name (\0 char) or the stream (pEnd address)
|
||||
const char *pNameEnd;
|
||||
for (pNameEnd = pName; pNameEnd < pEnd && *pNameEnd; ++pNameEnd);
|
||||
m_strName.assign (pName, pNameEnd);
|
||||
// return aligned size of the chunk including the string 0 terminator, but
|
||||
// no more than the declared size of the stream
|
||||
return min(nSize, (unsigned)align4 (pNameEnd+1-(const char*)pStream));
|
||||
}
|
||||
}
|
||||
|
||||
void CryBoneDesc::setDefaultGlobal(const Matrix44& matDefault)
|
||||
{
|
||||
m_matInvDefGlobal=OrthoUniformGetInverted (matDefault);
|
||||
}
|
||||
|
||||
|
||||
// scales the bone with the given multiplier
|
||||
void CryBoneDesc::scale (float fScale)
|
||||
{
|
||||
m_matInvDefGlobal.SetTranslationOLD(m_matInvDefGlobal.GetTranslationOLD()*fScale);
|
||||
}
|
||||
170
CryAnimation/CryBoneDesc.h
Normal file
170
CryAnimation/CryBoneDesc.h
Normal file
@@ -0,0 +1,170 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// File:CryBoneDesc.h
|
||||
//
|
||||
// History:
|
||||
// Created by Sergiy Migdalskiy 01/14/2003
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef _CRY_BONE_DESC_HDR_
|
||||
#define _CRY_BONE_DESC_HDR_
|
||||
|
||||
// this class contains the information that's common to all instances of bones
|
||||
// in the given model: like bone name, and misc. shared properties
|
||||
// We use a base structure to separate the bunch of parameters that get serialized
|
||||
// together
|
||||
#pragma pack(push,4)
|
||||
template <class TBonePhysics>
|
||||
struct TCryBoneDescData
|
||||
{
|
||||
unsigned int m_nControllerID; // unic id of bone (generated from bone name in the max)
|
||||
|
||||
// [Sergiy] physics info for different lods
|
||||
// lod 0 is the physics of alive body, lod 1 is the physics of a dead body
|
||||
TBonePhysics m_PhysInfo[2];
|
||||
float m_fMass;
|
||||
|
||||
Matrix44 m_matInvDefGlobal; // allows to get difference to def pose matrices
|
||||
|
||||
int m_nLimbId; // set by model state class
|
||||
|
||||
// this bone parent is this[m_nOffsetParent], 0 if the bone is root. Normally this is <= 0
|
||||
int m_nOffsetParent;
|
||||
|
||||
// The whole hierarchy of bones is kept in one big array that belongs to the ModelState
|
||||
// Each bone that has children has its own range of bone objects in that array,
|
||||
// and this points to the beginning of that range and defines the number of bones.
|
||||
unsigned m_numChildren;
|
||||
// the beginning of the subarray of children is at this[m_nOffsetChildren]
|
||||
// this is 0 if there are no children
|
||||
int m_nOffsetChildren;
|
||||
};
|
||||
|
||||
typedef TCryBoneDescData<BONE_PHYSICS> CryBoneDescData;
|
||||
// compatible structure
|
||||
typedef TCryBoneDescData<BONE_PHYSICS_COMP> CryBoneDescData_Comp;
|
||||
|
||||
#define __copy(x) left.x = right.x
|
||||
|
||||
inline void CopyCryBone (CryBoneDescData_Comp& left, const CryBoneDescData& right)
|
||||
{
|
||||
__copy(m_nControllerID);
|
||||
CopyPhysInfo (left.m_PhysInfo[0], right.m_PhysInfo[0]);
|
||||
CopyPhysInfo (left.m_PhysInfo[1], right.m_PhysInfo[1]);
|
||||
__copy(m_fMass);
|
||||
__copy(m_matInvDefGlobal);
|
||||
__copy(m_nLimbId);
|
||||
__copy(m_nOffsetParent);
|
||||
__copy(m_numChildren);
|
||||
__copy(m_nOffsetChildren);
|
||||
}
|
||||
|
||||
inline void CopyCryBone (CryBoneDescData& left, const CryBoneDescData_Comp& right)
|
||||
{
|
||||
__copy(m_nControllerID);
|
||||
CopyPhysInfo (left.m_PhysInfo[0], right.m_PhysInfo[0]);
|
||||
CopyPhysInfo (left.m_PhysInfo[1], right.m_PhysInfo[1]);
|
||||
__copy(m_fMass);
|
||||
__copy(m_matInvDefGlobal);
|
||||
__copy(m_nLimbId);
|
||||
__copy(m_nOffsetParent);
|
||||
__copy(m_numChildren);
|
||||
__copy(m_nOffsetChildren);
|
||||
}
|
||||
|
||||
#undef __copy
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
|
||||
class CryBoneDesc: public CryBoneDescData
|
||||
{
|
||||
public:
|
||||
CryBoneDesc ();
|
||||
~CryBoneDesc ();
|
||||
|
||||
// returns the bone name, if available
|
||||
const char* getNameCStr()const;
|
||||
const string& getName()const;
|
||||
|
||||
unsigned getControllerId()const {return m_nControllerID;}
|
||||
|
||||
// sets the name of the bone out of the given buffer of the given max size
|
||||
void setName (const char* szName);
|
||||
|
||||
unsigned numChildren ()const {return m_numChildren;}
|
||||
bool hasParent() const {return m_nOffsetParent != 0;}
|
||||
int getParentIndexOffset()const {return m_nOffsetParent;}
|
||||
int getFirstChildIndexOffset() const {return m_nOffsetChildren;}
|
||||
|
||||
const Matrix44& getInvDefGlobal() const {return m_matInvDefGlobal;}
|
||||
void setDefaultGlobal(const Matrix44& mxDefault);
|
||||
int getLimbId () const {return m_nLimbId;}
|
||||
|
||||
BONE_PHYSICS& getPhysInfo (int nLod) {return m_PhysInfo[nLod];}
|
||||
|
||||
// updates this bone physics, from the given entity descriptor, and of the given lod
|
||||
void UpdatePhysics (const BONE_ENTITY& entity, int nLod);
|
||||
void setPhysics (int nLod, const BONE_PHYSICS& BonePhysics)
|
||||
{
|
||||
assert (nLod >= 0 && nLod < sizeof(m_PhysInfo)/sizeof(m_PhysInfo[0]));
|
||||
m_PhysInfo[nLod] = BonePhysics;
|
||||
}
|
||||
// the physics for the given LOD is not available
|
||||
void resetPhysics (int nLod)
|
||||
{
|
||||
assert (nLod >= 0 && nLod < sizeof(m_PhysInfo)/sizeof(m_PhysInfo[0]));
|
||||
memset (&m_PhysInfo[nLod], 0, sizeof(m_PhysInfo[nLod]));
|
||||
}
|
||||
const BONE_PHYSICS& getPhysics (int nLod)const
|
||||
{
|
||||
assert (nLod >= 0 && nLod < sizeof(m_PhysInfo)/sizeof(m_PhysInfo[0]));
|
||||
return m_PhysInfo[nLod];
|
||||
}
|
||||
|
||||
// Returns the id of the bone mesh chunk from which the bone physical geometry
|
||||
// should be taken. The caller should take this id, find the corresponding chunk in the CCG/CGF file
|
||||
// and construct physical geometry using IGeomManager::CreateMesh.
|
||||
// Then, register it with RegisterGeometry(). It will return phys_geometry* that the caller
|
||||
// should put into the corresponding LOD m_PhysInfo.pPhysGeom
|
||||
// CryBoneInfo::PostInitPhysGeom uses this same id to find the physical geometry in the map
|
||||
INT_PTR getPhysGeomId (unsigned nLOD) {return (INT_PTR)m_PhysInfo[nLOD].pPhysGeom;}
|
||||
|
||||
// compares two bone descriptions and returns true if they're the same bone
|
||||
// (the same name and the same position in the hierarchy)
|
||||
bool isEqual(const CryBoneDesc& desc)const;
|
||||
|
||||
// Serializes the description:
|
||||
// returns the number of required bytes for serialization, if the data pointer is NULL
|
||||
// returns 0 (if the buffer size is not enough) or the number of bytes written, if the data pointer is given
|
||||
unsigned Serialize (bool bSave, void *pStream, unsigned nSize);
|
||||
|
||||
// scales the bone with the given multiplier
|
||||
void scale (float fScale);
|
||||
|
||||
protected:
|
||||
|
||||
// loads the bone from a raw chunk data (no header)
|
||||
// returns false if the bone couldn't be loaded
|
||||
bool LoadRaw (const BONE_ENTITY* pEntity);
|
||||
|
||||
//! Performs post-initialization. This step is requred to initialize the pPhysGeom of the bones
|
||||
//! After the bone has been loaded but before it is first used. When the bone is first loaded, pPhysGeom
|
||||
//! is set to the value equal to the chunk id in the file where the physical geometry (BoneMesh) chunk is kept.
|
||||
//! After those chunks are loaded, and chunk ids are mapped to the registered physical geometry objects,
|
||||
//! call this function to replace pPhysGeom chunk ids with the actual physical geometry object pointers.
|
||||
//! NOTE:
|
||||
//! The entries of the map that were used are deleted
|
||||
typedef std::map<INT_PTR, struct phys_geometry*> ChunkIdToPhysGeomMap;
|
||||
bool PostInitPhysGeom (ChunkIdToPhysGeomMap& mapChunkIdToPhysGeom, int nLodLevel);
|
||||
|
||||
friend class CryBoneHierarchyLoader;
|
||||
|
||||
protected:
|
||||
string m_strName;
|
||||
};
|
||||
|
||||
#endif
|
||||
228
CryAnimation/CryBoneHierarchyLoader.cpp
Normal file
228
CryAnimation/CryBoneHierarchyLoader.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// 22 Sep 2002 :- Created by Sergiy Migdalskiy <sergiy@crytek.de>
|
||||
//
|
||||
// Contains:
|
||||
// class - loading context - responsible for loading the linearized bone hierarchy from the BONEANIM
|
||||
// chunk
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "MathUtils.h"
|
||||
#include "CryBoneHierarchyLoader.h"
|
||||
#include "ChunkFileReader.h"
|
||||
#include "CgfUtils.h"
|
||||
|
||||
CryBoneHierarchyLoader::CryBoneHierarchyLoader ():
|
||||
#ifdef DEBUG_STD_CONTAINERS
|
||||
m_arrBones("CryBoneHierarchyLoader.Bones"),
|
||||
m_arrIdToIndex ("CryBoneHierarchyLoader.indexidmaps"),
|
||||
m_arrIndexToId ("CryBoneHierarchyLoader.indexidmaps"),
|
||||
#endif
|
||||
m_pChunkBoneAnim(NULL),
|
||||
m_pChunkBoneAnimSize(0),
|
||||
|
||||
m_pBoneAnimRawData (NULL),
|
||||
m_pBoneAnimRawDataEnd (NULL),
|
||||
m_szLastError ("")
|
||||
{
|
||||
}
|
||||
|
||||
unsigned CryBoneHierarchyLoader::load (const BONEANIM_CHUNK_DESC* pChunk, unsigned nChunkSize)
|
||||
{
|
||||
m_arrBones.clear();
|
||||
m_arrIndexToId.clear();
|
||||
m_arrIdToIndex.clear();
|
||||
|
||||
m_pChunkBoneAnim = pChunk;
|
||||
m_pChunkBoneAnimSize = nChunkSize;
|
||||
|
||||
m_pBoneAnimRawData = pChunk+1;
|
||||
m_pBoneAnimRawDataEnd = ((const char*) pChunk) + nChunkSize;
|
||||
|
||||
if (nChunkSize < sizeof(*pChunk))
|
||||
{
|
||||
m_szLastError = "Couldn't read the data." ;
|
||||
m_pBoneAnimRawData = pChunk;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pChunk->nBones <= 0)
|
||||
{
|
||||
m_szLastError = "There must be at least one bone.";
|
||||
m_pBoneAnimRawData = pChunk;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const BONE_ENTITY* pBones = (const BONE_ENTITY*) (pChunk+1);
|
||||
if (m_pBoneAnimRawDataEnd < (const byte*)(pBones + pChunk->nBones))
|
||||
{
|
||||
m_szLastError = "Premature end of data.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check for one-and-only-one root rule
|
||||
if (pBones[0].ParentID != -1)
|
||||
{
|
||||
m_szLastError = "The first bone in the hierarchy has a parent, but it is expected to be the root bone.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 1; i < pChunk->nBones; ++i)
|
||||
{
|
||||
if (pBones[i].ParentID == -1)
|
||||
{
|
||||
m_szLastError = "The skeleton has multiple roots. Only single-rooted skeletons are supported in this version.";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
m_arrBones.resize (numBones());
|
||||
m_arrIndexToId.resize (numBones(), -1);
|
||||
m_arrIdToIndex.resize (numBones(), -1);
|
||||
m_nNextBone = 0;
|
||||
|
||||
int nRootBoneIndex = (int)allocateBones (1);
|
||||
|
||||
if (nRootBoneIndex < 0 || !load(nRootBoneIndex, nRootBoneIndex))
|
||||
m_pBoneAnimRawData = pChunk;
|
||||
|
||||
updateInvDefGlobalMatrices();
|
||||
return (const byte*)m_pBoneAnimRawData - (const byte*)m_pChunkBoneAnim;
|
||||
}
|
||||
|
||||
// updates the bone InvDefGlobal matrices, if the default pose matrices
|
||||
// are available
|
||||
void CryBoneHierarchyLoader::updateInvDefGlobalMatrices()
|
||||
{
|
||||
if (m_arrInitPose.empty() || m_arrBones.empty())
|
||||
return;
|
||||
|
||||
if (m_arrInitPose.size() != m_arrBones.size())
|
||||
{
|
||||
#ifdef _CRY_ANIMATION_BASE_HEADER_
|
||||
g_GetLog()->LogError ("\003there are %d bones and %d initial pose matrices. ignoring matrices.", m_arrBones.size(), m_arrInitPose.size());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned nBone = 0; nBone < m_arrBones.size(); ++nBone)
|
||||
{
|
||||
unsigned nBoneID = mapIndexToId(nBone);
|
||||
m_arrBones[nBone].setDefaultGlobal (m_arrInitPose[nBoneID]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// loads the default positions of each bone; if the bone chunk is loaded,
|
||||
// updates the bone inverse default pose matrices
|
||||
// returns number of bytes read if successful, 0 if not
|
||||
unsigned CryBoneHierarchyLoader::load (const BONEINITIALPOS_CHUNK_DESC_0001*pChunk, unsigned nChunkSize)
|
||||
{
|
||||
if (nChunkSize < sizeof(*pChunk) || pChunk->numBones < 0 || pChunk->numBones > 0x10000)
|
||||
return 0;
|
||||
const char* pChunkEnd = ((const char*)pChunk) + nChunkSize;
|
||||
const SBoneInitPosMatrix* pDefMatrix = (const SBoneInitPosMatrix*)(pChunk+1);
|
||||
|
||||
// the end of utilized data in the chunk
|
||||
const char* pUtilizedEnd = (const char*)(pDefMatrix + pChunk->numBones);
|
||||
if (pUtilizedEnd > pChunkEnd)
|
||||
return 0;
|
||||
|
||||
m_arrInitPose.resize (pChunk->numBones);
|
||||
for (unsigned nBone = 0; nBone < pChunk->numBones; ++nBone)
|
||||
{
|
||||
Matrix44& matBone = m_arrInitPose[nBone];
|
||||
copyMatrix (matBone,pDefMatrix[nBone]);
|
||||
// for some reason Max supplies unnormalized matrices.
|
||||
matBone.NoScale();
|
||||
}
|
||||
|
||||
updateInvDefGlobalMatrices();
|
||||
return pUtilizedEnd - (const char*)pChunk;
|
||||
}
|
||||
|
||||
void CryBoneHierarchyLoader::scale (float fScale)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < m_arrInitPose.size(); ++i)
|
||||
m_arrInitPose[i].ScaleTranslationOLD (fScale);
|
||||
|
||||
updateInvDefGlobalMatrices();
|
||||
}
|
||||
|
||||
// allocates the required number of bones in the plain hierarchy array, starting at the next available place
|
||||
// -1 if couldn't allocate
|
||||
int CryBoneHierarchyLoader::allocateBones(int numBones)
|
||||
{
|
||||
if (m_nNextBone + numBones > (int)this->numBones())
|
||||
return -1; // the request is for too many bones
|
||||
|
||||
int nResult = m_nNextBone;
|
||||
m_nNextBone += numBones;
|
||||
assert (m_nNextBone <= (int)this->numBones());
|
||||
return nResult;
|
||||
}
|
||||
|
||||
|
||||
// loads the whole hierarchy of bones, using the state machine
|
||||
// when this funciton is called, the bone is already allocated
|
||||
bool CryBoneHierarchyLoader::load (int nBoneParentIndex, int nBoneIndex)
|
||||
{
|
||||
const BONE_ENTITY* pEntity;
|
||||
if (!EatRawDataPtr(pEntity, 1, m_pBoneAnimRawData, m_pBoneAnimRawDataEnd))
|
||||
return false;
|
||||
|
||||
// initialize the next bone
|
||||
CryBoneDesc& rBoneDesc = m_arrBones[nBoneIndex];
|
||||
if (!rBoneDesc.LoadRaw (pEntity))
|
||||
return false;
|
||||
|
||||
// set the mapping entries
|
||||
m_arrIndexToId[nBoneIndex] = pEntity->BoneID;
|
||||
m_arrIdToIndex[pEntity->BoneID] = nBoneIndex;
|
||||
|
||||
rBoneDesc.m_nOffsetParent = nBoneParentIndex - nBoneIndex;
|
||||
|
||||
// load children
|
||||
if (pEntity->nChildren)
|
||||
{
|
||||
int nChildrenIndexBase = allocateBones(pEntity->nChildren);
|
||||
if (nChildrenIndexBase < 0)
|
||||
return false;
|
||||
|
||||
// load the children
|
||||
rBoneDesc.m_numChildren = pEntity->nChildren;
|
||||
rBoneDesc.m_nOffsetChildren = nChildrenIndexBase - nBoneIndex;
|
||||
|
||||
for (int nChild = 0; nChild < pEntity->nChildren; ++nChild)
|
||||
{
|
||||
if (!load(nBoneIndex, nChildrenIndexBase + nChild))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rBoneDesc.m_numChildren = 0;
|
||||
rBoneDesc.m_nOffsetChildren = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// compares the two bone structures. Returns true if they're equal
|
||||
// (e.g. for validation of different lods)
|
||||
bool CryBoneHierarchyLoader::isEqual (const CryBoneHierarchyLoader& right)const
|
||||
{
|
||||
if (m_arrBones.size() != right.m_arrBones.size())
|
||||
return false;
|
||||
for (unsigned nBone = 0; nBone < m_arrBones.size(); ++nBone)
|
||||
{
|
||||
if (!m_arrBones[nBone].isEqual(right.m_arrBones[nBone]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
101
CryAnimation/CryBoneHierarchyLoader.h
Normal file
101
CryAnimation/CryBoneHierarchyLoader.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// 22 Sep 2002 :- Created by Sergiy Migdalskiy <sergiy@crytek.de>
|
||||
//
|
||||
// Contains:
|
||||
// class - loading context - responsible for loading the linearized bone hierarchy from the BONEANIM
|
||||
// chunk
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef _CRY_BONE_HIERARCHY_LOADER_HDR_
|
||||
#define _CRY_BONE_HIERARCHY_LOADER_HDR_
|
||||
|
||||
#include "CryBoneDesc.h"
|
||||
|
||||
// this class is responsible for building the bone hierarchy array out of the chunk in file
|
||||
// it does recursive building so that the hierarchy is continuous, each bone has continuous child array
|
||||
class CryBoneHierarchyLoader
|
||||
{
|
||||
public:
|
||||
CryBoneHierarchyLoader();
|
||||
|
||||
// loads and creates the array of bones;
|
||||
// returns number of bytes read if successful, 0 if not
|
||||
// updates the CryBoneDesc inv def matrices, if the initial pose matrices are already available
|
||||
unsigned load(const BONEANIM_CHUNK_DESC* pChunk, unsigned nChunkSize);
|
||||
|
||||
// loads the default positions of each bone; if the bone chunk is loaded,
|
||||
// updates the bone inverse default pose matrices
|
||||
// returns number of bytes read if successful, 0 if not
|
||||
unsigned load (const BONEINITIALPOS_CHUNK_DESC_0001*pChunk, unsigned nChunkSize);
|
||||
|
||||
void scale (float fScale);
|
||||
|
||||
// maps the given index of the bone into the id with which the bone is identified in the file
|
||||
int mapIndexToId(int nIndex) const {return m_arrIndexToId[nIndex];}
|
||||
const int* getIndexToIdMap ()const {return &m_arrIndexToId[0];}
|
||||
int mapIdToIndex (int nId)const {return m_arrIdToIndex[nId];}
|
||||
const int* getIdToIndexMap ()const {return &m_arrIdToIndex[0];}
|
||||
|
||||
// initializes the map id->index (inverse of mapIndexToId)
|
||||
void getIdToIndexMap (unsigned* pMap)const;
|
||||
|
||||
const CryBoneDesc& getBoneByIndex (unsigned nIndex) const {return m_arrBones[nIndex];}
|
||||
|
||||
// compares the two bone structures. Returns true if they're equal
|
||||
// (e.g. for validation of different lods)
|
||||
bool isEqual(const CryBoneHierarchyLoader& right)const;
|
||||
|
||||
// returns the number of loaded bones, or 0 if no bones were loaded yet
|
||||
unsigned numBones() const {return m_pChunkBoneAnim ? m_pChunkBoneAnim->nBones : 0;}
|
||||
|
||||
bool hasInitPos () const {return !m_arrInitPose.empty();}
|
||||
const Matrix44& getInitPosMatrixByIndex(int nBoneIndex){return m_arrInitPose[mapIndexToId(nBoneIndex)];}
|
||||
|
||||
const char* getLastError() const {return m_szLastError;}
|
||||
|
||||
// array of bones that's initialized in the constructor
|
||||
// this array is allocated and is not reallocated afterwards; it gives the number of entries in the id<->index map tables
|
||||
// The bones are in the internal (Index) indexation
|
||||
typedef std::vector<CryBoneDesc> CryBoneDescArray;
|
||||
CryBoneDescArray m_arrBones;
|
||||
|
||||
// the array of default bone positions; it's empty if no
|
||||
// init pos chunk was loaded; it doesn't matter in which
|
||||
// order the bones and init pose chunk are loaded:
|
||||
// when both are loaded, the bone descriptors receive the
|
||||
// inverse of these matrices, when they're available.
|
||||
// NOTE:
|
||||
// This is in BoneID indexation
|
||||
std::vector<Matrix44> m_arrInitPose;
|
||||
protected:
|
||||
// loads the whole hierarchy of bones, using the state machine
|
||||
bool load (int nBoneParentIndex, int nBoneIndex);
|
||||
|
||||
// allocates the required number of bones in the plain hierarchy array, starting at the next available place
|
||||
int allocateBones(int numBones);
|
||||
|
||||
// updates the bone InvDefGlobal matrices, if the default pose matrices
|
||||
// are available
|
||||
void updateInvDefGlobalMatrices();
|
||||
protected:
|
||||
const BONEANIM_CHUNK_DESC* m_pChunkBoneAnim;
|
||||
unsigned m_pChunkBoneAnimSize;
|
||||
|
||||
// the current raw data pointer
|
||||
const void* m_pBoneAnimRawData, *m_pBoneAnimRawDataEnd;
|
||||
|
||||
// the currently free position in the array of bones
|
||||
int m_nNextBone;
|
||||
|
||||
// the mapping bone index->bone id and vice versa
|
||||
// have the same size as m_arrBones; -1 means no entry
|
||||
std::vector<int> m_arrIndexToId, m_arrIdToIndex;
|
||||
|
||||
const char* m_szLastError;
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
175
CryAnimation/CryBoneInfo.cpp
Normal file
175
CryAnimation/CryBoneInfo.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
#include "stdafx.h"
|
||||
#include "CryBoneInfo.h"
|
||||
#include <StlUtils.h>
|
||||
#include "ControllerManager.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
CryBoneInfo::CryBoneInfo()
|
||||
//:m_arrControllers ("CryBoneInfo.Controllers")
|
||||
{
|
||||
}
|
||||
|
||||
CryBoneInfo::~CryBoneInfo()
|
||||
{
|
||||
IPhysicalWorld *pIPhysicalWorld = GetPhysicalWorld();
|
||||
IGeomManager* pPhysGeomManager = pIPhysicalWorld?pIPhysicalWorld->GetGeomManager():NULL;
|
||||
|
||||
for(int nLod=0; nLod<2; nLod++)
|
||||
if (m_PhysInfo[nLod].pPhysGeom && (INT_PTR)m_PhysInfo[nLod].pPhysGeom!=-1)
|
||||
{
|
||||
if ((INT_PTR)m_PhysInfo[nLod].pPhysGeom<0x400)
|
||||
{
|
||||
TRACE("Error: trying to release wrong bone phys geometry");
|
||||
}
|
||||
else if (pPhysGeomManager)
|
||||
{
|
||||
phys_geometry* pPhysGeom = m_PhysInfo[nLod].pPhysGeom;
|
||||
pPhysGeomManager->UnregisterGeometry(pPhysGeom);
|
||||
}
|
||||
else
|
||||
TRACE("todo: delete bones phys");
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// binds this bone to a controller from the specified animation, using
|
||||
// the controller manager
|
||||
// NOT RECURSIVE anymore
|
||||
// PARAMETERS:
|
||||
// nAnimID - local for this bone animation ID (will be used when
|
||||
// subsequently referring to the animation)
|
||||
// nGlobalAnimID - global animation id, identifies the animation within
|
||||
// the controller manager context
|
||||
// RETURNS:
|
||||
// The binded controller, if the bind was successful, or NULL if there's
|
||||
// no such controller (animation is missing)
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
IController* CryBoneInfo::BindController(CControllerManager::Animation& GlobalAnim, unsigned nAnimID)
|
||||
{
|
||||
if (m_arrControllers.size() <= nAnimID)
|
||||
// this is an autopointer array, so there's no need to re-initialize the autopointers after construction
|
||||
m_arrControllers.resize (nAnimID+1/*, NULL*/);
|
||||
|
||||
IController* pController = m_arrControllers[nAnimID] = GlobalAnim.GetController (m_nControllerID);
|
||||
|
||||
return pController;
|
||||
}
|
||||
|
||||
// unbinds the bone from the given animation's controller
|
||||
void CryBoneInfo::UnbindController (unsigned nAnimID)
|
||||
{
|
||||
if (m_arrControllers.size() > nAnimID)
|
||||
m_arrControllers[nAnimID] = NULL;
|
||||
}
|
||||
|
||||
unsigned CryBoneInfo::sizeofThis()const
|
||||
{
|
||||
unsigned nSize = sizeof(*this);
|
||||
nSize += sizeofVector(m_arrControllers);
|
||||
return nSize;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Updates the given lod level bone physics info from the bones found in
|
||||
// the given chunk.
|
||||
// THis is required to update the dead body physics info from the lod1 geometry
|
||||
// without rebuilding the new bone structure. If the lod1 has another bone structure,
|
||||
// then the bones are mapped to the lod0 ones using controller ids. Matching bones'
|
||||
// physics info is updated
|
||||
void CryBoneInfo::UpdateHierarchyPhysics (const BONEANIM_CHUNK_DESC* pChunk, unsigned nChunkSize, int nLodLevel)
|
||||
{
|
||||
UnsignedToCryBoneMap mapCtrlId;
|
||||
AddHierarchyToControllerIdMap(mapCtrlId);
|
||||
|
||||
// the first bone entity
|
||||
const BONE_ENTITY* pBoneEntity = (const BONE_ENTITY*)(pChunk+1);
|
||||
// the actual end of the chunk
|
||||
const BONE_ENTITY* pBoneEntityEnd = (const BONE_ENTITY*)(((const char*)pChunk)+nChunkSize);
|
||||
|
||||
// if you get this assert, it means the lod 1 file is corrupted
|
||||
if (pBoneEntity + pChunk->nBones > pBoneEntityEnd)
|
||||
{
|
||||
assert (0);
|
||||
return;
|
||||
}
|
||||
|
||||
// update physics for each bone entity
|
||||
for (; pBoneEntity < pBoneEntityEnd; ++pBoneEntity)
|
||||
{
|
||||
CryBoneInfo* pBone = find_in_map(mapCtrlId, pBoneEntity->ControllerID, (CryBoneInfo*)NULL);
|
||||
if (pBone)
|
||||
pBone->UpdatePhysics(*pBoneEntity, nLodLevel);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// adds this bone and all its children to the given map controller id-> bone ptr
|
||||
void CryBoneInfo::AddHierarchyToControllerIdMap (UnsignedToCryBoneMap& mapControllerIdToCryBone)
|
||||
{
|
||||
mapControllerIdToCryBone.insert (UnsignedToCryBoneMap::value_type(m_nControllerID, this));
|
||||
for (unsigned nChild = 0; nChild < numChildren(); ++nChild)
|
||||
{
|
||||
getChild(nChild)->AddHierarchyToControllerIdMap(mapControllerIdToCryBone);
|
||||
}
|
||||
}
|
||||
|
||||
//! Performs post-initialization. This step is requred to initialize the pPhysGeom of the bones
|
||||
//! After the bone has been loaded but before it is first used. When the bone is first loaded, pPhysGeom
|
||||
//! is set to the value equal to the chunk id in the file where the physical geometry (BoneMesh) chunk is kept.
|
||||
//! After those chunks are loaded, and chunk ids are mapped to the registered physical geometry objects,
|
||||
//! call this function to replace pPhysGeom chunk ids with the actual physical geometry object pointers.
|
||||
//! RETURNS:
|
||||
// true if the corresponding physical geometry object has been found
|
||||
//! NOTE:
|
||||
//! The entries of the map that were used are deleted
|
||||
bool CryBoneInfo::PostInitPhysGeom (ChunkIdToPhysGeomMap& mapChunkIdToPhysGeom, int nLodLevel)
|
||||
{
|
||||
phys_geometry*& pPhysGeom = m_PhysInfo[nLodLevel].pPhysGeom;
|
||||
ChunkIdToPhysGeomMap::iterator it = mapChunkIdToPhysGeom.find ((INT_PTR)pPhysGeom);
|
||||
if (it != mapChunkIdToPhysGeom.end())
|
||||
{
|
||||
// remap the chunk id to the actual pointer to the geometry
|
||||
pPhysGeom = it->second;
|
||||
mapChunkIdToPhysGeom.erase (it);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pPhysGeom = NULL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CryBoneInfo::PostInitialize()
|
||||
{
|
||||
if (getParent())
|
||||
{
|
||||
CryQuat qrel = CryQuat((matrix3x3in4x4Tf&)getParent()->getInvDefGlobal() * ((matrix3x3in4x4Tf&)getInvDefGlobal()).T());
|
||||
m_pqDefRelTransform.vRotLog = -log(qrel).v; // '-' since pqTransform.buildMtarix assumes flipped quaternion
|
||||
m_pqDefRelTransform.vPos = getParent()->getInvDefGlobal().TransformPointOLD(
|
||||
(matrix3x3in4x4f&)getInvDefGlobal() * -getInvDefGlobal().GetTranslationOLD());
|
||||
|
||||
CryBoneInfo *pParent,*pPhysParent;
|
||||
for(int nLod=0; nLod<2; nLod++)
|
||||
{
|
||||
for(pParent=pPhysParent=getParent(); pPhysParent && !pPhysParent->m_PhysInfo[nLod].pPhysGeom; pPhysParent=pPhysParent->getParent());
|
||||
if (pPhysParent)
|
||||
m_qRelPhysParent[nLod] = CryQuat((matrix3x3in4x4Tf&)pParent->getInvDefGlobal() * (matrix3x3in4x4f&)pPhysParent->getInvDefGlobal());
|
||||
else
|
||||
m_qRelPhysParent[nLod].SetIdentity();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pqDefRelTransform.vRotLog.Set(0,0,0);
|
||||
m_pqDefRelTransform.vPos.Set(0,0,0);
|
||||
}
|
||||
}
|
||||
|
||||
CryBoneInfo& CryBoneInfo::operator = (const CryBoneDesc& rThat)
|
||||
{
|
||||
*static_cast<CryBoneDesc*>(this) = rThat;
|
||||
m_arrControllers.clear();
|
||||
return *this;
|
||||
}
|
||||
75
CryAnimation/CryBoneInfo.h
Normal file
75
CryAnimation/CryBoneInfo.h
Normal file
@@ -0,0 +1,75 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// File:CryBoneInfo.h
|
||||
//
|
||||
// History:
|
||||
// Created by Sergiy Migdalskiy
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _CRY_BONE_INFO_HDR_
|
||||
#define _CRY_BONE_INFO_HDR_
|
||||
|
||||
#include "Controller.h"
|
||||
#include "CryAnimationInfo.h"
|
||||
#include "CryBoneDesc.h"
|
||||
|
||||
// this class contains the information that's common to all instances of bones
|
||||
// in the given model: like bone name, and misc. shared properties
|
||||
class CryBoneInfo: public CryBoneDesc
|
||||
{
|
||||
public:
|
||||
CryBoneInfo ();
|
||||
~CryBoneInfo ();
|
||||
|
||||
CryBoneInfo* getChild (unsigned i) {assert(i < numChildren()); return this + m_nOffsetChildren + i;}
|
||||
const CryBoneInfo* getChild (unsigned i) const {assert(i < numChildren()); return this + m_nOffsetChildren + i;}
|
||||
CryBoneInfo* getParent () {return m_nOffsetParent ? this + m_nOffsetParent : NULL;}
|
||||
const CryBoneInfo* getParent () const {return m_nOffsetParent ? this + m_nOffsetParent : NULL;}
|
||||
|
||||
// binds this bone to a controller from the specified animation, using the controller manager
|
||||
class IController* BindController (GlobalAnimation& GlobalAnim, unsigned nAnimID);
|
||||
// unbinds the bone from the given animation's controller
|
||||
void UnbindController (unsigned nAnimID);
|
||||
|
||||
CryBoneInfo& operator = (const CryBoneDesc& rThat);
|
||||
unsigned sizeofThis()const;
|
||||
|
||||
void PostInitialize();
|
||||
IController::PQLog &getDefRelTransform() { return m_pqDefRelTransform; }
|
||||
CryQuat &getqRelPhysParent(int nLod) { return m_qRelPhysParent[nLod]; }
|
||||
|
||||
protected:
|
||||
// updates the given lod level bone physics info from the bones found in the given chunk
|
||||
void UpdateHierarchyPhysics (const BONEANIM_CHUNK_DESC* pChunk, unsigned nChunkSize, int nLodLevel);
|
||||
|
||||
typedef std::map<unsigned, CryBoneInfo*> UnsignedToCryBoneMap;
|
||||
// adds this bone and all its children to the given map controller id-> bone ptr
|
||||
void AddHierarchyToControllerIdMap (UnsignedToCryBoneMap& mapControllerIdToCryBone);
|
||||
|
||||
//! Performs post-initialization. This step is requred to initialize the pPhysGeom of the bones
|
||||
//! After the bone has been loaded but before it is first used. When the bone is first loaded, pPhysGeom
|
||||
//! is set to the value equal to the chunk id in the file where the physical geometry (BoneMesh) chunk is kept.
|
||||
//! After those chunks are loaded, and chunk ids are mapped to the registered physical geometry objects,
|
||||
//! call this function to replace pPhysGeom chunk ids with the actual physical geometry object pointers.
|
||||
//! NOTE:
|
||||
//! The entries of the map that were used are deleted
|
||||
typedef std::map<int, struct phys_geometry*> ChunkIdToPhysGeomMap;
|
||||
bool PostInitPhysGeom (ChunkIdToPhysGeomMap& mapChunkIdToPhysGeom, int nLodLevel);
|
||||
|
||||
friend class CryBone;
|
||||
friend class CryModelState;
|
||||
friend class CryModelAnimationContainer;
|
||||
friend class CryModelGeometryLoader;
|
||||
|
||||
protected:
|
||||
// the array of controllers is formed only once at the load time, so we can actually use FixedArray here
|
||||
typedef std::vector<IController_AutoPtr>ControllerArray;
|
||||
ControllerArray m_arrControllers;
|
||||
IController::PQLog m_pqDefRelTransform;
|
||||
CryQuat m_qRelPhysParent[2];
|
||||
};
|
||||
|
||||
#endif
|
||||
259
CryAnimation/CryCharBody.cpp
Normal file
259
CryAnimation/CryCharBody.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CryEngine Source code
|
||||
//
|
||||
// File:CryCharBody.cpp
|
||||
// Implementation of CryCharBody class
|
||||
//
|
||||
// History:
|
||||
// August 15, 2002: Created by Sergiy Migdalskiy <sergiy@crytek.de>
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#include "stdafx.h"
|
||||
//#include "CryAnimation.h"
|
||||
#include "CryAnimationScriptCommands.h"
|
||||
#include "CryCharManager.h"
|
||||
#include "CryCharInstance.h"
|
||||
#include "StringUtils.h"
|
||||
#include "CryCharBody.h"
|
||||
#include "CryModelState.h"
|
||||
#include "CryModelLoader.h"
|
||||
|
||||
using namespace CryStringUtils;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// CryCharBody
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CryCharBody::CryCharBody (CryCharManager* pManager, const string& strFileName):
|
||||
m_pCryModel (NULL),
|
||||
m_fAnimationFrameRate (30), // the default animation rate
|
||||
m_strFilePath (strFileName), // we're optimistic and think that the object will be loaded successfully
|
||||
m_pManager (pManager)
|
||||
{
|
||||
string strGeom = strFileName;
|
||||
ReplaceExtension (strGeom, "cgf");
|
||||
|
||||
string strCid = strFileName;
|
||||
ReplaceExtension (strCid, "cid");
|
||||
|
||||
CryModelLoader ModelLoader (pManager->GetControllerManager()); //GetRenderer()->CreateModel();
|
||||
|
||||
//m_fAnimationFrameRate = GetConsoleVariable ("FrameRate", strCid, m_fAnimationFrameRate);
|
||||
float fScale = g_fDefaultAnimationScale; //GetConsoleVariable ("Scale", strCid, g_fDefaultAnimationScale);
|
||||
|
||||
m_pCryModel = ModelLoader.loadNew(this, strGeom, fScale);
|
||||
|
||||
#if 0
|
||||
// needed for XBox development
|
||||
extern void exportTestModel(CryGeometryInfo* pGeometry, CLeafBuffer* pLeafBuffer);
|
||||
// on this stage, if m_pCryModel == NULL, it means the loader couldn't load the model
|
||||
if (m_pCryModel)
|
||||
exportTestModel( m_pCryModel->getGeometryInfo(), m_pCryModel->m_pDefaultModelState->m_pLeafBuffers[0]);
|
||||
#endif
|
||||
|
||||
pManager->RegisterBody (this);
|
||||
}
|
||||
|
||||
|
||||
CryCharBody::~CryCharBody()
|
||||
{
|
||||
if (!m_setInstances.empty())
|
||||
{
|
||||
g_GetLog()->LogToFile("*ERROR* ~CryCharBody(%s): %u character instances still not deleted. Forcing deletion.", m_strFilePath.c_str(), m_setInstances.size());
|
||||
CleanupInstances();
|
||||
}
|
||||
|
||||
//GetLog()->LogToFile("Deleting body \"%s\" (%d references)", m_strFilePath.c_str(), m_nRefCounter);
|
||||
//GetRenderer()->DeleteModel(m_pCryModel);
|
||||
if (m_pCryModel)
|
||||
{
|
||||
delete m_pCryModel;
|
||||
m_pCryModel = NULL;
|
||||
}
|
||||
|
||||
m_pManager->UnregisterBody (this);
|
||||
}
|
||||
|
||||
CryModel *CryCharBody::GetModel()
|
||||
{
|
||||
return m_pCryModel;
|
||||
}
|
||||
|
||||
const string& CryCharBody::GetFilePath()const
|
||||
{
|
||||
return m_strFilePath;
|
||||
}
|
||||
|
||||
const char* CryCharBody::GetFilePathCStr()const
|
||||
{
|
||||
return m_strFilePath.c_str();
|
||||
}
|
||||
|
||||
const char* CryCharBody::GetNameCStr()const
|
||||
{
|
||||
return FindFileNameInPath(m_strFilePath.c_str());
|
||||
}
|
||||
|
||||
float CryCharBody::GetFrameRate ()const
|
||||
{
|
||||
return m_fAnimationFrameRate;
|
||||
}
|
||||
|
||||
void CryCharBody::RegisterInstance (CryCharInstance* pInstance)
|
||||
{
|
||||
m_setInstances.insert (pInstance);
|
||||
}
|
||||
|
||||
void CryCharBody::UnregisterInstance (CryCharInstance* pInstance)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (m_setInstances.find(pInstance) == m_setInstances.end())
|
||||
{
|
||||
g_GetLog()->LogToFile("*ERROR* CryCharBody::UnregisterInstance(0x%x): twice-deleting the character instance 0x%08X!!!", this, pInstance);
|
||||
}
|
||||
#endif
|
||||
m_setInstances.erase(pInstance);
|
||||
}
|
||||
|
||||
// destroys all characters
|
||||
// THis may (and should) lead to destruction and self-deregistration of this body
|
||||
void CryCharBody::CleanupInstances()
|
||||
{
|
||||
// since after each instance deletion the body may be destructed itself,
|
||||
// we'll lock it for this time
|
||||
if (!m_setInstances.empty())
|
||||
g_GetISystem()->Warning (VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, 0, "CryCharBody.CleanupInstances", "Forcing deletion of %d instances for body %s. CRASH POSSIBLE because other subsystems may have stored dangling pointer(s).", NumRefs(), m_strFilePath.c_str());
|
||||
CryCharBody_AutoPtr pLock = this; // don't remove this line, it's for locking the body in memory untill every instance is finished.
|
||||
while (!m_setInstances.empty())
|
||||
delete *m_setInstances.begin();
|
||||
}
|
||||
|
||||
// Returns the scale of the model - not used now
|
||||
float CryCharBody::GetScale() const
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Returns the interface for animations applicable to this model
|
||||
ICryAnimationSet* CryCharBody::GetAnimationSet ()
|
||||
{
|
||||
return m_pCryModel->GetAnimationSet();
|
||||
}
|
||||
|
||||
// Return name of bone from bone table, return zero id nId is out of range (the game gets this id from physics)
|
||||
const char * CryCharBody::GetBoneName(int nId) const
|
||||
{
|
||||
if ((unsigned)nId < m_pCryModel->numBoneInfos())
|
||||
return m_pCryModel->getBoneInfo (nId).getNameCStr();
|
||||
else
|
||||
return ""; // invalid bone id
|
||||
}
|
||||
|
||||
//! Returns the number of bones; all bone ids are in the range from 0 to this number exclusive; 0th bone is the root
|
||||
int CryCharBody::NumBones() const
|
||||
{
|
||||
return m_pCryModel->numBoneInfos();
|
||||
}
|
||||
|
||||
void CryCharBody::GetSize(ICrySizer* pSizer)
|
||||
{
|
||||
#if ENABLE_GET_MEMORY_USAGE
|
||||
if (!pSizer->Add (*this))
|
||||
return;
|
||||
pSizer->AddString (m_strFilePath);
|
||||
m_pCryModel->GetSize(pSizer);
|
||||
pSizer->AddContainer (m_setInstances);
|
||||
CryCharInstanceRegistry::const_iterator it = m_setInstances.begin(), itEnd = m_setInstances.end();
|
||||
for (; it != itEnd; ++it)
|
||||
(*it)->GetMemoryUsage(pSizer);
|
||||
#endif
|
||||
}
|
||||
|
||||
// makes all character instances spawn some particles (for test purposes)
|
||||
void CryCharBody::SpawnTestParticles(bool bStart)
|
||||
{
|
||||
ParticleParams params;
|
||||
params.fFocus = 1.5f;
|
||||
params.fLifeTime = 0.5f;
|
||||
params.fSize = 0.02f;
|
||||
params.fSizeSpeed = 0;
|
||||
params.fSpeed = 1.f;
|
||||
params.vGravity(0,0,-5.f);
|
||||
params.nCount = 15;
|
||||
params.eBlendType = ParticleBlendType_ColorBased;
|
||||
params.nTexId = g_GetIRenderer()->LoadTexture("cloud");
|
||||
float fAlpha = 1;
|
||||
params.vColorStart(fAlpha,fAlpha,fAlpha);
|
||||
params.vColorEnd(fAlpha,fAlpha,fAlpha);
|
||||
params.vDirection(0,0,1);
|
||||
params.vPosition = Vec3d (1,0.85f,0.75f);
|
||||
params.fTailLenght = 0.25;
|
||||
|
||||
CryParticleSpawnInfo spawn;
|
||||
spawn.fSpawnRate = 30;
|
||||
spawn.nFlags = CryParticleSpawnInfo::FLAGS_SPAWN_FROM_BONE;
|
||||
spawn.nBone = GetBoneByName("weapon_bone");
|
||||
spawn.vBonePos = Vec3d(0,0,0);
|
||||
|
||||
CryCharInstanceRegistry::iterator it = m_setInstances.begin(), itEnd = m_setInstances.end();
|
||||
for (; it != itEnd; ++it)
|
||||
{
|
||||
if (bStart)
|
||||
(*it)->AddParticleEmitter(params, spawn);
|
||||
else
|
||||
(*it)->RemoveParticleEmitter (-1);
|
||||
}
|
||||
}
|
||||
|
||||
//! returns the file name of the character model
|
||||
const char* CryCharBody::GetFileName()
|
||||
{
|
||||
return m_strFilePath.c_str();
|
||||
}
|
||||
|
||||
// dumps the model info into the log, one line
|
||||
void CryCharBody::DumpModel()
|
||||
{
|
||||
g_GetLog()->LogToFile ("\001%60s %3d instance%s, %3d animations (%3d unique)", m_strFilePath.c_str(), m_setInstances.size(), m_setInstances.size()==1?"":"s", m_pCryModel->numAnimations(), m_pCryModel->numUniqueAnimations());
|
||||
g_GetLog()->LogToConsole ("\001%60s %3d instance%s, %3d animations (%3d unique)", m_strFilePath.c_str(), m_setInstances.size(), m_setInstances.size()==1?"":"s", m_pCryModel->numAnimations(), m_pCryModel->numUniqueAnimations());
|
||||
}
|
||||
|
||||
//! Returns the index of the bone by its name or -1 if no such bone exists; this is Case-Sensitive
|
||||
int CryCharBody::GetBoneByName (const char* szName)
|
||||
{
|
||||
return GetModel()->findBone(szName);
|
||||
}
|
||||
|
||||
//Executes a per-body script command
|
||||
bool CryCharBody::ExecScriptCommand (int nCommand, void* pParams, void* pResult)
|
||||
{
|
||||
if (CASCMD_DUMP_MODELS == nCommand)
|
||||
{
|
||||
DumpModel();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CASCMD_EXPORT_MODELS_ASCII == nCommand)
|
||||
{
|
||||
m_pCryModel->ExportModelsASC();
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (nCommand)
|
||||
{
|
||||
case CASCMD_DUMP_DECALS:
|
||||
case CASCMD_DUMP_STATES:
|
||||
g_GetLog()->Log ("\001%60s %3d instance%s", m_strFilePath.c_str(), m_setInstances.size(), m_setInstances.size()==1?"":"s");
|
||||
break;
|
||||
}
|
||||
|
||||
CryCharInstanceRegistry::iterator it = m_setInstances.begin(), itEnd = m_setInstances.end();
|
||||
for (; it != itEnd; ++it)
|
||||
{
|
||||
(*it)->ExecScriptCommand(nCommand, pParams, pResult);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
110
CryAnimation/CryCharBody.h
Normal file
110
CryAnimation/CryCharBody.h
Normal file
@@ -0,0 +1,110 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CryEngine Source code
|
||||
//
|
||||
// File:CryCharBody.h
|
||||
// Declaration of CryCharBody class
|
||||
//
|
||||
// History:
|
||||
// August 15, 2002: Created by Sergiy Migdalskiy <sergiy@crytek.de>
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _CRY_CHAR_BODY_HEADER_
|
||||
#define _CRY_CHAR_BODY_HEADER_
|
||||
|
||||
#include "CryModel.h"
|
||||
|
||||
class CryCharManager;
|
||||
class CryCharInstance;
|
||||
|
||||
class CryCharBody:
|
||||
public ICryCharModel
|
||||
{
|
||||
public:
|
||||
CryCharBody (CryCharManager* pManager, const string& strFileName);
|
||||
~CryCharBody();
|
||||
|
||||
|
||||
// Returns the pointer to the loaded model for this body.
|
||||
// also works as an indicator of load operation success: if the file was not successfully loaded, then returns NULL
|
||||
CryModel *GetModel();
|
||||
CVertexBuffer* GetVertexBuffer ();
|
||||
|
||||
const string& GetFilePath()const;
|
||||
const char* GetFilePathCStr()const;
|
||||
const char* GetNameCStr()const;
|
||||
|
||||
float GetFrameRate ()const;
|
||||
|
||||
void RegisterInstance (CryCharInstance*);
|
||||
void UnregisterInstance (CryCharInstance*);
|
||||
|
||||
// destroys all characters
|
||||
// THis may (and should) lead to destruction and self-deregistration of this body
|
||||
void CleanupInstances();
|
||||
|
||||
// Returns the scale of the model - not used now
|
||||
virtual float GetScale() const;
|
||||
|
||||
// Returns the interface for animations applicable to this model
|
||||
virtual ICryAnimationSet* GetAnimationSet ();
|
||||
|
||||
// Return name of bone from bone table, return zero id nId is out of range (the game gets this id from physics)
|
||||
virtual const char * GetBoneName(int nId) const;
|
||||
|
||||
// Returns the number of bones; all bone ids are in the range from 0 to this number exclusive; 0th bone is the root
|
||||
virtual int NumBones() const;
|
||||
|
||||
// Returns the index of the bone by its name or -1 if no such bone exists; this is Case-Sensitive
|
||||
virtual int GetBoneByName (const char* szName);
|
||||
|
||||
void GetSize(ICrySizer* pSizer);
|
||||
|
||||
// makes all character instances spawn some particles (for test purposes)
|
||||
void SpawnTestParticles(bool bStart);
|
||||
|
||||
// returns the file name of the character model
|
||||
const char* GetFileName();
|
||||
|
||||
// dumps the model info into the log, one line
|
||||
void DumpModel();
|
||||
|
||||
//Executes a per-body script command
|
||||
bool ExecScriptCommand (int nCommand, void* pParams, void* pResult);
|
||||
|
||||
// returns true if the instance is registered in this body
|
||||
bool DoesInstanceExist (CryCharInstance* pInstance)
|
||||
{
|
||||
return m_setInstances.find (pInstance) != m_setInstances.end();
|
||||
}
|
||||
|
||||
unsigned NumInstances()
|
||||
{
|
||||
return (unsigned)m_setInstances.size();
|
||||
}
|
||||
|
||||
// returns the extra data attached to the character by the artist during exporting
|
||||
// as the scene user properties. if there's no such data, returns NULL
|
||||
const char* GetProperty(const char* szName) {return m_pCryModel->GetProperty(szName);}
|
||||
|
||||
virtual ClassEnum GetClass() {return CLASS_CRYCHARBODY;}
|
||||
protected:
|
||||
// the character file name, empty string means no geometry was loaded (e.g. because of an error)
|
||||
const string m_strFilePath;
|
||||
|
||||
CryModel * m_pCryModel;
|
||||
float m_fAnimationFrameRate;
|
||||
|
||||
CryCharManager* m_pManager;
|
||||
|
||||
// the set of all child objects created; used for the final clean up
|
||||
typedef std::set<CryCharInstance*> CryCharInstanceRegistry;
|
||||
CryCharInstanceRegistry m_setInstances;
|
||||
};
|
||||
|
||||
TYPEDEF_AUTOPTR(CryCharBody);
|
||||
|
||||
typedef std::set<CryCharBody_AutoPtr> CryCharBody_AutoSet;
|
||||
|
||||
#endif
|
||||
137
CryAnimation/CryCharDecal.cpp
Normal file
137
CryAnimation/CryCharDecal.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#include "stdafx.h"
|
||||
#include "DebugUtils.h"
|
||||
#include "CryCharDecalBuilder.h"
|
||||
#include "CryCharDecal.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// creates the decal topology: finds the faces and vertices and connects
|
||||
// them into a small mesh representing the decal, calculate the UV coordinates.
|
||||
// the vertex coordinate in the decal info is in WCS; the LCS version is passed through ptSourceLCS
|
||||
void CryCharDecal::buildFrom (CryCharDecalBuilder& builder)
|
||||
{
|
||||
m_ptSource = builder.getSourceLCS();
|
||||
#if DECAL_USE_HELPERS
|
||||
m_matBullet = builder.getBulletMatrix();
|
||||
m_fSize = builder.getDecalInfo().fSize;
|
||||
#endif
|
||||
|
||||
m_fBuildTime = g_fCurrTime;
|
||||
m_fFadeInTime = builder.getDecalInfo().fSize / (0.04f + 0.02f*(rand()%0xFF)/256.0f); // suppose the speed is like this
|
||||
|
||||
m_fDeathTime = m_fBuildTime + 60*60*24; // die in very distant future - in one day
|
||||
m_fFadeOutTime = -1; // no fade out
|
||||
|
||||
assert (builder.numDecalFaces());
|
||||
assert (builder.numDecalVertices());
|
||||
|
||||
m_nTextureId = builder.getDecalInfo().nTid;
|
||||
m_arrFaces.resize (builder.numDecalFaces());
|
||||
memcpy (&m_arrFaces[0], builder.getDecalFaces(), builder.numDecalFaces()*sizeof(m_arrFaces[0]));
|
||||
m_arrVertices.resize (builder.numDecalVertices());
|
||||
memcpy (&m_arrVertices[0], builder.getDecalVertices(), builder.numDecalVertices()*sizeof(m_arrVertices[0]));
|
||||
}
|
||||
|
||||
|
||||
#if DECAL_USE_HELPERS
|
||||
|
||||
unsigned CryCharDecal::numHelperFaces () const
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
unsigned CryCharDecal::numHelperVertices() const
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
|
||||
void CryCharDecal::debugDraw(const Matrix& matCharacter)
|
||||
{
|
||||
static const float fColorDecalOuter[4] = {1,1,0.5,1};
|
||||
static const float fColorDecalInner[4] = {1,0.5,0.5,1};
|
||||
debugDrawSphere(m_matBullet*matCharacter, m_fSize * getIntensity(), fColorDecalInner);
|
||||
debugDrawSphere(m_matBullet*matCharacter, m_fSize*1.02f, fColorDecalOuter);
|
||||
}
|
||||
|
||||
Vec3d CryCharDecal::getHelperVertex (unsigned i) const
|
||||
{
|
||||
assert (i < numHelperVertices());
|
||||
static const float fForward = 0.18f, fUp = fForward/10, fRight = fUp;
|
||||
static const Vec3d arrOctahedronVtx[6] = {
|
||||
Vec3d(0,0,fForward/10),
|
||||
Vec3d(0,fUp,0),
|
||||
Vec3d(fRight,0,0),
|
||||
Vec3d(0,-fUp,0),
|
||||
Vec3d(-fRight,0,0),
|
||||
Vec3d(0,0,-fForward)
|
||||
};
|
||||
|
||||
return m_matBullet.TransformPoint(arrOctahedronVtx[i]);
|
||||
}
|
||||
|
||||
CryCharDecalFace CryCharDecal::getHelperFace (unsigned i) const
|
||||
{
|
||||
assert (i < numHelperFaces());
|
||||
if (i < 4)
|
||||
return CryCharDecalFace (0, i+1, (i+1)%4 + 1);
|
||||
else
|
||||
return CryCharDecalFace (5, (i-4+1)%4 + 1, i-4+1);
|
||||
}
|
||||
|
||||
CryUV CryCharDecal::getHelperUV (unsigned i) const
|
||||
{
|
||||
assert (i < numHelperVertices());
|
||||
CryUV uv;
|
||||
if (i == 0 || i == 5)
|
||||
uv.u = uv.v = 0;
|
||||
else
|
||||
{
|
||||
if (i & 1)
|
||||
uv.u = 1, uv.v = 0;
|
||||
else
|
||||
uv.u = uv.v = 1;
|
||||
}
|
||||
return uv;
|
||||
}
|
||||
#endif
|
||||
|
||||
// returns the decal multiplier: 0 - no decal, 1 - full decal size
|
||||
float CryCharDecal::getIntensity()const
|
||||
{
|
||||
if (g_fCurrTime >= m_fDeathTime)
|
||||
// we've faded out
|
||||
return 0;
|
||||
|
||||
if (g_fCurrTime > m_fDeathTime - m_fFadeOutTime)
|
||||
// we're fading out
|
||||
return 1-sqr(1-(m_fDeathTime - g_fCurrTime) / m_fFadeOutTime);
|
||||
|
||||
|
||||
float fLifeTime = (g_fCurrTime - m_fBuildTime);
|
||||
if (fLifeTime > m_fFadeInTime)
|
||||
// we've faded in
|
||||
return 1;
|
||||
else
|
||||
// we're fading in
|
||||
return 1 - sqr(1 - fLifeTime / m_fFadeInTime);
|
||||
}
|
||||
|
||||
const Vec3d& CryCharDecal::getSourceLCS()const
|
||||
{
|
||||
return m_ptSource;
|
||||
}
|
||||
|
||||
// returns true if this is a dead/empty decal and should be discarded
|
||||
bool CryCharDecal::isDead()
|
||||
{
|
||||
return g_fCurrTime >= m_fDeathTime;
|
||||
}
|
||||
|
||||
// starts fading out the decal from this moment on
|
||||
void CryCharDecal::startFadeOut(float fTimeout)
|
||||
{
|
||||
m_fFadeOutTime = fTimeout;
|
||||
m_fDeathTime = g_fCurrTime + fTimeout;
|
||||
}
|
||||
|
||||
|
||||
float CryCharDecal::g_fCurrTime = 0;
|
||||
107
CryAnimation/CryCharDecal.h
Normal file
107
CryAnimation/CryCharDecal.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Sep 25 2002 :- Created by Sergiy Migdalskiy
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _CRY_ANIMATION_CRY_CHAR_DECAL_HDR_
|
||||
#define _CRY_ANIMATION_CRY_CHAR_DECAL_HDR_
|
||||
|
||||
#define DECAL_USE_HELPERS 0
|
||||
|
||||
#include <TArray.h>
|
||||
#include "SparseArrayDriver.h"
|
||||
#include "CryCharDecalCommon.h"
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// the decal structure describing the realized decal with the geometry
|
||||
// it consists of primary faces/vertices (with the full set of getter methods declared with the special macro)
|
||||
// and helper faces/vertices. Primary faces index into the array of primary vertices. Primary vertices
|
||||
// refer to the indices of the character vertices and contain the new UVs
|
||||
// Helper vertices are explicitly set in the object CS, and helper faces index within those helper vertex array.
|
||||
class CryCharDecal
|
||||
{
|
||||
public:
|
||||
// sets the current game time for decals (for fading in / out)
|
||||
static void setGlobalTime (float fTime) {g_fCurrTime = fTime;}
|
||||
|
||||
// initializes it; expects the decal to come with vPos in LCS rather than WCS
|
||||
CryCharDecal (){}
|
||||
void buildFrom (class CryCharDecalBuilder& builder);
|
||||
|
||||
// starts fading out the decal from this moment on
|
||||
void startFadeOut(float fTimeout);
|
||||
|
||||
// returns true if this is a dead/empty decal and should be discarded
|
||||
bool isDead();
|
||||
|
||||
// natural order of decal sorting is by material (texture id)
|
||||
bool operator < (const CryCharDecal& rThat)const
|
||||
{
|
||||
return getTextureId() < rThat.getTextureId();
|
||||
}
|
||||
|
||||
|
||||
DECLARE_VECTOR_GETTER_METHODS(CryCharDecalFace, Face, Faces, m_arrFaces);
|
||||
DECLARE_VECTOR_GETTER_METHODS(CryCharDecalVertex, Vertex, Vertices, m_arrVertices);
|
||||
|
||||
#if DECAL_USE_HELPERS
|
||||
// helper vertex access methods - see the class header comment for more info
|
||||
unsigned numHelperFaces () const;
|
||||
unsigned numHelperVertices() const;
|
||||
Vec3 getHelperVertex (unsigned i) const;
|
||||
CryUV getHelperUV (unsigned i) const;
|
||||
CryCharDecalFace getHelperFace (unsigned i) const;
|
||||
|
||||
const Vec3& getSourceWCS()const;
|
||||
|
||||
void debugDraw(const Matrix& matCharacter);
|
||||
#endif
|
||||
const Vec3& getSourceLCS () const;
|
||||
|
||||
int getTextureId ()const {return m_nTextureId;}
|
||||
|
||||
// returns the decal multiplier: 0 - no decal, 1 - full decal size
|
||||
float getIntensity()const;
|
||||
protected:
|
||||
// the faces comprising the decal
|
||||
// the face that's the center of the decal is 0th
|
||||
std::vector<CryCharDecalFace> m_arrFaces;
|
||||
|
||||
// the mapping of the new vertices to the old ones
|
||||
std::vector<CryCharDecalVertex> m_arrVertices;
|
||||
|
||||
// the texture of the decal
|
||||
int m_nTextureId;
|
||||
|
||||
// the point, in LCS , where the shot was made
|
||||
Vec3 m_ptSource;
|
||||
|
||||
#if DECAL_USE_HELPERS
|
||||
// the Transformation Matrix that transforms the point in helper CS
|
||||
// into the character CS; Z axis is the direction of the bullet
|
||||
Matrix m_matBullet;
|
||||
float m_fSize;
|
||||
#endif
|
||||
|
||||
// this is the time when the decal was built, in seconds
|
||||
float m_fBuildTime;
|
||||
|
||||
// this is the fade in time, in seconds
|
||||
float m_fFadeInTime;
|
||||
|
||||
// this is the fade out time, in seconds; <0 at the start (means no fade out)
|
||||
float m_fFadeOutTime;
|
||||
|
||||
// this is the time by which the decal should be faded out; 1e+30 at the start (very long life)
|
||||
float m_fDeathTime;
|
||||
|
||||
protected:
|
||||
static float g_fCurrTime;
|
||||
};
|
||||
|
||||
#endif
|
||||
338
CryAnimation/CryCharDecalBuilder.cpp
Normal file
338
CryAnimation/CryCharDecalBuilder.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
#include "stdafx.h"
|
||||
#include "CryGeometryInfo.h"
|
||||
#include "MathUtils.h"
|
||||
#include "CryCharDecalBuilder.h"
|
||||
|
||||
#include "CVars.h"
|
||||
|
||||
CryCharDecalBuilder::CryCharDecalBuilder (CryEngineDecalInfo& rDecal, CryGeometryInfo* pGeometry, const Vec3d* pVertices):
|
||||
m_rDecal (rDecal),
|
||||
m_pGeometry (pGeometry),
|
||||
m_pSkinVertices (pVertices)
|
||||
{
|
||||
// transform the bullet position to the local coordinates
|
||||
m_ptSourceLCS = rDecal.vPos;
|
||||
|
||||
// // normalize the direction vector, if it's present
|
||||
float dHitDirectionLength = rDecal.vHitDirection.Length();
|
||||
if (dHitDirectionLength > 0.01)
|
||||
{
|
||||
// we need the hit direction and source in the LCS to project the character
|
||||
m_ptHitDirectionLCS = rDecal.vHitDirection / dHitDirectionLength;
|
||||
|
||||
m_ptSourceLCS = m_ptSourceLCS;
|
||||
|
||||
// to project the character in the bullet CS, we need some fake TM (fake because we just choose any X and Y axes)
|
||||
// Z axis looks in the direction of the hit
|
||||
BuildMatrixFromFwdZRot (m_ptHitDirectionLCS, m_ptSourceLCS, rDecal.fAngle, m_matBullet);
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
m_matBullet(2,i) *= 2.0f;
|
||||
m_matInvBullet = GetInverted44(m_matBullet);
|
||||
|
||||
initVerticesBCS();
|
||||
//initVertexDistances();
|
||||
|
||||
findParticipatingFaces();
|
||||
}
|
||||
else
|
||||
{
|
||||
g_GetLog()->LogWarning ("\003CryCharDecalBuilder::CryCharDecalBuilder: hit has unknown direction");
|
||||
}
|
||||
}
|
||||
|
||||
// calculate distances to each vertex
|
||||
void CryCharDecalBuilder::initVertexDistances()
|
||||
{
|
||||
m_arrVertexDistance.reinit (m_pGeometry->numExtToIntMapEntries());
|
||||
|
||||
// the index of the vertex nearest to the hit (in External indexation)
|
||||
m_nNearestVertex = 0;
|
||||
float fNearestVertexDistance2 = 0;
|
||||
for (unsigned nVertex = 0; nVertex < m_pGeometry->numExtToIntMapEntries(); ++nVertex)
|
||||
{
|
||||
const Vec3d& ptVertex = m_pSkinVertices[nVertex];
|
||||
float fDistance2 = m_arrVertexDistance[nVertex] = GetSquaredDistance(ptVertex,m_ptSourceLCS);
|
||||
if (!nVertex || fNearestVertexDistance2 > fDistance2)
|
||||
{
|
||||
fNearestVertexDistance2 = fDistance2;
|
||||
m_nNearestVertex = nVertex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// find the vertices that participate in decal generation and add them to the array of vertices
|
||||
// Uses m_arrVerticesBCS
|
||||
void CryCharDecalBuilder::findParticipatingFaces()
|
||||
{
|
||||
m_arrDecalVertexMapping.reinit (m_pGeometry->numUsedVertices(), -1);
|
||||
float fSize2 = g_GetCVars()->ca_EnableDecals() == 2 ? sqr(m_rDecal.fSize*3) : m_rDecal.fSize;
|
||||
|
||||
// find the faces to which the distance near enough
|
||||
for (unsigned nFace = 0; nFace < m_pGeometry->numFaces(); ++nFace)
|
||||
{
|
||||
GeomFace Face = m_pGeometry->getFace(nFace);
|
||||
// vertices belonging to the face, in external indexation
|
||||
|
||||
float fTriangleDistance = GetDistanceToTriangleBCS (Face);
|
||||
if (fTriangleDistance > -fSize2 && fTriangleDistance <= fSize2)
|
||||
{
|
||||
// the triangle participates
|
||||
addFaceBCS (Face);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// returns the distance to the given 2D point in the plane xy
|
||||
// from the point (0,0)
|
||||
double distanceToPoint2Dxy (const Vec3d& v)
|
||||
{
|
||||
return sqrt(sqr((double)v.x)+sqr((double)v.y));
|
||||
}
|
||||
|
||||
// returns the distance to the given 2D line in the plane xy
|
||||
// from the point (0,0). The line is FINITE, and limited by v0 and v1
|
||||
// the hints are the distances to the corresponding vertices
|
||||
float distanceToLine2DxyHint (const Vec3d& v0, float d0, const Vec3d& v1, float d1)
|
||||
{
|
||||
// the coefficients of the line equation x -> ax + bx t, y -> ay + by t
|
||||
float dx = v1.x - v0.x, dy = v1.y - v0.y;
|
||||
double f2 = dx*(double)dx+dy*(double)dy;
|
||||
if (f2 < 0.005)
|
||||
return (d0+d1)/2;
|
||||
|
||||
// the point of intersection of the normal from 00 with the line, 0 means v0, 1 means v1, > 1 means beyond v1, <0 means before v0
|
||||
double t = -(v0.x*dx + v0.y*dy)/f2;
|
||||
|
||||
if (t <= 0)
|
||||
{
|
||||
float fDistance = d0;
|
||||
assert (d1 > fDistance);
|
||||
return fDistance;
|
||||
}
|
||||
else
|
||||
if (t >= 1)
|
||||
{
|
||||
float fDistance = d1;
|
||||
assert (d0 > fDistance);
|
||||
return fDistance;
|
||||
}
|
||||
else
|
||||
{
|
||||
float fDistance = (float)fabs((v0.x*dy-v0.y*dx) / sqrt (f2));
|
||||
assert (fabs(fDistance - (float)fabs((v1.x*dy-v1.y*dx) / sqrt (f2))) < 1e-3);
|
||||
return fDistance;
|
||||
}
|
||||
}
|
||||
|
||||
// returns the distance to the given 2D line in the plane xy
|
||||
// from the point (0,0). The line is FINITE, and limited by v0 and v1
|
||||
float distanceToLine2Dxy (const Vec3d& v0, const Vec3d& v1)
|
||||
{
|
||||
// the coefficients of the line equation x -> v0.x + dx t, y -> v0.y + dy t
|
||||
float dx = v1.x - v0.x, dy = v1.y - v0.y;
|
||||
double f2 = dx*(double)dx+dy*(double)dy;
|
||||
if (f2 < 0.005)
|
||||
// distance to the middle of the interval
|
||||
return float(cry_sqrtf(sqr(v0.x+v1.x)+sqr(v0.y+v1.y))/2);
|
||||
|
||||
// the point of intersection of the normal from 00 with the line, 0 means v0, 1 means v1, > 1 means beyond v1, <0 means before v0
|
||||
double t = -(v0.x*dx + v0.y*dy)/f2;
|
||||
assert (fabs ((v0.x+dx*t)*dx+(v0.y+dy*t)*dy) < cry_sqrtf(sqr(v0.x+v1.x)+sqr(v0.y+v1.y))/200000);
|
||||
|
||||
if (t <= 0)
|
||||
{
|
||||
double fDistance = distanceToPoint2Dxy(v0);
|
||||
assert (distanceToPoint2Dxy(v1) > fDistance);
|
||||
return (float)fDistance;
|
||||
}
|
||||
else
|
||||
if (t >= 1)
|
||||
{
|
||||
double fDistance = distanceToPoint2Dxy(v1);
|
||||
assert (distanceToPoint2Dxy(v0) > fDistance);
|
||||
return (float)fDistance;
|
||||
}
|
||||
else
|
||||
{
|
||||
float fDistance = (float)fabs((v0.x*dy-v0.y*dx) / sqrt (f2));
|
||||
assert (fabs(fDistance - (float)fabs((v1.x*dy-v1.y*dx) / sqrt (f2))) < 1e-3);
|
||||
return fDistance;
|
||||
}
|
||||
}
|
||||
|
||||
// returns the distance to a very thin triangle - that is,
|
||||
// it's so thin that it's hard to determine whether it contains 0 or not,
|
||||
// but it shouldn't really matter because it's possible to determine the
|
||||
// distance to each of the sides (and if some side is degenerate, then
|
||||
// to the vertices of that side)
|
||||
float distanceToThinTriangle (const Vec3d v[3])
|
||||
{
|
||||
float d[3] = {
|
||||
(float)distanceToPoint2Dxy(v[0]),
|
||||
(float)distanceToPoint2Dxy(v[1]),
|
||||
(float)distanceToPoint2Dxy(v[2])
|
||||
};
|
||||
return min3 (distanceToLine2DxyHint (v[0],d[0],v[1],d[1]), distanceToLine2DxyHint (v[1],d[1],v[2],d[2]), distanceToLine2DxyHint (v[2],d[2],v[0],d[0]));
|
||||
}
|
||||
|
||||
// returns the distance of the given triangle from the origin (bullet)
|
||||
float CryCharDecalBuilder::GetDistanceToTriangleBCS (GeomFace nVertex)
|
||||
{
|
||||
Vec3d v[3] = {m_arrVerticesBCS[nVertex[0]], m_arrVerticesBCS[nVertex[1]], m_arrVerticesBCS[nVertex[2]]};
|
||||
|
||||
if (g_GetCVars()->ca_EnableDecals() == 2)
|
||||
return sqr(v[0].x)+sqr(v[0].y)+sqr(v[1].x)+sqr(v[1].y)+sqr(v[2].x)+sqr(v[2].y);
|
||||
|
||||
// calculate the barycentric coordinates of the triangle
|
||||
float b0 = (v[1].x - v[0].x) * (v[2].y - v[0].y) - (v[2].x - v[0].x) * (v[1].y - v[0].y);
|
||||
|
||||
// the triangle either back-faces the bullet or has a negligible area
|
||||
// - just return a negative not to use this triangle
|
||||
if (g_GetCVars()->ca_PerforatingDecals())
|
||||
{
|
||||
if (fabs(b0) < 1e-3) // should be b0 < 1e-4 to return to the previous version
|
||||
// return -100000;
|
||||
return distanceToThinTriangle(v);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b0 < 1e-3)
|
||||
return -100000;
|
||||
}
|
||||
|
||||
float b1 = ( v[1].x * v[2].y - v[2].x * v[1].y ) / b0 ;
|
||||
float b2 = ( v[2].x * v[0].y - v[0].x * v[2].y ) / b0 ;
|
||||
float b3 = ( v[0].x * v[1].y - v[1].x * v[0].y ) / b0 ;
|
||||
|
||||
if (fabs(b1+b2+b3-1) > 0.03f)
|
||||
// this is some almost degenerate triangle, or something else is unstable
|
||||
if (g_GetCVars()->ca_PerforatingDecals())
|
||||
return distanceToThinTriangle(v);
|
||||
else
|
||||
return -100000;
|
||||
|
||||
//assert (fabs(b1+b2+b3-1) < 0.01f);
|
||||
#ifdef _DEBUG
|
||||
// the reconstructed by the barycentric coordinates point
|
||||
//Vec3d ptReconstructed = b1 * v[0] + b2 * v[1] + b3 * v[2];
|
||||
//assert (ptReconstructed.x < 1e-7/b0 && ptReconstructed.x > -1e-7/b0 && ptReconstructed.y < 1e-7/b0 && ptReconstructed.y > -1e-7/b0);
|
||||
#endif
|
||||
// b1, b2 and b3 are the barycentric coordinates of the point 0
|
||||
// if they're all > 0, the point lies inside the triangle (triangle is on the way of the bullet
|
||||
|
||||
if (b1 > 0)
|
||||
{
|
||||
if (b2 > 0)
|
||||
{
|
||||
if (b3 > 0)
|
||||
{
|
||||
// the triangle intersects Oz
|
||||
// Determine the intersection point - this will be the distance
|
||||
// the triangle faces the bullet - calculate the point of intersection
|
||||
return 0;//b1*v[0].z+b2*v[1].z+b3*v[2].z;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 0 lies outside the edge v0-v1, so we should calculate the distance to this line
|
||||
return distanceToLine2Dxy (v[0],v[1]);
|
||||
return max2(distanceToLine2Dxy (v[0],v[1]), min3(v[0].z,v[1].z,v[2].z));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b3 > 0)
|
||||
{
|
||||
// 0 lies outside the edge v0-v2, so we should calculate the distance to this line
|
||||
return distanceToLine2Dxy (v[0],v[2]);
|
||||
return max2(distanceToLine2Dxy (v[0],v[2]), min3(v[0].z,v[1].z,v[2].z));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 0 lies beside v0
|
||||
return (float)distanceToPoint2Dxy (v[0]);
|
||||
return max2((float)distanceToPoint2Dxy (v[0]), min3(v[0].z,v[1].z,v[2].z));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b2 > 0)
|
||||
{
|
||||
if (b3 > 0)
|
||||
{
|
||||
// 0 lies outside the edge v1-v2, so we should calculate the distance to this line
|
||||
return distanceToLine2Dxy(v[1],v[2]);
|
||||
return max2(distanceToLine2Dxy(v[1],v[2]), min3(v[0].z,v[1].z,v[2].z));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 0 lies beside v[1]
|
||||
return (float)distanceToPoint2Dxy(v[1]);
|
||||
return max2((float)distanceToPoint2Dxy(v[1]), min3(v[0].z,v[1].z,v[2].z));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert (b3 > 0);
|
||||
// 0 lies beside v2
|
||||
return (float)distanceToPoint2Dxy(v[2]);
|
||||
return max2((float)distanceToPoint2Dxy(v[2]), min3(v[0].z,v[1].z,v[2].z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// initializes the VerticesBCS array - vertices in the bullet coordinate system
|
||||
void CryCharDecalBuilder::initVerticesBCS()
|
||||
{
|
||||
unsigned numVertices = m_pGeometry->numVertices();
|
||||
m_arrVerticesBCS.reinit (numVertices);
|
||||
for (unsigned i = 0; i < numVertices; ++i)
|
||||
{
|
||||
m_arrVerticesBCS[i] = m_matInvBullet.TransformPointOLD(m_pSkinVertices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// adds the face, with the vertices if needed
|
||||
// in Character Coordinate System
|
||||
void CryCharDecalBuilder::addFaceCCS (int nVertex[3], Vec3d vVertices[3])
|
||||
{
|
||||
m_arrDecalFaces.push_back (CryCharDecalFace(addVertexCCS(nVertex[0], vVertices[0]), addVertexCCS(nVertex[1], vVertices[1]), addVertexCCS(nVertex[2], vVertices[2])));
|
||||
}
|
||||
|
||||
void CryCharDecalBuilder::addFaceBCS (CryCharDecalFace GeomIntFace)
|
||||
{
|
||||
m_arrDecalFaces.push_back (CryCharDecalFace(addVertexBCS(GeomIntFace[0]), addVertexBCS(GeomIntFace[1]), addVertexBCS(GeomIntFace[2])));
|
||||
}
|
||||
|
||||
|
||||
// maps the vertex and returns the internal index
|
||||
unsigned CryCharDecalBuilder::addVertexCCS (int nVertexExtIndex, const Vec3d& vVertex)
|
||||
{
|
||||
int& nVertexNewIndex = m_arrDecalVertexMapping[nVertexExtIndex];
|
||||
if (nVertexNewIndex == -1)
|
||||
{
|
||||
// the vertex in bullet coordinates will be the new UVs
|
||||
Vec3d vUVW = m_matInvBullet.TransformPointOLD(vVertex);
|
||||
m_arrDecalVertices.push_back (CryCharDecalVertex (nVertexExtIndex, (vUVW.x/m_rDecal.fSize+1)*0.5f, (vUVW.y/m_rDecal.fSize+1)*0.5f));
|
||||
nVertexNewIndex = int(m_arrDecalVertices.size()-1);
|
||||
}
|
||||
|
||||
return nVertexNewIndex;
|
||||
}
|
||||
|
||||
unsigned CryCharDecalBuilder::addVertexBCS (int nSkinVertexIndex)
|
||||
{
|
||||
int& nVertexNewIndex = m_arrDecalVertexMapping[nSkinVertexIndex];
|
||||
if (nVertexNewIndex == -1)
|
||||
{
|
||||
// the vertex in bullet coordinates will be the new UVs
|
||||
const Vec3d& vUVW = m_arrVerticesBCS[nSkinVertexIndex];
|
||||
m_arrDecalVertices.push_back (CryCharDecalVertex (nSkinVertexIndex, (vUVW.x/m_rDecal.fSize+1)*0.5f, (vUVW.y/m_rDecal.fSize+1)*0.5f));
|
||||
nVertexNewIndex = int(m_arrDecalVertices.size()-1);
|
||||
}
|
||||
|
||||
return nVertexNewIndex;
|
||||
}
|
||||
104
CryAnimation/CryCharDecalBuilder.h
Normal file
104
CryAnimation/CryCharDecalBuilder.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Sep 25 2002 :- Created by Sergiy Migdalskiy
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _CRY_ANIMATION_CRY_CHAR_DECAL_BUILDER_HDR_
|
||||
#define _CRY_ANIMATION_CRY_CHAR_DECAL_BUILDER_HDR_
|
||||
|
||||
#include "GeomCommon.h"
|
||||
#include "CryCharDecalCommon.h"
|
||||
#include "VertexBufferArrayDrivers.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// class that's used to build the decals for decal manager.
|
||||
// This is just the builder (factory) class, it's only used to build the instance of decal and is not
|
||||
// kept in memory
|
||||
// NOTE: This object memorizes all the references, they must be alive during the life cycle of this object
|
||||
class CryCharDecalBuilder
|
||||
{
|
||||
public:
|
||||
// remembers the parameters and gets ready to initialize the Decal upon request
|
||||
// memorizes all the references, they must be alive during the life cycle of this object
|
||||
CryCharDecalBuilder (struct CryEngineDecalInfo& rDecal, class CryGeometryInfo* pGeometry, const Vec3* pVertices);
|
||||
|
||||
// returns the coordinate of the bullet in the LCS of the character
|
||||
const Vec3& getSourceLCS () const {return m_ptSourceLCS;}
|
||||
// returns the coordinates of the bullet in the WCS
|
||||
//const Vec3& getSourceWCS () const {return m_rDecal.vPos;}
|
||||
// returns the matrix of the bullet in LCS
|
||||
const Matrix44& getBulletMatrix()const {return m_matBullet;}
|
||||
|
||||
// returns the original decal descriptor structure
|
||||
const CryEngineDecalInfo& getDecalInfo () const {return m_rDecal;}
|
||||
|
||||
DECLARE_VECTOR_GETTER_METHODS(CryCharDecalFace, DecalFace, DecalFaces, m_arrDecalFaces);
|
||||
DECLARE_VECTOR_GETTER_METHODS(CryCharDecalVertex, DecalVertex, DecalVertices, m_arrDecalVertices);
|
||||
protected:
|
||||
// calculate distances to each vertex
|
||||
void initVertexDistances();
|
||||
void findParticipatingFaces();
|
||||
|
||||
// adds the face, with the vertices if needeed
|
||||
void addFaceCCS (int nVertex[3], Vec3 vVertices[3]); // in Character Coordinate System
|
||||
void addFaceBCS (GeomFace GeomIntFace); // in the Bullet coordinate system
|
||||
|
||||
// maps the vertex and returns the interan index
|
||||
unsigned addVertexCCS (int nVertexExtIndex, const Vec3& vVertex); // in Character Coordinate System
|
||||
// maps the vertex and returns the interan index
|
||||
unsigned addVertexBCS (int nVertexExtIndex); // in Bullet Coordinate System
|
||||
|
||||
// initializes the VerticesBCS array - vertices in the bullet coordinate system
|
||||
void initVerticesBCS();
|
||||
|
||||
// returns the distance of the given triangle from the origin (bullet)
|
||||
float GetDistanceToTriangleBCS (GeomFace nVertex);
|
||||
protected:
|
||||
// the original structure describing the decal, with the vPos set to the position of the bullet in WCS
|
||||
CryEngineDecalInfo& m_rDecal;
|
||||
|
||||
// the inverse model TM. Model TM is the TM of the character. This inverse is used to transform from
|
||||
// the World space back into the character model space
|
||||
//const Matrix& m_matInvModel;
|
||||
|
||||
// the geometry of the character
|
||||
CryGeometryInfo* m_pGeometry;
|
||||
|
||||
// the temporary vertex array of the skinned character
|
||||
const Vec3* m_pSkinVertices;
|
||||
|
||||
// coordinates of the hit (the center of the bullet) in the Local Coordinate system of the character model
|
||||
Vec3 m_ptSourceLCS;
|
||||
// hit direction (the direction in which the bullet flies) in the LCS of the model
|
||||
Vec3 m_ptHitDirectionLCS;
|
||||
|
||||
// bullet TM and inverse TM, relative to the Local coordinate system of the character
|
||||
// these matrices are used to project the whole model skin in the bullet CS, the skin being
|
||||
// in the LCS of the character, so that the bullet appears in the (0,0,0)
|
||||
// Z axis looks in the direction of the hit
|
||||
Matrix44 m_matInvBullet, m_matBullet;
|
||||
|
||||
// the array of transformed vertices (where 0,0,0 is the hit point, and 0,0,1 is the direction of the bullet)
|
||||
// in other words, it's the Vertices in Bullet Coordinate System
|
||||
TElementaryArray<Vec3> m_arrVerticesBCS;
|
||||
|
||||
// distances to each vertex
|
||||
TElementaryArray<float> m_arrVertexDistance;
|
||||
unsigned m_nNearestVertex; // the nearest vertex, in the external indexation of m_arrVertices
|
||||
|
||||
// faces that will take part in the decal rendering
|
||||
std::vector<CryCharDecalFace> m_arrDecalFaces;
|
||||
// vertices that will take part in the rendering (referred to by the face array)
|
||||
std::vector<CryCharDecalVertex> m_arrDecalVertices;
|
||||
|
||||
// the mapping from the character vertices (in the external indexation) to the decal vertices
|
||||
// -1 means the vertex has not yet been mapped
|
||||
// It is always true that an element is >= -1 and < m_arrDecalVertices.size()
|
||||
TElementaryArray<int> m_arrDecalVertexMapping;
|
||||
};
|
||||
|
||||
#endif
|
||||
39
CryAnimation/CryCharDecalCommon.h
Normal file
39
CryAnimation/CryCharDecalCommon.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Sep 25 2002 :- Created by Sergiy Migdalskiy
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef _CRY_ANIMATION_CRY_CHAR_DECAL_COMMON_HDR_
|
||||
#define _CRY_ANIMATION_CRY_CHAR_DECAL_COMMON_HDR_
|
||||
|
||||
#include "TFace.h"
|
||||
// common structures, funcitons and declarations used by different parts of the character decal system
|
||||
|
||||
// the simple decal face - each vertex index refers to the 0-based vertex index
|
||||
// in the new vertex array
|
||||
typedef TFace<unsigned short> CryCharDecalFace;
|
||||
// this is the simple decal vertex - the index of the original vertex and UV coordinates
|
||||
struct CryCharDecalVertex
|
||||
{
|
||||
// the original vertex of the mesh
|
||||
// (external vertex index in GeomInfo;
|
||||
// explicit vertex index in the character
|
||||
// vertex buffer sparse array)
|
||||
unsigned nVertex;
|
||||
// the new UV coordinates
|
||||
CryUV uvNew;
|
||||
|
||||
CryCharDecalVertex () {}
|
||||
CryCharDecalVertex (unsigned nOrigVertex, float u, float v):
|
||||
nVertex (nOrigVertex)
|
||||
{
|
||||
uvNew.u = u;
|
||||
uvNew.v = v;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
666
CryAnimation/CryCharDecalManager.cpp
Normal file
666
CryAnimation/CryCharDecalManager.cpp
Normal file
@@ -0,0 +1,666 @@
|
||||
// NOTES:
|
||||
// CRenderer::CreateLeafBufferInitialized creates a buffer with reserve of vertices and no indices.
|
||||
// it can accept a fake pointer (like something on stack) for vertex array pointer.
|
||||
// UpdateIndices() actually just copies the indices into the vertex buffer structure
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <StlUtils.h>
|
||||
#include "CryGeometryInfo.h"
|
||||
#include "CryEngineDecalInfo.h"
|
||||
#include "VertexBufferArrayDrivers.h"
|
||||
#include "CryCharDecalManager.h"
|
||||
#include "CryCharDecalBuilder.h"
|
||||
#include "CryCharDecal.h"
|
||||
#include "CVars.h"
|
||||
#include "MathUtils.h"
|
||||
|
||||
// nVertexAllocStep == 2^nVertexAllocStepLog2 is the step with which the vertex buffer will be enlarged when needed
|
||||
enum {nVertexAllocStepLog2 = 8};
|
||||
enum {nVertexAllocStep = (1 << nVertexAllocStepLog2)};
|
||||
enum {nMaterialAllocStep = 1};
|
||||
// the max number of vertices used for the character until the decals start to disappear
|
||||
// note: this must be a multiple of nVertexAllocStep
|
||||
enum {nMaxDecalVertices = (nVertexAllocStep * 6u)};
|
||||
|
||||
CryCharDecalManager::CStatistics CryCharDecalManager::g_Statistics;
|
||||
|
||||
|
||||
CryCharDecalManager::CryCharDecalManager (class CryGeometryInfo* pGeomInfo):
|
||||
m_pGeometry (pGeomInfo),
|
||||
m_bNeedUpdateIndices (false)
|
||||
{
|
||||
m_pShader = g_GetIRenderer()->EF_LoadShader("DecalCharacter", eSH_World, EF_SYSTEM);
|
||||
}
|
||||
|
||||
CryCharDecalManager::~CryCharDecalManager ()
|
||||
{
|
||||
DeleteLeafBuffer();
|
||||
DeleteOldRenderElements();
|
||||
|
||||
if (!m_arrOldRE.empty())
|
||||
{
|
||||
if (!g_GetISystem()->IsQuitting())
|
||||
g_LogToFile ("Warning: ~CryCharDecalManager: There are still %d render elements present that may be in rendering queue. But since destruction was requested, attempting to destruct those elements", m_arrOldRE.size());
|
||||
for (unsigned i = 0; i < m_arrOldRE.size(); ++i)
|
||||
m_arrOldRE[i].destruct();
|
||||
}
|
||||
}
|
||||
|
||||
// cleans up the old leaf buffers
|
||||
void CryCharDecalManager::DeleteOldRenderElements()
|
||||
{
|
||||
for (RenderElementArray::iterator it = m_arrOldRE.begin(); it != m_arrOldRE.end();)
|
||||
if (it->canDestruct())
|
||||
{
|
||||
it->destruct();
|
||||
it = m_arrOldRE.erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Calculates the required number of vertices for the current decal array
|
||||
// If needed, recreates the current leaf buffer so that it contains the given
|
||||
// number of vertices and reserved indices, both uninitialized.
|
||||
// There are 0 used indices initially
|
||||
void CryCharDecalManager::ReserveVertexBufferVertices (const Vec3d* pInPositions)
|
||||
{
|
||||
// first, calculate how many vertices we'll need
|
||||
unsigned i, numDecals = (unsigned)m_arrDecals.size(), numVertices = 0;
|
||||
for (i = 0; i < numDecals; ++i)
|
||||
{
|
||||
numVertices += m_arrDecals[i].numVertices();
|
||||
#if DECAL_USE_HELPERS
|
||||
numVertices += m_arrDecals[i].numHelperVertices();
|
||||
#endif
|
||||
}
|
||||
|
||||
while (numVertices > nMaxDecalVertices && !m_arrDecals.empty())
|
||||
{
|
||||
unsigned nDecalToDelete = (unsigned)(rand()%m_arrDecals.size());
|
||||
numVertices -= m_arrDecals[nDecalToDelete].numVertices();
|
||||
#if DECAL_USE_HELPERS
|
||||
numVertices -= m_arrDecals[nDecalToDelete].numHelperVertices();
|
||||
#endif
|
||||
m_arrDecals.erase (m_arrDecals.begin()+nDecalToDelete);
|
||||
}
|
||||
|
||||
unsigned numMaterials = groupMaterials();
|
||||
|
||||
// make sure the vertex buffer contains enough space for all vertices
|
||||
|
||||
if (m_RE.numVertices() >= g_MeshInfo.numVertices && m_RE.numMaterials() >= numMaterials)
|
||||
return;// no need to reallocate
|
||||
|
||||
DeleteLeafBuffer();
|
||||
|
||||
// construct the data to initialize the new system buffer
|
||||
unsigned numVerticesToReserve = (numVertices + (nVertexAllocStep-1)) & ~(nVertexAllocStep-1);
|
||||
TElementaryArray<struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F> arrSourceVerts (numVerticesToReserve);
|
||||
RefreshVertexBufferVertices (pInPositions, &arrSourceVerts[0]);
|
||||
|
||||
unsigned numMaterialsToReserve = numMaterials + nMaterialAllocStep;
|
||||
|
||||
if (numVerticesToReserve > numVertices)
|
||||
memset (&arrSourceVerts[numVertices], 0, sizeof(struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F) * (numVerticesToReserve - numVertices));
|
||||
|
||||
m_RE.create (numVerticesToReserve, &arrSourceVerts[0], "AnimDecals", numMaterialsToReserve);
|
||||
}
|
||||
|
||||
|
||||
// sets up the given material to default state: just clean decal material
|
||||
/*
|
||||
void CryCharDecalManager::initDefaultMaterial (CMatInfo& rMat)
|
||||
{
|
||||
rMat.pRE = (CREOcLeaf*)GetRenderer()->EF_CreateRE(eDATA_OcLeaf);
|
||||
rMat.pRE->m_pBuffer = m_pLeafBuffer;
|
||||
rMat.pRE->m_pChunk = &rMat;
|
||||
rMat.pRE->m_CustomTexBind[0] = 0x1000;
|
||||
rMat.pShader = GetRenderer()->EF_LoadShader("DecalCharacter", -1, eSH_World, EF_SYSTEM);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// Request (add) a new decal to the character
|
||||
void CryCharDecalManager::Add (CryEngineDecalInfo& Decal)
|
||||
{
|
||||
//if (m_arrDecalRequests.empty())
|
||||
// only 1 decal per frame is supported
|
||||
m_arrDecalRequests.push_back(Decal);
|
||||
}
|
||||
|
||||
// discards the decal request queue (not yet realized decals added through Add())
|
||||
void CryCharDecalManager::DiscardRequests()
|
||||
{
|
||||
m_arrDecalRequests.clear();
|
||||
}
|
||||
|
||||
|
||||
// cleans up all decals, destroys the vertex buffer
|
||||
void CryCharDecalManager::clear()
|
||||
{
|
||||
m_arrDecalRequests.clear();
|
||||
m_arrDecals.clear();
|
||||
DeleteLeafBuffer();
|
||||
}
|
||||
|
||||
|
||||
// deletes the leaf buffer
|
||||
void CryCharDecalManager::DeleteLeafBuffer ()
|
||||
{
|
||||
if (m_RE.canDestruct())
|
||||
m_RE.destruct();
|
||||
else
|
||||
{
|
||||
m_arrOldRE.push_back(m_RE);
|
||||
m_RE.detach();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// cleans up the dead decals
|
||||
// updates indices if needed; doesn't update vertices
|
||||
// sets m_bNeedUpdateIndices to true if it has added something (and therefore refresh of indices is required)
|
||||
void CryCharDecalManager::DeleteOldDecals()
|
||||
{
|
||||
// if we delete one of the decals, we need to update indices;
|
||||
// updating vertices is not our business
|
||||
|
||||
for (CDecalArray::iterator it = m_arrDecals.begin(); it != m_arrDecals.end();)
|
||||
if (it->isDead())
|
||||
{
|
||||
m_bNeedUpdateIndices = true;
|
||||
it = m_arrDecals.erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// realizes (creates geometry for) unrealized(requested) decals
|
||||
// NOTE: this also fills the UVs in for the vertex stream
|
||||
void CryCharDecalManager::Realize (const Vec3d* pPositions)
|
||||
{
|
||||
DeleteOldRenderElements();
|
||||
|
||||
// write the deformed vertices into the videobuffer
|
||||
// NOTE:
|
||||
// we write to the old videobuffer; in the case Realization reallocates the current buffer,
|
||||
// the old videobuffer will still be rendered on this frame, so it must be updated now
|
||||
|
||||
// in case the following assert works, it means that the sequence of drawing/skinning
|
||||
// has been changed; in this case remove this assert and switch the order of the following
|
||||
// Refresh and Realize calls in order to avoid decal flickers.
|
||||
//assert (!m_RE.getLeafBuffer() || GetRenderer()->GetFrameID() == m_RE.getLastRenderFrameID());
|
||||
|
||||
if (!m_arrDecalRequests.empty())
|
||||
{
|
||||
int nnn = 0;
|
||||
}
|
||||
RefreshVertexBufferVertices (pPositions);
|
||||
if (m_bNeedUpdateIndices)
|
||||
RefreshVertexBufferIndices ();
|
||||
|
||||
DeleteOldDecals();
|
||||
RealizeNewDecalRequests (pPositions);
|
||||
}
|
||||
|
||||
|
||||
// if there are decal requests, then converts them into the decal objects
|
||||
// reserves the vertex/updates the index buffers, if need to be
|
||||
// sets m_bNeedUpdateIndices to true if it has added something (and therefore refresh of indices is required)
|
||||
void CryCharDecalManager::RealizeNewDecalRequests (const Vec3d* pPositions)
|
||||
{
|
||||
if (m_arrDecalRequests.empty())
|
||||
return; // nothing to udpate
|
||||
|
||||
// realize each unrealized decal, then clean up the array of unrealized decals
|
||||
for (unsigned nDecal = 0; nDecal < m_arrDecalRequests.size(); ++nDecal)
|
||||
{
|
||||
CryEngineDecalInfo& Decal = m_arrDecalRequests[nDecal];
|
||||
CryCharDecalBuilder builder (Decal, m_pGeometry, pPositions);
|
||||
if (!builder.numDecalFaces())
|
||||
continue; // we're not interested in this decal: we don't have any decals
|
||||
|
||||
// starts fading out all decals that are very close to this new one
|
||||
//fadeOutCloseDecals (builder.getSourceLCS(), sqr(Decal.fSize/4));
|
||||
|
||||
g_Statistics.onDecalAdd (builder.numDecalVertices(), builder.numDecalFaces());
|
||||
|
||||
CryCharDecal NewDecal;
|
||||
NewDecal.buildFrom (builder);
|
||||
m_arrDecals.insert (std::lower_bound(m_arrDecals.begin(), m_arrDecals.end(), NewDecal), NewDecal);
|
||||
//m_arrDecals.resize(m_arrDecals.size()+1);
|
||||
//m_arrDecals.back().buildFrom (builder);
|
||||
}
|
||||
|
||||
// after we realized the decal request, we don't need it anymore
|
||||
m_arrDecalRequests.clear();
|
||||
|
||||
// make sure we have enough vertices
|
||||
ReserveVertexBufferVertices(pPositions);
|
||||
|
||||
// need to recreate indices
|
||||
m_bNeedUpdateIndices = true;
|
||||
}
|
||||
|
||||
|
||||
// starts fading out all decals that are close enough to the given point
|
||||
// NOTE: the radius is m^2 - it's the square of the radius of the sphere
|
||||
void CryCharDecalManager::fadeOutCloseDecals (const Vec3d& ptCenter, float fRadius2)
|
||||
{
|
||||
for (unsigned i = 0; i < m_arrDecals.size(); ++i)
|
||||
{
|
||||
if ( GetLengthSquared((m_arrDecals[i].getSourceLCS()-ptCenter)) < fRadius2)
|
||||
m_arrDecals[i].startFadeOut (2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// returns true if the Realize() needs to be called
|
||||
// Since the Realize() updates the vertex buffers, creates decals from requests,
|
||||
// it's always needed when there are decals
|
||||
bool CryCharDecalManager::NeedRealize () const
|
||||
{
|
||||
return !m_arrDecals.empty() || !m_arrDecalRequests.empty()
|
||||
// we don't actually need the vertices that Realize() receives in this case,
|
||||
// but we need it to be called in order to free these buffers
|
||||
|| !m_arrOldRE.empty();
|
||||
}
|
||||
|
||||
void CopyVertex (Vec3d& vDst, const Vec3d& vSrc)
|
||||
{
|
||||
struct XYZ {unsigned x,y,z;};
|
||||
assert (sizeof(XYZ) == 12 && sizeof(Vec3d) == 12);
|
||||
(XYZ&)vDst = (XYZ&)vSrc;
|
||||
}
|
||||
|
||||
|
||||
// put the deformed vertices into the videobuffer of the given format,
|
||||
// using the deformed character skin
|
||||
void CryCharDecalManager::RefreshVertexBufferVertices (const Vec3d* pInPositions, struct_VERTEX_FORMAT_P3F_TEX2F* pDst)
|
||||
{
|
||||
// scan through all decals
|
||||
CDecalArray::const_iterator itDecal = m_arrDecals.begin(), itDecalEnd = itDecal + m_arrDecals.size();
|
||||
|
||||
for (; itDecal != itDecalEnd; ++itDecal)
|
||||
{
|
||||
// for each decal, there are a number of vertices to fill in; so we fill them in.
|
||||
// this is the number of vertices for the current decal
|
||||
unsigned numDecalVertices = itDecal->numVertices();
|
||||
for (unsigned nDecalVertex = 0; nDecalVertex < numDecalVertices; ++nDecalVertex)
|
||||
{
|
||||
const CryCharDecalVertex& rDecalVertex = itDecal->getVertex(nDecalVertex);
|
||||
const Vec3d& vCharPosition = pInPositions[rDecalVertex.nVertex];
|
||||
pDst->xyz = vCharPosition;
|
||||
pDst->st[0] = rDecalVertex.uvNew.u;
|
||||
pDst->st[1] = rDecalVertex.uvNew.v;
|
||||
++pDst;
|
||||
}
|
||||
|
||||
#if DECAL_USE_HELPERS
|
||||
unsigned numHelperVertices = itDecal->numHelperVertices();
|
||||
for (unsigned nHelperVertex = 0; nHelperVertex < numHelperVertices; ++nHelperVertex)
|
||||
{
|
||||
Vec3d vCharPosition = itDecal->getHelperVertex (nHelperVertex);
|
||||
CryUV uvCharUV = itDecal->getHelperUV (nHelperVertex);
|
||||
pDst->xyz = vCharPosition;
|
||||
pDst->st[0] = uvCharUV.u;
|
||||
pDst->st[1] = uvCharUV.v;
|
||||
++pDst;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// put the deformed vertices into the videobuffer of the given format
|
||||
void CryCharDecalManager::RefreshVertexBufferVertices (const Vec3d* pInPositions, struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F* pDst)
|
||||
{
|
||||
// scan through all decals
|
||||
CDecalArray::const_iterator itDecal = m_arrDecals.begin(), itDecalEnd = itDecal + m_arrDecals.size();
|
||||
|
||||
for (; itDecal != itDecalEnd; ++itDecal)
|
||||
{
|
||||
float fIntensity = itDecal->getIntensity() * 0.7f + 0.3f;
|
||||
int nIntensity = (int)((float)0xFF * fIntensity);
|
||||
DWORD dwColor = nIntensity | (nIntensity << 8) | (nIntensity << 16) | (nIntensity << 24);
|
||||
|
||||
// for each decal, there are a number of vertices to fill in; so we fill them in.
|
||||
// this is the number of vertices for the current decal
|
||||
unsigned numDecalVertices = itDecal->numVertices();
|
||||
for (unsigned nDecalVertex = 0; nDecalVertex < numDecalVertices; ++nDecalVertex)
|
||||
{
|
||||
const CryCharDecalVertex& rDecalVertex = itDecal->getVertex(nDecalVertex);
|
||||
const Vec3d& vCharPosition = pInPositions[rDecalVertex.nVertex];
|
||||
pDst->xyz = vCharPosition;
|
||||
pDst->color.dcolor = dwColor;
|
||||
pDst->st[0] = (rDecalVertex.uvNew.u-0.5f) / fIntensity + 0.5f;
|
||||
pDst->st[1] = (rDecalVertex.uvNew.v-0.5f) / fIntensity + 0.5f;
|
||||
++pDst;
|
||||
}
|
||||
|
||||
#if DECAL_USE_HELPERS
|
||||
unsigned numHelperVertices = itDecal->numHelperVertices();
|
||||
for (unsigned nHelperVertex = 0; nHelperVertex < numHelperVertices; ++nHelperVertex)
|
||||
{
|
||||
Vec3d vCharPosition = itDecal->getHelperVertex (nHelperVertex);
|
||||
CryUV uvCharUV = itDecal->getHelperUV (nHelperVertex);
|
||||
pDst->xyz = vCharPosition;
|
||||
pDst->color.decolor = 0xFFFFFFFF;
|
||||
pDst->st[0] = uvCharUV.u;
|
||||
pDst->st[1] = uvCharUV.v;
|
||||
++pDst;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// put the vertices out of the vertex/normal array (that belongs to the character) to the
|
||||
// decal vertex array. Decal vertices are a bit shifted toward the vertex normals (extruded)
|
||||
// to ensure the decals are above the character skin
|
||||
void CryCharDecalManager::RefreshVertexBufferVertices (const Vec3d* pInPositions)
|
||||
{
|
||||
CLeafBuffer* pLB = m_RE.getLeafBuffer();
|
||||
if (!pLB)
|
||||
return;
|
||||
CLeafBuffer* pVertexContainer = pLB->GetVertexContainer();
|
||||
if (!pVertexContainer)
|
||||
return;
|
||||
|
||||
if (pVertexContainer->m_pVertexBuffer == NULL)
|
||||
m_RE.recreate();
|
||||
|
||||
m_RE.lock (true);
|
||||
|
||||
bool bNeedCopy = true;
|
||||
|
||||
{
|
||||
// choose an optimized for the vertex format routine, if there is one
|
||||
switch (pVertexContainer->m_pVertexBuffer->m_vertexformat)
|
||||
{
|
||||
case VERTEX_FORMAT_P3F_TEX2F:
|
||||
RefreshVertexBufferVertices(pInPositions, (struct_VERTEX_FORMAT_P3F_TEX2F*)pVertexContainer->m_pVertexBuffer->m_VS[VSF_GENERAL].m_VData);
|
||||
bNeedCopy = false;
|
||||
break;
|
||||
case VERTEX_FORMAT_P3F_COL4UB_TEX2F:
|
||||
RefreshVertexBufferVertices(pInPositions, (struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F*)pVertexContainer->m_pVertexBuffer->m_VS[VSF_GENERAL].m_VData);
|
||||
bNeedCopy = false;
|
||||
break;
|
||||
default:
|
||||
assert (0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bNeedCopy)
|
||||
{
|
||||
// scan through all decals
|
||||
CDecalArray::const_iterator itDecal = m_arrDecals.begin(), itDecalEnd = itDecal + m_arrDecals.size();
|
||||
|
||||
// fill out the following sparse arrays found inthe leaf buffer
|
||||
CVertexBufferPosArrayDriver pOutPosition (m_RE.getLeafBuffer(), 0, false);
|
||||
CVertexBufferUVArrayDriver pOutUV (m_RE.getLeafBuffer(), 0, false);
|
||||
//CVertexBufferColorArrayDriver pOutColor (m_RE.getLeafBuffer(), 0, false);
|
||||
|
||||
for (; itDecal != itDecalEnd; ++itDecal)
|
||||
{
|
||||
// for each decal, there are a number of vertices to fill in; so we fill them in.
|
||||
// this is the number of vertices for the current decal
|
||||
unsigned numDecalVertices = itDecal->numVertices();
|
||||
for (unsigned nDecalVertex = 0; nDecalVertex < numDecalVertices; ++nDecalVertex)
|
||||
{
|
||||
const CryCharDecalVertex& rDecalVertex = itDecal->getVertex(nDecalVertex);
|
||||
const Vec3d& vCharPosition = pInPositions[rDecalVertex.nVertex];
|
||||
//Vec3d vCharNormal = arrInNormals[rDecalVertex.nVertex];
|
||||
// the decal mesh is slightly extruded and has the same normals
|
||||
*pOutPosition = vCharPosition;/* + vCharNormal * 0.02f*/;
|
||||
++pOutPosition;
|
||||
//*pOutColor = 0xFFFFFFFF;
|
||||
//++pOutColor;
|
||||
*pOutUV = rDecalVertex.uvNew;
|
||||
++pOutUV;
|
||||
}
|
||||
|
||||
#if DECAL_USE_HELPERS
|
||||
unsigned numHelperVertices = itDecal->numHelperVertices();
|
||||
for (unsigned nHelperVertex = 0; nHelperVertex < numHelperVertices; ++nHelperVertex)
|
||||
{
|
||||
Vec3d vCharPosition = itDecal->getHelperVertex (nHelperVertex);
|
||||
CryUV uvCharUV = itDecal->getHelperUV (nHelperVertex);
|
||||
*pOutPosition = vCharPosition;
|
||||
++pOutPosition;
|
||||
//*pOutColor = 0xFFFFFFFF;
|
||||
//++pOutColor;
|
||||
*pOutUV = uvCharUV;
|
||||
++pOutUV;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
m_RE.unlock(true);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// recalculates the index array for the vertex buffer and replaces it (so that the vertex buffer is prepared for rendering)
|
||||
// also makes sure the vertex buffer contains enough vertices to draw all the current decals (from m_arrDecals)
|
||||
void CryCharDecalManager::RefreshVertexBufferIndices ()
|
||||
{
|
||||
m_bNeedUpdateIndices = false;
|
||||
|
||||
if (!m_RE.getLeafBuffer())
|
||||
return; // nothing to update - no vertex buffer
|
||||
|
||||
if (m_RE.getLeafBuffer()->GetVertexContainer()->m_pVertexBuffer == NULL)
|
||||
m_RE.recreate();
|
||||
|
||||
// calculate the number of required indices in the arrIndices array
|
||||
unsigned numDecals = (unsigned)m_arrDecals.size();
|
||||
unsigned numMaterials = groupMaterials ();
|
||||
|
||||
// now we know the number of indices required
|
||||
if (g_MeshInfo.numIndices)
|
||||
{
|
||||
TElementaryArray<unsigned short> arrIndices;
|
||||
arrIndices.reinit (g_MeshInfo.numIndices);
|
||||
unsigned short* pIndex = &arrIndices[0];
|
||||
|
||||
// for each face, put the 3 indices referring to the vertex in the char decal manager
|
||||
// vertex array
|
||||
unsigned short nBaseVertexIndex = 0;
|
||||
|
||||
// scan through decals and add necessary indices (faces) to the index array
|
||||
for (unsigned i = 0; i < m_arrDecals.size(); ++i)
|
||||
{
|
||||
const CryCharDecal& rDecal = m_arrDecals[i];
|
||||
const CryCharDecalFace* pDecalFace = rDecal.getFaces(), *pDecalFaceEnd = pDecalFace + rDecal.numFaces();
|
||||
|
||||
// scan through decal faces, and add 3 indices for each face
|
||||
for (; pDecalFace != pDecalFaceEnd; ++pDecalFace)
|
||||
{
|
||||
for (unsigned nVertexIndex = 0; nVertexIndex < 3; ++nVertexIndex)
|
||||
*(pIndex++) = nBaseVertexIndex + (*pDecalFace)[nVertexIndex];
|
||||
}
|
||||
|
||||
// after scanning the decal faces, update the vertex base - we'll keep vertices in sequence:
|
||||
// 0th decal vertices first, then 1st decal vertices, etc. to the last decal.
|
||||
nBaseVertexIndex += rDecal.numVertices();
|
||||
|
||||
#if DECAL_USE_HELPERS
|
||||
|
||||
// the same way - with the helper faces/vertices
|
||||
// scan through all helper faces
|
||||
for (unsigned nHelperFace = 0; nHelperFace < rDecal.numHelperFaces(); ++nHelperFace)
|
||||
{
|
||||
CryCharDecalFace faceHelper = rDecal.getHelperFace(nHelperFace);
|
||||
for (unsigned nVertexIndex = 0; nVertexIndex < 3; ++nVertexIndex)
|
||||
*(pIndex++) = nBaseVertexIndex + faceHelper[nVertexIndex];
|
||||
}
|
||||
|
||||
nBaseVertexIndex += rDecal.numHelperVertices();
|
||||
#endif
|
||||
}
|
||||
|
||||
// we scanned through all decals, and vertex indices must be in the range and coinside
|
||||
// with the number that we calculated beforehand
|
||||
assert (g_MeshInfo.numVertices == nBaseVertexIndex);
|
||||
m_RE.updateIndices(&arrIndices[0], g_MeshInfo.numIndices);
|
||||
}
|
||||
|
||||
// now assign the materials from submesh infos
|
||||
m_RE.resizeMaterials(numMaterials, m_pShader);
|
||||
for (unsigned nMaterial = 0; nMaterial < numMaterials; ++nMaterial)
|
||||
assignMaterial (nMaterial, g_SubmeshInfo[nMaterial].nTextureId, g_SubmeshInfo[nMaterial].nFirstIndex, g_SubmeshInfo[nMaterial].numIndices, g_SubmeshInfo[nMaterial].nFirstVertex, g_SubmeshInfo[nMaterial].numVertices);
|
||||
}
|
||||
|
||||
// temporary locations for groupMaterials results
|
||||
// the information about the decal mesh currently (after groupMaterials)
|
||||
CryCharDecalManager::MeshInfo CryCharDecalManager::g_MeshInfo;
|
||||
// the material groups
|
||||
CryCharDecalManager::SubmeshInfo CryCharDecalManager::g_SubmeshInfo[CryCharDecalManager::g_numSubmeshInfos];
|
||||
|
||||
|
||||
// returns the number of materials in g_SubmeshInfo
|
||||
unsigned CryCharDecalManager::groupMaterials ()
|
||||
{
|
||||
// scan through all decals, grouping them by texture ids and
|
||||
// watching the max number of types not reaching g_numSubmeshInfos
|
||||
|
||||
g_MeshInfo.numIndices = g_MeshInfo.numVertices = 0;
|
||||
unsigned numDecals = (unsigned)m_arrDecals.size();
|
||||
SubmeshInfo* pNext = g_SubmeshInfo; // the next available slot in the submesh info
|
||||
SubmeshInfo* pEnd = g_SubmeshInfo + g_numSubmeshInfos;
|
||||
for (unsigned i = 0; i < numDecals; ++i)
|
||||
{
|
||||
CryCharDecal& rDecal = m_arrDecals[i];
|
||||
|
||||
if ((pNext == g_SubmeshInfo || pNext[-1].nTextureId != rDecal.getTextureId())
|
||||
&& pNext < pEnd) // we don't support more than the given amount of decal types
|
||||
{
|
||||
// add a new material group
|
||||
pNext->nFirstIndex = g_MeshInfo.numIndices;
|
||||
pNext->numIndices = 0;
|
||||
pNext->nFirstVertex = g_MeshInfo.numVertices;
|
||||
pNext->numVertices = 0;
|
||||
pNext->nTextureId = rDecal.getTextureId();
|
||||
++pNext;
|
||||
}
|
||||
|
||||
g_MeshInfo.numIndices += rDecal.numFaces() * 3;
|
||||
g_MeshInfo.numVertices += rDecal.numVertices();
|
||||
#if DECAL_USE_HELPERS
|
||||
g_MeshInfo.numIndices += rDecal.numHelperFaces() * 3;
|
||||
g_MeshInfo.numVertices += rDecal.numHelperVertices();
|
||||
#endif
|
||||
pNext[-1].numIndices = g_MeshInfo.numIndices - pNext[-1].nFirstIndex;
|
||||
pNext[-1].numVertices = g_MeshInfo.numVertices - pNext[-1].nFirstVertex;
|
||||
}
|
||||
return pNext - g_SubmeshInfo;
|
||||
}
|
||||
|
||||
// assigns the given material to the given range of indices/vertices
|
||||
void CryCharDecalManager::assignMaterial (unsigned nMaterial, int nTextureId, int nFirstIndex, int numIndices, int nFirstVertex, int numVertices)
|
||||
{
|
||||
m_RE.assignMaterial (nMaterial, m_pShader, g_GetCVars()->ca_DefaultDecalTexture()?0x1000:nTextureId, nFirstIndex, numIndices, nFirstVertex, numVertices);
|
||||
/*if (!m_RE.getLeafBuffer())
|
||||
return;
|
||||
CMatInfo& rMatInfo = (*m_RE.getLeafBuffer()->m_pMats)[nMaterial];
|
||||
rMatInfo.pRE->m_CustomTexBind[0] = nTextureId;
|
||||
m_RE.getLeafBuffer()->SetChunk(m_pShader, nFirstVertex, numVertices, nFirstIndex, numIndices, nMaterial);
|
||||
(*m_RE.getLeafBuffer()->m_pMats)[nMaterial].pRE->m_CustomTexBind[0] = GetCVars()->ca_DefaultDecalTexture()?0x1000 : nTextureId;*/
|
||||
}
|
||||
|
||||
// adds the render data to the renderer, so that the current decals can be rendered
|
||||
void CryCharDecalManager::AddRenderData (CCObject *pObj, const SRendParams & rRendParams)
|
||||
{
|
||||
if(!m_RE.getLeafBuffer() || !m_RE.getLeafBuffer()->m_pVertexBuffer || m_arrDecals.empty())
|
||||
return; // we don't add render data if there's no vertex buffer or no decals
|
||||
m_RE.render (pObj);
|
||||
#if DECAL_USE_HELPERS
|
||||
Vec3d vPos = rRendParams.vPos;
|
||||
Vec3d vAngles = rRendParams.vAngles;
|
||||
Matrix matTranRotMatrix;
|
||||
if (rRendParams.pMatrix)
|
||||
matTranRotMatrix = *rRendParams.pMatrix;
|
||||
else
|
||||
{
|
||||
//matTranRotMatrix.Identity();
|
||||
//matTranRotMatrix = GetTranslationMat(vPos)*matTranRotMatrix;
|
||||
//matTranRotMatrix = GetRotationZYX44(-gf_DEGTORAD*vAngles )*matTranRotMatrix; //NOTE: angles in radians and negated
|
||||
|
||||
//OPTIMIZED_BY_IVO
|
||||
matTranRotMatrix = Matrix34::GetRotationXYZ34( Deg2Rad(vAngles), vPos );
|
||||
matTranRotMatrix = GetTransposed44(matTranRotMatrix); //TODO: remove this after E3 and use Matrix34 instead of Matrix44
|
||||
}
|
||||
for (CDecalArray::iterator it = m_arrDecals.begin(); it != m_arrDecals.end(); ++it)
|
||||
{
|
||||
it->debugDraw(matTranRotMatrix);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CryCharDecalManager::LogStatistics()
|
||||
{
|
||||
if (!g_Statistics.empty())
|
||||
g_GetLog()->LogToFile ("%d decals created, %d total vertices, %d faces, %.1f vers/decal, %.1f faces/decal",
|
||||
g_Statistics.numDecals, g_Statistics.numDecalVertices, g_Statistics.numDecalFaces,
|
||||
g_Statistics.getAveVertsPerDecal(), g_Statistics.getAveFacesPerDecal());
|
||||
}
|
||||
|
||||
void CryCharDecalManager::CStatistics::onDecalAdd (unsigned numVertices, unsigned numFaces)
|
||||
{
|
||||
++numDecals;
|
||||
numDecalVertices += numVertices;
|
||||
numDecalFaces += numFaces;
|
||||
#ifdef _DEBUG
|
||||
if (g_GetCVars()->ca_Debug())
|
||||
{
|
||||
char szBuf[1024];
|
||||
sprintf (szBuf, "Decal added (%d vert, %d faces)\n", numVertices, numFaces);
|
||||
|
||||
#ifdef WIN32
|
||||
OutputDebugString (szBuf);
|
||||
#endif
|
||||
|
||||
#ifdef GAMECUBE
|
||||
OSReport(szBuf);
|
||||
#endif
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// returns the memory usage by this object into the sizer
|
||||
void CryCharDecalManager::GetMemoryUsage (ICrySizer* pSizer)
|
||||
{
|
||||
unsigned nSize = sizeof(*this);
|
||||
nSize += capacityofArray (m_arrDecalRequests);
|
||||
nSize += capacityofArray (m_arrDecals);
|
||||
nSize += capacityofArray (m_arrOldRE);
|
||||
pSizer->AddObject (this, nSize);
|
||||
}
|
||||
|
||||
void CryCharDecalManager::debugDump()
|
||||
{
|
||||
unsigned numMaterials = groupMaterials ();
|
||||
g_GetLog()->Log ("\001 %d decals: %d chunks used, mesh is %d verts %d indices", m_arrDecals.size(), numMaterials, g_MeshInfo.numVertices, g_MeshInfo.numIndices);
|
||||
unsigned i;
|
||||
for (i = 0; i < m_arrDecals.size(); ++i)
|
||||
{
|
||||
CryCharDecal& rDecal = m_arrDecals[i];
|
||||
g_GetLog()->Log("\001 decal %3d: %d verts, %d faces, \"%s\" (texId=%d)",i, rDecal.numVertices(), rDecal.numFaces(), g_GetIRenderer()->EF_GetTextureByID(rDecal.getTextureId())->GetName(), rDecal.getTextureId());
|
||||
}
|
||||
for (i = 0; i < numMaterials; ++i)
|
||||
{
|
||||
SubmeshInfo &rSubmesh = g_SubmeshInfo[i];
|
||||
g_GetLog()->Log ("\001 chunk %d: %d verts @%d, %d indices @%d, texture %d \"%s\"", i, rSubmesh.numVertices, rSubmesh.nFirstVertex, rSubmesh.numIndices, rSubmesh.nFirstIndex, rSubmesh.nTextureId, g_GetIRenderer()->EF_GetTextureByID(rSubmesh.nTextureId)->GetName());
|
||||
}
|
||||
}
|
||||
178
CryAnimation/CryCharDecalManager.h
Normal file
178
CryAnimation/CryCharDecalManager.h
Normal file
@@ -0,0 +1,178 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Sep 25 2002 :- Created by Sergiy Migdalskiy
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef _CRY_CHAR_DECAL_MANAGER_HDR_
|
||||
#define _CRY_CHAR_DECAL_MANAGER_HDR_
|
||||
|
||||
#include "CryCharDecal.h"
|
||||
#include "SparseArrayDriver.h"
|
||||
#include "CryCharRenderElement.h"
|
||||
|
||||
#ifndef DECAL_USE_HELPERS
|
||||
#error DECAL_USE_HELPERS must be defined in this header. Please include "CryCharDecal.h"
|
||||
#endif
|
||||
|
||||
// This class manages decals on characters.
|
||||
// It memorizes requests for decals, then realizes them using the character skin
|
||||
// geometry (constructs the decal face strip, UV-maps it)
|
||||
//
|
||||
// The workflow is as follows:
|
||||
// Request(Add) the decal. At this stage, its info will just be memorized, it won't be present in the decal list
|
||||
// Realize the decal. At this stage, the deformable geometry will be created for the decal.
|
||||
// Render the decal. After realization, the decals have their own geometry that bases on the deformed vertices
|
||||
// and normals of the character skin.
|
||||
class CryCharDecalManager
|
||||
{
|
||||
public:
|
||||
|
||||
static void LogStatistics();
|
||||
|
||||
CryCharDecalManager (class CryGeometryInfo* pGeomInfo);
|
||||
|
||||
~CryCharDecalManager ();
|
||||
|
||||
// Request (add) a new decal to the character
|
||||
void Add (CryEngineDecalInfo& Decal);
|
||||
|
||||
// discards the decal request queue (not yet realized decals added through Add())
|
||||
void DiscardRequests();
|
||||
|
||||
// realizes (creates geometry for) unrealized(requested) decals
|
||||
void Realize (const Vec3* pPositions);
|
||||
|
||||
// returns true if the Realize() needs to be called
|
||||
bool NeedRealize () const;
|
||||
|
||||
// this is the number of additional faces introduced by the decals
|
||||
unsigned numFaces() const;
|
||||
// this is the number of additional vertices introduced by the decals
|
||||
unsigned numVertices() const;
|
||||
|
||||
// adds the render data to the renderer, so that the current decals can be rendered
|
||||
void AddRenderData (CCObject *pObj, const SRendParams & rRendParams);
|
||||
|
||||
// cleans up all decals, destroys the vertex buffer
|
||||
void clear();
|
||||
|
||||
void debugDump();
|
||||
|
||||
// returns the memory usage by this object into the sizer
|
||||
void GetMemoryUsage (ICrySizer* pSizer);
|
||||
protected:
|
||||
// sets up the given material to default state: just clean decal material
|
||||
void initDefaultMaterial (CMatInfo& rMat);
|
||||
|
||||
// assigns the given material to the given range of indices/vertices
|
||||
void assignMaterial (unsigned nMaterial, int nTextureId, int nFirstIndex, int numIndices, int nFirstVertex, int numVertices);
|
||||
|
||||
// recreates the current leaf buffer so that it contains the given number of vertices and
|
||||
// reserved indices, both uninitialized. There are 0 used indices initially
|
||||
void ReserveVertexBufferVertices (const Vec3* pInPositions);
|
||||
|
||||
// recalculates the index array for the vertex buffer and replaces it (so that the vertex buffer is prepared for rendering)
|
||||
// also makes sure the vertex buffer contains enough vertices to draw all the current decals (from m_arrDecals)
|
||||
void RefreshVertexBufferIndices ();
|
||||
|
||||
// put the deformed vertices into the videomemory
|
||||
void RefreshVertexBufferVertices (const Vec3* pPositions);
|
||||
|
||||
// put the deformed vertices into the videobuffer of the given format
|
||||
void RefreshVertexBufferVertices (const Vec3* pPositions, struct_VERTEX_FORMAT_P3F_TEX2F* pDst);
|
||||
|
||||
// put the deformed vertices into the videobuffer of the given format
|
||||
void RefreshVertexBufferVertices (const Vec3* pPositions, struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F* pDst);
|
||||
|
||||
// cleans up the old leaf buffers
|
||||
void DeleteOldRenderElements();
|
||||
|
||||
// cleans up the dead decals
|
||||
// sets m_bNeedUpdateIndices to true if it has added something (and therefore refresh of indices is required)
|
||||
void DeleteOldDecals();
|
||||
|
||||
// if there are decal requests, then converts them into the decal objects
|
||||
// reserves the vertex/updates the index buffers, if need to be
|
||||
// sets m_bNeedUpdateIndices to true if it has added something (and therefore refresh of indices is required)
|
||||
void RealizeNewDecalRequests (const Vec3* pPositions);
|
||||
|
||||
// starts fading out all decals that are close enough to the given point
|
||||
// NOTE: the radius is m^2 - it's the square of the radius of the sphere
|
||||
void fadeOutCloseDecals (const Vec3& ptCenter, float fRadius2);
|
||||
protected:
|
||||
// deletes the leaf buffer
|
||||
void DeleteLeafBuffer ();
|
||||
|
||||
// this represents the contiguous subarray of vertices and indices
|
||||
struct MeshInfo {
|
||||
unsigned short numIndices, numVertices;
|
||||
};
|
||||
struct SubmeshInfo: public MeshInfo {
|
||||
unsigned short nFirstIndex, nFirstVertex;
|
||||
int nTextureId;
|
||||
};
|
||||
|
||||
// temporary locations for groupMaterials results
|
||||
// the information about the decal mesh currently (after groupMaterials)
|
||||
static MeshInfo g_MeshInfo;
|
||||
// the maximum number of decal types per character
|
||||
enum {g_numSubmeshInfos = 32};
|
||||
// the material groups
|
||||
static SubmeshInfo g_SubmeshInfo[g_numSubmeshInfos];
|
||||
// groups the decals and returns the information in gTmpMeshInfo and gTmpSubmeshInfo
|
||||
// returns the number of materials in g_SubmeshInfo
|
||||
unsigned groupMaterials ();
|
||||
|
||||
|
||||
// the array of requested (unrealized) decals
|
||||
std::vector<CryEngineDecalInfo> m_arrDecalRequests;
|
||||
|
||||
// the array of realized decals
|
||||
typedef std::vector<CryCharDecal> CDecalArray;
|
||||
CDecalArray m_arrDecals;
|
||||
|
||||
// the geometry of the character
|
||||
CryGeometryInfo* m_pGeometry;
|
||||
|
||||
// this shader is used by all decals
|
||||
IShader* m_pShader;
|
||||
|
||||
// if this is true, then the indices will be updated upon the next render method invokation
|
||||
bool m_bNeedUpdateIndices;
|
||||
|
||||
//
|
||||
// The following members are only dealt with by the Reserve method, constructor and destructor
|
||||
//
|
||||
|
||||
CryCharRenderElement m_RE;
|
||||
|
||||
|
||||
// this is the collection of leaf buffers that are to be deleted - each with its own last render frame
|
||||
typedef std::vector<CryCharRenderElement> RenderElementArray;
|
||||
RenderElementArray m_arrOldRE;
|
||||
|
||||
|
||||
struct CStatistics
|
||||
{
|
||||
unsigned numDecals;
|
||||
unsigned numDecalVertices;
|
||||
unsigned numDecalFaces;
|
||||
float getAveVertsPerDecal() {return float (numDecalVertices) / numDecals;}
|
||||
float getAveFacesPerDecal() {return float (numDecalFaces) / numDecals;}
|
||||
bool empty() const {return numDecals == 0;}
|
||||
void onDecalAdd (unsigned numVertices, unsigned numFaces);
|
||||
CStatistics():
|
||||
numDecalVertices(0),
|
||||
numDecals(0),
|
||||
numDecalFaces (0)
|
||||
{
|
||||
}
|
||||
};
|
||||
static CStatistics g_Statistics;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
163
CryAnimation/CryCharFxTrail.cpp
Normal file
163
CryAnimation/CryCharFxTrail.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "stdafx.h"
|
||||
#include "CryCharFxTrail.h"
|
||||
#include "VertexBufferArrayDrivers.h"
|
||||
#include "CryCharInstanceRenderParams.h"
|
||||
#include "CryModelState.h"
|
||||
|
||||
CryCharFxTrail::CryCharFxTrail (class CryModelState* pState, const CryCharFxTrailParams& params):
|
||||
m_Params(params), m_bVisible(true), m_numEntries(0), m_nHeadEntry(0), m_nLastFrameDeform(0), m_pParent(pState)
|
||||
{
|
||||
m_pTimes.reinit(numMaxEntries());
|
||||
|
||||
// I need to create a dummy array because otherwise the system buffer doesn't get created
|
||||
struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F *pDummy = new struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F[numVertices()];
|
||||
memset (pDummy, 0, sizeof(pDummy[0]) * numVertices());
|
||||
m_RE.create (numVertices(), pDummy, "AnimFxTrail", 1, false);
|
||||
delete[]pDummy;
|
||||
|
||||
m_pShader = g_GetIRenderer()->EF_LoadShader("TemplDecalAdd", eSH_World, EF_SYSTEM);
|
||||
|
||||
m_RE.resizeMaterials(1, m_pShader);
|
||||
}
|
||||
|
||||
CryCharFxTrail::~CryCharFxTrail()
|
||||
{
|
||||
m_RE.destruct();
|
||||
}
|
||||
|
||||
|
||||
// gets called upon Deform() of the model state
|
||||
void CryCharFxTrail::Deform (const Matrix44* pBones)
|
||||
{
|
||||
int nFrame = g_GetIRenderer()->GetFrameID(false);
|
||||
if (m_nLastFrameDeform != nFrame)
|
||||
{
|
||||
UpdateEntries(pBones);
|
||||
m_nLastFrameDeform = nFrame;
|
||||
}
|
||||
}
|
||||
|
||||
void CryCharFxTrail::UpdateEntries (const Matrix44* pBones)
|
||||
{
|
||||
float fTime = g_GetTimer()->GetCurrTime();
|
||||
|
||||
// add a new entry to the history
|
||||
if (m_numEntries)
|
||||
{
|
||||
// shrink the queue if the tail is too old
|
||||
if (fTime - m_pTimes[getTailEntry()] > m_Params.fLength)
|
||||
--m_numEntries;
|
||||
}
|
||||
|
||||
// grow the entry queue
|
||||
m_numEntries = min(numMaxEntries(), m_numEntries + 1);
|
||||
|
||||
// increment the head entry to fill the new data in
|
||||
m_nHeadEntry = (m_nHeadEntry + numMaxEntries() - 1) % numMaxEntries();
|
||||
|
||||
m_pTimes[m_nHeadEntry] = fTime;
|
||||
|
||||
CVertexBufferPosArrayDriver pPos(m_RE.getLeafBuffer());
|
||||
CVertexBufferColorArrayDriver pColor(m_RE.getLeafBuffer());
|
||||
CVertexBufferUVArrayDriver pUV(m_RE.getLeafBuffer());
|
||||
|
||||
unsigned i, nEntry = m_nHeadEntry;
|
||||
for (i = 0; i < m_Params.numVerts; ++i)
|
||||
{
|
||||
pPos[nEntry*m_Params.numVerts+i] = pBones[m_Params.nBone].TransformPointOLD(m_Params.vLine[i]);
|
||||
pUV[nEntry*m_Params.numVerts+i].u = float(i)/(m_Params.numVerts-1);
|
||||
pUV[nEntry*m_Params.numVerts+i].v = 0;
|
||||
pColor[nEntry*m_Params.numVerts+i] = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
// fill all the old entries with the corresponding UVs
|
||||
for (i = 1; i < m_numEntries; ++i)
|
||||
{
|
||||
nEntry = (m_nHeadEntry + i) % numMaxEntries();
|
||||
float fAge = (fTime - m_pTimes[nEntry]) / m_Params.fLength;
|
||||
for (unsigned j = 0; j < m_Params.numVerts; ++j)
|
||||
{
|
||||
pUV[nEntry*m_Params.numVerts+j].u = float(j)/(m_Params.numVerts-1);
|
||||
pUV[nEntry*m_Params.numVerts+j].v = fAge;
|
||||
pColor[nEntry*m_Params.numVerts+j] = ((unsigned)(0xFF * fAge) << 24) + 0xFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
m_RE.getLeafBuffer()->InvalidateVideoBuffer();
|
||||
|
||||
if (m_numEntries > 1)
|
||||
UpdateIndices();
|
||||
else
|
||||
m_RE.updateIndices(NULL, 0);
|
||||
}
|
||||
|
||||
void CryCharFxTrail::UpdateIndices()
|
||||
{
|
||||
std::vector<unsigned short> arrIndices;
|
||||
// 6 indices per quad; numVerts-1 quads per strip; numEntries-1 strips
|
||||
arrIndices.resize ((m_Params.numVerts-1) * 6 * (m_numEntries - 1));
|
||||
|
||||
unsigned nStrip, nQuad, nBase = 0;
|
||||
for (nStrip = 1; nStrip < m_numEntries; ++nStrip)
|
||||
{
|
||||
unsigned nEntry = (m_nHeadEntry + nStrip)%numMaxEntries();
|
||||
unsigned nPrevEntry = (m_nHeadEntry + nStrip - 1)%numMaxEntries();
|
||||
for (nQuad = 1; nQuad < m_Params.numVerts; ++nQuad)
|
||||
{
|
||||
arrIndices[nBase++] = nEntry*m_Params.numVerts + nQuad - 1;
|
||||
arrIndices[nBase++] = nEntry*m_Params.numVerts + nQuad;
|
||||
arrIndices[nBase++] = nPrevEntry*m_Params.numVerts + nQuad;
|
||||
arrIndices[nBase++] = nEntry*m_Params.numVerts + nQuad - 1;
|
||||
arrIndices[nBase++] = nPrevEntry*m_Params.numVerts + nQuad;
|
||||
arrIndices[nBase++] = nPrevEntry*m_Params.numVerts + nQuad - 1;
|
||||
}
|
||||
}
|
||||
|
||||
m_RE.updateIndices(&arrIndices[0], arrIndices.size());
|
||||
|
||||
// start with the whole diapason of vertices in the vertex buffer. If the circular queue
|
||||
// consists of only one piece (doesn't wrap around the end), it'll be this piece
|
||||
int nFirstVertex = 0;
|
||||
int numVertices = this->numVertices();
|
||||
|
||||
if (m_nHeadEntry + m_numEntries <= numMaxEntries())
|
||||
{
|
||||
// doesn't wrap around
|
||||
nFirstVertex = m_nHeadEntry * m_Params.numVerts;
|
||||
numVertices = m_numEntries * m_Params.numVerts;
|
||||
}
|
||||
|
||||
m_RE.assignMaterial(0, m_pShader, m_Params.nTextureId, 0, m_numEntries * m_Params.numVerts, nFirstVertex, numVertices);
|
||||
}
|
||||
|
||||
|
||||
void CryCharFxTrail::Render (const struct SRendParams & RendParams, Matrix44& mtxObjMatrix, CryCharInstanceRenderParams& rCharParams)
|
||||
{
|
||||
return;
|
||||
CCObject * pObj = rCharParams.NewCryCharCCObject(RendParams, mtxObjMatrix, NULL);
|
||||
|
||||
AddRenderData(pObj, RendParams);
|
||||
}
|
||||
|
||||
|
||||
// adds the render data to the renderer, so that the current decals can be rendered
|
||||
void CryCharFxTrail::AddRenderData (CCObject *pObj, const SRendParams & rRendParams)
|
||||
{
|
||||
// we must have the leaf buffer AND at least 2 history entries to form a quad
|
||||
if(m_bVisible && m_RE.getLeafBuffer() && m_numEntries >= 2)
|
||||
m_RE.render(pObj);
|
||||
}
|
||||
|
||||
|
||||
//! Renderer calls this function to allow update the video vertex buffers right before the rendering
|
||||
void CryCharFxTrail::ProcessSkinning (const Vec3& t, const Matrix44& mtxModel, int nTemplate, int nLod, bool bForceUpdate)
|
||||
{
|
||||
//Deform (m_pParent->getBoneGlobalMatrices());
|
||||
}
|
||||
|
||||
// returns the memory usage by this object into the sizer
|
||||
// TODO: use
|
||||
void CryCharFxTrail::GetMemoryUsage (ICrySizer* pSizer)
|
||||
{
|
||||
pSizer->AddObject(this, sizeof(*this) + sizeof(m_pTimes[0]) * numMaxEntries());
|
||||
}
|
||||
84
CryAnimation/CryCharFxTrail.h
Normal file
84
CryAnimation/CryCharFxTrail.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#ifndef _CRY_ANIMATION_CRY_CHAR_FX_TRAIL_HDR_
|
||||
#define _CRY_ANIMATION_CRY_CHAR_FX_TRAIL_HDR_
|
||||
|
||||
#include <CryCharFxTrailParams.h>
|
||||
#include "CryCharRenderElement.h"
|
||||
|
||||
// SFX : sword trail
|
||||
// The trail is composed of quads, which are formed with an index buffer referring to
|
||||
// "extrusion lines" in the vertex buffer. Extrusion line is (e.g. 2) vertices describing
|
||||
// the sword line in one of the previous frames. All this forms a circular queue of entries
|
||||
// Each entry has:
|
||||
// 2(currently) vertices in the vertex buffer
|
||||
// 1 float in the time history
|
||||
//
|
||||
// The circular queue moves BACKward (the head is circularly decremented)
|
||||
class CryCharFxTrail: public ICryCharFxTrail
|
||||
{
|
||||
public:
|
||||
CryCharFxTrail(class CryModelState* pState, const CryCharFxTrailParams& params);
|
||||
~CryCharFxTrail();
|
||||
|
||||
//! Renderer calls this function to allow update the video vertex buffers right before the rendering
|
||||
void ProcessSkinning (const Vec3& t, const Matrix44& mtxModel, int nTemplate, int nLod=-1, bool bForceUpdate=false);
|
||||
|
||||
// returns true if the given submesh is visible
|
||||
bool IsVisible() {return m_bVisible;}
|
||||
|
||||
// depending on bVisible, either makes the submesh visible or invisible
|
||||
void SetVisible(bool bVisible = true) {m_bVisible = bVisible;}
|
||||
|
||||
// adds the render data to the renderer, so that the current decals can be rendered
|
||||
void AddRenderData (CCObject *pObj, const SRendParams & rRendParams);
|
||||
|
||||
void Render (const struct SRendParams & RendParams, Matrix44& mtxObjMatrix, struct CryCharInstanceRenderParams& rCharParams);
|
||||
|
||||
// returns the memory usage by this object into the sizer
|
||||
// TODO: use
|
||||
void GetMemoryUsage (ICrySizer* pSizer);
|
||||
|
||||
// gets called upon Deform() of the model state
|
||||
void Deform (const Matrix44* pBones);
|
||||
protected:
|
||||
|
||||
// updates the system vertex buffer and the queue of entries/times
|
||||
void UpdateEntries(const Matrix44* pBones);
|
||||
void UpdateIndices();
|
||||
|
||||
// returns the tail entry, return value is invalid if m_numEntries == 0
|
||||
unsigned getTailEntry() const {return (m_nHeadEntry + m_numEntries - 1) % numMaxEntries();}
|
||||
|
||||
// max number of history entries
|
||||
unsigned numMaxEntries() const {return m_Params.numMaxQuads+1;}
|
||||
|
||||
unsigned numVertices()const {return m_Params.numVerts * numMaxEntries();}
|
||||
|
||||
protected:
|
||||
CryCharFxTrailParams m_Params;
|
||||
bool m_bVisible;
|
||||
CryCharRenderElement m_RE;
|
||||
|
||||
// the length of the trail history in the system vertex buffer
|
||||
// (number of vertices per extrusion line) * (length of history) = (total number of vertices in sysbuffer)
|
||||
// currently , only 2 verts per extrusion line are supported
|
||||
unsigned m_numEntries;
|
||||
|
||||
// the starting entry in the history queue
|
||||
unsigned m_nHeadEntry;
|
||||
|
||||
// last frame when the deform was called
|
||||
int m_nLastFrameDeform;
|
||||
|
||||
// the shader of the trail
|
||||
IShader *m_pShader;
|
||||
|
||||
// array [maxEntries] of times of each entry forming
|
||||
TElementaryArray<float> m_pTimes;
|
||||
|
||||
CryModelState* m_pParent;
|
||||
};
|
||||
|
||||
|
||||
TYPEDEF_AUTOPTR(CryCharFxTrail);
|
||||
|
||||
#endif
|
||||
1445
CryAnimation/CryCharInstance.cpp
Normal file
1445
CryAnimation/CryCharInstance.cpp
Normal file
File diff suppressed because it is too large
Load Diff
414
CryAnimation/CryCharInstance.h
Normal file
414
CryAnimation/CryCharInstance.h
Normal file
@@ -0,0 +1,414 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CryEngine Source code
|
||||
//
|
||||
// File:CryCharInstance.h
|
||||
// Declaration of CryCharInstance class
|
||||
//
|
||||
// History:
|
||||
// August 16, 2002: Created by Sergiy Migdalskiy <sergiy@crytek.de>
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#ifndef _CRY_CHAR_INSTANCE_HEADER_
|
||||
#define _CRY_CHAR_INSTANCE_HEADER_
|
||||
|
||||
struct CryEngineDecalInfo;
|
||||
struct CryParticleSpawnInfo;
|
||||
|
||||
#include <ICryAnimation.h>
|
||||
#include "CryModel.h"
|
||||
#include "CryCharBody.h"
|
||||
#include "IBindable.h"
|
||||
#include "CryCharInstanceRenderParams.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
struct AnimData;
|
||||
|
||||
class CryCharManager;
|
||||
|
||||
class CryCharInstanceBase: public ICryCharInstance
|
||||
{
|
||||
public:
|
||||
~CryCharInstanceBase()
|
||||
{
|
||||
DetachAll();
|
||||
}
|
||||
struct StatObjBind
|
||||
{
|
||||
IBindable* pObj;
|
||||
unsigned nBone;
|
||||
unsigned nFlags;
|
||||
StatObjBind(IBindable* obj, unsigned bone,unsigned flags):
|
||||
pObj(obj), nBone(bone), nFlags(flags) {assert (pObj);}
|
||||
};
|
||||
typedef std::vector<StatObjBind*> BindArray;
|
||||
BindArray m_arrBinds;
|
||||
|
||||
// detaches all objects from bones; returns the nubmer of bindings deleted
|
||||
unsigned DetachAll();
|
||||
|
||||
// detach all bindings to the given bone; returns the nubmer of bindings deleted
|
||||
unsigned DetachAllFromBone(unsigned nBone);
|
||||
|
||||
// attaches the object to the given bone. The returned value is the handle of the binding,
|
||||
// that can be used to examine the binding and delete it
|
||||
ObjectBindingHandle AttachToBone (IBindable*pObj, unsigned nBone, unsigned nFlags = 0);
|
||||
|
||||
// detaches the given binding; if it returns false, the binding handle is invalid
|
||||
// the binding becomes invalid immediately after detach
|
||||
bool Detach (ObjectBindingHandle nHandle);
|
||||
|
||||
// checks if the given binding is valid
|
||||
bool IsBindingValid (ObjectBindingHandle nHandle);
|
||||
|
||||
void PreloadResources ( float fDistance, float fTime, int nFlags );
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Implementation of ICryCharInstance interface, the main interface
|
||||
// in the Animation module
|
||||
class CryCharInstance : public CryCharInstanceBase, protected CryCharInstanceRenderParams
|
||||
{
|
||||
public:
|
||||
// Releases this instance.
|
||||
virtual void Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
CryCharBody *GetBody()
|
||||
{
|
||||
return m_pCryCharBody;
|
||||
}
|
||||
|
||||
//! Returns the model interface
|
||||
virtual ICryCharModel* GetModel();
|
||||
|
||||
virtual void Draw(const SRendParams & RendParams,const Vec3& t);
|
||||
//there must be only one function
|
||||
//! Render object ( register render elements into renderer )
|
||||
virtual void Render(const struct SRendParams & rParams, const Vec3& t, int nLodLevel);
|
||||
|
||||
//! marks all LODs as needed to be reskinned
|
||||
virtual void ForceReskin ();
|
||||
|
||||
//! returns the leaf buffer materials in this character (as they are used in the renderer)
|
||||
virtual const list2<CMatInfo>*getLeafBufferMaterials();
|
||||
|
||||
|
||||
//! Interface for the renderer - returns the CDLight describing the light in this character;
|
||||
//! returns NULL if there's no light with such index
|
||||
//! ICryCharInstance owns this light. This light may be destructed without notice upon next call to
|
||||
//! any other function but GetLight(). Nobody may every modify it.
|
||||
const CDLight* GetBoundLight (int nIndex);
|
||||
|
||||
//! Draw the character shadow volumes into the stencil buffer using a set of specified
|
||||
//! rendering parameters
|
||||
virtual void RenderShadowVolumes(const SRendParams *rParams, int nLimitLOD = 0);
|
||||
|
||||
virtual const char * GetShaderTemplateName();
|
||||
|
||||
virtual bool SetShaderTemplateName(const char *TemplName, int Id, const char *ShaderName=0,IMatInfo *pCustomMaterial=0,unsigned nFlags = 0);
|
||||
//! Sets shader template for rendering
|
||||
virtual bool SetShaderTemplate(int nTemplate, const char *TemplName, const char *ShaderName, bool bOnlyRegister=false, int * pnNewTemplateId=NULL);
|
||||
|
||||
|
||||
virtual void SetShaderFloat(const char *Name, float fVal, const char *ShaderName=NULL);
|
||||
//! Sets color parameter
|
||||
virtual void SetColor(float fR, float fG, float fB, float fA);
|
||||
|
||||
virtual const Vec3d GetCenter();
|
||||
virtual const float GetRadius();
|
||||
virtual void GetBBox(Vec3d& Mins, Vec3d& Maxs);
|
||||
|
||||
virtual bool SetAnimationFrame(const char * szString, int nFrame);
|
||||
virtual ICryBone * GetBoneByName(const char * szName);
|
||||
|
||||
// Attaches a static object to the bone (given the bone name) or detaches previous object from the bone.
|
||||
virtual ObjectBindingHandle AttachObjectToBone(IBindable * pWeaponModel, const char * szBoneName, bool bUseRelativeToDefPoseMatrix, unsigned nFlags);
|
||||
|
||||
// returns the number of bindings; valid until the next attach/detach operation
|
||||
virtual size_t GetBindingCount();
|
||||
|
||||
// fills the given array with GetBindingCount() pointers to IBindable
|
||||
virtual void EnumBindables(IBindable** pResult);
|
||||
|
||||
//! attach a light to a bone
|
||||
virtual LightHandle AttachLight (CDLight* pDLight, unsigned nBone, bool bCopyLight = false);
|
||||
//! detach the light from the bone
|
||||
virtual void DetachLight (CDLight* pDLight);
|
||||
|
||||
//! Attach a light (copying the light actually) to the bone
|
||||
//! Returns the handle identifying the light. With this handle, you can either
|
||||
//! Retrieve the light information or detach it.
|
||||
virtual LightHandle AttachLight (const CDLight& rDLight, const char* szBoneName);
|
||||
//! Detaches the light by the handle retuned by AttachLight
|
||||
virtual void DetachLight (LightHandle nHandle);
|
||||
//! Returns the light by the light handle; returns NULL if no such light found
|
||||
virtual CDLight* GetLight(LightHandle nHandle);
|
||||
//! Returns the light handle if the light is attached; returns invalid handle, if this light is not attached
|
||||
//! NOTE: if your light was attached with copying, then you won't get the handle for the original light pointer
|
||||
//! because the original light might have been attached several times and have several pointers in this case
|
||||
virtual LightHandle GetLightHandle (CDLight* pLight);
|
||||
|
||||
// virtual bool BindStatObjToBone(IBindable * pStatObj, const char * szBoneName);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// StartAnimation:
|
||||
// Searchs for the animation in animations list and starts palying it.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual bool StartAnimation (const char* szAnimName, const struct CryCharAnimationParams& Params);
|
||||
|
||||
//! Start the specified by parameters morph target
|
||||
virtual void StartMorph (const char* szMorphTarget,const CryCharMorphParams& params);
|
||||
|
||||
//! Start the morph target
|
||||
virtual void StartMorph (int nMorphTargetId, const CryCharMorphParams& params);
|
||||
|
||||
//! Finds the morph with the given id and sets its relative time.
|
||||
//! Returns false if the operation can't be performed (no morph)
|
||||
virtual bool SetMorphTime (int nMorphTargetId, float fTime);
|
||||
|
||||
//! Stops the animation at the specified layer. Returns true if there was some animation on that layer, and false otherwise
|
||||
virtual bool StopAnimation (int nLayer);
|
||||
|
||||
virtual bool StopMorph (int nMorphTargetId);
|
||||
virtual void StopAllMorphs();
|
||||
//! freezes all currently playing morphs at the point they're at
|
||||
virtual void FreezeAllMorphs();
|
||||
|
||||
|
||||
//! 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
|
||||
virtual void EnableLastIdleAnimationRestart (unsigned nLayer, bool bEnable = true);
|
||||
|
||||
// Checks if the animation with the given name exists, returns true if it does
|
||||
virtual bool IsAnimationPresent(const char* szAnimName);
|
||||
|
||||
|
||||
virtual const char * GetCurAnimation();
|
||||
|
||||
//! Returns the current animation in the layer or -1 if no animation is being played
|
||||
//! in this layer (or if there's no such layer)
|
||||
virtual int GetCurrentAnimation (unsigned nLayer);
|
||||
|
||||
virtual void ResetAnimations();
|
||||
|
||||
// calculates the mask ANDed with the frame id that's used to determine whether to skin the character on this frame or not.
|
||||
int GetUpdateFrequencyMask(Vec3d vPos, float fRadius);
|
||||
virtual void Update(Vec3d vPos, float fRadius, unsigned uFlags); // processes animations and recalc bones
|
||||
//! Updates the bones and the bounding box. Should be called if animation update
|
||||
//! cycle in EntityUpdate has already passed but you need the result of new animatmions
|
||||
//! started after Update right now.
|
||||
virtual void ForceUpdate();
|
||||
|
||||
virtual void UpdatePhysics( float fScale=1.0f ); // updates model physics (if any); is separate from Update
|
||||
|
||||
virtual void SetFlags(int nFlags) { m_nFlags=nFlags; }
|
||||
virtual int GetFlags() { return m_nFlags; }
|
||||
|
||||
//! Enables receiving OnStart/OnEnd of all animations from this character instance
|
||||
//! THe specified sink also receives the additional animation events specified through AddAnimationEvent interface
|
||||
virtual void AddAnimationEventSink(ICharInstanceSink * pCharInstanceSink);
|
||||
|
||||
//! Counterpart to AddAnimationEventSink
|
||||
virtual void RemoveAnimationEventSink(ICharInstanceSink * pCharInstanceSink);
|
||||
|
||||
//! Deletes all animation events
|
||||
virtual void RemoveAllAnimationEvents();
|
||||
|
||||
|
||||
//! Enables receiving OnStart/OnEnd of specified animation from this character instance
|
||||
//! The specified sink also receives the additional animation events specified through AddAnimationEvent interface for this animation
|
||||
virtual void AddAnimationEventSink(const char* szAnimName, ICharInstanceSink * pCharInstanceSink);
|
||||
|
||||
//! Counterpart to the AddAnimationEventSink
|
||||
virtual void RemoveAnimationEventSink(const char* szAnimName, ICharInstanceSink * pCharInstanceSink);
|
||||
|
||||
//! Adds an animation event; whenever the character plays the specified frame of the specified animation,
|
||||
//! it calls back the animation event sinkreceiving OnEvent notification of specified animation for all instances using this model
|
||||
virtual bool AddAnimationEvent(const char * szAnimName, int nFrameID, AnimSinkEventData UserData);
|
||||
|
||||
//! Deletes the animation event; from now on the sink won't be receiving the animation this event
|
||||
virtual bool RemoveAnimationEvent (const char* szAnimName, int nFrameID, AnimSinkEventData UserData);
|
||||
|
||||
//! Enable object animation time update. If the bUpdate flag is false, subsequent calls to Update will not animate the character
|
||||
virtual void EnableTimeUpdate (bool bUpdate);
|
||||
|
||||
//! Set the current time of the given layer, in seconds
|
||||
virtual void SetLayerTime (int nLayer, float fTimeSeconds);
|
||||
virtual float GetLayerTime (int nLayer);
|
||||
|
||||
// sets the given aniimation to the given layer as the default
|
||||
void SetDefaultIdleAnimation(unsigned nLayer, const char* szAnimName);
|
||||
|
||||
CryCharInstance (CryCharBody * pBody);
|
||||
~CryCharInstance ();
|
||||
|
||||
// FOR TEST ONLY enables/disables StartAnimation* calls; puts warning into the log if StartAnimation* is called while disabled
|
||||
void EnableStartAnimation (bool bEnable) ;
|
||||
|
||||
//Executes a per-character script command
|
||||
bool ExecScriptCommand (int nCommand, void* pParams, void* pResult);
|
||||
private:
|
||||
|
||||
void Create(const char * fname, CryCharBody * _pCryCharBody);
|
||||
|
||||
const Vec3d & GetSpitFirePos() { return m_v3pvSpitFirePosTranslated; }
|
||||
|
||||
//! Return the length of the given animation in seconds; 0 if no such animation found
|
||||
virtual void SetAnimationSpeed(float speed) { m_fAnimSpeedScale = speed; }
|
||||
virtual float GetAnimationSpeed() { return m_fAnimSpeedScale; }
|
||||
virtual void SetAnimationSpeed(int nLayer, float fSpeed);
|
||||
//! Set morph speed scale
|
||||
//! Finds the morph target with the given id, sets its morphing speed and returns true;
|
||||
//! if there's no such morph target currently playing, returns false
|
||||
virtual bool SetMorphSpeed (int nMorphTargetId, float fSpeed);
|
||||
|
||||
virtual void BuildPhysicalEntity(IPhysicalEntity *pent,float mass,int surface_idx,float stiffness_scale,int nLod=0);
|
||||
virtual IPhysicalEntity *CreateCharacterPhysics(IPhysicalEntity *pHost, float mass,int surface_idx,float stiffness_scale, int nLod=0);
|
||||
virtual int CreateAuxilaryPhysics(IPhysicalEntity *pHost, int nLod=0);
|
||||
virtual void SynchronizeWithPhysicalEntity(IPhysicalEntity *pent,const Vec3& posMaster,const Quat& qMaster);
|
||||
virtual IPhysicalEntity *RelinquishCharacterPhysics();
|
||||
virtual void SetCharacterPhysParams(float mass,int surface_idx);
|
||||
virtual IPhysicalEntity *GetCharacterPhysics();
|
||||
virtual IPhysicalEntity *GetCharacterPhysics(const char *pRootBoneName);
|
||||
virtual IPhysicalEntity *GetCharacterPhysics(int iAuxPhys);
|
||||
virtual void DestroyCharacterPhysics(int iMode=0);
|
||||
|
||||
virtual void SetLimbIKGoal(int limbid, vectorf ptgoal=vectorf(1E10f,0,0), int ik_flags=0, float addlen=0, vectorf goal_normal=vectorf(zero));
|
||||
virtual vectorf GetLimbEndPos(int limbid);
|
||||
|
||||
virtual void AddImpact(int partid, vectorf point,vectorf impact);
|
||||
virtual int TranslatePartIdToDeadBody(int partid);
|
||||
|
||||
virtual bool IsAnimStopped();
|
||||
|
||||
virtual vectorf GetOffset();
|
||||
virtual void SetOffset(vectorf offset);
|
||||
|
||||
// Spawn decal on the character
|
||||
virtual void CreateDecal(CryEngineDecalInfo& Decal);
|
||||
|
||||
//! cleans up all decals in this character
|
||||
virtual void ClearDecals();
|
||||
|
||||
|
||||
private:
|
||||
CryCharBody_AutoPtr m_pCryCharBody;
|
||||
CryModelState *m_pModelState;
|
||||
|
||||
protected:
|
||||
// the time of the last animation update call.
|
||||
// This is the time returned by ITimer::GetCurrTime()
|
||||
// If it's <=0, it means that update was never called
|
||||
float m_fLastAnimUpdateTime;
|
||||
|
||||
// This is the scale factor that affects the animation speed of the character.
|
||||
// All the animations are played with the constant real-time speed multiplied by this factor.
|
||||
// So, 0 means still animations (stuck at some frame), 1 - normal, 2 - twice as fast, 0.5 - twice slower than normal.
|
||||
float m_fAnimSpeedScale;
|
||||
|
||||
|
||||
char m_sShaderTemplateName[2][64];
|
||||
unsigned m_nShaderTemplateFlags;
|
||||
|
||||
|
||||
// m_matTranRotMatrix is implemented in a dangerous way - this should be changed
|
||||
|
||||
Matrix44 m_matTranRotMatrix; //!< this matrix is updated during rendering and only valid right after rendering
|
||||
|
||||
// the animation currently being played
|
||||
char m_sCurAnimation[32];
|
||||
|
||||
Vec3d m_v3pvSpitFirePos, m_v3pvSpitFirePosTranslated;
|
||||
|
||||
//bool m_bFirstUpdate; // prevent big fFrameTime at editor startup
|
||||
|
||||
CLeafBuffer * GetLeafBuffer();
|
||||
// Matrix m_matAttachedObjectMatrix;
|
||||
|
||||
// checks for possible memory corruptions in this object and its children
|
||||
void SelfValidate ()const;
|
||||
public:
|
||||
virtual Vec3d GetTPVWeaponHelper(const char * szHelperName, ObjectBindingHandle hInfo);
|
||||
virtual bool GetTPVWeaponHelperMatrix(const char * szHelperName, ObjectBindingHandle nHandle, Matrix44& matOut);
|
||||
//! Returns position of specified helper ( exported into cgf file )
|
||||
//! Actually returns the given bone's position
|
||||
//! Default implementation: 000
|
||||
virtual Vec3d GetHelperPos(const char * szHelperName);
|
||||
//! Returns the matrix of the specified helper ( exported into cgf file )
|
||||
//! Actually returns the given bone's matrix
|
||||
virtual const Matrix44 * GetHelperMatrixByName(const char * szHelperName);
|
||||
|
||||
virtual void SetTwiningMode(AnimTwinMode eTwinMode);
|
||||
virtual int GetDamageTableValue(int nId);
|
||||
|
||||
// int nDynLightMask= RendParams.nDLightMask;
|
||||
// int nTemplID = RendParams.nShaderTemplate;
|
||||
void DrawBoundObjects(const SRendParams & rRendParams, Matrix44 &inmatTranRotMatrix, int nLOD);
|
||||
|
||||
virtual bool IsCharacterActive();
|
||||
|
||||
// notifies the renderer that the character will soon be rendered
|
||||
void PreloadResources ( float fDistance, float fTime, int nFlags);
|
||||
|
||||
// Returns true if this character was created from the file the path refers to.
|
||||
// If this is true, then there's no need to reload the character if you need to change its model to this one.
|
||||
virtual bool IsModelFileEqual (const char* szFileName);
|
||||
|
||||
void ValidateBoundObjects();
|
||||
|
||||
//! Sets up particle spawning. After this funtion is called, every subsequenc frame,
|
||||
//! During the character deformation, particles will be spawned in the given characteristics.
|
||||
//! The returned handle is to be used to stop particle spawning
|
||||
//! -1 means invalid handle value (couldn't add the particle spawn task, or not implemented)
|
||||
virtual int AddParticleEmitter(ParticleParams& rParticleInfo, const CryParticleSpawnInfo& rSpawnInfo);
|
||||
|
||||
//! Stops particle spawning started with StartParticleSpawn that returned the parameter
|
||||
//! Returns true if the particle spawn was stopped, or false if the handle is invalid
|
||||
//! -1 means remove all particle emitters
|
||||
virtual bool RemoveParticleEmitter (int nHandle);
|
||||
|
||||
//! Sets the character scale relative to the model
|
||||
virtual void SetScale (const Vec3d& vScale);
|
||||
|
||||
void GetMemoryUsage(ICrySizer* pSizer)const;
|
||||
// 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
|
||||
ICryCharSubmesh* NewSubmesh (ICryCharModel* pModel, bool bVisible = false);
|
||||
|
||||
// adds submesh to the specified slot; replaces submesh if there's some there
|
||||
ICryCharSubmesh* NewSubmesh (unsigned nSlot, ICryCharModel* pModel, bool bVisible = false);
|
||||
|
||||
// removes submesh from the character
|
||||
void RemoveSubmesh (ICryCharSubmesh* pSubmesh);
|
||||
void RemoveSubmesh (unsigned nSlot);
|
||||
|
||||
// enumeration of submeshes
|
||||
size_t NumSubmeshes();
|
||||
|
||||
ICryCharSubmesh* GetSubmesh(unsigned i);
|
||||
|
||||
ICryCharFxTrail* NewFxTrail (unsigned nSlot, const struct CryCharFxTrailParams&);
|
||||
void RemoveFxTrail(unsigned nSlot);
|
||||
|
||||
protected:
|
||||
CryCharManager* m_pManager;
|
||||
|
||||
bool m_bEnableStartAnimation;
|
||||
|
||||
#ifdef _DEBUG
|
||||
// the last frame angles (orientation for this instance)
|
||||
Vec3d m_vLastAngles, m_vLastPos;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
58
CryAnimation/CryCharInstanceRenderParams.cpp
Normal file
58
CryAnimation/CryCharInstanceRenderParams.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "Stdafx.h"
|
||||
#include "CryAnimationBase.h"
|
||||
#include "CryCharInstanceRenderParams.h"
|
||||
|
||||
// creates a new CCObject with the most common parameters
|
||||
CCObject* CryCharInstanceRenderParams::NewCryCharCCObject(const struct SRendParams & RendParams, const Matrix44& mtxObjMatrix, IDeformableRenderMesh* pCharInstance)
|
||||
{
|
||||
IRenderer* pRenderer = g_GetIRenderer();
|
||||
CCObject * pObj = pRenderer->EF_GetObject(true);
|
||||
|
||||
pObj->m_ObjFlags |= FOB_TRANS_MASK;
|
||||
|
||||
// set scissor
|
||||
//pObj->m_nScissorX1 = RendParams.nScissorX1;
|
||||
//pObj->m_nScissorY1 = RendParams.nScissorY1;
|
||||
//pObj->m_nScissorX2 = RendParams.nScissorX2;
|
||||
//pObj->m_nScissorY2 = RendParams.nScissorY2;
|
||||
|
||||
//checl if it should be drawn close to the player
|
||||
if ((RendParams.dwFObjFlags & FOB_NEAREST) || (m_nFlags & CS_FLAG_DRAW_NEAR))
|
||||
{
|
||||
pObj->m_ObjFlags|=FOB_NEAREST;
|
||||
}
|
||||
else
|
||||
pObj->m_ObjFlags&=~FOB_NEAREST;
|
||||
|
||||
pObj->m_Color = m_Color;
|
||||
pObj->m_Color.a *= RendParams.fAlpha;
|
||||
|
||||
pObj->m_AmbColor = RendParams.vAmbientColor;
|
||||
|
||||
if (pRenderer->EF_GetHeatVision())
|
||||
pObj->m_ObjFlags |= FOB_HOTAMBIENT;
|
||||
|
||||
pObj->m_ObjFlags |= RendParams.dwFObjFlags;
|
||||
|
||||
int nTemplID = RendParams.nShaderTemplate;
|
||||
|
||||
pObj->m_pShadowCasters = RendParams.pShadowMapCasters;
|
||||
|
||||
if(RendParams.pShadowMapCasters && RendParams.pShadowMapCasters->size())
|
||||
pObj->m_ObjFlags |= FOB_INSHADOW;
|
||||
else
|
||||
pObj->m_ObjFlags &= ~FOB_INSHADOW;
|
||||
|
||||
pObj->m_nTemplId = nTemplID;
|
||||
|
||||
pObj->m_DynLMMask = RendParams.nDLightMask;
|
||||
pObj->m_SortId = RendParams.fCustomSortOffset;
|
||||
pObj->m_fDistanceToCam = RendParams.fSQDistance;
|
||||
|
||||
|
||||
pObj->m_Matrix = mtxObjMatrix;
|
||||
// pObj->m_Matrix = mtxObjMatrix;
|
||||
pObj->m_pCharInstance = pCharInstance;
|
||||
|
||||
return pObj;
|
||||
}
|
||||
15
CryAnimation/CryCharInstanceRenderParams.h
Normal file
15
CryAnimation/CryCharInstanceRenderParams.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef _CRY_ANIMATION_CRY_CHAR_INSTANCE_RENDER_PARAMS_HDR_
|
||||
#define _CRY_ANIMATION_CRY_CHAR_INSTANCE_RENDER_PARAMS_HDR_
|
||||
|
||||
struct CryCharInstanceRenderParams
|
||||
{
|
||||
CFColor m_Color;
|
||||
int m_nFlags;
|
||||
CDLight m_ambientLight;
|
||||
|
||||
// creates a new CCObject with the most common parameters
|
||||
CCObject* NewCryCharCCObject(const struct SRendParams & RendParams, const Matrix44& mtxObjMatrix, IDeformableRenderMesh* pCharInstance);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
606
CryAnimation/CryCharManager.cpp
Normal file
606
CryAnimation/CryCharManager.cpp
Normal file
@@ -0,0 +1,606 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CryEngine Source code
|
||||
//
|
||||
// File:CryCharManager.cpp
|
||||
// Implementation of CryCharManager class
|
||||
//
|
||||
// History:
|
||||
// August 16, 2002: Created by Sergiy Migdalskiy <sergiy@crytek.de>
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <IGame.h> // to examine whether we're in dedicated server mode
|
||||
#include "CryCharDecal.h"
|
||||
#include "CryCharInstance.h"
|
||||
#include "CryCharManager.h"
|
||||
#include "CryModelState.h"
|
||||
#include "ControllerManager.h"
|
||||
#include "cvars.h"
|
||||
#include "CryCharBody.h"
|
||||
#include "StringUtils.h"
|
||||
#include "CryModEffAnimation.h"
|
||||
#include "CryAnimationScriptCommands.h"
|
||||
#include "IncContHeap.h"
|
||||
|
||||
#include "AnimObjectManager.h"
|
||||
// for freeing resources
|
||||
|
||||
#include "CryCharDecalManager.h"
|
||||
#include "SSEUtils.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#undef THIS_FILE
|
||||
static char THIS_FILE[] = __FILE__;
|
||||
#endif
|
||||
|
||||
using namespace CryStringUtils;
|
||||
|
||||
|
||||
// init memory pool usage
|
||||
//#if defined(WIN32)
|
||||
_ACCESS_POOL;
|
||||
//#endif
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
float g_YLine=0.0f;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// The main symbol exported from CryAnimation: creator of the main (root) interface/object
|
||||
ICryCharManager * CreateCharManager(ISystem * pSystem, const char * szInterfaceVersion)
|
||||
{
|
||||
// check the interface timestamp
|
||||
#if !defined(LINUX)
|
||||
if(strcmp(szInterfaceVersion,gAnimInterfaceVersion))
|
||||
pSystem->GetILog()->LogError("CreateCharManager(): CryAnimation interface version error");
|
||||
#endif
|
||||
CryCharManager * pCryCharManager = new CryCharManager(pSystem);
|
||||
|
||||
g_CpuFlags = pSystem->GetCPUFlags();
|
||||
g_SecondsPerCycle = pSystem->GetSecondsPerCycle();
|
||||
|
||||
return static_cast<ICryCharManager*>(pCryCharManager);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// CryCharManager
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
CryCharManager::CryCharManager (ISystem * pSystem)
|
||||
#ifdef DEBUG_STD_CONTAINERS
|
||||
:m_arrBodyCache ("CryCharManager.BodyCache")
|
||||
#endif
|
||||
{
|
||||
cpu::detect();
|
||||
|
||||
g_Temp.init();
|
||||
g_InitInterfaces(pSystem);
|
||||
|
||||
#if ENABLE_FRAME_PROFILER
|
||||
if (!cpu::hasRDTSC())
|
||||
pSystem->GetILog()->LogToFile ("\002Error: (Critical) this is development version of animation system, with the profiling enabled. Built-in profiler uses RDTSC function for precise time profiling. This machine doesn't seem to have RDTSC instruction implemented. Please recompile with ENABLE_FRAME_PROFILER 0. Don't use ca_Profile 1");
|
||||
#endif
|
||||
|
||||
//g_ProfilerTimer.init();
|
||||
CryModelState::initClass();
|
||||
|
||||
m_pControllerManager = new CControllerManager();
|
||||
m_pAnimObjectManager = new CAnimObjectManager;
|
||||
#if !defined(LINUX)
|
||||
cpu::logCaps();
|
||||
#endif
|
||||
|
||||
#define REGISTER_COMMAND_SHORTCUT(name) g_GetConsole()->AddCommand("ca" #name, "Animation:" #name "()",VF_CHEAT)
|
||||
REGISTER_COMMAND_SHORTCUT(DumpAnims);
|
||||
REGISTER_COMMAND_SHORTCUT(DumpModels);
|
||||
REGISTER_COMMAND_SHORTCUT(TrashAnims);
|
||||
REGISTER_COMMAND_SHORTCUT(ClearDecals);
|
||||
REGISTER_COMMAND_SHORTCUT(DumpDecals);
|
||||
REGISTER_COMMAND_SHORTCUT(DumpStates);
|
||||
#undef REGISTER_COMMAND_SHORTCUT
|
||||
}
|
||||
|
||||
// returns the controller manager used by this character manager
|
||||
CControllerManager * CryCharManager::GetControllerManager()
|
||||
{
|
||||
return m_pControllerManager;
|
||||
}
|
||||
|
||||
|
||||
CryCharManager::~CryCharManager()
|
||||
{
|
||||
m_setLockedBodies.clear();
|
||||
if (!m_arrBodyCache.empty())
|
||||
{
|
||||
g_GetLog()->LogToFile("*ERROR* CryCharManager: %u body instances still not deleted. Forcing deletion (though some instances may still refer to them)", m_arrBodyCache.size());
|
||||
CleanupBodyCache();
|
||||
}
|
||||
|
||||
//CryModel::freeShadowVolumeVerts();
|
||||
CryModelState::deinitClass();
|
||||
|
||||
delete m_pAnimObjectManager;
|
||||
delete m_pControllerManager;
|
||||
|
||||
g_GetLog()->LogToFile ("\004CryAnimation profile statistics:");
|
||||
g_GetLog()->LogToFile ("\004load.model (Loading character models) : %4.2f sec (this is the TOTAL)", g_dTimeGeomLoad);
|
||||
g_GetLog()->LogToFile ("\004load.model.grarr (Generating Render Arrays) : %4.2f sec", g_dTimeGenRenderArrays);
|
||||
g_GetLog()->LogToFile ("\004load.model.shaders (Shader loading) : %4.2f sec", g_dTimeShaderLoad);
|
||||
g_GetLog()->LogToFile ("\004load.model.geom_prepare(Geometry (cgf) Preparing) : %4.2f sec", g_dTimeGeomChunkLoad);
|
||||
g_GetLog()->LogToFile ("\004load.model.geom_fileio : %4.2f sec", g_dTimeGeomChunkLoadFileIO);
|
||||
g_GetLog()->LogToFile ("\004load.model.anim (Animation (caf) Loading/Binding) : %4.2f sec", g_dTimeAnimLoadBind);
|
||||
g_GetLog()->LogToFile ("\004load.model.anim.nocal (scan for animation files) : %4.2f sec", g_dTimeAnimLoadBindNoCal);
|
||||
g_GetLog()->LogToFile ("\004load.model.anim.withcal (parse CAL file) : %4.2f sec", g_dTimeAnimLoadBindWithCal);
|
||||
g_GetLog()->LogToFile ("\004load.model.anim.prepare (preallocating) : %4.2f sec", g_dTimeAnimLoadBindPreallocate);
|
||||
g_GetLog()->LogToFile ("\004load.model.anim.fileio (Animation (caf) File i/o) : %4.2f sec",g_dTimeAnimLoadFile);
|
||||
g_GetLog()->LogToFile ("\004load.model.anim.bind (Animation (caf) Binding) : %4.2f sec", g_dTimeAnimBindControllers);
|
||||
g_GetLog()->LogToFile ("\004load.model.anim.postinit (Post-initialization) : %4.2f sec", g_dTimeGeomPostInit);
|
||||
g_GetLog()->LogToFile ("\004test.1: %4.2f sec", g_dTimeTest1);
|
||||
g_GetLog()->LogToFile ("\004test.2: %4.2f sec", g_dTimeTest2);
|
||||
g_GetLog()->LogToFile ("\004test.3: %4.2f sec", g_dTimeTest3);
|
||||
g_GetLog()->LogToFile ("\004test.4: %4.2f sec", g_dTimeTest4);
|
||||
g_dTimeGeomLoad = 0;
|
||||
g_dTimeAnimLoadBind = 0;
|
||||
g_dTimeAnimLoadBindNoCal = 0;
|
||||
g_dTimeAnimLoadBindWithCal = 0;
|
||||
g_dTimeAnimLoadBindPreallocate = 0;
|
||||
g_dTimeShaderLoad = 0;
|
||||
g_dTimeGeomChunkLoad = 0;
|
||||
g_dTimeGeomChunkLoadFileIO = 0;
|
||||
g_dTimeGenRenderArrays = 0;
|
||||
g_dTimeAnimBindControllers = 0;
|
||||
g_dTimeAnimLoadFile = 0;
|
||||
g_dTimeGeomPostInit = 0;
|
||||
g_dTimeTest1 = g_dTimeTest2 = g_dTimeTest3 = g_dTimeTest4 = 0;
|
||||
|
||||
if (g_nAsyncAnimCounter)
|
||||
g_GetLog()->LogToFile ("\003%d async anim loads; %d (ave %.2f) frame-skips", g_nAsyncAnimCounter, g_nAsyncAnimFrameDelays, double(g_nAsyncAnimFrameDelays)/g_nAsyncAnimCounter);
|
||||
g_nAsyncAnimFrameDelays = 0;
|
||||
g_nAsyncAnimCounter = 0;
|
||||
|
||||
g_GetLog()->LogToFile ("Memory usage dump:");
|
||||
g_GetLog()->LogToFile ("Scratchpad (temporary storage): %d kbytes", g_Temp.size()/1024);
|
||||
|
||||
CryCharDecalManager::LogStatistics();
|
||||
|
||||
g_DeleteInterfaces();
|
||||
CCryModEffAnimation::deinitClass();
|
||||
|
||||
g_Temp.done();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Loads a cgf and the corresponding caf file and creates an animated object,
|
||||
// or returns an existing object.
|
||||
ICryCharInstance * CryCharManager::MakeCharacter (const char * szCharacterFileName, unsigned nFlags)
|
||||
{
|
||||
|
||||
g_pIRenderer = g_pISystem->GetIRenderer();
|
||||
g_pIPhysicalWorld = g_pISystem->GetIPhysicalWorld();
|
||||
g_pI3DEngine = g_pISystem->GetI3DEngine();
|
||||
|
||||
if (!szCharacterFileName)
|
||||
return (NULL); // to prevent a crash in the frequent case the designers will mess
|
||||
// around with the entity parameters in the editor
|
||||
|
||||
string strPath = szCharacterFileName;
|
||||
UnifyFilePath(strPath);
|
||||
|
||||
if (strstr(strPath.c_str(),".cga") || (nFlags & nMakeAnimObject))
|
||||
{
|
||||
// Loading cga file.
|
||||
return m_pAnimObjectManager->MakeAnimObject( szCharacterFileName );
|
||||
}
|
||||
|
||||
// try to find already loaded model, or load a new one
|
||||
CryCharBody* pCryCharBody = FetchBody (strPath);
|
||||
|
||||
if(!pCryCharBody)
|
||||
{
|
||||
// the model has not been loaded
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CryCharInstance * pCryCharInstance = new CryCharInstance (pCryCharBody);
|
||||
|
||||
DecideModelLockStatus(pCryCharBody, nFlags);
|
||||
|
||||
return pCryCharInstance;
|
||||
}
|
||||
|
||||
// loads the character model (which is ref-counted, so you must assign it to an autopointer)
|
||||
ICryCharModel* CryCharManager::LoadModel(const char* szFileName, unsigned nFlags)
|
||||
{
|
||||
if (!szFileName)
|
||||
return NULL;
|
||||
|
||||
string strPath = szFileName;
|
||||
UnifyFilePath(strPath);
|
||||
|
||||
if (strstr(strPath.c_str(),".cga") || (nFlags & nMakeAnimObject))
|
||||
{
|
||||
// Loading cga file: not supported at this time
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// try to find already loaded model, or load a new one
|
||||
CryCharBody* pCryCharBody = FetchBody (strPath);
|
||||
|
||||
return pCryCharBody;
|
||||
}
|
||||
|
||||
// locks or unlocks the given body if needed, depending on the hint and console variables
|
||||
void CryCharManager::DecideModelLockStatus(CryCharBody* pCryCharBody, unsigned nHints)
|
||||
{
|
||||
// there's already a character, so we can safely remove the body from the lock pool
|
||||
// basic rules:
|
||||
// transient hint takes precedence over everything else
|
||||
// any hint takes precedence over the console settings
|
||||
// if there are no hints, console variable ca_KeepModels is used to determine
|
||||
// if the model should be kept in memory all the time (ca_KeepModels 1) or not (0)
|
||||
// Note: changing ca_KeepModels won't immediately lock or unlock all models. It will only affect
|
||||
// models which are actively used to create new characters or destroy old ones.
|
||||
if (nHints & nHintModelTransient)
|
||||
m_setLockedBodies.erase (pCryCharBody);
|
||||
else
|
||||
if (nHints & nHintModelPersistent)
|
||||
m_setLockedBodies.insert (pCryCharBody);
|
||||
else
|
||||
if (g_GetCVars()->ca_KeepModels())
|
||||
m_setLockedBodies.insert (pCryCharBody);
|
||||
else
|
||||
m_setLockedBodies.erase (pCryCharBody);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Reduces reference counter for object and deletes object if counter is 0
|
||||
void CryCharManager::RemoveCharacter (ICryCharInstance * pCryCharInstance, unsigned nFlags)
|
||||
{
|
||||
if (pCryCharInstance && !m_pAnimObjectManager->RemoveCharacter( pCryCharInstance ))
|
||||
{
|
||||
// check if the character exists (maybe it's a dangling pointer)
|
||||
bool bFound = false;
|
||||
for (CryCharBodyCache::iterator it = m_arrBodyCache.begin(); !bFound && it != m_arrBodyCache.end(); ++it)
|
||||
{
|
||||
bFound = (*it)->DoesInstanceExist (static_cast<CryCharInstance*>(pCryCharInstance));
|
||||
}
|
||||
|
||||
if (!bFound)
|
||||
{
|
||||
g_GetLog()->LogError ("\001attempt to delete invalid character pointer 0x%08X. Perhaps it was auto-deleted during Forced Resource Cleanup upon level reloading, see the log for details.", pCryCharInstance);
|
||||
return;
|
||||
}
|
||||
|
||||
CryCharInstance* pCharacter = static_cast<CryCharInstance*> (pCryCharInstance);
|
||||
DecideModelLockStatus(pCharacter->GetBody(), nFlags);
|
||||
// this will delete the character and possibly the corresponding Body instance
|
||||
pCharacter->Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Deletes itself
|
||||
void CryCharManager::Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
||||
// adds the body to the cache from which it can be reused if another instance of that model is to be created
|
||||
void CryCharManager::RegisterBody (CryCharBody* pBody)
|
||||
{
|
||||
m_arrBodyCache.insert (std::lower_bound(m_arrBodyCache.begin(), m_arrBodyCache.end(), pBody->GetFilePath(), OrderByFileName()), pBody);
|
||||
}
|
||||
|
||||
|
||||
// Deletes the given body from the model cache. This is done when there are no instances using this body info.
|
||||
void CryCharManager::UnregisterBody (CryCharBody* pBody)
|
||||
{
|
||||
ValidateBodyCache();
|
||||
CryCharBodyCache::iterator itCache = std::lower_bound (m_arrBodyCache.begin(), m_arrBodyCache.end(), pBody, OrderByFileName());
|
||||
if (itCache != m_arrBodyCache.end())
|
||||
{
|
||||
if (*itCache == pBody)
|
||||
m_arrBodyCache.erase (itCache);
|
||||
else
|
||||
{
|
||||
assert (false); // there must be no duplicate name pointers here
|
||||
while (++itCache != m_arrBodyCache.end())
|
||||
if (*itCache == pBody)
|
||||
{
|
||||
m_arrBodyCache.erase(itCache);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
assert (false); // this pointer must always be in the cache
|
||||
ValidateBodyCache();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Finds a cached or creates a new CryCharBody instance and returns it
|
||||
// returns NULL if the construction failed
|
||||
CryCharBody* CryCharManager::FetchBody (const string& strFileName)
|
||||
{
|
||||
ValidateBodyCache();
|
||||
|
||||
{
|
||||
CryCharBodyCache::iterator it = std::lower_bound (m_arrBodyCache.begin(), m_arrBodyCache.end(), strFileName, OrderByFileName());
|
||||
|
||||
if (it != m_arrBodyCache.end())
|
||||
{
|
||||
const string& strBodyFilePath = (*it)->GetFilePath();
|
||||
if (strBodyFilePath == strFileName)
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
|
||||
CryCharBody* pBody = new CryCharBody (this, strFileName);
|
||||
if (pBody->GetModel())
|
||||
return pBody;
|
||||
else
|
||||
{
|
||||
delete pBody;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// returns statistics about this instance of character animation manager
|
||||
// don't call this too frequently
|
||||
void CryCharManager::GetStatistics(Statistics& rStats)
|
||||
{
|
||||
memset (&rStats, 0, sizeof(rStats));
|
||||
rStats.numCharModels = (unsigned)m_arrBodyCache.size();
|
||||
for (CryCharBodyCache::const_iterator it = m_arrBodyCache.begin(); it != m_arrBodyCache.end(); ++it)
|
||||
rStats.numCharacters += (*it)->NumInstances();
|
||||
|
||||
rStats.numAnimObjectModels = rStats.numAnimObjects = m_pAnimObjectManager->NumObjects();
|
||||
}
|
||||
|
||||
// Validates the cache
|
||||
void CryCharManager::ValidateBodyCache()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
CryCharBodyCache::iterator itPrev = m_arrBodyCache.end();
|
||||
for (CryCharBodyCache::iterator it = m_arrBodyCache.begin(); it != m_arrBodyCache.end(); ++it)
|
||||
{
|
||||
if (itPrev != m_arrBodyCache.end())
|
||||
assert ((*itPrev)->GetFilePath() < (*it)->GetFilePath());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Deletes all the cached bodies and their associated character instances
|
||||
void CryCharManager::CleanupBodyCache()
|
||||
{
|
||||
m_setLockedBodies.clear();
|
||||
while (!m_arrBodyCache.empty())
|
||||
{
|
||||
CryCharBody* pBody = m_arrBodyCache.back();
|
||||
// cleaning up the instances referring to this body actually releases it and
|
||||
// deletes from the cache
|
||||
pBody->CleanupInstances();
|
||||
|
||||
if (m_arrBodyCache.empty())
|
||||
break;
|
||||
|
||||
// if the body still stays in the cache, it means someone (who?!) still refers to it
|
||||
if (m_arrBodyCache.back() == pBody)
|
||||
{
|
||||
assert(0); // actually ,after deleting all instances referring to the body, the body must be auto-deleted and deregistered.
|
||||
// if this doesn't happen, something is very wrong
|
||||
//delete pBody; // still, we'll delete the body
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CryCharManager::OrderByFileName::operator () (const CryCharBody* pLeft, const CryCharBody* pRight)
|
||||
{
|
||||
return pLeft->GetFilePath() < pRight->GetFilePath();
|
||||
}
|
||||
bool CryCharManager::OrderByFileName::operator () (const string& strLeft, const CryCharBody* pRight)
|
||||
{
|
||||
return strLeft < pRight->GetFilePath();
|
||||
}
|
||||
bool CryCharManager::OrderByFileName::operator () (const CryCharBody* pLeft, const string& strRight)
|
||||
{
|
||||
return pLeft->GetFilePath() < strRight;
|
||||
}
|
||||
|
||||
// puts the size of the whole subsystem into this sizer object, classified,
|
||||
// according to the flags set in the sizer
|
||||
void CryCharManager::GetMemoryUsage(class ICrySizer* pSizer)const
|
||||
{
|
||||
#if ENABLE_GET_MEMORY_USAGE
|
||||
if (!pSizer->Add(*this))
|
||||
return;
|
||||
if (!m_arrBodyCache.empty())
|
||||
{
|
||||
for (CryCharBodyCache::const_iterator it = m_arrBodyCache.begin(); it != m_arrBodyCache.end(); ++it)
|
||||
(*it)->GetSize (pSizer);
|
||||
pSizer->Add (&m_arrBodyCache[0], m_arrBodyCache.size());
|
||||
}
|
||||
m_pControllerManager->GetSize (pSizer);
|
||||
m_pAnimObjectManager->GetMemoryUsage (pSizer);
|
||||
/*
|
||||
{
|
||||
SIZER_COMPONENT_NAME(pSizer, "Test1M");
|
||||
{
|
||||
SIZER_COMPONENT_NAME (pSizer, "Test1M");
|
||||
pSizer->AddObject((void*)1, 0x10000-1);
|
||||
}
|
||||
{
|
||||
SIZER_COMPONENT_NAME (pSizer, "Test1M");
|
||||
pSizer->AddObject((void*)2, 0x10000-1);
|
||||
{
|
||||
SIZER_COMPONENT_NAME (pSizer, "Test1M");
|
||||
pSizer->AddObject((void*)3, 0x10000-1);
|
||||
}
|
||||
}
|
||||
{
|
||||
SIZER_COMPONENT_NAME(pSizer, "Test2M");
|
||||
{
|
||||
SIZER_COMPONENT_NAME (pSizer, "Test1M");
|
||||
pSizer->AddObject((void*)4, 0x10000-1);
|
||||
}
|
||||
{
|
||||
SIZER_COMPONENT_NAME (pSizer, "Test1M");
|
||||
pSizer->AddObject((void*)5, 0x10000-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
}
|
||||
|
||||
//! Executes a script command
|
||||
//! Returns true if the command was executed, or false if not
|
||||
//! All the possible values for nCommand are in the CryAnimationScriptCommands.h
|
||||
//! file in the CryAnimationScriptCommandEnum enumeration. All the parameter/result
|
||||
//! structures are also there.
|
||||
bool CryCharManager::ExecScriptCommand (int nCommand, void* pParams, void* pResult)
|
||||
{
|
||||
switch (nCommand)
|
||||
{
|
||||
case CASCMD_TEST_PARTICLES:
|
||||
case CASCMD_STOP_PARTICLES:
|
||||
{
|
||||
for (CryCharBodyCache::iterator it = m_arrBodyCache.begin(); it != m_arrBodyCache.end(); ++it)
|
||||
(*it)->SpawnTestParticles (nCommand == CASCMD_TEST_PARTICLES);
|
||||
}
|
||||
break;
|
||||
|
||||
case CASCMD_DUMP_ANIMATIONS:
|
||||
m_pControllerManager->DumpAnims();
|
||||
break;
|
||||
|
||||
case CASCMD_TRASH_ANIMATIONS:
|
||||
UnloadOldAnimations(pParams?*(int*)pParams:100);
|
||||
break;
|
||||
|
||||
case CASCMD_UNLOAD_ANIMATION:
|
||||
UnloadAnimation ((char*)pParams);
|
||||
break;
|
||||
|
||||
case CASCMD_DUMP_MODELS:
|
||||
case CASCMD_EXPORT_MODELS_ASCII:
|
||||
case CASCMD_CLEAR_DECALS:
|
||||
case CASCMD_DUMP_DECALS:
|
||||
case CASCMD_START_MANY_ANIMS:
|
||||
case CASCMD_DEBUG_DRAW:
|
||||
case CASCMD_DUMP_STATES:
|
||||
{
|
||||
for (CryCharBodyCache::iterator it = m_arrBodyCache.begin(); it != m_arrBodyCache.end(); ++it)
|
||||
(*it)->ExecScriptCommand(nCommand, pParams, pResult);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false; // unknown command
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CryCharManager::ClearDecals()
|
||||
{
|
||||
ExecScriptCommand(CASCMD_CLEAR_DECALS);
|
||||
}
|
||||
|
||||
//! Cleans up all resources - currently deletes all bodies and characters (even if there are references on them)
|
||||
void CryCharManager::ClearResources(void)
|
||||
{
|
||||
CleanupBodyCache();
|
||||
}
|
||||
|
||||
|
||||
// should be called every frame
|
||||
void CryCharManager::Update()
|
||||
{
|
||||
//update interfaces every frame
|
||||
g_YLine=16.0f;
|
||||
g_pIRenderer = g_pISystem->GetIRenderer();
|
||||
g_pIPhysicalWorld = g_pISystem->GetIPhysicalWorld();
|
||||
g_pI3DEngine = g_pISystem->GetI3DEngine();
|
||||
|
||||
g_nFrameID = g_GetIRenderer()->GetFrameID();
|
||||
g_bProfilerOn = g_GetISystem()->GetIProfileSystem()->IsProfiling();
|
||||
|
||||
IGame* pGame = g_GetISystem()->GetIGame();
|
||||
g_bUpdateBonesAlways = pGame? (pGame->GetModuleState(EGameServer) && pGame->GetModuleState(EGameMultiplayer)) : false;
|
||||
|
||||
m_pControllerManager->Update();
|
||||
CryCharDecal::setGlobalTime(g_GetTimer()->GetCurrTime());
|
||||
|
||||
if (g_GetCVars()->ca_DrawBones() > 1)
|
||||
ExecScriptCommand(CASCMD_DEBUG_DRAW);
|
||||
}
|
||||
|
||||
//! The specified animation will be unloaded from memory; it will be loaded back upon the first invokation (via StartAnimation())
|
||||
void CryCharManager::UnloadAnimation (const char* szFileName)
|
||||
{
|
||||
int nAnimId = m_pControllerManager->FindAnimationByFile(szFileName);
|
||||
if (nAnimId < 0)
|
||||
g_GetLog()->LogWarning ("\004CryCharManager::UnloadAnimation(%s): animation not loaded", szFileName);
|
||||
else
|
||||
m_pControllerManager->UnloadAnimation(nAnimId);
|
||||
}
|
||||
|
||||
//! Starts loading the specified animation. fWhenRequired is the timeout, in seconds, from the current moment,
|
||||
//! when the animation data will actually be needed
|
||||
void CryCharManager::StartLoadAnimation (const char* szFileName, float fWhenRequired)
|
||||
{
|
||||
m_pControllerManager->StartLoadAnimation(szFileName, g_fDefaultAnimationScale);
|
||||
}
|
||||
|
||||
void CryCharManager::UnloadOldAnimations(int numFrames)
|
||||
{
|
||||
unsigned numAnims = m_pControllerManager->NumAnimations(), numUnloaded = 0, numFound = 0;
|
||||
for (unsigned i = 0; i < numAnims; ++i)
|
||||
{
|
||||
if (m_pControllerManager->GetAnimation(i).nLastAccessFrameId + numFrames < g_nFrameID)
|
||||
{
|
||||
++numFound;
|
||||
if (m_pControllerManager->UnloadAnimation(i))
|
||||
++numUnloaded;
|
||||
}
|
||||
}
|
||||
if (numFound)
|
||||
g_GetLog()->LogToConsole("\001%d Animations were found, %d were unloaded", numFound, numUnloaded);
|
||||
else
|
||||
g_GetLog()->LogToConsole("\001No Animation were found/unloaded");
|
||||
}
|
||||
|
||||
//! Locks all models in memory
|
||||
void CryCharManager::LockResources()
|
||||
{
|
||||
m_arrTempLock.clear();
|
||||
m_arrTempLock.resize (m_arrBodyCache.size());
|
||||
for (size_t i = 0; i < m_arrBodyCache.size(); ++i)
|
||||
m_arrTempLock[i] = m_arrBodyCache[i];
|
||||
|
||||
m_pAnimObjectManager->LockResources();
|
||||
}
|
||||
|
||||
//! Unlocks all models in memory
|
||||
void CryCharManager::UnlockResources()
|
||||
{
|
||||
m_arrTempLock.clear();
|
||||
m_pAnimObjectManager->UnlockResources();
|
||||
}
|
||||
135
CryAnimation/CryCharManager.h
Normal file
135
CryAnimation/CryCharManager.h
Normal file
@@ -0,0 +1,135 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CryEngine Source code
|
||||
//
|
||||
// File:CryCharManager.h
|
||||
// Implementation of CryCharManager class
|
||||
//
|
||||
// History:
|
||||
// August 16, 2002: Created by Sergiy Migdalskiy <sergiy@crytek.de>
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _CRY_CHAR_MANAGER_HEADER_
|
||||
#define _CRY_CHAR_MANAGER_HEADER_
|
||||
|
||||
#include <ICryAnimation.h>
|
||||
#include "AnimObjectManager.h"
|
||||
|
||||
class CControllerManager;
|
||||
class CryCharInstance;
|
||||
class CAnimObjectManager;
|
||||
|
||||
#include "CryCharBody.h"
|
||||
|
||||
extern float g_YLine;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// This class contains a list of character bodies and list of character instances.
|
||||
// On attempt to create same character second time only new instance will be created.
|
||||
// Only this class can create actual characters.
|
||||
class CryCharManager : public ICryCharManager
|
||||
{
|
||||
public:
|
||||
CryCharManager (ISystem * pSystem);
|
||||
~CryCharManager ();
|
||||
|
||||
// Loads a cgf and the corresponding caf file and creates an animated object,
|
||||
// or returns an existing object.
|
||||
virtual ICryCharInstance * MakeCharacter(const char * szFilename, unsigned nFlags);
|
||||
|
||||
// loads the character model (which is ref-counted, so you must assign it to an autopointer)
|
||||
virtual ICryCharModel* LoadModel(const char* szFileName, unsigned nFlags = 0);
|
||||
|
||||
// Reduces reference counter for object and deletes object if counter is 0
|
||||
virtual void RemoveCharacter (ICryCharInstance * pCryCharInstance, unsigned nFlags);
|
||||
|
||||
// Deletes itself
|
||||
virtual void Release();
|
||||
|
||||
// returns the controller manager used by this character manager
|
||||
CControllerManager * GetControllerManager();
|
||||
|
||||
void RegisterBody (CryCharBody*);
|
||||
void UnregisterBody (CryCharBody*);
|
||||
|
||||
// puts the size of the whole subsystem into this sizer object, classified,
|
||||
// according to the flags set in the sizer
|
||||
void GetMemoryUsage(class ICrySizer* pSizer)const;
|
||||
|
||||
//! Executes a script command
|
||||
//! Returns true if the command was executed, or false if not
|
||||
//! All the possible values for nCommand are in the CryAnimationScriptCommands.h
|
||||
//! file in the CryAnimationScriptCommandEnum enumeration. All the parameter/result
|
||||
//! structures are also there.
|
||||
bool ExecScriptCommand (int nCommand, void* pParams = NULL, void* pResult = NULL);
|
||||
|
||||
// should be called every frame
|
||||
void Update();
|
||||
|
||||
//! Cleans up all resources - currently deletes all bodies and characters (even if there are references on them)
|
||||
virtual void ClearResources();
|
||||
|
||||
//! The specified animation will be unloaded from memory; it will be loaded back upon the first invokation (via StartAnimation())
|
||||
void UnloadAnimation (const char* szFileName);
|
||||
|
||||
//! Starts loading the specified animation. fWhenRequired is the timeout, in seconds, from the current moment,
|
||||
//! when the animation data will actually be needed
|
||||
void StartLoadAnimation (const char* szFileName, float fWhenRequired);
|
||||
|
||||
//! Unloads animations older than the given number of frames
|
||||
void UnloadOldAnimations (int numFrames);
|
||||
|
||||
// returns statistics about this instance of character animation manager
|
||||
// don't call this too frequently
|
||||
virtual void GetStatistics(Statistics& rStats);
|
||||
|
||||
//! Locks all models in memory
|
||||
void LockResources();
|
||||
|
||||
//! Unlocks all models in memory
|
||||
void UnlockResources();
|
||||
protected:
|
||||
|
||||
// destroys all characters
|
||||
void CleanupInstances();
|
||||
|
||||
// asserts the cache validity
|
||||
void ValidateBodyCache();
|
||||
|
||||
// deletes all registered bodies; the character instances got deleted even if they're still referenced
|
||||
void CleanupBodyCache();
|
||||
|
||||
// Finds a cached or creates a new CryCharBody instance and returns it
|
||||
// returns NULL if the construction failed
|
||||
CryCharBody* FetchBody (const string& strFileName);
|
||||
|
||||
// locks or unlocks the given body if needed, depending on the hint and console variables
|
||||
void DecideModelLockStatus(CryCharBody* pBody, unsigned nHints);
|
||||
void ClearDecals();
|
||||
private:
|
||||
CControllerManager * m_pControllerManager;
|
||||
// manager of animated objects.
|
||||
CAnimObjectManager * m_pAnimObjectManager;
|
||||
|
||||
class OrderByFileName
|
||||
{
|
||||
public:
|
||||
bool operator () (const CryCharBody* pLeft, const CryCharBody* pRight);
|
||||
bool operator () (const string& strLeft, const CryCharBody* pRight);
|
||||
bool operator () (const CryCharBody* pLeft, const string& strRight);
|
||||
};
|
||||
// this is the set of all existing instances of crycharbody
|
||||
typedef std::vector<CryCharBody*> CryCharBodyCache;
|
||||
CryCharBodyCache m_arrBodyCache;
|
||||
|
||||
// temporary locked objects with LockResources
|
||||
CryCharBody_AutoArray m_arrTempLock;
|
||||
|
||||
//typedef std::vector<CryCharBody_AutoPtr> CryCharBodyAutoArray;
|
||||
// this is the array of bodies that are kept locked even if the number of characters drops to zero
|
||||
CryCharBody_AutoSet m_setLockedBodies;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
257
CryAnimation/CryCharParticleManager.cpp
Normal file
257
CryAnimation/CryCharParticleManager.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
#include "stdafx.h"
|
||||
#include "drand.h"
|
||||
#include "MathUtils.h"
|
||||
#include "CryCharParticleManager.h"
|
||||
#include "CVars.h"
|
||||
|
||||
CryCharParticleManager::CryCharParticleManager():
|
||||
m_numActive (0),
|
||||
m_nLastFrame (0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// adds a particle spawn task, returns a handle to be used to
|
||||
int CryCharParticleManager::add (const ParticleParams& rParticleInfo, const CryParticleSpawnInfo& rSpawnInfo)
|
||||
{
|
||||
validateThis();
|
||||
// only 1 emitter is supported
|
||||
unsigned nHandle = 0;
|
||||
while (nHandle < m_arrEmitters.size() && m_arrEmitters[nHandle].m_bActive)
|
||||
++nHandle;
|
||||
|
||||
if (nHandle >= m_arrEmitters.size())
|
||||
m_arrEmitters.resize(nHandle + 1);
|
||||
m_arrEmitters[nHandle].m_ParticleInfo = rParticleInfo;
|
||||
m_arrEmitters[nHandle].m_SpawnInfo = rSpawnInfo;
|
||||
m_arrEmitters[nHandle].m_bActive = true;
|
||||
|
||||
++m_numActive;
|
||||
validateThis();
|
||||
|
||||
return nHandle;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// deletes a particle spawn task by the handle
|
||||
bool CryCharParticleManager::remove (int nHandle)
|
||||
{
|
||||
validateThis();
|
||||
if (nHandle == -1)
|
||||
{
|
||||
bool bResult = m_arrEmitters.empty();
|
||||
m_arrEmitters.clear();
|
||||
m_numActive = 0;
|
||||
return bResult;
|
||||
}
|
||||
|
||||
if (nHandle >= (int)m_arrEmitters.size() || nHandle < 0)
|
||||
return false;
|
||||
|
||||
if (!m_arrEmitters[nHandle].m_bActive)
|
||||
return false; // already deactivated
|
||||
|
||||
m_arrEmitters[nHandle].m_bActive = false;
|
||||
|
||||
size_t nSize = m_arrEmitters.size();
|
||||
while (nSize > 0 && !m_arrEmitters[nSize-1].m_bActive)
|
||||
--nSize;
|
||||
m_arrEmitters.resize (nSize);
|
||||
|
||||
--m_numActive;
|
||||
validateThis();
|
||||
|
||||
if (empty())
|
||||
m_nLastFrame = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns true if there are no emitters
|
||||
bool CryCharParticleManager::empty() const
|
||||
{
|
||||
return m_arrEmitters.empty();
|
||||
}
|
||||
|
||||
|
||||
// spawn the particles (using the external tangent info and mapping)
|
||||
void CryCharParticleManager::spawn (const SpawnParams& params)
|
||||
{
|
||||
float fTime = g_GetTimer()->GetCurrTime();
|
||||
if (g_nFrameID == m_nLastFrame)
|
||||
return;
|
||||
m_nLastFrame = g_nFrameID;
|
||||
|
||||
float fDeltaTime = g_GetTimer()->GetFrameTime();
|
||||
|
||||
for (unsigned i = 0; i < m_arrEmitters.size(); ++i)
|
||||
{
|
||||
Emitter& emitter = m_arrEmitters[i];
|
||||
if (emitter.m_bActive)
|
||||
{
|
||||
emitter.spawn (params, fDeltaTime);
|
||||
if (emitter.m_SpawnInfo.nFlags & CryParticleSpawnInfo::FLAGS_ONE_TIME_SPAWN)
|
||||
remove (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// evaluates if a vertex with such base is valid for spawning a particle
|
||||
bool CryCharParticleManager::Emitter::isValid (const SPipTangents& rBase)
|
||||
{
|
||||
if (!(m_SpawnInfo.nFlags & CryParticleSpawnInfo::FLAGS_RAIN_MODE))
|
||||
return true;
|
||||
|
||||
return (rBase.m_TNormal * m_ParticleInfo.vGravity < 0);
|
||||
}
|
||||
|
||||
|
||||
void CryCharParticleManager::Emitter::spawnFromBone(const SpawnParams& params)
|
||||
{
|
||||
// both position and normal (and later the gravity/wind direction) are in WCS
|
||||
|
||||
ParticleParams Particle = m_ParticleInfo;
|
||||
Particle.vPosition = m_SpawnInfo.vBonePos;
|
||||
|
||||
if ((unsigned)m_SpawnInfo.nBone < params.numBoneMatrices)
|
||||
{
|
||||
const Matrix44& mxBone = params.pBoneGlobalMatrices[m_SpawnInfo.nBone];
|
||||
Particle.vPosition = mxBone.TransformPointOLD(Particle.vPosition);
|
||||
Particle.vDirection = mxBone.TransformVectorOLD(Particle.vDirection);
|
||||
}
|
||||
|
||||
Particle.vPosition = params.pModelMatrix->TransformPointOLD (Particle.vPosition);
|
||||
Particle.vDirection = params.pModelMatrix->TransformVectorOLD (Particle.vDirection);
|
||||
|
||||
/*
|
||||
float fPitchCos = (float)(drand()*2-1);
|
||||
float fPitchSin = (float)sqrt(1-fPitchCos*fPitchCos);
|
||||
float fYaw = (float)(drand() * 2 * gPi);
|
||||
struct {float fCos, fSin;} Yaw;
|
||||
CosSin (fYaw, &Yaw.fCos);
|
||||
|
||||
m_ParticleInfo.vDirection.x = Yaw.fSin * fPitchSin;
|
||||
m_ParticleInfo.vDirection.y = fPitchCos;
|
||||
m_ParticleInfo.vDirection.z = Yaw.fCos * fPitchSin;
|
||||
*/
|
||||
|
||||
Get3DEngine()->SpawnParticles(Particle);
|
||||
}
|
||||
|
||||
// spawns one particle from the skin
|
||||
void CryCharParticleManager::Emitter::spawnFromSkin(const SpawnParams& params)
|
||||
{
|
||||
// find the face that's ok for spawning the particle
|
||||
Vec3 arrFace[2][4]; // the first [0..2] are the vertices of the face, the [3] one is the normal
|
||||
Vec3* pBestFace = arrFace[0], *pTempFace = arrFace[1];
|
||||
|
||||
Vec3 vWind = m_ParticleInfo.vGravity;
|
||||
|
||||
if (m_SpawnInfo.nFlags & m_SpawnInfo.FLAGS_RAIN_MODE)
|
||||
{
|
||||
float fBestBet; // minimize this dot product of gravity and normal
|
||||
Vec3 vWindLCS = UntransformVector (*params.pModelMatrix, vWind); // gravity in the LCS of the character
|
||||
|
||||
int nRainPower = g_GetCVars()->ca_RainPower_Clamp (3,40);
|
||||
|
||||
// attempt #0
|
||||
params.getFaceVN (irand() % params.numFaces, pBestFace);
|
||||
fBestBet = vWindLCS * pTempFace[3];
|
||||
|
||||
for (int nAttempt = 1; nAttempt < nRainPower; ++nAttempt)
|
||||
{
|
||||
params.getFaceVN(irand() % params.numFaces, pTempFace);
|
||||
|
||||
float fContraWind = vWindLCS * pTempFace[3];
|
||||
if (fContraWind < fBestBet)
|
||||
{
|
||||
fBestBet = fContraWind;
|
||||
std::swap (pBestFace, pTempFace);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
params.getFaceVN (irand() % params.numFaces, pBestFace);
|
||||
}
|
||||
|
||||
// get random point (in barycentric coordinates) on the triangle
|
||||
Vec3 vBR ((float)drand()+0.001f,(float)drand(),(float)drand());
|
||||
vBR /= vBR.x+vBR.y+vBR.z;
|
||||
Vec3 vSpawnPoint = vBR.x * pBestFace[0] + vBR.y * pBestFace[1] + vBR.z * pBestFace[2];
|
||||
|
||||
// both position and normal (and later the gravity/wind direction) are in WCS
|
||||
m_ParticleInfo.vPosition = params.pModelMatrix->TransformPointOLD (vSpawnPoint);
|
||||
|
||||
//CHANGED_BY_IVO - INVALID CHANGE, PLEASE REVISE
|
||||
Vec3 vNormal = params.pModelMatrix->TransformVectorOLD(pBestFace[3]);
|
||||
|
||||
// we've found something appropriate
|
||||
if (m_SpawnInfo.nFlags & m_SpawnInfo.FLAGS_RAIN_MODE)
|
||||
{
|
||||
m_ParticleInfo.vDirection =
|
||||
(2*(vNormal*vWind))*vNormal - vWind
|
||||
//vNormal
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ParticleInfo.vDirection = vNormal;
|
||||
}
|
||||
|
||||
Get3DEngine()->SpawnParticles(m_ParticleInfo);
|
||||
}
|
||||
|
||||
// spawn the particles (using the external tangent info and mapping)
|
||||
void CryCharParticleManager::Emitter::spawn (const SpawnParams& params, float fDeltaTime)
|
||||
{
|
||||
if (!m_bActive)
|
||||
return;
|
||||
for (m_fParticleAccumulator += fDeltaTime * m_SpawnInfo.fSpawnRate; m_fParticleAccumulator > 1; m_fParticleAccumulator -= 1)
|
||||
{
|
||||
spawnSingleParticle(params);
|
||||
}
|
||||
|
||||
if (m_fParticleAccumulator >= 0)
|
||||
{
|
||||
// dither the particles by time: if there are 1.9 particles to be spawned, the 1st particle is spawned and
|
||||
// the second is spawned in 90% of cases. If this emitter is re-added on the next frame anew, this will look in
|
||||
// the average as good as if it's here all the time. If it's kept, then the next frame it'll be spawned with less probability
|
||||
// (because of negative accumulator number)
|
||||
if (drand () < m_fParticleAccumulator)
|
||||
{
|
||||
m_fParticleAccumulator -= 1;
|
||||
spawnSingleParticle (params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// spawns only one particle with the params
|
||||
void CryCharParticleManager::Emitter::spawnSingleParticle (const SpawnParams& params)
|
||||
{
|
||||
if (m_SpawnInfo.nFlags & CryParticleSpawnInfo::FLAGS_SPAWN_FROM_BONE)
|
||||
spawnFromBone (params);
|
||||
else
|
||||
spawnFromSkin(params);
|
||||
}
|
||||
|
||||
void CryCharParticleManager::validateThis()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
// check the coherence of the number of active emitters
|
||||
unsigned numActiveEmitters = 0;
|
||||
for (unsigned i = 0; i < m_arrEmitters.size(); ++i)
|
||||
if (m_arrEmitters[i].m_bActive)
|
||||
++numActiveEmitters;
|
||||
assert (numActiveEmitters == m_numActive);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CryCharParticleManager::GetMemoryUsage (ICrySizer* pSizer)
|
||||
{
|
||||
size_t nSize = sizeof(*this);
|
||||
nSize += m_arrEmitters.size() * sizeof(Emitter);
|
||||
pSizer->AddObject(this, nSize);
|
||||
}
|
||||
123
CryAnimation/CryCharParticleManager.h
Normal file
123
CryAnimation/CryCharParticleManager.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Jan 15 2003 :- Created by Sergiy Migdalskiy
|
||||
//
|
||||
// Notes:
|
||||
// This class is used to isolate particle stuff from the ModelState
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef __CRY_ANIMATION_CRY_CHAR_PARTICLE_MANAGER_HDR__
|
||||
#define __CRY_ANIMATION_CRY_CHAR_PARTICLE_MANAGER_HDR__
|
||||
|
||||
#include "I3DEngine.h"
|
||||
#include "CryParticleSpawnInfo.h"
|
||||
#include "GeomCommon.h"
|
||||
|
||||
// This class is used for spawning particles (misc. types simultaneously, if needed)
|
||||
// from an animated character
|
||||
class CryCharParticleManager
|
||||
{
|
||||
public:
|
||||
|
||||
CryCharParticleManager();
|
||||
|
||||
// adds a particle spawn task, returns a handle to be used to
|
||||
int add (const ParticleParams& rParticleInfo, const CryParticleSpawnInfo& rSpawnInfo);
|
||||
|
||||
// deletes a particle spawn task by the handle
|
||||
bool remove (int nHandle);
|
||||
|
||||
// returns true if there are no emitters; when there are no emitters, it's not necessary to call spawn()
|
||||
bool empty() const;
|
||||
|
||||
// the spawn parameters
|
||||
struct SpawnParams
|
||||
{
|
||||
// the pointer to the (internal indexation) vertices
|
||||
const Vec3* pVertices;
|
||||
// the pointer to the (internal indexation) normals;
|
||||
// NOTE: this is optional and IS sometimes NULL.
|
||||
const class Vec3dA16* pNormalsA16;
|
||||
// the number of vertices (internal indexation) in pVertices array
|
||||
unsigned numVertices;
|
||||
|
||||
// faces, in internal indexation
|
||||
const GeomFace* pFaces;
|
||||
// number of faces
|
||||
unsigned numFaces;
|
||||
|
||||
// the model matrix of the character - transforms points to world
|
||||
const Matrix44* pModelMatrix;
|
||||
// the array of global matrices of bones
|
||||
const Matrix44* pBoneGlobalMatrices;
|
||||
// the number of bone matrices
|
||||
unsigned numBoneMatrices;
|
||||
|
||||
void setVertices (const Vec3* _pVertices, unsigned _numVertices)
|
||||
{
|
||||
this->pVertices = _pVertices;
|
||||
this->numVertices = _numVertices;
|
||||
}
|
||||
|
||||
void setFaces (const GeomFace* _pFaces, unsigned _numFaces)
|
||||
{
|
||||
this->pFaces = _pFaces;
|
||||
this->numFaces = _numFaces;
|
||||
}
|
||||
|
||||
// retrieves the vertices of the given face into [0..2] and the normal into [3]
|
||||
void getFaceVN (unsigned nFace, Vec3* pVerts)const
|
||||
{
|
||||
for (int v = 0; v< 3; ++v)
|
||||
pVerts[v] = this->pVertices[this->pFaces[nFace][v]];
|
||||
pVerts[3] = (pVerts[2]-pVerts[0])^(pVerts[1]-pVerts[0]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// spawn the particles (using the external tangent info and mapping)
|
||||
void spawn (const SpawnParams& params);
|
||||
|
||||
void validateThis();
|
||||
|
||||
void GetMemoryUsage (ICrySizer* pSizer);
|
||||
protected:
|
||||
struct Emitter
|
||||
{
|
||||
ParticleParams m_ParticleInfo;
|
||||
CryParticleSpawnInfo m_SpawnInfo;
|
||||
|
||||
// the quantity accumulator, to enable < 1 particles per frame
|
||||
// the number of particles accumulates here over time
|
||||
float m_fParticleAccumulator;
|
||||
|
||||
// if false, this entry is reserved (doesn't emit any particles)
|
||||
bool m_bActive;
|
||||
|
||||
// evaluates if a vertex with such base is valid for spawning a particle
|
||||
bool isValid (const SPipTangents& rBase);
|
||||
|
||||
Emitter () :
|
||||
m_fParticleAccumulator(0),
|
||||
m_bActive (false)
|
||||
{
|
||||
}
|
||||
|
||||
// spawn the necessary number of particles
|
||||
void spawn (const SpawnParams& params, float fTimeDelta);
|
||||
// spawns only one particle with the params
|
||||
void spawnSingleParticle (const SpawnParams& params);
|
||||
// spawns one particle from the skin
|
||||
void spawnFromSkin(const SpawnParams& params);
|
||||
// spawns one particle from bone
|
||||
void spawnFromBone(const SpawnParams& params);
|
||||
};
|
||||
std::vector<Emitter> m_arrEmitters;
|
||||
unsigned m_numActive; // number of active emitters in m_arrEmitters
|
||||
int m_nLastFrame;
|
||||
};
|
||||
|
||||
#endif
|
||||
86
CryAnimation/CryCharReShadowManager.cpp
Normal file
86
CryAnimation/CryCharReShadowManager.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include "stdafx.h"
|
||||
//#include "CryAnimation.h"
|
||||
#include "CVars.h"
|
||||
#include "CryCharReShadowVolume.h"
|
||||
#include "CryCharReShadowManager.h"
|
||||
|
||||
|
||||
CryCharReShadowManager::CryCharReShadowManager ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CryCharReShadowManager::~CryCharReShadowManager ()
|
||||
{
|
||||
for (unsigned i = 0; i < m_arrPool.size(); ++i)
|
||||
delete m_arrPool[i];
|
||||
m_arrPool.clear();
|
||||
}
|
||||
|
||||
// creates a new shadow volume object or retrieves an old one from the pool
|
||||
// May return NULL, if insufficient resources; in this case, no further action on
|
||||
// creating shadow volumes must be attempted
|
||||
CryCharReShadowVolume* CryCharReShadowManager::newShadow ()
|
||||
{
|
||||
// first try to find already existing resource:
|
||||
// the one with age > 1. If the age is:
|
||||
// 0: this is a different shadow that was rendered in this very frame
|
||||
// this means multiple shadows from different light sources, and we need
|
||||
// different resources for them
|
||||
// 1: this is a shadow that was rendered in the previous frame and may still
|
||||
// be being rendered, which means we'd have to wait the fences.
|
||||
for (unsigned i = 0; i < m_arrPool.size(); ++i)
|
||||
if (m_arrPool[i]->getAgeFrames() > (unsigned)(g_GetCVars()->ca_ShadowDoubleBuffer()&1))
|
||||
{
|
||||
return m_arrPool[i];
|
||||
}
|
||||
|
||||
// we didn't find any in the pool, so create a new one, if we didn't yet exceed the limit
|
||||
if (m_arrPool.size() > (unsigned) g_GetCVars()->ca_ShadowBufferLimit())
|
||||
return NULL; // we exceeded the limit
|
||||
|
||||
CryCharReShadowVolume* pNewShadow = new CryCharReShadowVolume();
|
||||
m_arrPool.push_back(pNewShadow );
|
||||
return pNewShadow;
|
||||
}
|
||||
|
||||
|
||||
// cleans up the shadow volume resources that weren't used lately
|
||||
// (collect the garbage)
|
||||
void CryCharReShadowManager::shrink()
|
||||
{
|
||||
// we leave at least one shadow resource always;
|
||||
// we delete excess shadow resources when they're not requested during
|
||||
// 50 frames AND 5 seconds
|
||||
|
||||
// 1 in case of single-buffer
|
||||
// 2 in case of double-buffer
|
||||
unsigned nMinPoolSize = 1 + (g_GetCVars()->ca_ShadowDoubleBuffer()&1);
|
||||
|
||||
if (nMinPoolSize > (unsigned) g_GetCVars()->ca_ShadowBufferLimit())
|
||||
nMinPoolSize = g_GetCVars()->ca_ShadowBufferLimit();
|
||||
|
||||
// delete the extra unused buffers.
|
||||
std::vector<CryCharReShadowVolume*>::iterator it;
|
||||
for (it = m_arrPool.begin(); it != m_arrPool.end() && m_arrPool.size() > nMinPoolSize; )
|
||||
{
|
||||
CryCharReShadowVolume* pShadow = *it;
|
||||
if (pShadow->getAgeFrames() > 50
|
||||
&& pShadow->getAgeSeconds() > 5)
|
||||
{
|
||||
delete pShadow;
|
||||
it = m_arrPool.erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CryCharReShadowManager::GetMemoryUsage (ICrySizer* pSizer)
|
||||
{
|
||||
pSizer->Add (*this);
|
||||
for (unsigned i = 0; i < m_arrPool.size(); ++i)
|
||||
m_arrPool[i]->GetMemoryUsage(pSizer);
|
||||
}
|
||||
39
CryAnimation/CryCharReShadowManager.h
Normal file
39
CryAnimation/CryCharReShadowManager.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created 12/05/2002 by Sergiy Migdalskiy
|
||||
//
|
||||
// This is the class that's used to implement support for multiple
|
||||
// shadow volumes per character, and the policy of double- or single-buffered
|
||||
// shadow volumes for performance optimization
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _CRY_CHAR_RE_SHADOW_MANAGER_HDR_
|
||||
#define _CRY_CHAR_RE_SHADOW_MANAGER_HDR_
|
||||
|
||||
class CryCharReShadowVolume;
|
||||
|
||||
class CryCharReShadowManager
|
||||
{
|
||||
public:
|
||||
CryCharReShadowManager ();
|
||||
~CryCharReShadowManager ();
|
||||
|
||||
// creates a new shadow volume object or retrieves an old one from the pool
|
||||
// May return NULL, if insufficient resources; in this case, no further action on
|
||||
// creating shadow volumes must be attempted
|
||||
CryCharReShadowVolume* newShadow ();
|
||||
|
||||
// cleans up the shadow volume resources that weren't used lately
|
||||
// (collect the garbage)
|
||||
void shrink();
|
||||
|
||||
void GetMemoryUsage (ICrySizer* pSizer);
|
||||
protected:
|
||||
// the pool of shadow volume objects
|
||||
std::vector<CryCharReShadowVolume*> m_arrPool;
|
||||
};
|
||||
|
||||
#endif
|
||||
151
CryAnimation/CryCharReShadowVolume.cpp
Normal file
151
CryAnimation/CryCharReShadowVolume.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
#include "stdafx.h"
|
||||
//#include "CryAnimation.h"
|
||||
#include "CVars.h"
|
||||
#include "CryCharReShadowVolume.h"
|
||||
|
||||
|
||||
CryCharReShadowVolume::CryCharReShadowVolume():
|
||||
m_pLeafBuffer (NULL),
|
||||
m_pMesh (NULL),
|
||||
m_nUsedMeshVertices(0),
|
||||
m_arrIndices ("CryCharReShadowVolume.Indices"),
|
||||
m_arrVertices ("CryCharReShadowVolume.Vertices"),
|
||||
m_nLastFrameSubmitted(0)
|
||||
{
|
||||
}
|
||||
|
||||
CryCharReShadowVolume::~CryCharReShadowVolume()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void CryCharReShadowVolume::clear()
|
||||
{
|
||||
if (m_pLeafBuffer)
|
||||
{
|
||||
g_GetIRenderer()->DeleteLeafBuffer (m_pLeafBuffer);
|
||||
m_pLeafBuffer = NULL;
|
||||
}
|
||||
|
||||
if (m_pMesh)
|
||||
{
|
||||
m_pMesh->m_arrLBuffers[0].pVB = NULL;
|
||||
m_pMesh->Release();
|
||||
m_pMesh = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// prepares for calculating the shadow volume
|
||||
// numVertices is the minimal length of the vertex buffer
|
||||
// numIndices is the minimal length of the index buffer
|
||||
void CryCharReShadowVolume::prepare (unsigned numIndices, unsigned numVertices)
|
||||
{
|
||||
bool bRecreate = false;
|
||||
|
||||
m_nUsedMeshVertices = numVertices;
|
||||
|
||||
// Realloc index in-mem buffer
|
||||
if (m_arrIndices.size() < numIndices)
|
||||
{
|
||||
m_arrIndices.reinit(numIndices);
|
||||
}
|
||||
|
||||
// Realloc vertex in-mem buffer
|
||||
if (m_arrVertices.size() < numVertices)
|
||||
{
|
||||
m_arrVertices.reinit(numVertices);
|
||||
}
|
||||
|
||||
if (m_pLeafBuffer && m_pLeafBuffer->m_SecVertCount < (int)numVertices)
|
||||
{
|
||||
g_GetIRenderer()->DeleteLeafBuffer (m_pLeafBuffer);
|
||||
m_pLeafBuffer = NULL;
|
||||
}
|
||||
|
||||
if (!m_pMesh)
|
||||
{
|
||||
m_pMesh = (CRETriMeshShadow *)g_GetIRenderer()->EF_CreateRE (eDATA_TriMeshShadow);
|
||||
m_pMesh->m_bAnimatedObject = true;
|
||||
}
|
||||
|
||||
if (!m_pMesh)
|
||||
{
|
||||
assert (0);
|
||||
g_GetLog()->LogError ("Cannot get the render element for the stencil shadow volume");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_pLeafBuffer)
|
||||
{
|
||||
assert(!m_arrVertices.empty());
|
||||
m_pLeafBuffer = g_GetIRenderer()->CreateLeafBufferInitialized(&m_arrVertices[0], numVertices, VERTEX_FORMAT_P3F, NULL, 0, R_PRIMV_TRIANGLES, "Character ShadowVolume");
|
||||
m_pLeafBuffer->SetChunk(0,0,numVertices,0,-1); // setup fake shunk to allow mfCheckUpdate
|
||||
m_pMesh->m_arrLBuffers[0].pVB = m_pLeafBuffer; // use always slot 0
|
||||
}
|
||||
m_pMesh->m_nRendIndices = numIndices;
|
||||
}
|
||||
|
||||
|
||||
// assuming the calculation of the shadow volume is finished, submits it to the renderer
|
||||
void CryCharReShadowVolume::submit (const SRendParams *rParams, IShader* pShadowCull)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
{
|
||||
for (int i = 0; i < m_pLeafBuffer->m_Indices.m_nItems; ++i)
|
||||
assert (m_arrIndices[i] < m_pLeafBuffer->m_SecVertCount);
|
||||
}
|
||||
#endif
|
||||
|
||||
//get the effect object from the renderer
|
||||
CCObject *pObj = g_GetIRenderer()->EF_GetObject (true);
|
||||
|
||||
// pObj->m_nScissorX1 = rParams->nScissorX1;
|
||||
// pObj->m_nScissorY1 = rParams->nScissorY1;
|
||||
// pObj->m_nScissorX2 = rParams->nScissorX2;
|
||||
// pObj->m_nScissorY2 = rParams->nScissorY2;
|
||||
|
||||
pObj->m_DynLMMask = rParams->nDLightMask; // used for scissor test
|
||||
Matrix34 t = Matrix34::CreateTranslationMat(rParams->vPos);
|
||||
Matrix33 r33 = Matrix33::CreateRotationXYZ( Deg2Rad(Ang3(rParams->vAngles[0],-rParams->vAngles[1],rParams->vAngles[2])));
|
||||
pObj->m_Matrix = GetTransposed44(t*r33);
|
||||
pObj->m_ObjFlags |= FOB_TRANS_MASK;
|
||||
|
||||
// m_pMesh->mfCheckUpdate(0);
|
||||
|
||||
// GetRenderer()->UpdateBuffer(m_pLeafBuffer->m_pVertexBuffer,&m_arrVertices[0],numMeshVertices(),true);
|
||||
//GetRenderer()->UpdateIndices(m_pLeafBuffer->m_pVertexBuffer,&m_arrIndices[0],numMeshIndices());
|
||||
|
||||
// update verts in system buffer
|
||||
// it's better to make call back function and write directly into video memory
|
||||
// indices are passed to CRETriMeshShadow by pointer as before
|
||||
m_pLeafBuffer->UpdateSysVertices(&m_arrVertices[0],numMeshVertices());
|
||||
m_pLeafBuffer->UpdateSysIndices(&m_arrIndices[0],m_pMesh->m_nRendIndices);
|
||||
|
||||
IShader * pSHStencil = g_GetIRenderer()->EF_LoadShader("<Stencil>", eSH_World, EF_SYSTEM);
|
||||
g_GetIRenderer()->EF_AddEf(0, (CRendElement *)m_pMesh , pSHStencil, NULL, pObj, -1, pShadowCull, rParams->nSortValue);
|
||||
|
||||
m_nLastFrameSubmitted = g_GetIRenderer()->GetFrameID();
|
||||
m_fLastTimeSubmitted = g_GetTimer()->GetCurrTime();
|
||||
}
|
||||
|
||||
|
||||
void CryCharReShadowVolume::GetMemoryUsage (ICrySizer* pSizer)
|
||||
{
|
||||
pSizer->Add(*this);
|
||||
pSizer->AddContainer(m_arrIndices);
|
||||
pSizer->AddContainer(m_arrVertices);
|
||||
}
|
||||
|
||||
|
||||
// returns the age of this shadow volume: the current frame - the frame it was submitted last
|
||||
unsigned CryCharReShadowVolume::getAgeFrames()
|
||||
{
|
||||
return g_GetIRenderer()->GetFrameID() - m_nLastFrameSubmitted;
|
||||
}
|
||||
|
||||
// returns the timeout from the last call to submit()
|
||||
float CryCharReShadowVolume::getAgeSeconds()
|
||||
{
|
||||
return g_GetTimer()->GetCurrTime() - m_fLastTimeSubmitted;
|
||||
}
|
||||
86
CryAnimation/CryCharReShadowVolume.h
Normal file
86
CryAnimation/CryCharReShadowVolume.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created 12/05/2002 by Sergiy Migdalskiy
|
||||
//
|
||||
// This is the class that's used by the model state (or cry char instance)
|
||||
// to render the shadow volumes. Generally, it's former part of the CryModelState
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _CRY_CHAR_RE_SHADOW_VOLUME_HDR_
|
||||
#define _CRY_CHAR_RE_SHADOW_VOLUME_HDR_
|
||||
|
||||
// This is the utility/helper class for rendering the shadow volumes,
|
||||
// all the dirty work (of double buffering and managing the memory)
|
||||
// should be in this class
|
||||
//
|
||||
// USAGE:
|
||||
// 1. get the vertex and index buffers and fill them in by the declared
|
||||
// number of vertices and indices referring to them
|
||||
// 2. submit that with submit() function that will create the renderer object and
|
||||
// update the buffers and do everything required.
|
||||
//
|
||||
// This sequence is to be done once and only once per frame per light per character
|
||||
class CryCharReShadowVolume
|
||||
{
|
||||
public:
|
||||
CryCharReShadowVolume();
|
||||
~CryCharReShadowVolume();
|
||||
|
||||
void clear();
|
||||
|
||||
// prepares for calculating the shadow volume
|
||||
// numVertices is the minimal length of the vertex buffer
|
||||
// numIndices is the minimal length of the index buffer
|
||||
void prepare (unsigned numIndices, unsigned numVertices);
|
||||
|
||||
// returns the pointer to the array of vertices to fill in
|
||||
// the vertices are used to form the shadow volume mesh/geometry
|
||||
Vec3d* getVertexBuffer () {return &m_arrVertices[0];}
|
||||
|
||||
// returns the pointer to the indices in the index buffer to fill in
|
||||
// the indices refer to the vertices in the vertex buffer returned by getVertexBuffer ()
|
||||
unsigned short* getIndexBuffer () {return &m_arrIndices[0];}
|
||||
|
||||
// assuming the calculation of the shadow volume is finished, submits it to the renderer
|
||||
void submit (const SRendParams *rParams, IShader* pShadowCull );
|
||||
|
||||
// returns the age of this shadow volume: the current frame - the frame it was submitted last
|
||||
unsigned getAgeFrames();
|
||||
|
||||
// returns the timeout from the last call to submit()
|
||||
float getAgeSeconds();
|
||||
|
||||
void GetMemoryUsage (ICrySizer* pSizer);
|
||||
protected:
|
||||
unsigned numMeshIndices() {return m_nUsedMeshVertices;}
|
||||
unsigned numMeshVertices() {return m_pLeafBuffer->m_SecVertCount;}
|
||||
|
||||
protected:
|
||||
//! shadow volume renderer managed vertex buffer
|
||||
CLeafBuffer* m_pLeafBuffer;
|
||||
|
||||
// this is the number of vertices actually used in the leaf buffer
|
||||
// (referred to by the index array)
|
||||
unsigned m_nUsedMeshVertices;
|
||||
|
||||
//! Shader RenderElements for stencil
|
||||
CRETriMeshShadow* m_pMesh;
|
||||
|
||||
//! shadow volume indices
|
||||
TFixedArray<unsigned short> m_arrIndices;
|
||||
|
||||
//! shadow volume memory vertex buffer
|
||||
//! TODO: get rid of it; rewrite the shadow code so that we don't keep it in every character
|
||||
TFixedArray<Vec3d> m_arrVertices;
|
||||
|
||||
// the synchronous time when the last submit() was called
|
||||
float m_fLastTimeSubmitted;
|
||||
|
||||
// the last frame submit() function was called
|
||||
int m_nLastFrameSubmitted;
|
||||
};
|
||||
|
||||
#endif
|
||||
161
CryAnimation/CryCharRenderElement.cpp
Normal file
161
CryAnimation/CryCharRenderElement.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Sergiy Migdalskiy
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "stdafx.h"
|
||||
#include "CryAnimationBase.h"
|
||||
#include "CryCharRenderElement.h"
|
||||
#include "CVars.h"
|
||||
|
||||
CryCharRenderElement::CryCharRenderElement ():
|
||||
m_pLeafBuffer (NULL),
|
||||
m_nLeafBufferLastRenderFrame (0),
|
||||
m_numVertBufVertices (0)
|
||||
{
|
||||
}
|
||||
|
||||
CryCharRenderElement::~CryCharRenderElement ()
|
||||
{
|
||||
}
|
||||
|
||||
// returns true if the leaf buffer can be deleted immediately
|
||||
bool CryCharRenderElement::canDestruct()
|
||||
{
|
||||
// we can't delete the leaf buffer if we rendered it in this frame, and the rendering is still pending
|
||||
return !m_pLeafBuffer || g_GetIRenderer()->GetFrameID(false) != m_nLeafBufferLastRenderFrame;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// creates the buffer with the given number of vertices and vertex format
|
||||
// can only be called for a clean (without initialized leaf buffer) object
|
||||
void CryCharRenderElement::create (int nVertCount, int nVertFormat, const char *szSource, unsigned numMaterials, bool bOnlyVideoBuffer)
|
||||
{
|
||||
assert (!m_pLeafBuffer);
|
||||
m_numVertBufVertices = nVertCount;
|
||||
|
||||
m_pLeafBuffer = GetIRenderer()->CreateLeafBufferInitialized (NULL, 0, nVertFormat, NULL, 0, R_PRIMV_TRIANGLES, szSource, eBT_Dynamic, numMaterials, 0, NULL, NULL, true);
|
||||
m_pLeafBuffer->m_bOnlyVideoBuffer = bOnlyVideoBuffer;
|
||||
// I suppose we can delete it just afterwards, but just incase let's pretend we've already rendered i in this frame
|
||||
m_nLeafBufferLastRenderFrame = GetIRenderer()->GetFrameID(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
// creates the buffer with the given number of vertices and vertex format
|
||||
// can only be called for a clean (without initialized leaf buffer) object
|
||||
void CryCharRenderElement::create (int nVertCount, const struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F* pSourceData, const char *szSource, unsigned numMaterials, bool bOnlyVideoBuffer)
|
||||
{
|
||||
assert (!m_pLeafBuffer);
|
||||
m_numVertBufVertices = nVertCount;
|
||||
|
||||
m_pLeafBuffer = g_GetIRenderer()->CreateLeafBufferInitialized ((void *)pSourceData, nVertCount, VERTEX_FORMAT_P3F_COL4UB_TEX2F, NULL, 0, R_PRIMV_TRIANGLES, szSource, eBT_Dynamic, numMaterials, 0, NULL, NULL, bOnlyVideoBuffer);
|
||||
|
||||
// I suppose we can delete it just afterwards, but just incase let's pretend we've already rendered i in this frame
|
||||
m_nLeafBufferLastRenderFrame = g_GetIRenderer()->GetFrameID(false);
|
||||
}
|
||||
|
||||
|
||||
// recreates videobuffer
|
||||
void CryCharRenderElement::recreate()
|
||||
{
|
||||
m_pLeafBuffer->CreateVidVertices(m_pLeafBuffer->m_SecVertCount, m_pLeafBuffer->m_nVertexFormat);
|
||||
}
|
||||
|
||||
// returns the number of vertices allocated in the current buffer, if any is allocated
|
||||
unsigned CryCharRenderElement::numVertices()const
|
||||
{
|
||||
return m_numVertBufVertices;
|
||||
}
|
||||
|
||||
// returns the number of materials in the leaf buffer
|
||||
unsigned CryCharRenderElement::numMaterials()const
|
||||
{
|
||||
return m_pLeafBuffer?m_pLeafBuffer->m_pMats->size():0;
|
||||
}
|
||||
|
||||
// sets the number of used materials in the leaf buffer
|
||||
void CryCharRenderElement::resizeMaterials (unsigned numMaterials, IShader* pShader)
|
||||
{
|
||||
if (m_pLeafBuffer)
|
||||
{
|
||||
unsigned numInitialized = (unsigned)m_pLeafBuffer->m_pMats->Count();
|
||||
m_pLeafBuffer->m_pMats->PreAllocate (numMaterials, numMaterials);
|
||||
|
||||
for (; numInitialized < numMaterials; ++numInitialized)
|
||||
{
|
||||
// uninitialized material
|
||||
CMatInfo& rMatInfo = (*m_pLeafBuffer->m_pMats)[numInitialized];
|
||||
rMatInfo = CMatInfo();
|
||||
rMatInfo.shaderItem.m_pShader = pShader; //gRenDev->EF_LoadShader((char*)szEfName, -1, eEF_World, 0);
|
||||
rMatInfo.pRE = (CREOcLeaf*)g_GetIRenderer()->EF_CreateRE(eDATA_OcLeaf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// deletes the current buffer, cleans up the object; can only be called when canDestruct() == true
|
||||
void CryCharRenderElement::destruct ()
|
||||
{
|
||||
// when we exit from the game, we can destruct this
|
||||
//assert (canDestruct());
|
||||
if (m_pLeafBuffer)
|
||||
{
|
||||
g_GetIRenderer()->DeleteLeafBuffer(m_pLeafBuffer);
|
||||
detach();
|
||||
}
|
||||
}
|
||||
|
||||
// detaches the leaf buffer from this object (forgets it)
|
||||
void CryCharRenderElement::detach()
|
||||
{
|
||||
m_numVertBufVertices = 0;
|
||||
m_pLeafBuffer = NULL;
|
||||
m_nLeafBufferLastRenderFrame = 0;
|
||||
}
|
||||
|
||||
|
||||
void CryCharRenderElement::update(bool bLock, bool bCopyToVideoBuffer)
|
||||
{
|
||||
if (m_pLeafBuffer->m_pVertexBuffer)
|
||||
{
|
||||
// now we filled in all the components of the vertex buffer - we need to synchronize it with the hardware
|
||||
if (!bCopyToVideoBuffer)
|
||||
g_GetIRenderer()->UpdateBuffer (m_pLeafBuffer->m_pVertexBuffer, m_pLeafBuffer->m_pSecVertBuffer->m_VS[VSF_GENERAL].m_VData, m_pLeafBuffer->m_SecVertCount, !bLock, 0);
|
||||
else // otherwise unlock buffers
|
||||
g_GetIRenderer()->UpdateBuffer (m_pLeafBuffer->m_pVertexBuffer, 0, 0, !bLock, 0, 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CryCharRenderElement::render (CCObject* pObj)
|
||||
{
|
||||
// we might not have the buffer now - nothing to render
|
||||
if (!m_pLeafBuffer)
|
||||
return;
|
||||
|
||||
m_nLeafBufferLastRenderFrame = g_GetIRenderer()->GetFrameID(false);
|
||||
|
||||
// add the render element to the renderer, using the local transformations & stuff of the
|
||||
// same object as the character's and default sort order (with default shader)
|
||||
m_pLeafBuffer->AddRenderElements(pObj);
|
||||
}
|
||||
|
||||
void CryCharRenderElement::assignMaterial (unsigned nMaterial, IShader* pShader, int nTextureId, int nFirstIndex, int numIndices, int nFirstVertex, int numVertices)
|
||||
{
|
||||
if (m_pLeafBuffer)
|
||||
{
|
||||
CMatInfo& rMatInfo = (*m_pLeafBuffer->m_pMats)[nMaterial];
|
||||
rMatInfo.pRE->m_CustomTexBind[0] = nTextureId;
|
||||
m_pLeafBuffer->SetChunk(pShader, nFirstVertex, numVertices, nFirstIndex, numIndices, nMaterial);
|
||||
(*m_pLeafBuffer->m_pMats)[nMaterial].pRE->m_CustomTexBind[0] = nTextureId;
|
||||
}
|
||||
}
|
||||
|
||||
void CryCharRenderElement::updateIndices (const unsigned short* pIndices, unsigned numIndices)
|
||||
{
|
||||
getLeafBuffer()->UpdateVidIndices(pIndices, (int)numIndices);
|
||||
getLeafBuffer()->m_NumIndices = numIndices;
|
||||
}
|
||||
82
CryAnimation/CryCharRenderElement.h
Normal file
82
CryAnimation/CryCharRenderElement.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Sergiy Migdalskiy
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef _CRY_CHAR_RENDER_ELEMENT_HDR_
|
||||
#define _CRY_CHAR_RENDER_ELEMENT_HDR_
|
||||
|
||||
|
||||
// buffer incarnation: the buffer with the last rendered frame
|
||||
// NOTE: this structure doesn't manage the leaf buffer; it's just a container
|
||||
// THe leaf buffer must be destructed manually through the methods of this class,
|
||||
// the destructor won't do that for you
|
||||
class CryCharRenderElement
|
||||
{
|
||||
public:
|
||||
// creates an empty element, without the leaf buffer attached
|
||||
CryCharRenderElement ();
|
||||
~CryCharRenderElement ();
|
||||
|
||||
// returns true if the leaf buffer can be deleted immediately
|
||||
bool canDestruct();
|
||||
|
||||
// detaches the leaf buffer from this object (forgets it)
|
||||
void detach();
|
||||
|
||||
// deletes the current buffer, cleans up the object; can only be called when canDelete() == true
|
||||
void destruct ();
|
||||
|
||||
// creates the buffer with the given number of vertices and vertex format
|
||||
// can only be called for a clean (without initialized leaf buffer) object
|
||||
//void create (int nVertCount, int nVertFormat, const char* szSource, unsigned numMaterials, bool bOnlyVideoBuffer = true);
|
||||
|
||||
// creates the buffer with the given number of vertices and vertex format
|
||||
// can only be called for a clean (without initialized leaf buffer) object
|
||||
// initializes the system buffer with the data from the array
|
||||
void create (int nVertCount, const struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F* pSource, const char* szSource, unsigned numMaterials, bool bOnlyVideoBuffer = true);
|
||||
|
||||
// recreates videobuffer
|
||||
void recreate();
|
||||
|
||||
// returns the number of vertices allocated in the current buffer, if any is allocated
|
||||
unsigned numVertices()const;
|
||||
|
||||
// returns the number of materials in the leaf buffer
|
||||
unsigned numMaterials()const;
|
||||
|
||||
// sets the number of used materials in the leaf buffer, initializes the materials (creates ocleaves for them)
|
||||
void resizeMaterials (unsigned numMaterials, IShader* pShader);
|
||||
|
||||
CLeafBuffer* getLeafBuffer() {return m_pLeafBuffer;}
|
||||
|
||||
int getVertexFormat() {return m_pLeafBuffer->m_nVertexFormat;}
|
||||
|
||||
void update(bool bLock, bool bCopyToVideoBuffer);
|
||||
|
||||
void lock (bool bCopyToVideoBuffer) {update (true, bCopyToVideoBuffer);}
|
||||
void unlock (bool bCopyToVideoBuffer) {update (false, bCopyToVideoBuffer);}
|
||||
|
||||
void render (CCObject* pObj);
|
||||
|
||||
void assignMaterial (unsigned nMaterial, IShader* pShader, int nTextureId, int nFirstIndex, int numIndices, int nFirstVertex, int numVertices);
|
||||
void updateIndices (const unsigned short* pIndices, unsigned numIndices);
|
||||
protected:
|
||||
|
||||
// the leaf buffer for rendering the decals
|
||||
CLeafBuffer* m_pLeafBuffer;
|
||||
|
||||
// this is the frame at which this leaf buffer was last rendered;
|
||||
// if it's the current frame, it cannot be deleted now.
|
||||
int m_nLeafBufferLastRenderFrame;
|
||||
|
||||
// this is the number of vertices/indices allocated in the current leaf buffer
|
||||
// these are kept here becaue CLeafBuffer doesn't have any normal interface to retrieve them
|
||||
unsigned m_numVertBufVertices;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
156
CryAnimation/CryGeomMorphTarget.cpp
Normal file
156
CryAnimation/CryGeomMorphTarget.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#include "stdafx.h"
|
||||
#include "StlUtils.h"
|
||||
#include "CryGeomMorphTarget.h"
|
||||
|
||||
|
||||
// create an identity morph target (doesn't morph anything)
|
||||
CryGeomMorphTarget::CryGeomMorphTarget()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// load the morph target from the chunk/size. Returns 0 is the chunk is completely wrong,
|
||||
// or the number of read bytes if it's ok (returns nSize if everything is fine)
|
||||
unsigned CryGeomMorphTarget::load (unsigned nLOD, const MESHMORPHTARGET_CHUNK_DESC_0001* pChunk, unsigned nSize)
|
||||
{
|
||||
if (nLOD >= g_nMaxLodLevels)
|
||||
return nSize; // pretend we have loaded everything, but ignore it
|
||||
|
||||
// the chunk must at least contain its header and the name (min 2 bytes)
|
||||
if (nSize < sizeof(*pChunk) + 2)
|
||||
return 0;
|
||||
if (nSize < sizeof(*pChunk) + sizeof(SMeshMorphTargetVertex)*pChunk->numMorphVertices + 2)
|
||||
return 0; // we don't want truncated chunks
|
||||
|
||||
m_arrVertex[nLOD].reinit (pChunk->numMorphVertices);
|
||||
const SMeshMorphTargetVertex* pSrcVertices = (const SMeshMorphTargetVertex*)(pChunk+1);
|
||||
if (pChunk->numMorphVertices)
|
||||
memcpy (&m_arrVertex[nLOD][0], pSrcVertices, sizeof(SMeshMorphTargetVertex)*pChunk->numMorphVertices);
|
||||
|
||||
const char* pName = (const char*)(pSrcVertices+pChunk->numMorphVertices);
|
||||
const char* pNameEnd = ((const char*)pChunk)+nSize;
|
||||
|
||||
setName (pName, pNameEnd-pNameEnd);
|
||||
|
||||
return nSize;
|
||||
}
|
||||
|
||||
|
||||
// scales the target vertices by the given factor
|
||||
void CryGeomMorphTarget::scale (unsigned nLOD,float fScale)
|
||||
{
|
||||
if (nLOD >= g_nMaxLodLevels)
|
||||
return;
|
||||
|
||||
MorphVertexArray::iterator it = m_arrVertex[nLOD].begin(), itEnd = it + m_arrVertex[nLOD].size();
|
||||
for (; it != itEnd; ++it)
|
||||
it->ptVertex *= fScale;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// given the source morph object, morphs it with the given weight to this morph target;
|
||||
// 1 means the morph target will replace the target with itself; 0 means it will just copy the source
|
||||
// into the destination (or leave it alone, if the two coincide)
|
||||
// PARAMETERS:
|
||||
// numVertices - the number of vertices in the source and destination buffers (to update)
|
||||
// fWeight - 0..1, 0 means no morph, 1 means full morphed
|
||||
void CryGeomMorphTarget::morph (unsigned nLOD, const Vec3d* pSrc, Vec3d* pDst, unsigned numVertices, float fWeight)const
|
||||
{
|
||||
if (nLOD >= g_nMaxLodLevels)
|
||||
{
|
||||
// pretend we did everything
|
||||
if (pSrc != pDst)
|
||||
memcpy (pDst, pSrc, sizeof(Vec3d) * numVertices);
|
||||
return;
|
||||
}
|
||||
|
||||
MorphVertexArray::const_iterator itMorphVertex = m_arrVertex[nLOD].begin(), itMorphVertexEnd = itMorphVertex + m_arrVertex[nLOD].size();
|
||||
if (pSrc == pDst)
|
||||
{
|
||||
// there's no need to copy them
|
||||
for (; itMorphVertex != itMorphVertexEnd; ++itMorphVertex)
|
||||
pDst[itMorphVertex->nVertexId] = pDst[itMorphVertex->nVertexId] * (1-fWeight) + itMorphVertex->ptVertex * fWeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we must copy everything
|
||||
for (unsigned nVertex = 0; nVertex < numVertices; )
|
||||
{
|
||||
// copy until the next morphed vertex
|
||||
while (nVertex < itMorphVertex->nVertexId)
|
||||
{
|
||||
pDst[nVertex] = pSrc[nVertex];
|
||||
++nVertex;
|
||||
}
|
||||
// morph the current vertex
|
||||
pDst[itMorphVertex->nVertexId] = pSrc[itMorphVertex->nVertexId] * (1-fWeight) + itMorphVertex->ptVertex * fWeight;
|
||||
// go to the next vertex in the for loop
|
||||
++nVertex;
|
||||
|
||||
if (++itMorphVertex == itMorphVertexEnd)
|
||||
{
|
||||
// finish the tail of array after the last morphed vertex
|
||||
for (; nVertex < numVertices; ++nVertex)
|
||||
pDst[nVertex] = pSrc[nVertex];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rotates the target morph vertices; transforms each vertex with the specified matrix, using only its
|
||||
// ROTATIONAL components (no translation)
|
||||
void CryGeomMorphTarget::rotate (unsigned nLOD, const Matrix44& tm)
|
||||
{
|
||||
if (nLOD >= g_nMaxLodLevels)
|
||||
return; // everything is ok, we just assume this LOD is identity
|
||||
MorphVertexArray::iterator it = m_arrVertex[nLOD].begin(), itEnd = it + m_arrVertex[nLOD].size();
|
||||
for (; it != itEnd; ++it)
|
||||
|
||||
//CHANGED_BY_IVO - INVALID CHANGE
|
||||
it->ptVertex = tm.TransformVectorOLD(it->ptVertex);
|
||||
//it->ptVertex = GetTransposed44(tm) * (it->ptVertex);
|
||||
}
|
||||
|
||||
// transforms the target morph vertices with the given matrix
|
||||
void CryGeomMorphTarget::transform (unsigned nLOD, const Matrix44& tm)
|
||||
{
|
||||
if (nLOD >= g_nMaxLodLevels)
|
||||
return; // everything is ok, we just assume this LOD is identity
|
||||
MorphVertexArray::iterator it = m_arrVertex[nLOD].begin(), itEnd = it + m_arrVertex[nLOD].size();
|
||||
for (; it != itEnd; ++it)
|
||||
it->ptVertex = tm.TransformPointOLD(it->ptVertex);
|
||||
}
|
||||
|
||||
const SMeshMorphTargetVertex* CryGeomMorphTarget::getMorphVertices (unsigned nLOD) const
|
||||
{
|
||||
if (nLOD < g_nMaxLodLevels)
|
||||
return m_arrVertex[nLOD].begin();
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned CryGeomMorphTarget::numMorphVertices (unsigned nLOD) const
|
||||
{
|
||||
if (nLOD < g_nMaxLodLevels)
|
||||
return (unsigned)m_arrVertex[nLOD].size();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CryGeomMorphTarget::setName (const char* szName, unsigned nMaxSize)
|
||||
{
|
||||
// this is for the artists to distinguish between morph targets and animations
|
||||
// this symbol is also used for speeding up CryModelAnimationContainer::Find()
|
||||
m_strName = "#" + string(szName);
|
||||
}
|
||||
size_t CryGeomMorphTarget::sizeofThis()const
|
||||
{
|
||||
unsigned nSize = sizeof(*this);
|
||||
nSize += m_strName.capacity();
|
||||
for (unsigned i = 0; i < g_nMaxLodLevels; ++i)
|
||||
nSize += sizeofArray (m_arrVertex[i]);
|
||||
return nSize;
|
||||
}
|
||||
58
CryAnimation/CryGeomMorphTarget.h
Normal file
58
CryAnimation/CryGeomMorphTarget.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#ifndef _CRY_GEOM_MORPH_TARGET_HDR_
|
||||
#define _CRY_GEOM_MORPH_TARGET_HDR_
|
||||
|
||||
// this is a container for the information about morphing a mesh
|
||||
class CryGeomMorphTarget
|
||||
{
|
||||
public:
|
||||
// create an identity morph target (doesn't morph anything)
|
||||
CryGeomMorphTarget();
|
||||
|
||||
// load the morph target from the chunk/size. Returns 0 is the chunk is completely wrong,
|
||||
// or the number of read bytes if it's ok (returns nSize if everything is fine)
|
||||
unsigned load (unsigned nLOD, const MESHMORPHTARGET_CHUNK_DESC_0001* pChunk, unsigned nSize);
|
||||
|
||||
// scales the target vertices by the given factor
|
||||
void scale (unsigned nLOD, float fScale);
|
||||
|
||||
// rotates the target morph vertices; transforms each vertex with the specified matrix, using only its
|
||||
// ROTATIONAL components (no translation)
|
||||
#if !defined(LINUX)
|
||||
__declspec(deprecated)
|
||||
#endif
|
||||
void rotate (unsigned nLOD, const Matrix44& tm);
|
||||
|
||||
// transforms the target morph vertices with the given matrix
|
||||
void transform (unsigned nLOD, const Matrix44& tm);
|
||||
|
||||
// given the source morph object, morphs it with the given weight to this morph target;
|
||||
// 1 means the morph target will replace the target with itself; 0 means it will just copy the source
|
||||
// into the destination (or leave it alone, if the two coincide)
|
||||
#if !defined(LINUX)
|
||||
__declspec(deprecated)
|
||||
#endif
|
||||
void morph (unsigned nLOD, const Vec3d* pSrc, Vec3d* pDst, unsigned numVertices, float fWeight)const;
|
||||
|
||||
// returns the name of the morph target
|
||||
const char* getNameCStr()const {return m_strName.c_str();}
|
||||
void setName (const char* szName, unsigned nMaxSize = 0xFFFFFFFF);
|
||||
|
||||
const SMeshMorphTargetVertex* getMorphVertices (unsigned nLOD) const;
|
||||
unsigned numMorphVertices (unsigned nLOD) const;
|
||||
|
||||
size_t sizeofThis()const;
|
||||
protected:
|
||||
// morphed vertices
|
||||
class MorphVertexArray: public TFixedArray<SMeshMorphTargetVertex>
|
||||
{public:
|
||||
MorphVertexArray(): TFixedArray<SMeshMorphTargetVertex>("CryGeomMorphTarget.MorphVertexArray") {}
|
||||
};
|
||||
// the max supported LODs for the morph targets
|
||||
enum {g_nMaxLodLevels = 1};
|
||||
MorphVertexArray m_arrVertex[g_nMaxLodLevels];
|
||||
|
||||
// the name
|
||||
string m_strName;
|
||||
};
|
||||
|
||||
#endif
|
||||
1187
CryAnimation/CryGeometryInfo.cpp
Normal file
1187
CryAnimation/CryGeometryInfo.cpp
Normal file
File diff suppressed because it is too large
Load Diff
341
CryAnimation/CryGeometryInfo.h
Normal file
341
CryAnimation/CryGeometryInfo.h
Normal file
@@ -0,0 +1,341 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// 08/28/2002 - Created by Sergiy Migdalskiy <sergiy@crytek.de>
|
||||
//
|
||||
// Contains:
|
||||
// Declaration of CryGeometryInfo, a structure holding geometry in the form that is to
|
||||
// the data structure of CGF
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef _CRY_GEOMETRY_INFO_HEADER_
|
||||
#define _CRY_GEOMETRY_INFO_HEADER_
|
||||
|
||||
class CIndexedMesh;
|
||||
class IStencilShadowConnectivity;
|
||||
|
||||
#include "CryVertexBinding.h"
|
||||
#include "CrySkinFull.h"
|
||||
#include "CrySkinRigidBasis.h"
|
||||
#include "CrySkinBuilderBase.h"
|
||||
#include "GeomCommon.h"
|
||||
|
||||
struct CCFMaterialGroup;
|
||||
|
||||
struct CCFIntFace;
|
||||
|
||||
// This class holds the geometry information (presumably read from a CGF file)
|
||||
// Including: vertices, faces, tangents, vertex mapping(s), bone bindings, texture coordinates
|
||||
// TODO: normal constructor/destructor,
|
||||
// maybe some loading operators,
|
||||
// normal TFixedArray<> arrays
|
||||
// normal getters/setters
|
||||
class CryGeometryInfo
|
||||
{
|
||||
public:
|
||||
CryGeometryInfo();
|
||||
~CryGeometryInfo();
|
||||
|
||||
// number of elements in the m_rDupVertToNorVert array
|
||||
//unsigned int m_nNumDupVerts;
|
||||
|
||||
void PrepareLinks (int numVertices);
|
||||
|
||||
// after constructing the skins etc. some data is not ever more used
|
||||
// this data can be cleared here
|
||||
void clearConstructionData();
|
||||
|
||||
// this is the number of vertices that are really used by the faces
|
||||
unsigned numUsedVertices()const;
|
||||
|
||||
bool hasSkin()const;
|
||||
|
||||
void GetSize (ICrySizer* pSizer)const;
|
||||
|
||||
// the primitive groups and the indices, as they appear in the vertex buffer
|
||||
std::vector<CCFMaterialGroup> m_arrPrimGroups;
|
||||
std::vector<unsigned short> m_arrIndices;
|
||||
// the triples for each face, internal indexation
|
||||
|
||||
typedef std::vector<GeomFace> FaceArray;
|
||||
FaceArray m_arrFaces;
|
||||
std::vector<GeomMtlID> m_arrFaceMtl;
|
||||
GeomMtlID getFaceMtl (unsigned i)const {return m_arrFaceMtl[i];}
|
||||
|
||||
size_t numFaces()const {return m_arrFaces.size();}
|
||||
GeomFace getFace (unsigned i)const {return m_arrFaces[i];}
|
||||
const GeomFace* getFaces ()const {return &m_arrFaces[0];}
|
||||
|
||||
// creates a fake array of faces; need to be deleted[]
|
||||
CryFace* newCryFaces()const;
|
||||
|
||||
void setNumUsedVertices(unsigned num) {m_numUsedVertices = num;}
|
||||
|
||||
void exportASC (FILE* f);
|
||||
protected:
|
||||
// connectivity object that gets pre-computed for each model once and then
|
||||
// used to extract edge topology by the stencil shadowing module
|
||||
IStencilShadowConnectivity* m_pStencilShadowConnectivity;
|
||||
|
||||
// the skinner for the vertices
|
||||
CrySkinFull m_SkinGeom, m_SkinNormal;
|
||||
|
||||
|
||||
// the skinner for the tangent bases
|
||||
CrySkinRigidBasis m_TangSkin;
|
||||
|
||||
// array of vertices
|
||||
typedef TFixedArray<Vec3> Vec3dArray;
|
||||
typedef TElementaryArray<Vec3> Vec3dElementaryArray;
|
||||
Vec3dElementaryArray m_arrVertices, m_arrNormals;
|
||||
// tangent data - it's always 4 * numVertices()
|
||||
TFixedArray<TangData> m_arrExtTangents;
|
||||
// geometry faces with indices in the m_arrVertices array
|
||||
unsigned m_numUsedVertices;
|
||||
// texture faces. This array is either empty, or of the same size as m_arrFaces
|
||||
typedef TElementaryArray<CryTexFace> TexFaceArray;
|
||||
TexFaceArray m_arrTexFaces;
|
||||
// UVs of the model. If there are texture faces, the indices in them refer to this array
|
||||
TFixedArray<CryUV> m_arrUVs;
|
||||
// UVs of the vertex buffer - external indexation
|
||||
TElementaryArray<CryUV> m_arrExtUVs;
|
||||
|
||||
// internal indexation vertex colors
|
||||
std::vector<DWORD> m_arrVColors;
|
||||
|
||||
// array of vertices: for each vertex, there is an array of bone links
|
||||
// there may be 0 or numVertices() links
|
||||
typedef TElementaryArray<CryVertexBinding> VertexBindingArray;
|
||||
VertexBindingArray m_arrLinks;
|
||||
|
||||
// array - mapping from the "duplicated" vertices (that the renderer generates) to the "normal" vertices (that getVertices() returns)
|
||||
TFixedArray<unsigned> m_arrExtToIntMap;
|
||||
|
||||
// back mapping (inverse of ExtToInt)
|
||||
TElementaryArray<unsigned> m_arrIntToExtMap;
|
||||
|
||||
// format of the vertex buffer in system memory now (m_arrVertBuf)
|
||||
int m_nVertBufFormat;
|
||||
TElementaryArray<char> m_arrVertBuf;
|
||||
public:
|
||||
// returns the vertex buffer of the given format; in this buffer, UVs will
|
||||
// already be set up properly and extra fields like color will be NULLed
|
||||
char* getVertBuf (int nVertFormat);
|
||||
/*
|
||||
void selfValidate()
|
||||
#ifndef _DEBUG
|
||||
{}
|
||||
#endif
|
||||
;
|
||||
*/
|
||||
|
||||
// returns the cached connectivity object for stencil shadows
|
||||
class IStencilShadowConnectivity* getStencilShadowConnectivity(const std::vector<MAT_ENTITY>&arrMaterials);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// builds the connectivity object for stencil shadows
|
||||
// PARAMETERS:
|
||||
// iEdgeBuilder - the builder to use to create the connectivity info
|
||||
// pbCastShadow - the array of flags, 1 flag per 1 material, if the flag is true, then this material casts shadow, otherwise not
|
||||
// numMaterials - number of items in the pbCastShadow array
|
||||
void buildStencilShadowConnectivity (const std::vector<MAT_ENTITY>&arrMaterials); //
|
||||
|
||||
void OutputOrphanedEdgeWarning(class IEdgeConnectivityBuilder *iEdgeBuilder, const std::vector<MAT_ENTITY>&arrMaterials);
|
||||
|
||||
// deserializes the stencil shadow connectivity object
|
||||
void setStencilShadowConnectivity (IStencilShadowConnectivity* pConnectivity);
|
||||
|
||||
bool hasGeomSkin()const;
|
||||
class CrySkinFull* getGeomSkin();
|
||||
class CrySkinFull* getNormalSkin();
|
||||
void buildGeomSkins(const class CryBoneInfo* pBoneInfo, unsigned numBoneInfos);
|
||||
bool loadVertexSkin (const void* pData, unsigned nSize);
|
||||
bool loadNormalSkin (const void* pData, unsigned nSize);
|
||||
bool loadTangSkin (const void* pData, unsigned nSize);
|
||||
|
||||
// the tangent bases are calculated in pieces, each piece calculating some number of vertices
|
||||
// the piece
|
||||
CrySkinRigidBasis* getTangSkin();
|
||||
|
||||
// builds the tangent base skins
|
||||
void buildTangSkins (const class CryBoneInfo* pBoneInfo, unsigned numBoneInfos);
|
||||
|
||||
// sorts the faces by the material indices.
|
||||
// in the given array, sets elements corresponding to used materials to true; doesn't touch the other elements
|
||||
// (i.e. the array elements must be set to false before calling this function)
|
||||
// IMPORTANT: Keeps the original order of faces in each material group
|
||||
void sortFacesByMaterial (std::vector<bool>& arrUsedMtls);
|
||||
|
||||
// creates an instance of the CIndexedMesh with the info from this geometry info.
|
||||
// this is needed to create the leaf buffers (while GenerateRenderArrays() execution)
|
||||
CIndexedMesh* new3DEngineIndexedMesh()const;
|
||||
|
||||
// Allocates the array that keeps the vertices
|
||||
void PrepareVertices (unsigned numVertices);
|
||||
// Allocates the face array
|
||||
void MakeIntFaces(const CryFace* pCryFaces, unsigned numFaces);
|
||||
// Allocattes the texture coordinate array
|
||||
void PrepareUVs (int numUVs);
|
||||
// Allocates texture face array
|
||||
void PrepareTexFaces (int numTexFaces);
|
||||
// Allocates the external to internal map entries
|
||||
void PrepareExtToIntMap (int numExtVertices);
|
||||
// Create the Internal-to-external map
|
||||
void CreateIntToExtMap ();
|
||||
|
||||
// loads the geometry from the chunk
|
||||
// returns the number of bytes read from the chunk (normally must be the whole chunk size; 0 means failure)
|
||||
unsigned Load (unsigned nLOD, const MESH_CHUNK_DESC_0744* pChunk, unsigned nChunkSize);
|
||||
|
||||
// optimize the vertex order for skinning
|
||||
void sortVertices();
|
||||
|
||||
// scales the model. Multiplies all vertex coordinates by the given factor
|
||||
void Scale (float fScale);
|
||||
|
||||
// rotates the model; transforms each vector with the specified matrix, using only its
|
||||
// ROTATIONAL components (no translation)
|
||||
void rotate (const Matrix44& tm);
|
||||
|
||||
// transforms the model with the given transform matrix
|
||||
void transform (const Matrix44& tm);
|
||||
|
||||
// Checks the face indices, if they are in range (0..numVertices())
|
||||
// returns true upon successful validation, false means there are indices out of range
|
||||
bool ValidateFaceIndices ();
|
||||
|
||||
// Checks the texture face indices, if they are in range (0..numUVs())
|
||||
// returns true upon successful validation, false means there are indices out of range
|
||||
bool ValidateTexFaceIndices ();
|
||||
|
||||
// calculates the bounding box for the object
|
||||
// if the bbox can't be calculated (e.g. the skin hasn't been yet loaded), returns false
|
||||
bool computeBBox (CryAABB& bb);
|
||||
|
||||
// forces the material indices to be in the range [0..numMaterials]
|
||||
void limitMaterialIDs(unsigned numMaterials);
|
||||
|
||||
// recalculates all the normals, assuming smooth geometry (non-smooth vertices were already split in the exporter)
|
||||
void recalculateNormals();
|
||||
|
||||
// remaps the ids of the bones in the CryLink structures
|
||||
void remapBoneIds (const unsigned* arrBoneIdMap, unsigned numBoneIds);
|
||||
|
||||
|
||||
// remaps the ids of materials. arrMtlIdMap[nOldMtlId] == nNewMtlId
|
||||
void remapMtlIds (const unsigned* arrMtlIdMap, unsigned numMtlIds);
|
||||
|
||||
// returns true if the geometry is degraded (e.g. no vertices or no faces)
|
||||
bool empty();
|
||||
|
||||
// creates the array of UVs that can be directly copied into videobuffer (no index mapping required, external indexation)
|
||||
// this relies on the pExtToInt mapping for the number of entries
|
||||
void initExtUVs (const class CVertexBufferUVArrayDriver& pUVs);
|
||||
|
||||
// creates and copies the data into the new face array
|
||||
void initFaces (unsigned numFaces, const CCFIntFace* pFaces, const void* pMtls);
|
||||
|
||||
// creates and initializes the array of extern UVs
|
||||
void initExtUVs (const CryUV* pUVs, unsigned numUVs);
|
||||
|
||||
// initializes the ext-to-int map
|
||||
void initExtToIntMap (const unsigned short* pExtToIntMap, unsigned numEntries);
|
||||
|
||||
|
||||
// after strip-generation of the mesh, rearranges the geometry info to match the sequence of vertices
|
||||
// in the stripified mesh
|
||||
void remapVerticesToExt ();
|
||||
|
||||
// creates (allocates with new[]) an array of bindings for each normal
|
||||
// there are numVertices() elements in this array
|
||||
CryVertexBinding* newNormalLinks (const CryBoneInfo* pBoneInfo);
|
||||
|
||||
// Getter methods for vertices
|
||||
DECLARE_ELEMENTARY_ARRAY_GETTER_METHODS(Vec3, Vertex, Vertices, m_arrVertices)
|
||||
unsigned numVertices() const {return m_numUsedVertices;}
|
||||
DECLARE_ELEMENTARY_ARRAY_GETTER_METHODS(Vec3, Normal, Normals, m_arrNormals)
|
||||
unsigned numNormals() const {return m_arrNormals.empty()?0:m_numUsedVertices;}
|
||||
// Getter methods for tangent basis
|
||||
DECLARE_ARRAY_GETTER_METHODS(TangData, ExtTangent, ExtTangents, m_arrExtTangents)
|
||||
// Getter methods for faces
|
||||
DECLARE_ELEMENTARY_ARRAY_GETTER_METHODS(CryTexFace, TexFace, TexFaces, m_arrTexFaces)
|
||||
unsigned numTexFaces() const;
|
||||
// destroys the texture faces
|
||||
void removeTexFaces();
|
||||
// Getter methods for the UVs
|
||||
DECLARE_ARRAY_GETTER_METHODS(CryUV, UV, UVs, m_arrUVs);
|
||||
// destroys the UV array
|
||||
void removeUVs();
|
||||
|
||||
// Getter methods for the external indexed UVs
|
||||
DECLARE_ELEMENTARY_ARRAY_GETTER_METHODS(CryUV, ExtUV, ExtUVs, m_arrExtUVs);
|
||||
unsigned numExtUVs() const {return m_arrExtUVs.empty()?0:numExtToIntMapEntries();}
|
||||
unsigned numExtVertices() const {return numExtToIntMapEntries();}
|
||||
|
||||
// Getter methods for the links
|
||||
DECLARE_ELEMENTARY_ARRAY_GETTER_METHODS(CryVertexBinding, Link, Links, m_arrLinks);
|
||||
unsigned numLinks() const;
|
||||
|
||||
// Getter/setter methods for the dup->nor mapping
|
||||
DECLARE_ARRAY_GETTER_METHODS(unsigned, ExtToIntMapEntry, ExtToIntMapEntries, m_arrExtToIntMap);
|
||||
// Getter/setter methods for the nor->dup mapping
|
||||
//unsigned numIntToExtMapEntries()const {return numVertices();}
|
||||
//DECLARE_ELEMENTARY_ARRAY_GETTER_METHODS(unsigned, IntToExtMapEntry, IntToExtMapEntries, m_arrIntToExtMap);
|
||||
};
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Declaration and implementation of skin sources, implementations
|
||||
// of an interface used to supply skin builder with vertex info.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// this class will just pass the number of vertices and links from the geometry through
|
||||
class CrySkinVertexSource: public ICrySkinSource
|
||||
{
|
||||
public:
|
||||
CrySkinVertexSource (CryGeometryInfo* pGeometry):
|
||||
ICrySkinSource (
|
||||
pGeometry->getLinks(),
|
||||
pGeometry->numLinks(),
|
||||
pGeometry->getVertices(),
|
||||
pGeometry->numVertices(),
|
||||
pGeometry->getExtTangents(),
|
||||
pGeometry->numExtTangents(),
|
||||
pGeometry->getExtToIntMapEntries()
|
||||
)
|
||||
{}
|
||||
|
||||
~CrySkinVertexSource ()
|
||||
{ /* no need to deallocate any stuff*/ }
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// this class will create and hold the links for actual normals
|
||||
class CrySkinNormalSource: public ICrySkinSource
|
||||
{
|
||||
public:
|
||||
CrySkinNormalSource (CryGeometryInfo* pGeometry, const CryBoneInfo* pBoneInfo):
|
||||
ICrySkinSource (
|
||||
pGeometry->newNormalLinks(pBoneInfo),
|
||||
pGeometry->numLinks(),
|
||||
pGeometry->getVertices(),
|
||||
pGeometry->numVertices(),
|
||||
pGeometry->getExtTangents(),
|
||||
pGeometry->numExtTangents(),
|
||||
pGeometry->getExtToIntMapEntries()
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
~CrySkinNormalSource()
|
||||
{
|
||||
delete [] m_pLinks;
|
||||
m_pLinks = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
79
CryAnimation/CryKeyInterpolation.cpp
Normal file
79
CryAnimation/CryKeyInterpolation.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
// Actually, the following files might have been made static methods of corresponding
|
||||
// key structures; but the key structures are declared in a common interface file for now.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "CryKeyInterpolation.h"
|
||||
|
||||
|
||||
// interpolates the given key linearly out of the given left and right keys, given the time
|
||||
void InterpolateCryBoneKey (const CryBoneKey& keyLeft, const CryBoneKey& keyRight, int nTime, CryBoneKey& keyOutput)
|
||||
{
|
||||
keyOutput.time = nTime;
|
||||
float fTime = float (nTime-keyLeft.time) / (keyRight.time-keyLeft.time);
|
||||
keyOutput.relpos = keyLeft.relpos + (keyRight.relpos - keyLeft.relpos) * fTime;
|
||||
keyOutput.relquat = Slerp (keyLeft.relquat, keyRight.relquat, fTime);
|
||||
keyOutput.relquat.Normalize();
|
||||
}
|
||||
|
||||
// check whether the difference between the two keys is within the specified bounds:
|
||||
// the Dot product of the quaternions (1==none), and the difference between positions (0=none)
|
||||
// NOTE: the position delta is defined as a square
|
||||
bool IsErrorSmall (const CryBoneKey& key1, const CryBoneKey& key2, float fMaxPosDelta2, float fMinQuatDot)
|
||||
{
|
||||
return GetLengthSquared((key1.relpos - key2.relpos)) < fMaxPosDelta2
|
||||
&& fabs(key1.relquat|(key2.relquat)) > fMinQuatDot;
|
||||
}
|
||||
|
||||
|
||||
// Reduces keyframes that can be interpolated by surrounding keys.
|
||||
// The error metric is max[1-|QDot|] max[]
|
||||
// Returns the count of optimized keys, and the reduced keys themselves in the passed in array.
|
||||
unsigned OptimizeKeys (CryBoneKey* pBoneKeys, unsigned nNumKeys, float fPosError, float fQuatError)
|
||||
{
|
||||
// for each starting interval key
|
||||
if (nNumKeys > 2)
|
||||
for (unsigned i = 0; i < nNumKeys-2; ++i)
|
||||
{
|
||||
CryBoneKey& keyLeft = pBoneKeys[i];
|
||||
unsigned nBest = i + 1; // best interval end
|
||||
// for each ending interval key
|
||||
for (unsigned j = i+2; j < nNumKeys; ++j)
|
||||
{
|
||||
CryBoneKey& keyRight = pBoneKeys[j];
|
||||
|
||||
nBest = j; // starting with empty interval (no keys to reduce)
|
||||
|
||||
// for each key inside the interval
|
||||
for (unsigned k = i+1; k < j; ++k)
|
||||
{
|
||||
CryBoneKey& keyMiddle = pBoneKeys[k];
|
||||
CryBoneKey keyInterp;
|
||||
InterpolateCryBoneKey(keyLeft, keyRight, keyMiddle.time, keyInterp);
|
||||
if (!IsErrorSmall(keyMiddle, keyInterp, fPosError, fQuatError))
|
||||
{
|
||||
nBest = j - 1; // this interval is bad, but the previous one was good
|
||||
j = nNumKeys; // stop iterating
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nBest > i+1)
|
||||
{
|
||||
memmove (&pBoneKeys[i+1], &pBoneKeys[nBest], (nNumKeys-nBest)*sizeof(CryBoneKey));
|
||||
nNumKeys -= nBest - i - 1;
|
||||
}
|
||||
}
|
||||
|
||||
while (nNumKeys > 1 && IsErrorSmall(pBoneKeys[nNumKeys-1], pBoneKeys[nNumKeys-2], fPosError, fQuatError))
|
||||
{
|
||||
nNumKeys--;
|
||||
}
|
||||
|
||||
while (nNumKeys > 1 && IsErrorSmall(pBoneKeys[0], pBoneKeys[1], fPosError, fQuatError))
|
||||
{
|
||||
nNumKeys--;
|
||||
memmove (pBoneKeys, pBoneKeys+1, nNumKeys*sizeof(CryBoneKey));
|
||||
}
|
||||
|
||||
return nNumKeys;
|
||||
}
|
||||
28
CryAnimation/CryKeyInterpolation.h
Normal file
28
CryAnimation/CryKeyInterpolation.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Sergiy Migdalskiy
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// This is the declaration of key interpolation and optimization functions
|
||||
// This may be used in the engine and in the export plugin
|
||||
// CryBoneKey is assumed to be already declared when you include this header.
|
||||
|
||||
#ifndef _CRY_KEY_INTERPOLATION_HEADER_
|
||||
#define _CRY_KEY_INTERPOLATION_HEADER_
|
||||
|
||||
// interpolates the given key linearly out of the given left and right keys, given the time
|
||||
extern void InterpolateCryBoneKey (const CryBoneKey& keyLeft, const CryBoneKey& keyRight, int nTime, CryBoneKey& keyOutput);
|
||||
|
||||
// check whether the difference between the two keys is within the specified bounds
|
||||
extern bool IsErrorSmall (const CryBoneKey& key1, const CryBoneKey& key2, float fMaxPosDelta2, float fMinQuatDot);
|
||||
|
||||
// Reduces keyframes that can be interpolated by surrounding keys.
|
||||
extern unsigned OptimizeKeys (CryBoneKey* pBoneKeys, unsigned nNumKeys, float fPosError = 1e-6f, float fQuatError = 0.9999f);
|
||||
|
||||
|
||||
#endif
|
||||
587
CryAnimation/CryModEffAnimation.cpp
Normal file
587
CryAnimation/CryModEffAnimation.cpp
Normal file
@@ -0,0 +1,587 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Taken over by Sergiy Migdalskiy (no previous history record here)
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
//#include "CryAnimation.h"
|
||||
#include "CryBone.h"
|
||||
#include "CryModelState.h"
|
||||
#include "CryModEffector.h"
|
||||
#include "CryModEffAnimation.h"
|
||||
#include "CryModel.h"
|
||||
#include "CVars.h"
|
||||
|
||||
CCryModEffAnimation::CCryModEffAnimation (CryModelState* pParent):
|
||||
// m_bMatrixPlusInUse(false),
|
||||
m_fAnimTime (0.0f),
|
||||
m_nAnimId (-1),
|
||||
m_fBlendInTime (0.0f),
|
||||
m_fBlendOutTime (0.0f),
|
||||
m_fOrigBlendOutTime (0.0f),
|
||||
m_pParent (pParent),
|
||||
m_fStopTime (0)
|
||||
{
|
||||
m_uFlags.bLoop = 0;
|
||||
m_arrFadeAnims.reserve(2);
|
||||
}
|
||||
|
||||
bool CCryModEffAnimation::IsStopped()
|
||||
{
|
||||
return (m_nAnimId < 0) && m_arrFadeAnims.empty();
|
||||
}
|
||||
|
||||
// does this animation effector actively plays some animations?
|
||||
bool CCryModEffAnimation::isActive ()
|
||||
{
|
||||
return m_nAnimId >= 0;
|
||||
}
|
||||
|
||||
|
||||
// this calls all the necessary callbacks
|
||||
void CCryModEffAnimation::OnTimeChanged (int nAnimId, float fPrevTime, float fAnimTime,float fSpeed)
|
||||
{
|
||||
if (g_GetCVars()->ca_DisableAnimEvents())
|
||||
return;
|
||||
|
||||
ICharInstanceSink * pSink = m_pParent->getAnimationEventSink(nAnimId);
|
||||
const AnimData& rAnim = m_pParent->getAnimationSet()->getAnimation(nAnimId);
|
||||
|
||||
typedef CryModelState::AnimEventArray AnimEventArray;
|
||||
const AnimEventArray& arrEvents = m_pParent->getAnimEvents(nAnimId);
|
||||
// TODO: make binary search or iterator
|
||||
if(pSink && !arrEvents.empty() && fPrevTime != fAnimTime)
|
||||
{
|
||||
bool bSecondPass = false;
|
||||
float fSecondPassEnd;
|
||||
|
||||
if (m_uFlags.bLoop
|
||||
&& rAnim.fStart < rAnim.fStop
|
||||
&& (fSpeed >= 0 ? fAnimTime > rAnim.fStop : fAnimTime < rAnim.fStart)
|
||||
)
|
||||
{
|
||||
bSecondPass = true;
|
||||
if (fSpeed >= 0)
|
||||
{
|
||||
fSecondPassEnd = rAnim.fStart + cry_fmod(fAnimTime - rAnim.fStart, rAnim.getLength());
|
||||
fAnimTime = rAnim.fStop;
|
||||
}
|
||||
else
|
||||
{
|
||||
fSecondPassEnd = rAnim.fStop - cry_fmod(rAnim.fStop - fAnimTime, rAnim.getLength());
|
||||
fAnimTime = rAnim.fStart;
|
||||
}
|
||||
}
|
||||
|
||||
AnimEventArray::const_iterator it;
|
||||
for(it = arrEvents.begin(); it != arrEvents.end(); ++it)
|
||||
{
|
||||
float fTriggerTime = it->fTime;
|
||||
// this condition takes into account both variants, when the previous and current event are forward- or backward-playing
|
||||
if ( (fPrevTime <= fTriggerTime && fTriggerTime < fAnimTime)
|
||||
|| (fPrevTime >= fTriggerTime && fTriggerTime > fAnimTime))
|
||||
{
|
||||
DEFINE_PROFILER_SECTION("AllCallbacks");
|
||||
pSink->OnAnimationEvent (rAnim.strName.c_str(), it->UserData);
|
||||
if(g_GetCVars()->ca_Debug())
|
||||
g_GetLog()->Log("\004 %p->OnAnimationEvent (\"%s\",frame:%d,time:%g, trigger time: %g, time change: %g to %g)", m_pParent, rAnim.strName.c_str(), g_GetIRenderer()->GetFrameID(), fAnimTime, fTriggerTime, fPrevTime, fAnimTime);
|
||||
}
|
||||
}
|
||||
|
||||
if (bSecondPass)
|
||||
{
|
||||
fAnimTime = fSecondPassEnd;
|
||||
fPrevTime = fSpeed >= 0 ? rAnim.fStart : rAnim.fStop;
|
||||
|
||||
for(it = arrEvents.begin(); it != arrEvents.end(); ++it)
|
||||
{
|
||||
float fTriggerTime = it->fTime;
|
||||
if ( (fPrevTime <= fTriggerTime && fTriggerTime < fAnimTime)
|
||||
|| (fPrevTime >= fTriggerTime && fTriggerTime > fAnimTime))
|
||||
{
|
||||
DEFINE_PROFILER_SECTION("AllCallbacks");
|
||||
pSink->OnAnimationEvent (rAnim.strName.c_str(), it->UserData);
|
||||
if(g_GetCVars()->ca_Debug())
|
||||
g_GetLog()->Log("\004 %p->OnAnimationEvent(2nd) (\"%s\",frame:%d,time:%g, trigger time: %g, time change: %g to %g)", m_pParent, rAnim.strName.c_str(), g_GetIRenderer()->GetFrameID(), fAnimTime, fTriggerTime, fPrevTime, fAnimTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// returns fVal/fBase, clamped between 0 and 1
|
||||
float blendValue (float fVal, float fBase)
|
||||
{
|
||||
if (!(fVal > 0)) // this will also account for (impossible here) nans..
|
||||
return 0;
|
||||
else
|
||||
if (!(fVal < fBase))
|
||||
return 1;
|
||||
else
|
||||
return fVal/fBase;
|
||||
}
|
||||
|
||||
|
||||
void CCryModEffAnimation::SetNoLoop()
|
||||
{
|
||||
m_uFlags.bLoop = 0;
|
||||
}
|
||||
|
||||
void CCryModEffAnimation::SetNoLoopNoBlendOut()
|
||||
{
|
||||
m_fBlendOutTime = 0;
|
||||
m_uFlags.bLoop = 0;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Adds the current time of the animation
|
||||
// Loops the animation if necessary
|
||||
// Triggers all the necessary events (end animation and alike)
|
||||
// Processes blending
|
||||
// Returns the current animation time as it should be passed to the controller
|
||||
unsigned CCryModEffAnimation::Tick(
|
||||
float fDeltaTime,
|
||||
const std::vector<ICharInstanceSink *>& arrSinks,
|
||||
CAnimationLayerInfoArray& arrOutLayers
|
||||
)
|
||||
{
|
||||
//Validator validator(this);
|
||||
if(m_nAnimId < 0)
|
||||
return TickFadingAnims(fDeltaTime,1,arrOutLayers);
|
||||
|
||||
CryModelAnimationContainer* pAnimations = m_pParent->getAnimationSet();
|
||||
|
||||
pAnimations->OnAnimationTick (m_nAnimId);
|
||||
const AnimData& rCurrentAnimation = pAnimations->getAnimation(m_nAnimId);
|
||||
|
||||
// First animation
|
||||
|
||||
float fStart = m_fStartTime;
|
||||
float fStop = m_fStopTime;
|
||||
|
||||
ICharInstanceSink * pCharInstanceSink = m_pParent->getAnimationEventSink(m_nAnimId);
|
||||
|
||||
if (!m_uFlags.bLoop && m_fAnimTime >= fStop + m_fBlendOutTime && fDeltaTime > 0)
|
||||
{
|
||||
// we have already applied the last frame of the last animation, so stop
|
||||
stop();
|
||||
return TickFadingAnims(fDeltaTime,1,arrOutLayers); // no animation
|
||||
}
|
||||
|
||||
float fPrevTime = m_fAnimTime;
|
||||
m_fAnimTime += fDeltaTime;
|
||||
|
||||
OnTimeChanged (m_nAnimId, fPrevTime, m_fAnimTime, fDeltaTime);
|
||||
|
||||
float fCurrentTime;
|
||||
|
||||
if(m_fAnimTime > fStop || m_fAnimTime < fStart)
|
||||
{
|
||||
if(m_uFlags.bLoop)
|
||||
{
|
||||
if (fStop > fStart)
|
||||
{
|
||||
if (fDeltaTime >= 0)
|
||||
m_fAnimTime = fStart + cry_fmod(m_fAnimTime - fStart, fStop-fStart);
|
||||
else
|
||||
m_fAnimTime = fStop - cry_fmod(fStop - m_fAnimTime, fStop-fStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fDeltaTime >= 0)
|
||||
m_fAnimTime = rCurrentAnimation.fStop;
|
||||
else
|
||||
m_fAnimTime = rCurrentAnimation.fStart;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_GetCVars()->ca_Debug())
|
||||
g_GetLog()->Log ("\003 %p->OnEndAnimation (%s)", m_pParent, rCurrentAnimation.strName.c_str());
|
||||
// The animation time intersected the stop time.
|
||||
// NOTE: this callback is not called a few times, if there were a few intersection (e.g. per frame)
|
||||
// because of a few cycles of animation passed.
|
||||
if(pCharInstanceSink)
|
||||
{
|
||||
DEFINE_PROFILER_SECTION("AllCallbacks");
|
||||
pCharInstanceSink->OnEndAnimation (rCurrentAnimation.strName.c_str());
|
||||
}
|
||||
|
||||
if (!m_uFlags.bLoop)
|
||||
{
|
||||
if (g_GetCVars()->ca_TickVersion())
|
||||
{
|
||||
m_fAnimTime = fPrevTime;
|
||||
stop();
|
||||
return TickFadingAnims(fDeltaTime,1,arrOutLayers);
|
||||
}
|
||||
else
|
||||
{
|
||||
fCurrentTime = fDeltaTime >= 0 ? fStop : fStart;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fCurrentTime = m_fAnimTime;
|
||||
|
||||
// in the looped animation, we don't have to have blend out time.
|
||||
// When the animation is looped, it ends only when another animation starts,
|
||||
// which will take care of blending this animation out (the new animation
|
||||
// will blend itself in)
|
||||
m_fBlendOutTime = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
fCurrentTime = m_fAnimTime;
|
||||
|
||||
// what weight to use to blend the bone to the orientation/position dictated by the controller?
|
||||
// by default, it's 1, meaning fully replace the O/P of the bone with the one from the controller
|
||||
float fBlendWeight = 1;
|
||||
|
||||
// Compute the current blend factor
|
||||
assert (m_fBlendInTime >= m_fBlendInCountdown);
|
||||
if (m_fBlendInCountdown > 0)
|
||||
{
|
||||
m_fBlendInCountdown -= (float)fabs(fDeltaTime);
|
||||
if (m_fBlendInCountdown < 0)
|
||||
m_fBlendInCountdown = 0;
|
||||
|
||||
fBlendWeight = blendValue(m_fBlendInTime - m_fBlendInCountdown, m_fBlendInTime);
|
||||
if (!m_uFlags.bLoop)
|
||||
{
|
||||
if (fDeltaTime >= 0)
|
||||
{
|
||||
if (!(m_fAnimTime < fStop))
|
||||
m_fAnimTime = fStop;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(m_fAnimTime > fStart))
|
||||
m_fAnimTime = fStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if (m_fBlendOutTime > 0)
|
||||
{
|
||||
if (fDeltaTime >= 0)
|
||||
{
|
||||
if (m_fAnimTime > fStop)
|
||||
fBlendWeight = blendValue((fStop + m_fBlendOutTime) - m_fAnimTime, m_fBlendOutTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_fAnimTime < fStart)
|
||||
fBlendWeight = blendValue(m_fAnimTime - (fStart - m_fBlendOutTime), m_fBlendOutTime);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned numFadingAnimations = 0;
|
||||
|
||||
if (g_GetCVars()->ca_TickVersion() >= 2)
|
||||
{
|
||||
if (fBlendWeight >= 0.99f)
|
||||
m_arrFadeAnims.clear();
|
||||
else
|
||||
numFadingAnimations = TickFadingAnims(fDeltaTime, 1-fBlendWeight, arrOutLayers, m_fBlendInCountdown == 0);
|
||||
}
|
||||
else
|
||||
numFadingAnimations = TickFadingAnims(fDeltaTime, 1-fBlendWeight, arrOutLayers);
|
||||
|
||||
//m_fAnimTime = fCurrentTime;
|
||||
arrOutLayers.push_back(CAnimationLayerInfo(m_nAnimId, fCurrentTime*getTicksPerSecond(), fBlendWeight));
|
||||
|
||||
return 1 + numFadingAnimations;
|
||||
}
|
||||
|
||||
|
||||
float CCryModEffAnimation::getBlending()const
|
||||
{
|
||||
if (m_fBlendInCountdown > 0)
|
||||
return (m_fBlendInTime - m_fBlendInCountdown) / m_fBlendInTime;
|
||||
else
|
||||
if (m_fBlendOutTime > 0 && m_fAnimTime > m_fStopTime)
|
||||
return (m_fStopTime + m_fBlendOutTime - m_fAnimTime) / m_fBlendOutTime;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Clocks all fading subanimations and adds the animation layers
|
||||
// deletes expired fading animations
|
||||
unsigned CCryModEffAnimation::TickFadingAnims (float fDelta, float fWeightLeft, CAnimationLayerInfoArray& arrLayers, bool bFade)
|
||||
{
|
||||
g_arrLocalLayers.clear();
|
||||
|
||||
FadingAnimArray::iterator it;
|
||||
for (it = m_arrFadeAnims.begin(); it != m_arrFadeAnims.end() && fWeightLeft > 0;)
|
||||
{
|
||||
if (it->Tick (fDelta, bFade))
|
||||
{
|
||||
if (it->fBlending >= fWeightLeft)
|
||||
g_arrLocalLayers.push_back(CAnimationLayerInfo(it->nAnimId, it->fTime*getTicksPerSecond(), 1));
|
||||
else
|
||||
g_arrLocalLayers.push_back(CAnimationLayerInfo(it->nAnimId, it->fTime*getTicksPerSecond(), blendValue(it->fBlending , fWeightLeft)));
|
||||
fWeightLeft -= it->fBlending;
|
||||
++it;
|
||||
}
|
||||
else
|
||||
it = m_arrFadeAnims.erase(it);
|
||||
}
|
||||
|
||||
if (!m_arrFadeAnims.empty() && it != m_arrFadeAnims.end())
|
||||
m_arrFadeAnims.erase(it, m_arrFadeAnims.end());
|
||||
|
||||
for (int i = (int)g_arrLocalLayers.size() - 1; i >= 0; --i)
|
||||
arrLayers.push_back(g_arrLocalLayers[i]);
|
||||
|
||||
return (int)m_arrFadeAnims.size();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// returns the number of any animation currently being played
|
||||
// this can be the current animation, including animation of fadeout, if the
|
||||
// layer doesn't have the current animation played, but has an animation fading out
|
||||
int CCryModEffAnimation::GetAnyCurrentAnimation()const
|
||||
{
|
||||
if (m_nAnimId < 0)
|
||||
{
|
||||
for (unsigned i = 0; i < m_arrFadeAnims.size(); ++i)
|
||||
if (m_arrFadeAnims[i].nAnimId >= 0)
|
||||
return m_arrFadeAnims[i].nAnimId;
|
||||
return -1; // no animations found at all
|
||||
}
|
||||
else
|
||||
return m_nAnimId;
|
||||
}
|
||||
|
||||
|
||||
float CCryModEffAnimation::GetPhase()const
|
||||
{
|
||||
if (m_nAnimId >= 0)
|
||||
{
|
||||
CryModelAnimationContainer* pAnimations = m_pParent->getAnimationSet();
|
||||
const AnimData& anim = pAnimations->getAnimation(m_nAnimId);
|
||||
if (anim.bLoop && anim.fStart < anim.fStop)
|
||||
// only if the old animation is looped, and the new animation is looped, we use the phase
|
||||
return (m_fAnimTime - anim.fStart) / anim.getLength();
|
||||
else
|
||||
return 0; // there's no phase in non-looped (or 0-length) animation by the current definition
|
||||
}
|
||||
else
|
||||
return 0;// by default, there's no phase for non-existing non-playing animation
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Starts new animation
|
||||
// Blends with the existing pose of the bone if necessary
|
||||
void CCryModEffAnimation::StartAnimation (unsigned nAnimID, float fBlendInTime, float fBlendOutTime, CCryModEffAnimation* pSynchronizeWith, float fSpeed, unsigned nStartAnimFlags)
|
||||
{
|
||||
if (nAnimID < 0)
|
||||
{
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
m_nStartAnimFlags = nStartAnimFlags;
|
||||
CryModelAnimationContainer* pAnimations = m_pParent->getAnimationSet();
|
||||
pAnimations->OnAnimationStart (nAnimID);
|
||||
// the animation might have changed
|
||||
const AnimData &anim = pAnimations->getAnimation(nAnimID);
|
||||
|
||||
// ignore multiple requests to start the same animation unless the animation has already been played long enough (50% of the time)
|
||||
if (nAnimID == m_nAnimId)
|
||||
{
|
||||
if (g_GetCVars()->ca_RestartBehaviour() == 0)
|
||||
{
|
||||
if (fSpeed >= 0 ? m_fAnimTime < (anim.fStart+anim.fStop)/2 : m_fAnimTime > (anim.fStart+anim.fStop)/2)
|
||||
return;
|
||||
}
|
||||
else // with behaviour 1 always restart
|
||||
return;
|
||||
}
|
||||
|
||||
// the phase of the old animation, 0..1, used to achieve smoother blending if the animations are
|
||||
// similar loops (only if they're both loops)
|
||||
float fOldAnimationPhase = pSynchronizeWith->GetPhase();
|
||||
|
||||
// call OnEnd for animation we drop
|
||||
if (m_nAnimId >= 0)
|
||||
{
|
||||
if (g_GetCVars()->ca_Debug())
|
||||
{
|
||||
const AnimData& animOld = pAnimations->getAnimation(m_nAnimId);
|
||||
g_GetLog()->LogToFile ("\005Overriding existing animation #%u \"%s\" [%f..%f] at time %f", m_nAnimId, animOld.strName.c_str(), animOld.fStart, animOld.fStop, m_fAnimTime);
|
||||
}
|
||||
|
||||
ICharInstanceSink * pCharInstanceSink = m_pParent->getAnimationEventSink(m_nAnimId);
|
||||
if (pCharInstanceSink)
|
||||
{
|
||||
DEFINE_PROFILER_SECTION("AllCallbacks");
|
||||
const char* szAnimName = pAnimations->getAnimation(m_nAnimId).strName.c_str();
|
||||
if (g_GetCVars()->ca_Debug())
|
||||
g_GetLog()->Log ("\003 %p->OnEndAnimation (%s)", m_pParent, szAnimName);
|
||||
pCharInstanceSink->OnEndAnimation (szAnimName);
|
||||
}
|
||||
}
|
||||
|
||||
this->stop();
|
||||
|
||||
m_fStartTime = anim.fStart;
|
||||
m_fStopTime = anim.fStop;
|
||||
m_uFlags.bLoop = anim.bLoop ? 1 : 0;
|
||||
|
||||
// set new animation
|
||||
m_nAnimId = nAnimID;
|
||||
|
||||
m_fAnimTime = anim.bLoop ? anim.fStart + fOldAnimationPhase * anim.getLength() : fSpeed > 0 ? m_fStartTime : m_fStopTime;
|
||||
|
||||
// start blending from old to new if blending allowed
|
||||
m_fBlendInCountdown = m_fBlendInTime = fBlendInTime;
|
||||
m_fOrigBlendOutTime = m_fBlendOutTime = fBlendOutTime;
|
||||
|
||||
ICharInstanceSink* pSink = m_pParent->getAnimationEventSink(nAnimID);
|
||||
|
||||
if (pSink)
|
||||
{
|
||||
DEFINE_PROFILER_SECTION("AllCallbacks");
|
||||
const char *szAnimName = anim.strName.c_str();
|
||||
if (g_GetCVars()->ca_Debug())
|
||||
g_GetLog()->Log ("\003 %p->OnStartAnimation (%s)", m_pParent, szAnimName);
|
||||
pSink->OnStartAnimation (szAnimName);
|
||||
}
|
||||
}
|
||||
|
||||
void CCryModEffAnimation::stop ()
|
||||
{
|
||||
if (m_nAnimId < 0)
|
||||
return;
|
||||
|
||||
if (m_fOrigBlendOutTime > 0)
|
||||
{
|
||||
FadingAnim anim;
|
||||
anim.fBlending = getBlending();
|
||||
anim.fBlendOutSpeed = 1/m_fOrigBlendOutTime;
|
||||
anim.fTime = m_fAnimTime;
|
||||
anim.nAnimId = m_nAnimId;
|
||||
anim.bLoop = m_uFlags.bLoop?true:false;
|
||||
|
||||
anim.bRun = anim.bLoop || (m_nStartAnimFlags&CryCharAnimationParams::FLAGS_ALIGNED);
|
||||
|
||||
CryModelAnimationContainer* pAnimations = m_pParent->getAnimationSet();
|
||||
const AnimData& rCurrentAnimation = pAnimations->getAnimation(m_nAnimId);
|
||||
anim.fLoopEnd = rCurrentAnimation.fStop;
|
||||
anim.fLoopLength = rCurrentAnimation.getLength();
|
||||
|
||||
m_arrFadeAnims.insert(m_arrFadeAnims.begin(), anim);
|
||||
}
|
||||
|
||||
// small safety check in case the queue gets really long...
|
||||
if (g_GetCVars()->ca_TickVersion() >= 2 && m_arrFadeAnims.size() > 8)
|
||||
m_arrFadeAnims.resize (8);
|
||||
|
||||
//m_uFlags.bLoop = 0;
|
||||
//m_fStopTime = m_fAnimTime;
|
||||
//m_fBlendOutTime = m_fOrigBlendOutTime;
|
||||
m_nAnimId = -1;
|
||||
}
|
||||
|
||||
// forcibly sets the current time of the animation, in seconds
|
||||
void CCryModEffAnimation::SetCurrentTime (float fTime)
|
||||
{
|
||||
//m_fBlendInTime = m_fBlendOutTime = 0;
|
||||
if (m_nAnimId < 0)
|
||||
return;
|
||||
|
||||
// filter out the out-of-range values
|
||||
if (fTime < m_fStartTime)
|
||||
fTime = m_fStartTime;
|
||||
else
|
||||
if (fTime > m_fStopTime)
|
||||
fTime = m_fStopTime;
|
||||
|
||||
m_arrFadeAnims.clear();
|
||||
|
||||
float fPrevTime = m_fAnimTime;
|
||||
m_fAnimTime = fTime;
|
||||
|
||||
// so as to avoid calling extra callbacks
|
||||
OnTimeChanged (m_nAnimId, fPrevTime, m_fAnimTime, fTime-fPrevTime);
|
||||
}
|
||||
|
||||
void CCryModEffAnimation::Reset()
|
||||
{
|
||||
m_fBlendInCountdown = m_fBlendInTime = m_fBlendOutTime = 0.f;
|
||||
m_nAnimId = -1;
|
||||
m_arrFadeAnims.clear();
|
||||
}
|
||||
|
||||
float CCryModEffAnimation::getTicksPerSecond()const
|
||||
{
|
||||
CryModelAnimationContainer* pAnimations = m_pParent->getAnimationSet();
|
||||
return pAnimations->getTicksPerSecond();
|
||||
}
|
||||
|
||||
// ticks the fading animation; returns false when the animation completely fades out
|
||||
bool CCryModEffAnimation::FadingAnim::Tick (float fDeltaTime, bool bFade)
|
||||
{
|
||||
if (bRun)
|
||||
{
|
||||
if (bLoop)
|
||||
{
|
||||
if (fLoopLength)
|
||||
{
|
||||
float fLoopStart = fLoopEnd - fLoopLength;
|
||||
if (fDeltaTime > 0)
|
||||
fTime = fLoopStart + cry_fmod ((fTime + fDeltaTime) - fLoopStart, fLoopLength);
|
||||
else
|
||||
fTime = fLoopEnd - cry_fmod (fLoopEnd - (fTime + fDeltaTime), fLoopLength);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fTime += fDeltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
if (bFade)
|
||||
fBlending -= cry_fabsf(fDeltaTime*fBlendOutSpeed);
|
||||
return fBlending > 0;
|
||||
}
|
||||
|
||||
void CCryModEffAnimation::initClass()
|
||||
{
|
||||
g_arrLocalLayers.reserve (3);
|
||||
}
|
||||
|
||||
string CCryModEffAnimation::dump()
|
||||
{
|
||||
CryModelAnimationContainer* pAnimations = m_pParent->getAnimationSet();
|
||||
const AnimData& rAnim = pAnimations->getAnimation(m_nAnimId);
|
||||
string strResult;
|
||||
char szBuf[0x100];
|
||||
strResult += "main \"" + rAnim.strName + "\"";
|
||||
sprintf (szBuf, " t=%.2f", m_fAnimTime);
|
||||
strResult += szBuf;
|
||||
if (!m_arrFadeAnims.empty())
|
||||
{
|
||||
strResult += ", fade";
|
||||
for (FadingAnimArray::iterator it = m_arrFadeAnims.begin(); it != m_arrFadeAnims.end(); ++it)
|
||||
{
|
||||
const AnimData& fadeAnim = pAnimations->getAnimation(it->nAnimId);
|
||||
sprintf (szBuf, " \"%s\" (t=%.2f b=%.2f)", fadeAnim.strName.c_str(), it->fTime, it->fBlending);
|
||||
strResult += szBuf;
|
||||
}
|
||||
}
|
||||
return strResult;
|
||||
}
|
||||
|
||||
void CCryModEffAnimation::deinitClass()
|
||||
{
|
||||
g_arrLocalLayers.clear();
|
||||
}
|
||||
CAnimationLayerInfoArray CCryModEffAnimation::g_arrLocalLayers;
|
||||
145
CryAnimation/CryModEffAnimation.h
Normal file
145
CryAnimation/CryModEffAnimation.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Oscar Blasco
|
||||
// Taken over by Vladimir Kajalin, Andrey Honich
|
||||
// Taken over by Sergiy Migdalskiy
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _EFFECTOR_ANIM_H
|
||||
#define _EFFECTOR_ANIM_H
|
||||
|
||||
#include "AnimationLayerInfo.h"
|
||||
#include "CryAnimationInfo.h"
|
||||
|
||||
class CryModelAnimationContainer;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Animation system supports playback of several animations at the same time.
|
||||
// It allows to blend for example <20>Walk<6C> animation and <20>Reload<61> animation.
|
||||
// CCryModEffAnimation contains current state of this layers.
|
||||
// It performs animations timing and bone animation calculations.
|
||||
//
|
||||
class CCryModEffAnimation:
|
||||
public _i_reference_target_t
|
||||
{
|
||||
protected:
|
||||
float m_fAnimTime;
|
||||
int m_nAnimId;
|
||||
|
||||
// the blend time
|
||||
float m_fBlendInTime, m_fBlendOutTime;
|
||||
// the original blend-out time, used in STopAnimation()
|
||||
float m_fOrigBlendOutTime;
|
||||
|
||||
// the countdown that counts from blendin to 0:
|
||||
// the animation may sometimes start not from the beginning, but from the middle,
|
||||
// and we won't be able to determine the correct blending - that's what this is needed for
|
||||
float m_fBlendInCountdown;
|
||||
|
||||
// the animation start/stop time. Normally, it should be the same as the animation info start/stop time,
|
||||
// but sometimes we can wish to stop the animation before it reaches the start/end; in this case, we
|
||||
// set this start/stop time to the current time and the animation starts fading out immediately
|
||||
float m_fStopTime, m_fStartTime;
|
||||
|
||||
//float m_fTicksPerSecond; // 1/m_pAnimations->getSecsPerTick()
|
||||
CryModelState* m_pParent;
|
||||
unsigned m_nStartAnimFlags;
|
||||
|
||||
struct SFlags{
|
||||
unsigned bLoop:1;
|
||||
};
|
||||
|
||||
SFlags m_uFlags;
|
||||
|
||||
// this calls all the necessary callbacks
|
||||
void OnTimeChanged (int nAnimId, float fPrevTime, float m_fAnimTime,float fSpeed);
|
||||
|
||||
float getTicksPerSecond()const;
|
||||
|
||||
// the animation that is fading out
|
||||
struct FadingAnim
|
||||
{
|
||||
int nAnimId;
|
||||
float fTime; // current animation time
|
||||
float fBlending; // 1..0 - this gets decreased with each tick
|
||||
float fBlendOutSpeed; // - this is the speed fBlending decreased with each tick
|
||||
float fLoopLength, fLoopEnd;
|
||||
bool bRun; // if this is true, the time gets increased with each tick
|
||||
bool bLoop; // if this is true, and bRun, then the time runs within the given loop interval
|
||||
|
||||
bool Tick (float fDeltaTime, bool bFade);
|
||||
};
|
||||
typedef std::vector<FadingAnim> FadingAnimArray;
|
||||
// the died animations
|
||||
FadingAnimArray m_arrFadeAnims;
|
||||
|
||||
// ticks the fading animations and adds them to the animation layer array
|
||||
unsigned TickFadingAnims (float fDelta, float fWeightLeft, std::vector<CAnimationLayerInfo>& arrLayers, bool bFade = true);
|
||||
|
||||
float getBlending()const;
|
||||
|
||||
static std::vector<CAnimationLayerInfo>g_arrLocalLayers;
|
||||
|
||||
public:
|
||||
class Validator
|
||||
{
|
||||
public:
|
||||
Validator(CCryModEffAnimation* pParent):
|
||||
m_pParent(pParent)
|
||||
{
|
||||
m_pParent->selfValidate();
|
||||
}
|
||||
~Validator()
|
||||
{
|
||||
m_pParent->selfValidate();
|
||||
}
|
||||
CCryModEffAnimation* m_pParent;
|
||||
};
|
||||
friend class Validator;
|
||||
|
||||
void selfValidate()
|
||||
{
|
||||
assert (m_fAnimTime <= m_fStopTime + 0.01f && m_fAnimTime >= m_fStartTime - 0.01f );
|
||||
}
|
||||
|
||||
string dump();
|
||||
unsigned getStartAnimFlags ()const {return m_nStartAnimFlags;}
|
||||
|
||||
static void initClass();
|
||||
static void deinitClass();
|
||||
|
||||
CCryModEffAnimation(CryModelState* pParent);
|
||||
|
||||
// advances the current time of the played animation and returns the blending factor by which this animation affects the bone pose
|
||||
unsigned Tick (float deltatime, const std::vector<ICharInstanceSink *>& arrSinks, std::vector<CAnimationLayerInfo>& arrLayers);
|
||||
|
||||
void SetNoLoopNoBlendOut();
|
||||
void SetNoLoop();
|
||||
|
||||
// forcibly sets the current time of the animation, in seconds
|
||||
void SetCurrentTime (float fTime);
|
||||
float GetCurrentTime() const { return m_fAnimTime; };
|
||||
void Reset();
|
||||
void StartAnimation(unsigned nAnimID, float fBlendInTime, float fBlendOutTime, CCryModEffAnimation* pSynchronizeWith,float fSpeed, unsigned nStartAnimFlags = 0);
|
||||
void stop ();
|
||||
float GetPhase()const;
|
||||
|
||||
float GetTimeTillEnd() {return m_fStopTime - m_fAnimTime;}
|
||||
|
||||
// returns the number of any animation currently being played
|
||||
// this can be the current animation, including animation of fadeout, if the
|
||||
// layer doesn't have the current animation played, but has an animation fading out
|
||||
int GetAnyCurrentAnimation()const;
|
||||
|
||||
// is this animation effector completely stopped?
|
||||
bool IsStopped();
|
||||
|
||||
// does this animation effector actively plays some animations?
|
||||
bool isActive ();
|
||||
};
|
||||
|
||||
TYPEDEF_AUTOPTR(CCryModEffAnimation);
|
||||
|
||||
#endif
|
||||
161
CryAnimation/CryModEffIKSolver.cpp
Normal file
161
CryAnimation/CryModEffIKSolver.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "CryBone.h"
|
||||
#include "CryBoneInfo.h"
|
||||
#include "CryModelState.h"
|
||||
#include "CryModEffector.h"
|
||||
#include "CryModEffIKSolver.h"
|
||||
|
||||
CCryModEffIKSolver::CCryModEffIKSolver(CryModelState* pParent):
|
||||
m_pParent (pParent),
|
||||
m_nPrimaryBone (-1)
|
||||
{
|
||||
m_flags = 0;
|
||||
m_ptGoal.zero();
|
||||
m_GoalNormal.zero();
|
||||
m_bActive = true;
|
||||
m_fFeetAngle = m_fDeltaTime = 0;
|
||||
}
|
||||
|
||||
|
||||
void CCryModEffIKSolver::Tick(float deltatime)
|
||||
{
|
||||
const Matrix44& rBoneMtx = m_pParent->getBoneMatrixGlobal (m_pParent->getBoneGrandChildIndex(m_nPrimaryBone,0,0));
|
||||
vectorf pt = rBoneMtx.GetTranslationOLD();
|
||||
for(int i=0;i<3;i++) if (m_ptGoal[i]==IK_NOT_USED)
|
||||
m_ptGoal[i] = pt[i];
|
||||
m_bActive = !((m_flags & ik_leg) && (m_flags & ik_avoid_stretching) && m_ptGoal.z+0.01f<pt.z);
|
||||
m_fDeltaTime = deltatime;
|
||||
}
|
||||
|
||||
void RotateMatrix(Matrix44 &mtx, vectorf axis,float cosa,float sina,vectorf pivot)
|
||||
{
|
||||
vectorf curaxis; int i;
|
||||
Matrix44 rotmtx; rotmtx.SetIdentity();
|
||||
|
||||
for(i=0,curaxis.zero();i<3;i++) {
|
||||
curaxis[i] = 1;
|
||||
*(vectorf*)&rotmtx(i,0) = curaxis.rotated(axis,cosa,sina);
|
||||
curaxis[i] = 0;
|
||||
}
|
||||
rotmtx.SetTranslationOLD(pivot-pivot.rotated(axis,cosa,sina));
|
||||
|
||||
mtx *= rotmtx;
|
||||
}
|
||||
|
||||
void CCryModEffIKSolver::UpdateBoneChildren(int iBone)
|
||||
{
|
||||
int i,iChild;
|
||||
for(i=m_pParent->numBoneChildren(iBone)-1;i>=0;i--)
|
||||
{
|
||||
iChild = m_pParent->getBoneChildIndex(iBone,i);
|
||||
m_pParent->getBoneMatrixGlobal(iChild) =
|
||||
m_pParent->getBoneChild(iBone,i)->m_matRelativeToParent * m_pParent->getBoneMatrixGlobal(iBone);
|
||||
UpdateBoneChildren(iChild);
|
||||
}
|
||||
}
|
||||
|
||||
// Only one joint version
|
||||
void CCryModEffIKSolver::ApplyToBone(int nLayer)
|
||||
{
|
||||
if (!m_bActive) {
|
||||
m_fFeetAngle = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int i,nbones;
|
||||
int arrBones[4] =
|
||||
{
|
||||
m_nPrimaryBone,
|
||||
m_pParent->getBoneChildIndex(m_nPrimaryBone,0),
|
||||
m_pParent->getBoneGrandChildIndex(m_nPrimaryBone,0,0),
|
||||
-1
|
||||
};
|
||||
vectorf pt[4],a,b,c,n,goal;
|
||||
float alen,blen,clen,nlen,beta,cosa,sina;
|
||||
|
||||
if (m_pParent->numBoneChildren(arrBones[2]) && m_flags & ik_leg) {
|
||||
arrBones[3] = m_pParent->getBoneChildIndex (arrBones[2],0);
|
||||
nbones = 4;
|
||||
} else
|
||||
nbones = 3;
|
||||
|
||||
for (i = 0; i < nbones; ++i)
|
||||
pt[i] = m_pParent->getBoneMatrixGlobal(arrBones[i]).GetTranslationOLD();
|
||||
goal = m_ptGoal;
|
||||
|
||||
a = pt[1]-pt[0]; b = pt[2]-pt[1]; c = goal-pt[0];
|
||||
n = (a^b).normalized();
|
||||
alen = a.len(); blen = b.len()+m_additLen; clen = c.len();
|
||||
if (alen*clen<1E-6f)
|
||||
return;
|
||||
|
||||
beta = (alen*alen+clen*clen-blen*blen)/(2*alen*clen);
|
||||
beta = min(0.9999f,max(-0.9999f,beta));
|
||||
a = a.rotated(n,beta,cry_sqrtf(1-beta*beta)).normalized();
|
||||
c/=clen; n=a^c; nlen=n.len();
|
||||
if (nlen<1E-6)
|
||||
return;
|
||||
n/=nlen;
|
||||
for(i=0;i<nbones;++i) {
|
||||
RotateMatrix(m_pParent->getBoneMatrixGlobal(arrBones[i]), n,a*c,nlen, pt[0]);
|
||||
pt[i] = m_pParent->getBoneMatrixGlobal(arrBones[i]).GetTranslationOLD();
|
||||
}
|
||||
|
||||
c = goal-pt[1]; b = pt[2]-pt[1];
|
||||
n = b^c; nlen = n.len();
|
||||
if (nlen>0.001) {
|
||||
n/=nlen; beta = cry_atan2f(nlen,b*c);
|
||||
struct {float cosa, sina;}acs;
|
||||
cry_sincosf(beta, &acs.cosa);
|
||||
cosa = acs.cosa; //(float)cry_cosf(beta);
|
||||
sina = acs.sina; //(float)cry_sinf(beta);
|
||||
for(i=1;i<nbones;i++) {
|
||||
RotateMatrix(m_pParent->getBoneMatrixGlobal(arrBones[i]), n,cosa,sina,pt[1]);
|
||||
pt[i] = m_pParent->getBoneMatrixGlobal(arrBones[i]).GetTranslationOLD();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_GoalNormal.len2()>0) {
|
||||
c = m_pParent->getBoneMatrixGlobal(arrBones[nbones-1]).GetRow(1);
|
||||
n = c^m_GoalNormal; nlen = n.len();
|
||||
if (nlen>0.001) {
|
||||
n/=nlen; beta = cry_atan2f(nlen,c*m_GoalNormal);
|
||||
if (beta<0.95f) {
|
||||
if (fabsf(beta)>0.7f)
|
||||
beta = sgnnz(beta)*0.7f;
|
||||
m_fFeetAngle = m_fFeetAngle*(1-m_fDeltaTime*2)+beta*m_fDeltaTime*2;
|
||||
struct {float cosa, sina;}acs;
|
||||
cry_sincosf(m_fFeetAngle, &acs.cosa);
|
||||
cosa = acs.cosa; //(float)cos(m_fFeetAngle);
|
||||
sina = acs.sina; //(float)sin(m_fFeetAngle);
|
||||
for(i=2;i<nbones;i++)
|
||||
RotateMatrix(m_pParent->getBoneMatrixGlobal(arrBones[i]), n,cosa,sina,pt[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
UpdateBoneChildren(arrBones[nbones-1]);
|
||||
}
|
||||
|
||||
|
||||
void CCryModEffIKSolver::Reset()
|
||||
{
|
||||
}
|
||||
|
||||
void CCryModEffIKSolver::SetGoal(vectorf ptgoal,int flags,float addlen,vectorf goal_normal,float goal_height)
|
||||
{
|
||||
m_ptGoal = ptgoal;
|
||||
m_flags = flags;
|
||||
m_additLen = addlen;
|
||||
m_GoalNormal = goal_normal;
|
||||
m_GoalHeight = goal_height;
|
||||
}
|
||||
|
||||
bool CCryModEffIKSolver::AffectsBone(int nBone)
|
||||
{
|
||||
return m_bActive &&
|
||||
( nBone == m_nPrimaryBone
|
||||
|| nBone == m_pParent->getBoneChildIndex(m_nPrimaryBone,0)
|
||||
|| nBone == m_pParent->getBoneGrandChildIndex(m_nPrimaryBone,0,0)
|
||||
);
|
||||
}
|
||||
33
CryAnimation/CryModEffIKSolver.h
Normal file
33
CryAnimation/CryModEffIKSolver.h
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
#ifndef _EFFECTOR_IKSOLVER_H
|
||||
#define _EFFECTOR_IKSOLVER_H
|
||||
|
||||
class CryModelState;
|
||||
|
||||
class CCryModEffIKSolver
|
||||
{
|
||||
private:
|
||||
vectorf m_ptGoal,m_GoalNormal;
|
||||
float m_additLen,m_GoalHeight;
|
||||
int m_flags;
|
||||
// the model state owning this object - used to retrieve the bone info
|
||||
CryModelState* m_pParent;
|
||||
int m_nPrimaryBone;
|
||||
bool m_bActive;
|
||||
float m_fDeltaTime,m_fFeetAngle;
|
||||
|
||||
public:
|
||||
CCryModEffIKSolver(CryModelState* pParent);
|
||||
|
||||
void SetPrimaryBone(int nBone){ m_nPrimaryBone = nBone;} // Could be rootbone or a bone which will be affected.
|
||||
void Tick(float deltatime);
|
||||
void ApplyToBone(int nLayer);
|
||||
void Reset();
|
||||
|
||||
void SetGoal(vectorf ptgoal,int flags,float addlen=0,vectorf goal_normal=vectorf(0,0,0),float goal_height=0);
|
||||
bool AffectsBone(int nBone);
|
||||
void UpdateBoneChildren(int iBone);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
67
CryAnimation/CryModEffMorph.cpp
Normal file
67
CryAnimation/CryModEffMorph.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "stdafx.h"
|
||||
#include "CryModEffMorph.h"
|
||||
|
||||
CryModEffMorph::CryModEffMorph(/*CryModelAnimationContainer* pAnimations*/)
|
||||
// :m_pAnimations(pAnimations)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// starts the morphing sequence
|
||||
void CryModEffMorph::StartMorph (int nMorphTargetId, const CryCharMorphParams& rParams)
|
||||
{
|
||||
m_Params = rParams;
|
||||
m_nMorphTargetId = nMorphTargetId;
|
||||
m_fTime = rParams.fStartTime;
|
||||
m_nFlags = rParams.nFlags;
|
||||
}
|
||||
|
||||
void CryModEffMorph::stop()
|
||||
{
|
||||
m_nMorphTargetId = -1;
|
||||
}
|
||||
|
||||
// advances the current time of the played animation and returns the blending factor by which this animation affects the bone pose
|
||||
void CryModEffMorph::Tick (float fDeltaTime)
|
||||
{
|
||||
if (m_nMorphTargetId < 0)
|
||||
return;
|
||||
|
||||
if (!(m_nFlags & m_Params.FLAGS_FREEZE))
|
||||
m_fTime += fDeltaTime * m_Params.fSpeed;
|
||||
|
||||
if (!(m_nFlags & m_Params.FLAGS_NO_BLENDOUT) && m_fTime > m_Params.fBlendIn + m_Params.fBlendOut + m_Params.fLength)
|
||||
// we're finished
|
||||
m_nMorphTargetId = -1;
|
||||
}
|
||||
|
||||
// returns the blending for the morph target
|
||||
float CryModEffMorph::getBlending()const
|
||||
{
|
||||
float fTimeStable = m_fTime - m_Params.fBlendIn;
|
||||
|
||||
if (fTimeStable < 0) // blending in...
|
||||
return m_fTime*m_Params.fAmplitude/m_Params.fBlendIn;
|
||||
|
||||
if (m_nFlags & m_Params.FLAGS_NO_BLENDOUT)
|
||||
return m_Params.fAmplitude; // never blending out - stable morph
|
||||
|
||||
float fTimeBlendOut = fTimeStable - m_Params.fLength;
|
||||
|
||||
if (fTimeBlendOut < 0)
|
||||
return m_Params.fAmplitude;
|
||||
|
||||
return m_Params.fAmplitude * (1 - fTimeBlendOut/m_Params.fBlendOut);
|
||||
}
|
||||
|
||||
// returns false when this morph target is inactive
|
||||
bool CryModEffMorph::isActive()const
|
||||
{
|
||||
return m_nMorphTargetId >= 0;
|
||||
}
|
||||
|
||||
// returns the morph target, or -1 if none
|
||||
int CryModEffMorph::getMorphTargetId () const
|
||||
{
|
||||
return m_nMorphTargetId;
|
||||
}
|
||||
49
CryAnimation/CryModEffMorph.h
Normal file
49
CryAnimation/CryModEffMorph.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef _CRY_MODEL_EFFECTOR_ANIMATION_MORPH_HDR_
|
||||
#define _CRY_MODEL_EFFECTOR_ANIMATION_MORPH_HDR_
|
||||
|
||||
#include "CryAnimationBase.h"
|
||||
#include <CryCharMorphParams.h>
|
||||
|
||||
class CryModelAnimationContainer;
|
||||
|
||||
class CryModEffMorph
|
||||
{
|
||||
public:
|
||||
CryModEffMorph(/*CryModelAnimationContainer* pAnimations*/);
|
||||
|
||||
// advances the current time of the played animation and returns the blending factor by which this animation affects the bone pose
|
||||
void Tick (float fDeltaTime);
|
||||
|
||||
// starts the morphing sequence
|
||||
void StartMorph (int nMorphTargetId, const CryCharMorphParams& rParams);
|
||||
|
||||
// returns false when this morph target is inactive
|
||||
bool isActive()const ;
|
||||
|
||||
// returns the blending factor for this morph target
|
||||
float getBlending()const;
|
||||
|
||||
// returns the morph target
|
||||
int getMorphTargetId () const;
|
||||
|
||||
void setTime(float fTime) {m_fTime = fTime;}
|
||||
void setSpeed (float fSpeed) {m_Params.fSpeed = fSpeed;}
|
||||
void stop();
|
||||
|
||||
float getTime() const {return m_fTime;}
|
||||
void freeze() {m_nFlags |= m_Params.FLAGS_FREEZE;}
|
||||
protected:
|
||||
|
||||
// the animation container that will answer all questions regarding the morph target
|
||||
//CryModelAnimationContainer* m_pAnimations;
|
||||
|
||||
// the blend time
|
||||
CryCharMorphParams m_Params;
|
||||
// time of morphing
|
||||
float m_fTime;
|
||||
// morph target id
|
||||
int m_nMorphTargetId;
|
||||
unsigned m_nFlags; // the copy of the flags from m_Params
|
||||
};
|
||||
|
||||
#endif
|
||||
30
CryAnimation/CryModEffector.h
Normal file
30
CryAnimation/CryModEffector.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Oscar Blasco
|
||||
// Taken over by Vladimir Kajalin, Andrey Honich
|
||||
// Taken over by Sergiy Migdalskiy <sergiy@crytek.de>
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _EFFECTOR_H
|
||||
#define _EFFECTOR_H
|
||||
|
||||
|
||||
class ICryModEffector: public _i_reference_target_t
|
||||
{
|
||||
public:
|
||||
// changes the curernt time by the given value, given in ticks
|
||||
virtual void Tick (float deltatime, float fBlendSpeed) = 0; // If 0 the effector will be removed fron list.
|
||||
// forcibly sets the current time of the animation, in frames
|
||||
virtual void SetCurrentTime (float fTime, float fBlendSpeed) {}
|
||||
virtual void ApplyToBone(CryBone *bone, unsigned layer, AnimTwinMode eTwinMode) = 0;
|
||||
virtual bool IsStopped() { return true; };
|
||||
virtual bool IsProceduralAnimsStopped() { return true; };
|
||||
virtual void Reset() = 0;
|
||||
};
|
||||
|
||||
TYPEDEF_AUTOPTR(ICryModEffector);
|
||||
|
||||
#endif // _EFFECTOR_H
|
||||
100
CryAnimation/CryModel-Data.cpp
Normal file
100
CryAnimation/CryModel-Data.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Oscar Blasco
|
||||
// Taken over by Vladimir Kajalin, Andrey Honich
|
||||
// Taken over by Sergiy Migdalskiy
|
||||
//
|
||||
// This file contains definitions of static variables and tables used by the CryModel class
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "CryModel.h"
|
||||
|
||||
// the static array of shadow volume vertices
|
||||
// this holds the vertices of a deformed characters between calls to Deform()
|
||||
// and functions that use its output; this avoids necessity to reallocate the array many times each frame
|
||||
//TFixedArray<float> CryModel::g_arrShadowVolumeVerts ("CryModel.ShadowVolumeVerts");
|
||||
|
||||
// the following string tables are for recognition of different parts of the body
|
||||
// by the bone name. depending on this recognition, the damage will be calculated.
|
||||
const char *CryModel::g_szDamageBonesHead[] =
|
||||
{
|
||||
"Bip01 Neck",
|
||||
"Bip01 Neck1",
|
||||
"Bip01 Head",
|
||||
NULL
|
||||
};
|
||||
|
||||
// the following string tables are for recognition of different parts of the body
|
||||
// by the bone name. depending on this recognition, the damage will be calculated.
|
||||
const char *CryModel::g_szDamageBonesTorso[] =
|
||||
{
|
||||
"Bip01 Pelvis",
|
||||
"Bip01 Spine",
|
||||
"Bip01 Spine1",
|
||||
"Bip01 Spine2",
|
||||
NULL
|
||||
};
|
||||
|
||||
// the following string tables are for recognition of different parts of the body
|
||||
// by the bone name. depending on this recognition, the damage will be calculated.
|
||||
const char *CryModel::g_szDamageBonesArmL[] =
|
||||
{
|
||||
"Bip01 L Clavicle",
|
||||
"Bip01 L UpperArm",
|
||||
"Bip01 L Forearm",
|
||||
"Bip01 L Hand",
|
||||
|
||||
"Bip01 L Finger0",
|
||||
"Bip01 L Finger01",
|
||||
"Bip01 L Finger1",
|
||||
"Bip01 L Finger11",
|
||||
"Bip01 L Finger2",
|
||||
"Bip01 L Finger21",
|
||||
NULL
|
||||
};
|
||||
|
||||
// the following string tables are for recognition of different parts of the body
|
||||
// by the bone name. depending on this recognition, the damage will be calculated.
|
||||
const char *CryModel::g_szDamageBonesArmR[] =
|
||||
{
|
||||
"Bip01 R Clavicle",
|
||||
"Bip01 R UpperArm",
|
||||
"Bip01 R Forearm",
|
||||
"Bip01 R Hand",
|
||||
|
||||
"Bip01 R Finger0",
|
||||
"Bip01 R Finger01",
|
||||
"Bip01 R Finger1",
|
||||
"Bip01 R Finger11",
|
||||
"Bip01 R Finger2",
|
||||
"Bip01 R Finger21",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
// the following string tables are for recognition of different parts of the body
|
||||
// by the bone name. depending on this recognition, the damage will be calculated.
|
||||
const char *CryModel::g_szDamageBonesLegL[] =
|
||||
{
|
||||
"Bip01 L Thigh",
|
||||
"Bip01 L Calf",
|
||||
"Bip01 L Foot",
|
||||
"Bip01 L Toe0",
|
||||
NULL
|
||||
};
|
||||
|
||||
// the following string tables are for recognition of different parts of the body
|
||||
// by the bone name. depending on this recognition, the damage will be calculated.
|
||||
const char *CryModel::g_szDamageBonesLegR[] =
|
||||
{
|
||||
"Bip01 R Thigh",
|
||||
"Bip01 R Calf",
|
||||
"Bip01 R Foot",
|
||||
"Bip01 R Toe0",
|
||||
NULL
|
||||
};
|
||||
1000
CryAnimation/CryModel.cpp
Normal file
1000
CryAnimation/CryModel.cpp
Normal file
File diff suppressed because it is too large
Load Diff
293
CryAnimation/CryModel.h
Normal file
293
CryAnimation/CryModel.h
Normal file
@@ -0,0 +1,293 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Oscar Blasco
|
||||
// Taken over by Vladimir Kajalin, Andrey Honich
|
||||
// Taken over by Sergiy Migdalskiy
|
||||
//
|
||||
// Contains the geometry management code for multi-LOD models, shader/material, bone info management
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _CRY_MODEL_HEADER_
|
||||
#define _CRY_MODEL_HEADER_
|
||||
|
||||
#include "IPhysics.h"
|
||||
#include "CryHeaders.h"
|
||||
#include "CryBone.h"
|
||||
#include "CryModEffector.h"
|
||||
#include "CryModEffIKSolver.h"
|
||||
#include "CryModEffAnimation.h"
|
||||
#include "CryGeometryInfo.h"
|
||||
#include "BoneLightBindInfo.h"
|
||||
#include "CryAnimationInfo.h"
|
||||
|
||||
#include "CryModelAnimationContainer.h"
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// CryModel class
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class CryModelState;
|
||||
class CryModelSubmesh;
|
||||
class IStencilShadowConnectivity;
|
||||
|
||||
struct CCFAnimGeomInfo;
|
||||
struct CCFBoneGeometry;
|
||||
struct CCFBGBone;
|
||||
struct CCFMorphTargetSet;
|
||||
struct CCFCharLightDesc;
|
||||
|
||||
#define ENABLE_BONE_BBOXES 0
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// This class contain data which can be shared between different several models of same type.
|
||||
// It loads and contains all geometry, materials, and also damage table.
|
||||
// Damage table contains amount of damage for each bone.
|
||||
// Also it contains default model state.
|
||||
class CryModel :
|
||||
public CryModelAnimationContainer
|
||||
{
|
||||
friend class CryModelState;
|
||||
friend class CryModelLoader;
|
||||
friend class CryModelGeometryLoader;
|
||||
friend class CryModelSubmesh;
|
||||
friend class CryModelSubmeshGeometry;
|
||||
public:
|
||||
|
||||
// returns the geometry of the given lod (0 is the highest detail lod)
|
||||
CryGeometryInfo* getGeometryInfo (unsigned nLodLevel = 0);
|
||||
const CryGeometryInfo* getGeometryInfo (unsigned nLodLevel = 0) const;
|
||||
|
||||
// returns the number of levels of details.
|
||||
// 1 means there's only 0th (basic) level of details, or there are effectively no additional LODs
|
||||
unsigned numLODs ()const {return (unsigned)m_arrGeomInfo.size();}
|
||||
|
||||
// this enum contains the
|
||||
enum DamageAreaEnum
|
||||
{
|
||||
g_nDamageAreaDefault = 0,
|
||||
g_nDamageAreaHead = 1,
|
||||
g_nDamageAreaTorso = 2,
|
||||
g_nDamageAreaArmL = 3,
|
||||
g_nDamageAreaArmR = 4,
|
||||
g_nDamageAreaLegL = 5,
|
||||
g_nDamageAreaLegR = 6
|
||||
};
|
||||
|
||||
// fills the array m_arrDamageTable, using the bone names
|
||||
void InitDamageTable();
|
||||
|
||||
enum {g_nMaxMaterialCount = 128};
|
||||
|
||||
// retrieves the pointer to the static array of shadow volume vertices
|
||||
//static float* getShadowVolumeVerts ();
|
||||
|
||||
// expands the size of the shadow volume vertex array to the specified size
|
||||
// the size defines the number of floats the array must have (at least)
|
||||
//static void expandShadowVolumeVerts(unsigned nSize);
|
||||
|
||||
// frees the array of shadow volume vertices; this should be normally called
|
||||
// in the destructor of CryModelManager, but can safely be called whenever you wish
|
||||
// since expand will always restore the array
|
||||
//static void freeShadowVolumeVerts( );
|
||||
|
||||
void ComputeStaticBoundingBox();
|
||||
|
||||
// Loads it from geom file; returns the number of bytes used from the chunk
|
||||
///unsigned LoadFromGeom (const MESH_CHUNK_DESC_0744* pChunk, unsigned nChunkSize, float scale, const int nLodLevel, bool bTestForBoneInfo=true);
|
||||
|
||||
bool LoadGeomChunks (const string& filename, float scale, int nLodLevel);
|
||||
|
||||
//void LoadTextures(const string& );
|
||||
|
||||
// computes, caches and returns the connectivity object for stencil shadows
|
||||
// (or just returns, if the object is already cached)
|
||||
IStencilShadowConnectivity* getStencilShadowConnectivity(unsigned nLOD);
|
||||
|
||||
unsigned numLocalBoneLights() const {return (unsigned)m_arrLocalBoneLights.size();}
|
||||
unsigned numGlobalBoneLights() const {return (unsigned)m_arrGlobalBoneLights.size();}
|
||||
unsigned numBoneLights() const {return numLocalBoneLights()+numGlobalBoneLights();}
|
||||
CBoneLightBindInfo& getBoneLight(unsigned i)
|
||||
{
|
||||
if (i < numLocalBoneLights())
|
||||
return m_arrLocalBoneLights[i];
|
||||
else
|
||||
{
|
||||
assert(i < numGlobalBoneLights());
|
||||
return m_arrGlobalBoneLights[i-numLocalBoneLights()];
|
||||
}
|
||||
}
|
||||
CBoneLightBindInfo& getLocalBoneLight(unsigned i) {return m_arrLocalBoneLights[i];}
|
||||
CBoneLightBindInfo& getGlobalBoneLight(unsigned i) {return m_arrGlobalBoneLights[i];}
|
||||
|
||||
void clearConstructionData();
|
||||
|
||||
const Vec3& getModelOffset()const;
|
||||
public:
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ----------------------------- GAME INTERFACE FUNCTIONS ----------------------------- //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
CryModel (class CryCharBody* pBody, class CControllerManager* pControllerManager);
|
||||
virtual ~CryModel();
|
||||
|
||||
// puts the size of the whole subsystem into this sizer object, classified,
|
||||
// according to the flags set in the sizer
|
||||
void GetSize(class ICrySizer* pSizer)const;
|
||||
|
||||
const char* getFilePathCStr();
|
||||
|
||||
void DrawStaticGeom(); // Draws static geometry, useful to check the Geom
|
||||
//virtual void GetStaticBoundingBox(Vec3 * pvMin, Vec3 * pvMax);
|
||||
|
||||
// returns the radius of the bounding sphere of the object
|
||||
float getStaticBSphereRadius() {return m_fStaticBSphereRadius;}
|
||||
|
||||
void InvertMarkedTangentBasises();
|
||||
|
||||
void ExportModelsASC();
|
||||
//DECLARE_ARRAY_GETTER_METHODS(MAT_ENTITY, Material, Materials, m_arrMaterials);
|
||||
size_t numMaterials ()const {return m_arrMaterials.size();}
|
||||
const MAT_ENTITY& getMaterial (unsigned i)const {return m_arrMaterials[i];}
|
||||
MAT_ENTITY& getMaterial (unsigned i) {return m_arrMaterials[i];}
|
||||
|
||||
// Returns the interface for animations applicable to this model
|
||||
virtual ICryAnimationSet* GetAnimationSet ();
|
||||
|
||||
// builds the skins out of morph targets
|
||||
void buildMorphSkins ();
|
||||
|
||||
const CrySkinMorph& getMorphSkin (unsigned nLOD, int nMorphTargetId);
|
||||
|
||||
// builds the skins for tangent base and geometry skin calculation for each LOD
|
||||
void buildGeomSkins();
|
||||
|
||||
// builds all stencil shadow connectivities
|
||||
void buildStencilShadowInfos();
|
||||
|
||||
// deletes all unused materials in the material array
|
||||
void deleteUnusedMaterials ();
|
||||
|
||||
CryModelState * m_pDefaultModelState;
|
||||
|
||||
// 1. completely initializes the CryModel from the given CCG file
|
||||
// 2. Loads textures
|
||||
// 3. Generates render arrays
|
||||
// returns true if successfully initialized
|
||||
bool initFromCCG (const string& strTextureDir, const string& strAnimationDir, class CCFMemReader& Reader, float fScale);
|
||||
|
||||
// loads the LOD: geometry info and leaf buffers
|
||||
// nSize is the size of the buffer including header; the raw data follows the header immediately
|
||||
bool loadCCGLOD (unsigned nLOD, const CCFAnimGeomInfo* pHeader, unsigned nSize);
|
||||
|
||||
// loads the bone geometry and initializes the physical geometry for corresponding LOD for bones
|
||||
bool loadCCGBoneGeomLOD (unsigned nLOD, float fScale, const CCFBoneGeometry* pHeader, unsigned nSize);
|
||||
|
||||
// loads the morph target set from CCG; scales all morph targets
|
||||
bool loadCCGMorphTargetSet (const CCFMorphTargetSet* pData, unsigned nSize, float fScale);
|
||||
|
||||
// loads the light array
|
||||
bool loadCCGLights (const CCFCharLightDesc* pData, unsigned nSize,float fScale);
|
||||
|
||||
// loads the bone geometry for a particular bone
|
||||
bool loadCCGBoneGeom (IGeomManager* pGeomManager, unsigned nLOD, float fScale, const CCFBGBone* pHeader, unsigned nSize);
|
||||
|
||||
// loads the animation list from the chunk
|
||||
bool loadCCGAnimScript (float fScale, string strAnimDir, const void* pData, unsigned nSize);
|
||||
|
||||
// given the pointers to the chunk datas of the anim info chunks, loads those animations
|
||||
bool loadAnimations (float fScale, const std::vector<const struct CCFAnimInfo*>& arrAnimInfos);
|
||||
|
||||
void loadCCGUserProperties (const char* pData, unsigned nSize);
|
||||
|
||||
// fills the iGameID for each material, using its name to enumerate the physical materials in 3D Engine
|
||||
void fillMaterialGameId();
|
||||
|
||||
CryBBoxA16* getBoneBBoxes ()
|
||||
{
|
||||
#if ENABLE_BONE_BBOXES
|
||||
return m_arrBoneBBoxes.begin();
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
void computeBoneBBoxes();
|
||||
|
||||
// returns the extra data attached to the character by the artist during exporting
|
||||
// as the scene user properties. if there's no such data, returns NULL
|
||||
const char* GetProperty(const char* szName);
|
||||
|
||||
protected:
|
||||
|
||||
// finds all lod file names (starting
|
||||
//void
|
||||
|
||||
// performs post-initialization tasks one-time after loading the model
|
||||
void LoadPostInitialize(bool bBoneInfoDefPoseNeedInitialization);
|
||||
|
||||
void LoadPostInitializeCCG();
|
||||
|
||||
CryCharBody* m_pBody;
|
||||
protected:
|
||||
// the set of geometries for each lod level
|
||||
TFixedArray<CryGeometryInfo> m_arrGeomInfo;
|
||||
|
||||
// these are the special user-defined scene properties exported from Max
|
||||
typedef std::map<string,string> UserPropMap;
|
||||
UserPropMap m_mapUserProps;
|
||||
|
||||
// the radius of the bounding sphere of the object
|
||||
float m_fStaticBSphereRadius;
|
||||
|
||||
// this is the mapping BoneNumber->DamageExtermity
|
||||
// this is initialized only for the LOD 0
|
||||
// There are as many elements in this array, as there are bones at all
|
||||
// the values are from the enum DamageAreaEnum
|
||||
// Numbering by BoneID
|
||||
TFixedArray<unsigned char> m_arrDamageTable;
|
||||
|
||||
#if ENABLE_BONE_BBOXES
|
||||
typedef TAllocator16<CryBBoxA16> BBoxAllocatorA16;
|
||||
// the vertex stream: contains the rigid and smooth vertex structures
|
||||
typedef TFixedArray<CryBBoxA16, BBoxAllocatorA16> BBoxArrayA16;
|
||||
BBoxArrayA16 m_arrBoneBBoxes;
|
||||
#endif
|
||||
|
||||
// array of materials used to render this model
|
||||
// this is a fixed array because reallocations occur only on load and then the array is only read from
|
||||
std::vector <MAT_ENTITY> m_arrMaterials;
|
||||
|
||||
// local and global bone light and heat source bindings. the locals go first
|
||||
std::vector<CBoneLightBindInfo> m_arrLocalBoneLights, m_arrGlobalBoneLights;
|
||||
void addBoneLights (const std::vector<CBoneLightBindInfo>& arrLights);
|
||||
unsigned m_numLocalBoneLights;
|
||||
|
||||
// offset of the model LCS - by this value, the whole model is offset
|
||||
Vec3 m_vModelOffset;
|
||||
|
||||
bool m_bDeleteLBMats;
|
||||
bool m_bFromCCG;
|
||||
// the material physical game id that will be used as default for this character
|
||||
int m_nDefaultGameID;
|
||||
protected: // static data
|
||||
|
||||
// the following string tables are for recognition of different parts of the body
|
||||
// by the bone name. depending on this recognition, the damage will be calculated.
|
||||
// each array must end with the NULL string, to mark its end
|
||||
static const char* g_szDamageBonesHead[];
|
||||
static const char* g_szDamageBonesTorso[];
|
||||
static const char* g_szDamageBonesArmL[];
|
||||
static const char* g_szDamageBonesArmR[];
|
||||
static const char* g_szDamageBonesLegL[];
|
||||
static const char* g_szDamageBonesLegR[];
|
||||
|
||||
// the static array of shadow volume vertices
|
||||
// this holds the vertices of a deformed characters between calls to Deform()
|
||||
// and functions that use its output; this avoids necessity to reallocate the array many times each frame
|
||||
//static TFixedArray<float> g_arrShadowVolumeVerts;
|
||||
};
|
||||
|
||||
#endif // _CryModel_H_
|
||||
569
CryAnimation/CryModelAnimationContainer.cpp
Normal file
569
CryAnimation/CryModelAnimationContainer.cpp
Normal file
@@ -0,0 +1,569 @@
|
||||
#include "stdafx.h"
|
||||
#include <CryCompiledFile.h>
|
||||
#include <stlutils.h>
|
||||
//#include "CryAnimation.h"
|
||||
#include "ControllerManager.h"
|
||||
#include "CryModelAnimationContainer.h"
|
||||
#include "CVars.h"
|
||||
#include "CryBoneInfo.h"
|
||||
#include "StringUtils.h"
|
||||
#include "CryAnimationInfo.h"
|
||||
#include "CryGeomMorphTarget.h"
|
||||
#include "CrySkinMorph.h"
|
||||
using namespace CryStringUtils;
|
||||
|
||||
CryModelAnimationContainer::CryModelAnimationContainer (CControllerManager * pControllerManager):
|
||||
m_pControllerManager(pControllerManager),
|
||||
m_arrBones ("CryModelAnimationContainer.BoneInfo"),
|
||||
m_arrMorphTargets ("CryModelAnimationContainer.MorphTargets")
|
||||
{
|
||||
m_pControllerManager->Register(this);
|
||||
}
|
||||
|
||||
CryModelAnimationContainer::~CryModelAnimationContainer()
|
||||
{
|
||||
// before releasing the animations, we should release the controllers from bones
|
||||
m_arrBones.clear();
|
||||
// now there are no controllers referred to by this object, we can release the animations
|
||||
for (AnimationArray::iterator it = m_arrAnimations.begin(); it != m_arrAnimations.end(); ++it)
|
||||
m_pControllerManager->AnimationRelease(it->nGlobalAnimId, this);
|
||||
m_pControllerManager->Unregister(this);
|
||||
}
|
||||
|
||||
void CryModelAnimationContainer::OnAnimationGlobalUnload(int nGlobalAnimId)
|
||||
{
|
||||
std::vector<LocalAnimId>::iterator it = std::lower_bound (m_arrAnimByGlobalId.begin(), m_arrAnimByGlobalId.end(), nGlobalAnimId, AnimationGlobIdPred(m_arrAnimations));
|
||||
|
||||
assert (it == m_arrAnimByGlobalId.begin() || m_arrAnimations[(it-1)->nAnimId].nGlobalAnimId < nGlobalAnimId);
|
||||
|
||||
for (;it != m_arrAnimByGlobalId.end() && m_arrAnimations[it->nAnimId].nGlobalAnimId == nGlobalAnimId; ++it)
|
||||
{
|
||||
AUTO_PROFILE_SECTION(g_dTimeAnimBindControllers);
|
||||
// bind loaded controllers to the bones
|
||||
for (unsigned nBone = 0; nBone < numBoneInfos(); ++nBone)
|
||||
{
|
||||
CryBoneInfo& rBoneInfo = getBoneInfo(nBone);
|
||||
rBoneInfo.UnbindController (it->nAnimId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CryModelAnimationContainer::OnAnimationGlobalLoad (int nGlobalAnimId)
|
||||
{
|
||||
std::vector<LocalAnimId>::iterator it = std::lower_bound (m_arrAnimByGlobalId.begin(), m_arrAnimByGlobalId.end(), nGlobalAnimId, AnimationGlobIdPred(m_arrAnimations));
|
||||
|
||||
assert (it == m_arrAnimByGlobalId.begin() || m_arrAnimations[(it-1)->nAnimId].nGlobalAnimId < nGlobalAnimId);
|
||||
|
||||
// scan through the sequence of animations with the given Global AnimId
|
||||
// and bind each of them to the bones
|
||||
if (it != m_arrAnimByGlobalId.end() && m_arrAnimations[it->nAnimId].nGlobalAnimId == nGlobalAnimId)
|
||||
{
|
||||
AUTO_PROFILE_SECTION(g_dTimeAnimBindControllers);
|
||||
GlobalAnimation& GlobalAnim = m_pControllerManager->GetAnimation (nGlobalAnimId);
|
||||
|
||||
m_fTicksPerFrame = (float)GlobalAnim.nTicksPerFrame;
|
||||
m_fSecsPerTick = (float)GlobalAnim.fSecsPerTick;
|
||||
float fSecsPerFrame = m_fSecsPerTick * m_fTicksPerFrame;
|
||||
float fStart = GlobalAnim.rangeGlobal.start * fSecsPerFrame;
|
||||
float fStop = GlobalAnim.rangeGlobal.end * fSecsPerFrame;
|
||||
|
||||
do {
|
||||
AnimData &LocalAnim = m_arrAnimations[it->nAnimId];
|
||||
assert (LocalAnim.nGlobalAnimId == nGlobalAnimId);
|
||||
LocalAnim.fStart = fStart;
|
||||
LocalAnim.fStop = fStop;
|
||||
|
||||
if (GlobalAnim.IsLoaded())
|
||||
// bind loaded controllers (if any) to the bones
|
||||
for (unsigned nBone = 0; nBone < numBoneInfos(); ++nBone)
|
||||
{
|
||||
CryBoneInfo& rBoneInfo = getBoneInfo(nBone);
|
||||
rBoneInfo.BindController (GlobalAnim, it->nAnimId);
|
||||
}
|
||||
} while(++it != m_arrAnimByGlobalId.end() && m_arrAnimations[it->nAnimId].nGlobalAnimId == nGlobalAnimId);
|
||||
}
|
||||
}
|
||||
|
||||
// adds an animation record to the animation set
|
||||
void CryModelAnimationContainer::RegisterAnimation(const char * szFileName, int nGlobalAnimId, const char* szAnimName)
|
||||
{
|
||||
const CControllerManager::Animation& GlobalAnim = m_pControllerManager->GetAnimation (nGlobalAnimId);
|
||||
m_fTicksPerFrame = (float)GlobalAnim.nTicksPerFrame;
|
||||
m_fSecsPerTick = (float)GlobalAnim.fSecsPerTick;
|
||||
float fSecsPerFrame = m_fSecsPerTick * m_fTicksPerFrame;
|
||||
float fStart = GlobalAnim.rangeGlobal.start * fSecsPerFrame;
|
||||
float fStop = GlobalAnim.rangeGlobal.end * fSecsPerFrame;
|
||||
|
||||
AnimData LocalAnim;
|
||||
LocalAnim.fStart = fStart;
|
||||
LocalAnim.fStop = fStop;
|
||||
|
||||
LocalAnim.bLoop = stristr(szFileName, "loop") != NULL;
|
||||
LocalAnim.nGlobalAnimId = nGlobalAnimId;
|
||||
LocalAnim.strName = szAnimName;
|
||||
|
||||
// hack to fix random goto default pose problem
|
||||
if(LocalAnim.fStop <= LocalAnim.fStart)
|
||||
LocalAnim.fStop = LocalAnim.fStart+1.0f/30.0f;
|
||||
|
||||
if(LocalAnim.fStart > LocalAnim.fStop)
|
||||
{
|
||||
g_GetLog()->LogToFile (" Invalid start(%g) > stop(%g) values in animation: %s", LocalAnim.fStart, LocalAnim.fStop, LocalAnim.strName.c_str());
|
||||
return;
|
||||
}
|
||||
int nLocalAnimId = m_arrAnimations.size();
|
||||
m_arrAnimations.push_back(LocalAnim);
|
||||
m_arrAnimByGlobalId.insert (std::lower_bound(m_arrAnimByGlobalId.begin(), m_arrAnimByGlobalId.end(), nGlobalAnimId, AnimationGlobIdPred(m_arrAnimations)), LocalAnimId(nLocalAnimId));
|
||||
m_arrAnimByLocalName.insert (std::lower_bound(m_arrAnimByLocalName.begin(), m_arrAnimByLocalName.end(), szAnimName, AnimationNamePred(m_arrAnimations)), nLocalAnimId);
|
||||
selfValidate();
|
||||
|
||||
// The caller MUST TAKE CARE to bind the animation if it's already loaded before it has registered itself within this manager
|
||||
m_pControllerManager->AnimationAddRef(nGlobalAnimId, this);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Loads animation file. Returns the global anim id of the file, or -1 if error
|
||||
// SIDE EFFECT NOTES:
|
||||
// THis function does not put up a warning in the case the animation couldn't be loaded.
|
||||
// It returns an error (false) and the caller must process it.
|
||||
int CryModelAnimationContainer::LoadCAF(const char * szFileName, float fScale, int nAnimID, const char * szAnimName, unsigned nGlobalAnimFlags)
|
||||
{
|
||||
int nGlobalAnimID;
|
||||
|
||||
int nLocalAnimId = findAnimation (szAnimName);
|
||||
|
||||
if (nLocalAnimId == -1)
|
||||
{
|
||||
AUTO_PROFILE_SECTION(g_dTimeAnimLoadFile);
|
||||
// find already loaded controllers
|
||||
nGlobalAnimID = m_pControllerManager->StartLoadAnimation (szFileName, fScale, nGlobalAnimFlags);
|
||||
|
||||
if (nGlobalAnimID < 0)
|
||||
return nGlobalAnimID;
|
||||
|
||||
m_pControllerManager->GetAnimation(nGlobalAnimID).nFlags |= nGlobalAnimFlags;
|
||||
|
||||
RegisterAnimation(szFileName, nGlobalAnimID, szAnimName);
|
||||
|
||||
if (!g_GetCVars()->ca_AnimationDeferredLoad() || (nGlobalAnimFlags&GlobalAnimation::FLAGS_DISABLE_DELAY_LOAD))
|
||||
{
|
||||
if (g_GetCVars()->ca_Debug() && (g_GetCVars()->ca_AnimationUnloadDelay()>0 || !(nGlobalAnimFlags&GlobalAnimation::FLAGS_DISABLE_AUTO_UNLOAD)))
|
||||
g_GetLog()->LogWarning ("\003Performance penalty: animation deferred load is disabled, but automatic animation unload is enabled. This will most surely lead to poor performance upon loading the level (because everything is loaded, more memory is used and massive precalculations are needed) and even some time after (because of animation controllers trashing) (animation %s)", szAnimName);
|
||||
// deferred load is disabled, try to load this animation immediately
|
||||
|
||||
m_pControllerManager->LoadAnimation (nGlobalAnimID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nGlobalAnimID = m_arrAnimations[nLocalAnimId].nGlobalAnimId;
|
||||
g_GetLog()->LogError ("\003Trying to load animation with alias \"%s\" from file \"%s\" into the animation container. Such animation alias already exists and uses file \"%s\". Please use another animation alias.",
|
||||
szAnimName,
|
||||
szFileName,
|
||||
m_pControllerManager->GetAnimation(nGlobalAnimID).strFileName.c_str());
|
||||
}
|
||||
|
||||
return nGlobalAnimID;
|
||||
}
|
||||
|
||||
// Returns the index of the animation in the set, -1 if there's no such animation
|
||||
int CryModelAnimationContainer::Find (const char* szAnimationName)
|
||||
{
|
||||
if (szAnimationName[0] == '#')
|
||||
return int(m_arrAnimations.size() + findMorphTarget (szAnimationName));
|
||||
else
|
||||
return findAnimation(szAnimationName);
|
||||
}
|
||||
|
||||
|
||||
//! Returns the index of the morph target in the set, -1 if there's no such morph target
|
||||
int CryModelAnimationContainer::FindMorphTarget (const char* szMorphTarget)
|
||||
{
|
||||
return findMorphTarget(szMorphTarget);
|
||||
}
|
||||
|
||||
// returns the index of the animation
|
||||
int CryModelAnimationContainer::findAnimation (const char*szAnimationName)
|
||||
{
|
||||
int nResult = -1;
|
||||
std::vector<int>::iterator it = std::lower_bound(m_arrAnimByLocalName.begin(), m_arrAnimByLocalName.end(), szAnimationName, AnimationNamePred(m_arrAnimations));
|
||||
if (it != m_arrAnimByLocalName.end() && !stricmp(m_arrAnimations[*it].strName.c_str(),szAnimationName))
|
||||
nResult = *it;
|
||||
|
||||
#ifdef _DEBUG
|
||||
int nTestResult = -1;
|
||||
AnimationArray::iterator itTest = m_arrAnimations.begin(), itTestEnd = m_arrAnimations.end();
|
||||
|
||||
for (; itTest != itTestEnd; ++itTest)
|
||||
if(!stricmp(szAnimationName, itTest->strName.c_str()))
|
||||
{
|
||||
nTestResult = itTest - m_arrAnimations.begin();
|
||||
break;
|
||||
}
|
||||
|
||||
assert (nTestResult == nResult);
|
||||
#endif
|
||||
|
||||
return nResult;
|
||||
}
|
||||
|
||||
// returns the index of the morph target, in the indexation of the array of morph targets
|
||||
int CryModelAnimationContainer::findMorphTarget (const char* szMorphTargetName)
|
||||
{
|
||||
for (int i = 0; i < (int)m_arrMorphTargets.size(); ++i)
|
||||
if (!stricmp(m_arrMorphTargets[i].getNameCStr(), szMorphTargetName))
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Returns the number of animations in this set
|
||||
int CryModelAnimationContainer::Count()
|
||||
{
|
||||
return (int)(m_arrAnimations.size() + m_arrMorphTargets.size());
|
||||
}
|
||||
|
||||
//! Returns the number of morph targets in the set
|
||||
int CryModelAnimationContainer::CountMorphTargets()
|
||||
{
|
||||
return (int)m_arrMorphTargets.size();
|
||||
}
|
||||
|
||||
|
||||
// Returns the given animation length, in seconds
|
||||
float CryModelAnimationContainer::GetLength (int nAnimationId)
|
||||
{
|
||||
if ((unsigned)nAnimationId < numAnimations())
|
||||
{
|
||||
m_pControllerManager->LoadAnimationInfo(m_arrAnimations[nAnimationId].nGlobalAnimId);
|
||||
return m_arrAnimations[nAnimationId].getLength();
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
//! Returns the given animation's start, in seconds; 0 if the id is invalid
|
||||
float CryModelAnimationContainer::GetStart (int nAnimationId)
|
||||
{
|
||||
if ((unsigned)nAnimationId < numAnimations())
|
||||
{
|
||||
m_pControllerManager->LoadAnimationInfo(m_arrAnimations[nAnimationId].nGlobalAnimId);
|
||||
return m_arrAnimations[nAnimationId].fStart;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Returns the given animation name
|
||||
const char* CryModelAnimationContainer::GetName (int nAnimationId)
|
||||
{
|
||||
if (nAnimationId >=0)
|
||||
{
|
||||
if (nAnimationId < (int)m_arrAnimations.size())
|
||||
return m_arrAnimations[nAnimationId].strName.c_str();
|
||||
nAnimationId -= (int)m_arrAnimations.size();
|
||||
if (nAnimationId < (int)m_arrMorphTargets.size())
|
||||
return m_arrMorphTargets[nAnimationId].getNameCStr();
|
||||
return "!ANIMATION ID OUT OF RANGE!";
|
||||
}
|
||||
else
|
||||
return "!NEGATIVE ANIMATION ID!";
|
||||
}
|
||||
|
||||
//! Returns the name of the morph target
|
||||
const char* CryModelAnimationContainer::GetNameMorphTarget (int nMorphTargetId)
|
||||
{
|
||||
if (nMorphTargetId< 0)
|
||||
return "!NEGATIVE MORPH TARGET ID!";
|
||||
if ((unsigned)nMorphTargetId >= m_arrMorphTargets.size())
|
||||
return "!MORPH TARGET ID OUT OF RANGE!";
|
||||
return m_arrMorphTargets[nMorphTargetId].getNameCStr();
|
||||
}
|
||||
|
||||
|
||||
// Retrieves the animation loop flag
|
||||
bool CryModelAnimationContainer::IsLoop (int nAnimationId)
|
||||
{
|
||||
if ((unsigned)nAnimationId < numAnimations())
|
||||
{
|
||||
m_pControllerManager->LoadAnimationInfo(m_arrAnimations[nAnimationId].nGlobalAnimId);
|
||||
return m_arrAnimations[nAnimationId].bLoop;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// updates the physics info of the given lod from the given bone animation descriptor
|
||||
void CryModelAnimationContainer::UpdateRootBonePhysics (const BONEANIM_CHUNK_DESC* pChunk, unsigned nChunkSize, int nLodLevel)
|
||||
{
|
||||
if (numBoneInfos())
|
||||
getRootBoneInfo().UpdateHierarchyPhysics (pChunk, nChunkSize, nLodLevel);
|
||||
}
|
||||
|
||||
// finds the bone by its name; returns the index of the bone, or -1 if not found
|
||||
// TODO: for performance, we could once build a map name->index
|
||||
int CryModelAnimationContainer::findBone (const char* szName)const
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
int nTestResult = -1;
|
||||
TFixedArray<CryBoneInfo>::const_iterator it = m_arrBones.begin(), itEnd = it + m_arrBones.size();
|
||||
for (; it != itEnd; ++it)
|
||||
{
|
||||
if (!strcmp(it->getNameCStr(), szName))
|
||||
{
|
||||
nTestResult = it - m_arrBones.begin();
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int nResult = find_in_map(m_mapBoneNameIndex, szName, -1);
|
||||
#ifdef _DEBUG
|
||||
assert (nResult == nTestResult);
|
||||
#endif
|
||||
return nResult;
|
||||
}
|
||||
|
||||
void CryModelAnimationContainer::onBonesChanged()
|
||||
{
|
||||
m_mapBoneNameIndex.clear();
|
||||
for (unsigned i = 0; i < m_arrBones.size(); ++i)
|
||||
m_mapBoneNameIndex[m_arrBones[i].getNameCStr()] = i;
|
||||
}
|
||||
|
||||
void CryModelAnimationContainer::onBonePhysicsChanged()
|
||||
{
|
||||
for (unsigned i = 0; i < m_arrBones.size(); ++i)
|
||||
getBoneInfo(i).PostInitialize();
|
||||
}
|
||||
|
||||
|
||||
//! Performs post-initialization. This step is requred to initialize the pPhysGeom of the bones
|
||||
//! After the bone has been loaded but before it is first used. When the bone is first loaded, pPhysGeom
|
||||
//! is set to the value equal to the chunk id in the file where the physical geometry (BoneMesh) chunk is kept.
|
||||
//! After those chunks are loaded, and chunk ids are mapped to the registered physical geometry objects,
|
||||
//! call this function to replace pPhysGeom chunk ids with the actual physical geometry object pointers.
|
||||
//! NOTE:
|
||||
//! The entries of the map that were used are deleted upon exit
|
||||
bool CryModelAnimationContainer::PostInitBonePhysGeom (ChunkIdToPhysGeomMap& mapChunkIdToPhysGeom, int nLodLevel)
|
||||
{
|
||||
// map each bone,
|
||||
// and set the hasphysics to 1 if at least one bone has recognized and mapped its physical geometry
|
||||
bool bHasPhysics = false;
|
||||
for (unsigned i=0; i < numBoneInfos(); ++i)
|
||||
{
|
||||
if (getBoneInfo(i).PostInitPhysGeom (mapChunkIdToPhysGeom, nLodLevel))
|
||||
// yes, this model has the physics
|
||||
bHasPhysics = true;
|
||||
}
|
||||
return bHasPhysics;
|
||||
}
|
||||
|
||||
|
||||
//! Modifies the animation loop flag
|
||||
void CryModelAnimationContainer::SetLoop (int nAnimationId, bool bIsLooped)
|
||||
{
|
||||
if ((unsigned)nAnimationId < numAnimations())
|
||||
{
|
||||
m_pControllerManager->LoadAnimationInfo(m_arrAnimations[nAnimationId].nGlobalAnimId);
|
||||
m_arrAnimations[nAnimationId].bLoop = bIsLooped;
|
||||
}
|
||||
}
|
||||
|
||||
// returns the reference to the given animation , or the default animation, if the animation
|
||||
// id is out of range
|
||||
const AnimData &CryModelAnimationContainer::getAnimation (int nAnimationId) const
|
||||
{
|
||||
if ((size_t)nAnimationId < m_arrAnimations.size())
|
||||
return m_arrAnimations[nAnimationId];
|
||||
else
|
||||
{
|
||||
static const AnimData DefaultAnimation;
|
||||
return DefaultAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
// records statistics about the given animation (the id is the same as for getAnimation()):
|
||||
// should be called upon ticking the animation
|
||||
void CryModelAnimationContainer::OnAnimationTick(int nAnimationId)
|
||||
{
|
||||
m_pControllerManager->OnTickAnimation(getAnimation(nAnimationId).nGlobalAnimId);
|
||||
}
|
||||
void CryModelAnimationContainer::OnAnimationApply(int nAnimationId)
|
||||
{
|
||||
m_pControllerManager->OnApplyAnimation(getAnimation(nAnimationId).nGlobalAnimId);
|
||||
}
|
||||
// records statistics about the given animation (the id is the same as for getAnimation()):
|
||||
// should be called upon starting the animation
|
||||
void CryModelAnimationContainer::OnAnimationStart(int nAnimationId)
|
||||
{
|
||||
m_pControllerManager->OnStartAnimation(getAnimation(nAnimationId).nGlobalAnimId);
|
||||
}
|
||||
|
||||
|
||||
// returns the reference to the given morph target, or the default morph target if the
|
||||
// morph target id is out of range
|
||||
const CryGeomMorphTarget& CryModelAnimationContainer::getMorphTarget (int nMorphTargetId) const
|
||||
{
|
||||
if ((size_t)nMorphTargetId < m_arrMorphTargets.size())
|
||||
return m_arrMorphTargets[nMorphTargetId];
|
||||
else
|
||||
{
|
||||
assert (0);// this will lead to a memory leak, if it is called
|
||||
static const CryGeomMorphTarget DefaultMorphTarget;
|
||||
return DefaultMorphTarget;
|
||||
}
|
||||
}
|
||||
|
||||
// returns the number of morph targets
|
||||
unsigned CryModelAnimationContainer::numMorphTargets() const
|
||||
{
|
||||
return (unsigned)m_arrMorphTargets.size();
|
||||
}
|
||||
|
||||
// resets the morph target array, and allocates the given number of targets, with the given number of LODs each
|
||||
void CryModelAnimationContainer::reinitMorphTargets (unsigned numMorphTargets, unsigned numLODs)
|
||||
{
|
||||
m_arrMorphTargets.reinit(numMorphTargets);
|
||||
}
|
||||
|
||||
// prepares to load the specified number of CAFs by reserving the space for the controller pointers
|
||||
void CryModelAnimationContainer::prepareLoadCAFs (unsigned nReserveAnimations)
|
||||
{
|
||||
nReserveAnimations += (unsigned)m_arrAnimations.size();
|
||||
for (unsigned i = 0; i < numBoneInfos(); ++i)
|
||||
{
|
||||
CryBoneInfo::ControllerArray& arrControllers = getBoneInfo(i).m_arrControllers;
|
||||
arrControllers.reserve (nReserveAnimations);
|
||||
}
|
||||
m_arrAnimations.reserve (nReserveAnimations);
|
||||
m_arrAnimByGlobalId.reserve (nReserveAnimations);
|
||||
m_arrAnimByLocalName.reserve (nReserveAnimations);
|
||||
}
|
||||
|
||||
//! Deserializes the bones from the CCF chunk using serialization function from CryBoneInfo
|
||||
//! THe serialized data follows the given header; the total size (including the header) is passed
|
||||
bool CryModelAnimationContainer::loadCCGBones (const CCFBoneDescArrayHeader*pHeader, unsigned nSize)
|
||||
{
|
||||
unsigned nBone, numBones = pHeader->numBones;
|
||||
if (numBones > 1000)
|
||||
return false; // too many bones
|
||||
|
||||
m_arrBones.reinit (numBones);
|
||||
|
||||
const char* pBoneData = (const char*)(pHeader+1);
|
||||
const char* pDataEnd = (const char*)pHeader+ nSize;
|
||||
|
||||
for (nBone = 0; nBone < numBones; ++nBone)
|
||||
{
|
||||
if (pBoneData >= pDataEnd)
|
||||
return false;
|
||||
|
||||
CryBoneInfo& rBone = m_arrBones[nBone];
|
||||
unsigned nReadBytes = rBone.Serialize(false, (void*)pBoneData, pDataEnd - pBoneData);
|
||||
// we don't use the information from these pointers right now, because we bind physical geometry using bone id
|
||||
rBone.m_PhysInfo[0].pPhysGeom = rBone.m_PhysInfo[1].pPhysGeom = NULL;
|
||||
// we need the bone name to be in the lookup map
|
||||
m_mapBoneNameIndex[rBone.getNameCStr()] = nBone;
|
||||
|
||||
if (!nReadBytes)
|
||||
return false;
|
||||
|
||||
pBoneData += nReadBytes;
|
||||
|
||||
if (pBoneData > pDataEnd)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pBoneData != pDataEnd)
|
||||
g_GetLog()->LogWarning ("\003CryModelAnimationContainer::deserializeBones: %d bytes left unread in the bone chunk", pDataEnd - pBoneData);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// scales the skeleton (its initial pose)
|
||||
void CryModelAnimationContainer::scaleBones (float fScale)
|
||||
{
|
||||
for (unsigned nBone = 0; nBone < numBoneInfos(); ++nBone)
|
||||
m_arrBones[nBone].scale (fScale);
|
||||
}
|
||||
|
||||
|
||||
void CryModelAnimationContainer::selfValidate()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
assert (m_arrAnimByGlobalId.size() == m_arrAnimations.size());
|
||||
assert (m_arrAnimByLocalName.size() == m_arrAnimations.size());
|
||||
for (unsigned i = 0; i < m_arrAnimByGlobalId.size()-1; ++i)
|
||||
{
|
||||
assert (m_arrAnimations[m_arrAnimByGlobalId[i].nAnimId].nGlobalAnimId <= m_arrAnimations[m_arrAnimByGlobalId[i+1].nAnimId].nGlobalAnimId);
|
||||
assert (stricmp(m_arrAnimations[m_arrAnimByLocalName[i]].strName.c_str(), m_arrAnimations[m_arrAnimByLocalName[i+1]].strName.c_str())<0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t CryModelAnimationContainer::sizeofThis()const
|
||||
{
|
||||
size_t nSize = sizeof(*this);
|
||||
for (AnimationArray::const_iterator itAnim = m_arrAnimations.begin(); itAnim != m_arrAnimations.end(); ++itAnim)
|
||||
nSize += itAnim->sizeofThis();
|
||||
nSize += sizeofArray(m_arrAnimByGlobalId);
|
||||
nSize += sizeofArray(m_arrAnimByLocalName);
|
||||
|
||||
for (unsigned nBone = 0; nBone < m_arrBones.size(); ++nBone)
|
||||
nSize += m_arrBones[nBone].sizeofThis();
|
||||
|
||||
for (unsigned nMorphSkin = 0; nMorphSkin < m_arrMorphSkins.size(); ++nMorphSkin)
|
||||
nSize += m_arrMorphSkins[nMorphSkin].sizeofThis();
|
||||
|
||||
for (unsigned nMorphTarget = 0; nMorphTarget < m_arrMorphTargets.size(); ++nMorphTarget)
|
||||
nSize += m_arrMorphTargets[nMorphTarget].sizeofThis();
|
||||
|
||||
nSize += sizeofArray(m_arrTempBoneIdToIndex);
|
||||
for (std::set<string>::const_iterator it = m_setDummyAnimations.begin(); it != m_setDummyAnimations.end(); ++it)
|
||||
nSize += it->capacity() + sizeof(*it);
|
||||
return nSize;
|
||||
}
|
||||
|
||||
// returns the number of animations that aren't shared
|
||||
unsigned CryModelAnimationContainer::numUniqueAnimations()
|
||||
{
|
||||
unsigned numResult =0;
|
||||
for (AnimationArray::const_iterator it = m_arrAnimations.begin(); it != m_arrAnimations.end(); ++it)
|
||||
if (m_pControllerManager->GetAnimation(it->nGlobalAnimId).nRefCount == 1)
|
||||
++numResult;
|
||||
|
||||
return numResult;
|
||||
}
|
||||
|
||||
const AnimData* CryModelAnimationContainer::getAnimationInfo(unsigned i)
|
||||
{
|
||||
if(m_pControllerManager->LoadAnimationInfo(m_arrAnimations[i].nGlobalAnimId))
|
||||
return &m_arrAnimations[i];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//! Unloads animation from memory
|
||||
//! The client must take into account that the animation can be shared and, if unloaded, the other
|
||||
//! character models (animation sets) will have to load it back to use.
|
||||
void CryModelAnimationContainer::UnloadAnimation (int nAnimId)
|
||||
{
|
||||
if ((unsigned)nAnimId < m_arrAnimations.size())
|
||||
{
|
||||
m_pControllerManager->UnloadAnimation(m_arrAnimations[nAnimId].nGlobalAnimId);
|
||||
}
|
||||
}
|
||||
|
||||
//! Loads the animation data in memory. fWhenRequired is the timeout in seconds from current moment when
|
||||
//! the animation data will actually be required
|
||||
void CryModelAnimationContainer::StartLoadAnimation (int nAnimId, float fWhenRequired)
|
||||
{
|
||||
if ((unsigned)nAnimId < m_arrAnimations.size())
|
||||
{
|
||||
m_pControllerManager->LoadAnimation(m_arrAnimations[nAnimId].nGlobalAnimId);
|
||||
}
|
||||
}
|
||||
292
CryAnimation/CryModelAnimationContainer.h
Normal file
292
CryAnimation/CryModelAnimationContainer.h
Normal file
@@ -0,0 +1,292 @@
|
||||
#ifndef _CRY_MODEL_ANIMTATION_CONTAINER_HDR_
|
||||
#define _CRY_MODEL_ANIMTATION_CONTAINER_HDR_
|
||||
|
||||
#include "CryAnimationInfo.h"
|
||||
#include "CryBoneInfo.h"
|
||||
|
||||
template<class T>
|
||||
struct LessString
|
||||
{
|
||||
bool operator () (const char* left, const char* right)const
|
||||
{
|
||||
return strcmp(left, right) < 0;
|
||||
}
|
||||
bool operator () (const string& left, const string& right)const
|
||||
{
|
||||
return left < right;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Key, class Traits = LessString<Key> >
|
||||
class BoneNameHashCompare
|
||||
{
|
||||
Traits comp;
|
||||
public:
|
||||
enum { bucket_size = 8 };
|
||||
enum { min_buckets = 16 };
|
||||
BoneNameHashCompare(){}
|
||||
BoneNameHashCompare(Traits pred): comp(pred) {}
|
||||
|
||||
inline size_t operator( )( const Key& key ) const
|
||||
{
|
||||
int h = 5381;
|
||||
|
||||
for(int i = 0, k; k = key[i]; i++)
|
||||
h = ((h<<5)+h)^k; // bernstein k=33 xor
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
inline bool operator( )(
|
||||
const Key& _Key1,
|
||||
const Key& _Key2
|
||||
) const
|
||||
{
|
||||
return comp (_Key1, _Key2);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
class CryGeomMorphTarget;
|
||||
class CrySkinMorph;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Implementaiton of ICryAnimationSet, holding the information about animations
|
||||
// and bones for a single model. Animations also include the subclass of morph targets
|
||||
class CryModelAnimationContainer: public ICryAnimationSet
|
||||
{
|
||||
public:
|
||||
CryModelAnimationContainer (class CControllerManager * pControllerManager);
|
||||
~CryModelAnimationContainer();
|
||||
|
||||
// gets called when the given animation (by global id) is unloaded.
|
||||
// the animation controls need to be unbound to free up the memory
|
||||
void OnAnimationGlobalUnload(int nGlobalAnimId);
|
||||
|
||||
// gets called when the given animation (by global id) is loaded
|
||||
// the animation controls need to be bound at this point
|
||||
void OnAnimationGlobalLoad (int nGlobalAnimId);
|
||||
|
||||
// for internal use only
|
||||
//DECLARE_ARRAY_GETTER_METHODS (AnimData, AnimationInfo, Animations, m_arrAnimations);
|
||||
size_t numAnimations() const {return m_arrAnimations.size();}
|
||||
// tries to load the animation info if isn't present in memory; returns NULL if can't load
|
||||
const AnimData* getAnimationInfo(unsigned i);
|
||||
|
||||
//! Unloads animation from memory
|
||||
//! The client must take into account that the animation can be shared and, if unloaded, the other
|
||||
//! character models (animation sets) will have to load it back to use.
|
||||
virtual void UnloadAnimation (int nAnimId);
|
||||
|
||||
//! Loads the animation data in memory. fWhenRequired is the timeout in seconds from current moment when
|
||||
//! the animation data will actually be required
|
||||
virtual void StartLoadAnimation (int nAnimId, float fWhenRequired);
|
||||
|
||||
// returns the number of animations that aren't shared
|
||||
unsigned numUniqueAnimations();
|
||||
|
||||
// Returns the index of the animation in the set, -1 if there's no such animation
|
||||
virtual int Find (const char* szAnimationName);
|
||||
|
||||
//! Returns the index of the morph target in the set, -1 if there's no such morph target
|
||||
virtual int FindMorphTarget (const char* szMorphTarget);
|
||||
|
||||
// returns the index of the animation
|
||||
int findAnimation (const char*szAnimationName);
|
||||
|
||||
// returns the index of the morph target, in the indexation of the array of morph targets
|
||||
int findMorphTarget (const char* szMorphTargetName);
|
||||
|
||||
// Returns the number of animations in this set
|
||||
virtual int Count();
|
||||
|
||||
//! Returns the number of morph targets in the set
|
||||
virtual int CountMorphTargets();
|
||||
|
||||
// Returns the given animation length, in seconds
|
||||
virtual float GetLength (int nAnimationId);
|
||||
|
||||
//! Returns the given animation's start, in seconds; 0 if the id is invalid
|
||||
virtual float GetStart (int nAnimationId);
|
||||
|
||||
// Returns the given animation name
|
||||
virtual const char* GetName (int nAnimationId);
|
||||
//! Returns the name of the morph target
|
||||
const char* GetNameMorphTarget (int nMorphTargetId);
|
||||
|
||||
// Retrieves the animation loop flag
|
||||
virtual bool IsLoop (int nAnimationId);
|
||||
|
||||
|
||||
//! Modifies the animation loop flag
|
||||
virtual void SetLoop (int nAnimationId, bool bIsLooped);
|
||||
|
||||
//! Enables receiving OnEvent notification of specified animation for all instances using this model
|
||||
//virtual bool SetAnimationEvent(const char * szAnimName, int nFrameID, int nUserData, ICharInstanceSink * pCharInstanceSink);
|
||||
|
||||
DECLARE_ARRAY_GETTER_METHODS(CryBoneInfo, BoneInfo, BoneInfos, m_arrBones);
|
||||
|
||||
CryBoneInfo& getRootBoneInfo() {return getBoneInfo(0);}
|
||||
|
||||
// updates the physics info of the given lod from the given bone animation descriptor
|
||||
void UpdateRootBonePhysics (const BONEANIM_CHUNK_DESC* pChunk, unsigned nChunkSize, int nLodLevel);
|
||||
|
||||
// loads the root bone (and the hierarchy) and returns true if loaded successfully
|
||||
bool LoadBones (const BONEANIM_CHUNK_DESC* pChunk, unsigned nChunkSize, const TFixedArray<const char*>& arrBoneNames);
|
||||
|
||||
// finds the bone by its name; returns the index of the bone
|
||||
int findBone (const char* szName) const;
|
||||
|
||||
// FOR INTERNAL USE ONLY (Inside CryAnimation system)
|
||||
|
||||
// returns the reference to the given animation , or the default animation, if the animation
|
||||
// id is out of range
|
||||
const AnimData &getAnimation (int nAnimationId) const;
|
||||
// records statistics about the given animation (the id is the same as for getAnimation()):
|
||||
// should be called upon ticking the animation
|
||||
void OnAnimationTick(int nAnimationId);
|
||||
void OnAnimationApply(int nAnimationId);
|
||||
// records statistics about the given animation (the id is the same as for getAnimation()):
|
||||
// should be called upon starting the animation
|
||||
void OnAnimationStart(int nAnimationId);
|
||||
|
||||
// returns the reference to the given morph target, or the default morph target if the
|
||||
// morph target id is out of range
|
||||
const CryGeomMorphTarget& getMorphTarget (int nMorphTargetId) const;
|
||||
unsigned numMorphTargets() const;
|
||||
|
||||
float getTicksPerFrame()const {return m_fTicksPerFrame;}
|
||||
float getSecondsPerTick () const {return m_fSecsPerTick;}
|
||||
float getSecondsPerFrame () const {return m_fTicksPerFrame*m_fSecsPerTick;}
|
||||
float getTicksPerSecond () const {return float(1.0 / m_fSecsPerTick);}
|
||||
|
||||
// cleans up the unused memory in the arrays of controllers in the bones
|
||||
void shrinkControllerArrays();
|
||||
// prepares to load the specified number of CAFs by reserving the space for the controller pointers
|
||||
void prepareLoadCAFs (unsigned nReserveAnimations);
|
||||
|
||||
bool isDummyAnimation (const char* szAnimName) {return m_setDummyAnimations.find (szAnimName) != m_setDummyAnimations.end();}
|
||||
protected:
|
||||
void selfValidate();
|
||||
|
||||
// adds an animation record to the animation set
|
||||
void RegisterAnimation(const char * szFileName, int nGlobalAnimId, const char* szAnimName);
|
||||
void RegisterDummyAnimation (const char* szAnimName) {m_setDummyAnimations.insert (szAnimName);}
|
||||
|
||||
// when the animinfo is given, it's used to set the values of the global animation as if the animation has already been loaded once -
|
||||
// so that the next time the anim info is available and there's no need to load the animation synchronously
|
||||
// Returns the global anim id of the file, or -1 if error
|
||||
int LoadCAF (const char * szFileName, float fScale, int nAnimID, const char * szAnimName, unsigned nGlobalAnimFlags);
|
||||
|
||||
//! Performs post-initialization. This step is requred to initialize the pPhysGeom of the bones
|
||||
//! After the bone has been loaded but before it is first used. When the bone is first loaded, pPhysGeom
|
||||
//! is set to the value equal to the chunk id in the file where the physical geometry (BoneMesh) chunk is kept.
|
||||
//! After those chunks are loaded, and chunk ids are mapped to the registered physical geometry objects,
|
||||
//! call this function to replace pPhysGeom chunk ids with the actual physical geometry object pointers.
|
||||
//! NOTE:
|
||||
//! The entries of the map that were used are deleted
|
||||
typedef CryBoneInfo::ChunkIdToPhysGeomMap ChunkIdToPhysGeomMap;
|
||||
bool PostInitBonePhysGeom (ChunkIdToPhysGeomMap& mapChunkIdToPhysGeom, int nLodLevel);
|
||||
|
||||
//! Deserializes the bones from the CCF chunk using serialization function from CryBoneInfo
|
||||
//! THe serialized data follows the given header; the total size (including the header) is passed
|
||||
bool loadCCGBones (const struct CCFBoneDescArrayHeader*pHeader, unsigned nSize);
|
||||
// scales the skeleton (its initial pose)
|
||||
void scaleBones (float fScale);
|
||||
|
||||
void onBonesChanged();
|
||||
void onBonePhysicsChanged();
|
||||
protected:
|
||||
// the dummy animations: when they are requested, nothing should be played
|
||||
std::set<string> m_setDummyAnimations;
|
||||
// Animations List
|
||||
typedef std::vector<AnimData> AnimationArray;
|
||||
AnimationArray m_arrAnimations;
|
||||
struct LocalAnimId{
|
||||
int nAnimId;
|
||||
explicit LocalAnimId(int n):nAnimId(n){}
|
||||
};
|
||||
// the index vector: the animations in order of nGlobalAnimId
|
||||
std::vector<LocalAnimId> m_arrAnimByGlobalId;
|
||||
|
||||
// used to sort the m_arrAnimByGlobalId
|
||||
struct AnimationGlobIdPred
|
||||
{
|
||||
const std::vector<AnimData>& m_arrAnims;
|
||||
AnimationGlobIdPred(const std::vector<AnimData>& arrAnims):
|
||||
m_arrAnims(arrAnims)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator () (LocalAnimId left, LocalAnimId right)const
|
||||
{
|
||||
return m_arrAnims[left.nAnimId].nGlobalAnimId < m_arrAnims[right.nAnimId].nGlobalAnimId;
|
||||
}
|
||||
bool operator () (int left, LocalAnimId right)const
|
||||
{
|
||||
return left < m_arrAnims[right.nAnimId].nGlobalAnimId;
|
||||
}
|
||||
bool operator () (LocalAnimId left, int right)const
|
||||
{
|
||||
return m_arrAnims[left.nAnimId].nGlobalAnimId < right;
|
||||
}
|
||||
};
|
||||
|
||||
// the index vector: the animations in order of their local names
|
||||
std::vector<int> m_arrAnimByLocalName;
|
||||
|
||||
struct AnimationNamePred
|
||||
{
|
||||
const std::vector<AnimData>& m_arrAnims;
|
||||
AnimationNamePred(const std::vector<AnimData>& arrAnims):
|
||||
m_arrAnims(arrAnims)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator () (int left, int right)const
|
||||
{
|
||||
return stricmp(m_arrAnims[left].strName.c_str(),m_arrAnims[right].strName.c_str()) < 0;
|
||||
}
|
||||
bool operator () (const char* left, int right)const
|
||||
{
|
||||
return stricmp(left,m_arrAnims[right].strName.c_str()) < 0;
|
||||
}
|
||||
bool operator () (int left, const char* right)const
|
||||
{
|
||||
return stricmp(m_arrAnims[left].strName.c_str(),right) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
// the controller manager used for loading the animations
|
||||
class CControllerManager * m_pControllerManager;
|
||||
|
||||
// This is the bone hierarchy. All the bones of the hierarchy are present in this array
|
||||
TFixedArray<CryBoneInfo> m_arrBones;
|
||||
|
||||
typedef std::map<const char*, unsigned, LessString<const char*> > MapBoneNameIndex;
|
||||
MapBoneNameIndex m_mapBoneNameIndex;
|
||||
|
||||
// the array of morph targets for this (also currently treated as animations)
|
||||
typedef TFixedArray<CryGeomMorphTarget> CryGeomMorphTargetArray;
|
||||
CryGeomMorphTargetArray m_arrMorphTargets;
|
||||
|
||||
// the array of optimized morph targets, working on another skinning algorithm
|
||||
// only LOD-0 skins
|
||||
typedef TFixedArray<CrySkinMorph> CrySkinMorphArray;
|
||||
CrySkinMorphArray m_arrMorphSkins;
|
||||
|
||||
// resets the morph target array, and allocates the given number of targets, with the given number of LODs each
|
||||
void reinitMorphTargets (unsigned numMorphTargets, unsigned numLODs);
|
||||
|
||||
float m_fTicksPerFrame;
|
||||
float m_fSecsPerTick;
|
||||
|
||||
|
||||
// NOTE: this is to be deprecated
|
||||
TFixedArray<unsigned> m_arrTempBoneIdToIndex;
|
||||
|
||||
size_t sizeofThis()const;
|
||||
};
|
||||
|
||||
#endif
|
||||
32
CryAnimation/CryModelArrays.cpp
Normal file
32
CryAnimation/CryModelArrays.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CryEngine Source code
|
||||
//
|
||||
// File:CryModelArrays.cpp
|
||||
// Description: Implementation of CryModelState class
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <VertexBufferSource.h>
|
||||
#include <CryCompiledFile.h>
|
||||
#include <StringUtils.h>
|
||||
#include "CryModel.h"
|
||||
#include "CryModelState.h"
|
||||
#include "MeshIdx.h"
|
||||
#include "i3dengine.h"
|
||||
#include "CVars.h"
|
||||
#include "VertexBufferArrayDrivers.h"
|
||||
#include "CryCharDecalManager.h"
|
||||
#include "CryGeomMorphTarget.h"
|
||||
#include "CryModEffMorph.h"
|
||||
#include "DebugUtils.h"
|
||||
#include "RenderUtils.h"
|
||||
#include "CrySkinMorph.h"
|
||||
#include "SSEUtils.h"
|
||||
#include "IncContHeap.h"
|
||||
#include "drand.h"
|
||||
#include "StringUtils.h"
|
||||
#include "MakMatInfoFromMAT_ENTITY.h"
|
||||
|
||||
|
||||
741
CryAnimation/CryModelGeometryLoader.cpp
Normal file
741
CryAnimation/CryModelGeometryLoader.cpp
Normal file
@@ -0,0 +1,741 @@
|
||||
#include "stdafx.h"
|
||||
#include <StlUtils.h>
|
||||
#include <I3DEngine.h>
|
||||
|
||||
#include "CryModel.h"
|
||||
#include "CryGeomMorphTarget.h"
|
||||
#include "CryCharBody.h"
|
||||
#include "CryModelState.h"
|
||||
#include "ChunkFileReader.h"
|
||||
#include "CryModelGeometryLoader.h"
|
||||
#include "CryBoneHierarchyLoader.h"
|
||||
#include "StringUtils.h"
|
||||
#include "CVars.h"
|
||||
#include "CgfUtils.h"
|
||||
|
||||
CryModelGeometryLoader::CryModelGeometryLoader ():
|
||||
#ifdef DEBUG_STD_CONTAINERS
|
||||
m_arrGeomBoneNameTable ("CryModelGeometryLoader.m_arrGeomBoneNameTable"),
|
||||
m_mapLights ("CryModelGeometryLoader.mapLights"),
|
||||
m_mapObjectNodes ("CryModelGeometryLoader.mapObjectNodes"),
|
||||
m_arrTempBoneIdToIndex("TempBoneIdToIndex")
|
||||
m_mapLimbPhysGeoms("CryModelGeometryLoader.mapLimbPhysGeoms"),
|
||||
#endif
|
||||
m_numBoneLightBinds (0),
|
||||
m_pBoneLightBind(NULL),
|
||||
m_pModel (NULL),
|
||||
m_nLOD (-1),
|
||||
m_numMorphTargets (0)
|
||||
{
|
||||
}
|
||||
|
||||
CryModelGeometryLoader::~CryModelGeometryLoader ()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void CryModelGeometryLoader::clear()
|
||||
{
|
||||
m_pReader = NULL;
|
||||
m_arrGeomBoneNameTable.clear();
|
||||
m_numBoneLightBinds = 0;
|
||||
m_pBoneLightBind = NULL;
|
||||
m_mapLimbPhysGeoms.clear();
|
||||
m_pModel = NULL;
|
||||
m_nLOD = -1;
|
||||
m_mapLights.clear();
|
||||
m_mapObjectNodes.clear();
|
||||
m_numMorphTargets = 0;
|
||||
m_arrTempBoneIdToIndex.clear();
|
||||
|
||||
m_bFirstTimeWarningBoneGeometryMtl = true;
|
||||
m_bMeshFound = false;
|
||||
m_bGeometryFound = false;
|
||||
m_bBonesFound = false;
|
||||
m_nGeometryChunkID = 0;
|
||||
|
||||
m_pPhysicalGeometryManager = GetPhysicalWorld() ? GetPhysicalWorld()->GetGeomManager() : NULL;
|
||||
}
|
||||
|
||||
bool CryModelGeometryLoader::load (CryModel* pModel, CChunkFileReader* pReader, unsigned nLOD, float fScale)
|
||||
{
|
||||
//CAutoClear<CryModelGeometryLoader> _autoClean(this);
|
||||
clear();
|
||||
|
||||
// lock the reader - we'll use the reader's memory to represent some of the structures
|
||||
m_pReader = pReader;
|
||||
m_pModel = pModel;
|
||||
m_nLOD = nLOD;
|
||||
m_fScale = fScale;
|
||||
|
||||
if (!prepare())
|
||||
return false;
|
||||
|
||||
if (!loadStage1())
|
||||
return false;
|
||||
indicateProgress();
|
||||
|
||||
if (!loadMaterials())
|
||||
return false;
|
||||
|
||||
// we load bones, mesh and bone mesh here
|
||||
// in LOD 0 we initialize the bones, in LOD 1 we update their physics; lod 2 has no effect
|
||||
if (!loadStage2())
|
||||
return false;
|
||||
|
||||
if (!loadMorphTargets())
|
||||
return false;
|
||||
|
||||
if (!rebindBoneIndices())
|
||||
return false;
|
||||
|
||||
if (!finalize())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CryModelGeometryLoader::prepare()
|
||||
{
|
||||
//read the file header
|
||||
const FILE_HEADER& fh = m_pReader->getFileHeader();
|
||||
|
||||
if(fh.Version != GeomFileVersion || fh.FileType != FileType_Geom)
|
||||
// invalid file version or type
|
||||
return false;
|
||||
|
||||
if(!m_pModel->m_pDefaultModelState) // load bones only for 0 lod
|
||||
m_pModel->m_pDefaultModelState = new CryModelState(m_pModel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// loads the materials; returns true if succeeds
|
||||
bool CryModelGeometryLoader::loadMaterials()
|
||||
{
|
||||
m_nFirstMat = (unsigned)m_pModel->m_arrMaterials.size(); // the base number of materials that was before loading this geometry (used to shift the material references)
|
||||
unsigned numLoadedMaterials = m_nFirstMat; // the number of actually loaded objects
|
||||
m_pModel->m_arrMaterials.resize (numLoadedMaterials + m_pReader->numChunksOfType(ChunkType_Mtl));
|
||||
|
||||
int i;
|
||||
for (i = 0; i < m_pReader->numChunks(); i++)
|
||||
{
|
||||
const CHUNK_HEADER& chunkHeader = m_pReader->getChunkHeader(i);
|
||||
if (chunkHeader.ChunkType == ChunkType_Mtl)
|
||||
{
|
||||
const void* pChunk = m_pReader->getChunkData(i);
|
||||
unsigned nChunkSize = m_pReader->getChunkSize(i);
|
||||
CMatEntityNameTokenizer mt;
|
||||
if (loadMaterial ((CHUNK_HEADER*)pChunk, nChunkSize, m_pModel->m_arrMaterials[numLoadedMaterials], mt))
|
||||
++numLoadedMaterials;
|
||||
else
|
||||
{
|
||||
// some materials are MULTI, and they're ignored during the load, and that's normal
|
||||
//g_GetLog()->LogToFile ("*WARNING* Cannot load standard material (chunk id 0x%08X) from file %s", chunkHeader.ChunkID, getFilePathCStr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_pModel->m_arrMaterials.resize (numLoadedMaterials);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Attempts to load the material from the given material chunk
|
||||
// If the chunk is MULTi material or too old version, returns an error
|
||||
// If successful, the read material is copied to the rMaterial
|
||||
// PARAMETERS:
|
||||
// pMaterialChunk - the chunk to load from
|
||||
// nChunkSize - the size of the chunk
|
||||
// rMateria - [OUT] - upon success, contains the material data upon exit
|
||||
// RETURNS:
|
||||
// true - the material was loaded successfully
|
||||
// false - the material was not loaded
|
||||
bool CryModelGeometryLoader::loadMaterial (CHUNK_HEADER* pMaterialChunk, unsigned nChunkSize, MAT_ENTITY& rMaterial, CMatEntityNameTokenizer& mt)
|
||||
{
|
||||
IPhysMaterialEnumerator *pMatEnum = Get3DEngine()->GetPhysMaterialEnumerator();
|
||||
MatChunkLoadErrorEnum nError = LoadMatEntity(*pMaterialChunk, pMaterialChunk, nChunkSize, rMaterial);
|
||||
if (nError == MCLE_Success)
|
||||
{
|
||||
mt.tokenize (rMaterial.name);
|
||||
if (pMaterialChunk->ChunkVersion == MTL_CHUNK_DESC_0746::VERSION)
|
||||
{
|
||||
if (*mt.szPhysMtl)
|
||||
rMaterial.iGameID = pMatEnum ? pMatEnum->EnumPhysMaterial(mt.szPhysMtl) : 0;
|
||||
else
|
||||
rMaterial.iGameID = 0;
|
||||
}
|
||||
rMaterial = rMaterial;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nError!= MCLE_IgnoredType)
|
||||
g_GetLog()->LogError ("cannot read material chunk 0x%X", pMaterialChunk->ChunkID);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// loads the bone name list into m_arrGeomBoneNameTable
|
||||
// m_arrGeomBoneNameTable points to the chunk data places where the actual names are; so no need to deallocate the strings
|
||||
bool CryModelGeometryLoader::loadBoneNameList (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize)
|
||||
{
|
||||
bool bOk = LoadBoneNameList (chunkHeader, pChunk, nChunkSize, m_arrGeomBoneNameTable);
|
||||
if (!bOk)
|
||||
return false;
|
||||
|
||||
if (m_nLOD> 0)
|
||||
{
|
||||
if (m_arrGeomBoneNameTable.size() != m_pModel->numBoneInfos())
|
||||
{
|
||||
g_GetLog()->LogError ("lod %d file %s has inconsistent number of bones (%d, expected %d). Please re-export the lod", m_nLOD, getFilePathCStr(), m_arrGeomBoneNameTable.size(), m_pModel->numBoneInfos());
|
||||
|
||||
char str[256];
|
||||
|
||||
if(g_GetISystem()->GetSSFileInfo(m_pModel->m_pBody->GetFilePathCStr(),str,256))
|
||||
g_GetLog()->LogToFile (" %s",str);
|
||||
|
||||
//return true; // we only load the table of bone names for lod 0
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// loads the bone light binding array
|
||||
bool CryModelGeometryLoader::loadBoneLightBinding (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize)
|
||||
{
|
||||
const BONELIGHTBINDING_CHUNK_DESC_0001* pBLBChunk = (const BONELIGHTBINDING_CHUNK_DESC_0001*)pChunk;
|
||||
m_pBoneLightBind = (const SBoneLightBind*)(pBLBChunk+1);
|
||||
m_numBoneLightBinds = pBLBChunk->numBindings;
|
||||
|
||||
if (((const char*)pBLBChunk) + nChunkSize > (const char*)(m_pBoneLightBind + m_numBoneLightBinds))
|
||||
{
|
||||
g_GetLog()->LogError ("\003BoneLightBinding chunk in %s is truncated", m_pModel->m_pBody->GetNameCStr());
|
||||
m_numBoneLightBinds = 0;
|
||||
m_pBoneLightBind = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CryModelGeometryLoader::loadBoneInitialPos (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize)
|
||||
{
|
||||
const BONEINITIALPOS_CHUNK_DESC_0001* pBIPChunk = (const BONEINITIALPOS_CHUNK_DESC_0001*)pChunk;
|
||||
if (chunkHeader.ChunkVersion != pBIPChunk->VERSION)
|
||||
{
|
||||
g_GetLog()->LogError ("\003BoneInitialPos chunk is of unknown version %d", chunkHeader.ChunkVersion);
|
||||
return false; // unrecognized version
|
||||
}
|
||||
|
||||
unsigned numBytesLoaded = m_BoneLoader.load (pBIPChunk, nChunkSize);
|
||||
if (!numBytesLoaded)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns true if the bone infos have the initial position set
|
||||
bool CryModelGeometryLoader::hasBoneInfoInitPos()
|
||||
{
|
||||
return m_BoneLoader.hasInitPos();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// for LODs > 0, builds the m_arrTempBoneIdToIndex by matching the bone names
|
||||
// against the bone names in the already loaded LOD 0
|
||||
void CryModelGeometryLoader::buildBoneIndicesByNames()
|
||||
{
|
||||
// We don't keep the bone id map in the model anymore - so we map ids by names anyway
|
||||
assert (m_nLOD > 0 && m_arrTempBoneIdToIndex.empty());
|
||||
|
||||
// since the bone map is not native to this file, map the bone indices by names:
|
||||
// take the names of bones in the LOD file and try to match them against the bone names
|
||||
// in the master file (lod0). For those without corresponding names, set the default of 0
|
||||
// This is used to map the LOD-bone-id to the Master-bone-index
|
||||
m_arrTempBoneIdToIndex.reinit((unsigned)m_arrGeomBoneNameTable.size(), 0);
|
||||
// TODO: for performance, we could once build a map name->index
|
||||
for (unsigned i = 0; i < m_arrGeomBoneNameTable.size(); ++i)
|
||||
{
|
||||
int nBone = m_pModel->findBone (m_arrGeomBoneNameTable[i]);
|
||||
if (nBone >= 0)
|
||||
m_arrTempBoneIdToIndex[i] = nBone;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// load the stuff that doesn't need anything else to be already loaded:
|
||||
// + load bone name table,
|
||||
// + for LOD>0, associate the bones with bone indices
|
||||
// + load bone-light binding table,
|
||||
// + load light and node maps,
|
||||
// + calculate the number of morph targets
|
||||
bool CryModelGeometryLoader::loadStage1()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < m_pReader->numChunks(); i++)
|
||||
{
|
||||
const CHUNK_HEADER& chunkHeader = m_pReader->getChunkHeader(i);
|
||||
const void* pChunk = m_pReader->getChunkData(i);
|
||||
unsigned nChunkSize = m_pReader->getChunkSize(i);
|
||||
switch (chunkHeader.ChunkType)
|
||||
{
|
||||
case ChunkType_BoneNameList:
|
||||
if (!loadBoneNameList(chunkHeader, pChunk, nChunkSize))
|
||||
return false;
|
||||
if (m_nLOD > 0)
|
||||
// if the LOD0 already was loaded, then we should find the index of each existing bone by name
|
||||
buildBoneIndicesByNames();
|
||||
break;
|
||||
|
||||
case ChunkType_BoneLightBinding:
|
||||
if (!loadBoneLightBinding(chunkHeader, pChunk, nChunkSize))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case ChunkType_BoneInitialPos:
|
||||
if (!loadBoneInitialPos(chunkHeader, pChunk, nChunkSize))
|
||||
return false;
|
||||
// we should scale the initial position in case we use it for bone rest pose initialization
|
||||
m_BoneLoader.scale (m_fScale);
|
||||
break;
|
||||
|
||||
case ChunkType_MeshMorphTarget:
|
||||
if (chunkHeader.ChunkVersion == MESHMORPHTARGET_CHUNK_DESC_0001::VERSION)
|
||||
++m_numMorphTargets;
|
||||
break;
|
||||
|
||||
case ChunkType_Light:
|
||||
{
|
||||
const LIGHT_CHUNK_DESC* pLight = (const LIGHT_CHUNK_DESC*)pChunk;
|
||||
m_mapLights[chunkHeader.ChunkID] = pLight;
|
||||
}
|
||||
break;
|
||||
|
||||
case ChunkType_Node:
|
||||
{
|
||||
const NODE_CHUNK_DESC* pNode = (const NODE_CHUNK_DESC*)pChunk;
|
||||
m_mapObjectNodes[pNode->ObjectID] = pNode;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// loads the stuff that needs the stuff loaded at stage 1
|
||||
bool CryModelGeometryLoader::loadStage2()
|
||||
{
|
||||
//check chunks
|
||||
for (int i = 0; i < m_pReader->numChunks(); i++)
|
||||
{
|
||||
const CHUNK_HEADER& chunkHeader = m_pReader->getChunkHeader(i);
|
||||
const void* pChunk = m_pReader->getChunkData(i);
|
||||
unsigned nChunkSize = m_pReader->getChunkSize(i);
|
||||
|
||||
switch (chunkHeader.ChunkType)
|
||||
{
|
||||
case ChunkType_Mesh:
|
||||
if (!loadMesh (chunkHeader, pChunk, nChunkSize))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case ChunkType_BoneAnim:
|
||||
if (!loadBoneAnim (chunkHeader, pChunk, nChunkSize))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case ChunkType_BoneMesh:
|
||||
if (!loadBoneMesh (chunkHeader, pChunk, nChunkSize))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!m_bGeometryFound)
|
||||
{
|
||||
g_GetLog()->LogToFile("File %s contains no geometry", getFilePathCStr());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// loads the morph targets - after the other things are known
|
||||
bool CryModelGeometryLoader::loadMorphTargets()
|
||||
{
|
||||
if (m_nLOD > 0) // we don't support the LOD>0 morph targets yet
|
||||
return true;
|
||||
|
||||
m_pModel->reinitMorphTargets (m_numMorphTargets, m_pModel->numLODs());
|
||||
// prepare to load the morph targets
|
||||
m_numMorphTargets = 0; // the morph target currently being loaded
|
||||
|
||||
// load the morph targets
|
||||
for (int i = 0; i < m_pReader->numChunks(); i++)
|
||||
{
|
||||
const CHUNK_HEADER& chunkHeader = m_pReader->getChunkHeader(i);
|
||||
const void* pChunk = m_pReader->getChunkData(i);
|
||||
unsigned nChunkSize = m_pReader->getChunkSize(i);
|
||||
switch (chunkHeader.ChunkType)
|
||||
{
|
||||
case ChunkType_MeshMorphTarget:
|
||||
if (chunkHeader.ChunkVersion == MESHMORPHTARGET_CHUNK_DESC_0001::VERSION)
|
||||
{
|
||||
const MESHMORPHTARGET_CHUNK_DESC_0001* pMeshMorphTarget = (const MESHMORPHTARGET_CHUNK_DESC_0001*)pChunk;
|
||||
if (pMeshMorphTarget->nChunkIdMesh == m_nGeometryChunkID)
|
||||
{
|
||||
CryGeomMorphTarget& rMorphTarget = m_pModel->m_arrMorphTargets[m_numMorphTargets];
|
||||
if (rMorphTarget.load (m_nLOD,pMeshMorphTarget, nChunkSize) == nChunkSize)
|
||||
{
|
||||
// rotate the object in the world coordinates for the rest
|
||||
const NODE_CHUNK_DESC* pNodeChunk = find_in_map(m_mapObjectNodes, m_nGeometryChunkID, (const NODE_CHUNK_DESC *)NULL);
|
||||
if (pNodeChunk)
|
||||
{
|
||||
Matrix44 tm = pNodeChunk->tm;
|
||||
tm *= m_fScale;
|
||||
rMorphTarget.transform (m_nLOD, tm);
|
||||
//rMorphTarget.scale(m_nLOD, scale);
|
||||
}
|
||||
else
|
||||
rMorphTarget.scale (m_nLOD, m_fScale);
|
||||
|
||||
++m_numMorphTargets;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// clean up the unused morph targets
|
||||
if (m_pModel->m_arrMorphTargets.size() != m_numMorphTargets)
|
||||
m_pModel->m_arrMorphTargets.resize (m_numMorphTargets); // impossible situation in the current conditions of one mesh per file
|
||||
|
||||
if (!m_bBonesFound && !m_numMorphTargets && m_nLOD == 0)
|
||||
{
|
||||
// if there's no bones, then it's an error: the mesh without bones should not go through the Animation module
|
||||
g_GetLog()->LogWarning ("\003CryModel::LoadGeomChunks: Neither bones nor morph targets found in %s", m_pModel->m_pBody->GetNameCStr());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// corrects the bone ids in the geometry info according to the new numeration of the bones
|
||||
bool CryModelGeometryLoader::rebindBoneIndices()
|
||||
{
|
||||
if (!m_arrGeomBoneNameTable.empty())
|
||||
m_pModel->getGeometryInfo(m_nLOD)->remapBoneIds(&m_arrTempBoneIdToIndex[0], (unsigned)m_arrTempBoneIdToIndex.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------
|
||||
|
||||
bool CryModelGeometryLoader::finalize()
|
||||
{
|
||||
if (!m_nLOD)
|
||||
{
|
||||
m_pModel->m_pDefaultModelState->m_bHasPhysics = false;
|
||||
// build the bone index by name and call PostInitialize for bone infos
|
||||
m_pModel->onBonesChanged();
|
||||
}
|
||||
if (m_nLOD < 2)
|
||||
{
|
||||
// change the pPhysGeom from indices (chunk ids) to the actual physical geometry pointers
|
||||
// This modifies the given map by deleting or zeroing elements of the map that were used during the mapping
|
||||
if (m_pModel->PostInitBonePhysGeom (m_mapLimbPhysGeoms, m_nLOD))
|
||||
{
|
||||
m_pModel->m_pDefaultModelState->m_bHasPhysics = true;
|
||||
m_pModel->onBonePhysicsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// clean up the physical geometry objects that were not used
|
||||
for (ChunkIdToPhysGeomMap::iterator it = m_mapLimbPhysGeoms.begin(); it != m_mapLimbPhysGeoms.end(); ++it)
|
||||
{
|
||||
phys_geometry* pPhysGeom = it->second;
|
||||
if (pPhysGeom)
|
||||
GetPhysicalWorld()->GetGeomManager()->UnregisterGeometry(pPhysGeom);
|
||||
}
|
||||
m_mapLimbPhysGeoms.clear();
|
||||
|
||||
if(m_pModel->m_arrDamageTable.empty() && m_nLOD == 0 && !m_arrGeomBoneNameTable.empty())
|
||||
{
|
||||
m_pModel->m_arrDamageTable.reinit(m_arrGeomBoneNameTable.size());
|
||||
m_pModel->InitDamageTable();
|
||||
}
|
||||
|
||||
// there must be at least one shader for the object; create a default material/shader if there are no materials
|
||||
if(m_pModel->m_arrMaterials.empty())
|
||||
{ // make some default material
|
||||
g_GetLog()->LogWarning ("\002CryModel::LoadGeomChunks: Materials not found in %s", getFilePathCStr());
|
||||
MAT_ENTITY me;
|
||||
memset(&me, 0, sizeof(MAT_ENTITY));
|
||||
strcpy(me.map_d.name, "default");
|
||||
m_pModel->m_arrMaterials.push_back(me);
|
||||
}
|
||||
|
||||
// fix the wrong mat id's
|
||||
m_pModel->getGeometryInfo(m_nLOD)->limitMaterialIDs((unsigned)m_pModel->m_arrMaterials.size());
|
||||
|
||||
// add the light bindings to the internal array
|
||||
std::vector<CBoneLightBindInfo> arrBoneLights;
|
||||
arrBoneLights.reserve (m_numBoneLightBinds);
|
||||
for (unsigned i = 0; i < m_numBoneLightBinds; ++i)
|
||||
{
|
||||
SBoneLightBind binding = m_pBoneLightBind[i];
|
||||
|
||||
if (binding.nBoneId >= m_arrTempBoneIdToIndex.size())
|
||||
{
|
||||
g_GetLog()->LogError ("\002CryModel::LoadGeomChunks: invalid light-bone binding in the file for bone id %d, light 0x%08X. Bone id is out of range.", binding.nBoneId, binding.nLightChunkId);
|
||||
continue;
|
||||
}
|
||||
binding.nBoneId = m_arrTempBoneIdToIndex[binding.nBoneId];
|
||||
|
||||
// find the light chunk and the chunk of the node of that light
|
||||
const LIGHT_CHUNK_DESC* pLightChunk = find_in_map (m_mapLights, binding.nLightChunkId, (const LIGHT_CHUNK_DESC *)NULL);
|
||||
// the node chunk is required to determine the light's transformation
|
||||
// (it's stored in the light's node)
|
||||
const NODE_CHUNK_DESC* pNodeChunk = find_in_map(m_mapObjectNodes, binding.nLightChunkId, (const NODE_CHUNK_DESC *)NULL);
|
||||
|
||||
if (!pLightChunk)
|
||||
{
|
||||
g_GetLog()->LogError ("\002invalid light-bone binding in the file for bone id %d, light 0x%08X", binding.nBoneId, binding.nLightChunkId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pNodeChunk)
|
||||
{
|
||||
g_GetLog()->LogWarning ("\002there is bone light in the file, but there is no Node for it. Unknown light name. Assuming a heat source. Bone id %d, light 0x%08X", binding.nBoneId, binding.nLightChunkId);
|
||||
}
|
||||
|
||||
arrBoneLights.resize (arrBoneLights.size()+1);
|
||||
arrBoneLights.back().load (binding, *pLightChunk, pNodeChunk?pNodeChunk->name:"_hs_Unknown", m_fScale);
|
||||
}
|
||||
|
||||
m_pModel->addBoneLights(arrBoneLights);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CryModelGeometryLoader::loadMesh (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize)
|
||||
{
|
||||
m_bGeometryFound = true;
|
||||
m_nGeometryChunkID = chunkHeader.ChunkID;
|
||||
if(m_bMeshFound)
|
||||
{
|
||||
g_GetLog()->LogToFile ("\003CryModel::LoadGeomChunks: more than one geom found (not supported) in %s", m_pModel->m_pBody->GetNameCStr());
|
||||
return false;
|
||||
}
|
||||
|
||||
const MESH_CHUNK_DESC_0744* pMeshChunk = (const MESH_CHUNK_DESC_0744*)pChunk;
|
||||
if (pMeshChunk->chdr.ChunkVersion == MESH_CHUNK_DESC_0744::VERSION)
|
||||
{
|
||||
// read the geometry info into the corresponding lod
|
||||
CryGeometryInfo* pGeomInfo = m_pModel->getGeometryInfo(m_nLOD);
|
||||
unsigned nReadData = pGeomInfo->Load (m_nLOD, pMeshChunk, nChunkSize);
|
||||
if (!pGeomInfo->numLinks() || m_arrGeomBoneNameTable.empty())
|
||||
{
|
||||
// rotate the object in the world coordinates for the rest
|
||||
const NODE_CHUNK_DESC* pNodeChunk = find_in_map(m_mapObjectNodes, m_nGeometryChunkID,(const NODE_CHUNK_DESC *)NULL);
|
||||
if (pNodeChunk)
|
||||
{
|
||||
Matrix44 tm = pNodeChunk->tm;
|
||||
tm *= m_fScale;
|
||||
pGeomInfo->transform (tm);
|
||||
//pGeomInfo->Scale (scale);
|
||||
}
|
||||
else
|
||||
pGeomInfo->Scale (m_fScale);
|
||||
}
|
||||
else
|
||||
pGeomInfo->Scale (m_fScale);
|
||||
|
||||
|
||||
// check if we read the whole chunk
|
||||
if (nReadData != nChunkSize)
|
||||
g_GetLog()->LogToFile ("\003CryModel::LoadGeomChunks: unexpected geometry chunk size: read %d bytes of %d", nReadData, nChunkSize);
|
||||
if (nReadData == 0)
|
||||
return false;
|
||||
|
||||
// check if we read the bone info
|
||||
if (pGeomInfo->numLinks() == 0 && m_numMorphTargets == 0)
|
||||
{
|
||||
// Mesh without bone info should not go through the Animation module
|
||||
g_GetLog()->LogWarning ("\003CryModel::LoadGeomChunks: the mesh doesn't have any bone information.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_GetLog()->LogError ("\003CryModel::LoadGeomChunks: old file format, mesh chunk version 0x%08X (expected 0x%08X)",pMeshChunk->chdr.ChunkVersion, MESH_CHUNK_DESC_0744::VERSION);
|
||||
return false;
|
||||
}
|
||||
if (g_GetCVars()->ca_Debug())
|
||||
g_GetLog()->LogToFile ("\005 %d faces in lod %d", m_pModel->getGeometryInfo(m_nLOD)->numFaces(), m_nLOD);
|
||||
|
||||
m_bMeshFound = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CryModelGeometryLoader::loadBoneAnim (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize)
|
||||
{
|
||||
if (!m_bBonesFound)
|
||||
{
|
||||
switch(m_nLOD)
|
||||
{
|
||||
case 0:// load bones structure
|
||||
if (!loadBones (static_cast<const BONEANIM_CHUNK_DESC*>(pChunk), nChunkSize))
|
||||
return false; // failed to load bones
|
||||
m_pModel->m_pDefaultModelState->CreateBones();
|
||||
m_bBonesFound = true;
|
||||
break;
|
||||
case 1:
|
||||
m_pModel->UpdateRootBonePhysics (static_cast<const BONEANIM_CHUNK_DESC*>(pChunk), nChunkSize, m_nLOD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_GetLog()->LogWarning ("\003multiple bone animation chunks found; ignoring");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// loads the root bone (and the hierarchy) and returns true if loaded successfully
|
||||
// this gets called only for LOD 0
|
||||
bool CryModelGeometryLoader::loadBones (const BONEANIM_CHUNK_DESC* pChunk, unsigned nChunkSize)
|
||||
{
|
||||
if (pChunk->nBones != m_arrGeomBoneNameTable.size())
|
||||
{
|
||||
g_GetLog()->LogToFile ("\002Number of bones does not match in the bone hierarchy chunk (%d) and the bone name chunk (%d)", pChunk->nBones, m_arrGeomBoneNameTable.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned numBytesRead = m_BoneLoader.load(pChunk, nChunkSize);
|
||||
if (!numBytesRead)
|
||||
{
|
||||
g_GetLog()->LogError ("\002CryModelGeometryLoader::loadBones: cannot load the boneanimation chunk");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (numBytesRead != nChunkSize)
|
||||
g_GetLog()->LogWarning ("\002CryModelGeometryLoader::loadBones: bone animation chunk is too big (%u, expected %u). Ignoring the extra bytes.", numBytesRead, nChunkSize);
|
||||
|
||||
m_pModel->m_arrBones.resize (pChunk->nBones);
|
||||
for (int nBone = 0; nBone < pChunk->nBones; ++nBone)
|
||||
m_pModel->m_arrBones[nBone] = m_BoneLoader.getBoneByIndex(nBone);
|
||||
|
||||
unsigned numBones = m_pModel->numBoneInfos();
|
||||
m_arrTempBoneIdToIndex.reinit (numBones, -1);
|
||||
// fill in names of the bones, (limb ids?)
|
||||
for (unsigned nBone = 0; nBone < numBones; ++nBone)
|
||||
{
|
||||
unsigned nBoneId = (unsigned)m_BoneLoader.mapIndexToId(nBone);
|
||||
m_pModel->getBoneInfo(nBone).setName(m_arrGeomBoneNameTable[nBoneId]);
|
||||
m_arrTempBoneIdToIndex[nBoneId] = nBone;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CryModelGeometryLoader::loadBoneMesh (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize)
|
||||
{ // load bones geometry
|
||||
|
||||
const MESH_CHUNK_DESC_0744* pMeshChunk = (const MESH_CHUNK_DESC_0744*)pChunk;
|
||||
if (pMeshChunk->chdr.ChunkVersion != MESH_CHUNK_DESC_0744::VERSION)
|
||||
{
|
||||
g_GetLog()->LogError ("\002CryModel::LoadGeomChunks: old file format, mesh chunk version 0x%08X (expected 0x%08X)",pMeshChunk->chdr.ChunkVersion, MESH_CHUNK_DESC_0744::VERSION);
|
||||
return false;
|
||||
}
|
||||
|
||||
// load the geometry from the chunk
|
||||
CryGeometryInfo GeomInfo;
|
||||
unsigned nReadData = GeomInfo.Load (2, pMeshChunk, nChunkSize);
|
||||
if (nReadData != nChunkSize)
|
||||
g_GetLog()->LogError ("\002CryModel::LoadGeomChunks: unexpected bone mesh chunk size: read %d bytes of %d", nReadData, nChunkSize);
|
||||
if (nReadData != 0) // to account for a lot of broken mesh chunks; <<TODO>> change this line to "else" when all cgf are not broken
|
||||
{
|
||||
GeomInfo.Scale(m_fScale);
|
||||
TElementaryArray<vectorf> points ("CryModelGeometryLoader::loadBoneMesh.points");
|
||||
points.reinit(GeomInfo.numVertices());
|
||||
TElementaryArray<int> indices ("CryModelGeometryLoader::loadBoneMesh.indices");
|
||||
indices.reinit(GeomInfo.numFaces()*3);
|
||||
TElementaryArray<unsigned char> pmats ("CryModelGeometryLoader::loadBoneMesh.pmats");
|
||||
pmats.reinit(GeomInfo.numFaces());
|
||||
|
||||
unsigned j, k;
|
||||
for (j = 0; j < GeomInfo.numVertices(); ++j)
|
||||
points[j] = GeomInfo.getVertex(j);
|
||||
|
||||
for (j = 0; j < GeomInfo.numFaces(); ++j)
|
||||
{
|
||||
GeomMtlID nMtlID = GeomInfo.getFaceMtl(j);
|
||||
const GeomFace& face = GeomInfo.getFace(j);
|
||||
for (k = 0; k < 3; ++k)
|
||||
indices[j*3+k] = face[k];
|
||||
|
||||
if( nMtlID >= 0 &&
|
||||
(unsigned)nMtlID + m_nFirstMat < m_pModel->numMaterials())
|
||||
pmats[j] = m_pModel->getMaterial(nMtlID+m_nFirstMat).iGameID;
|
||||
else
|
||||
{
|
||||
if(m_bFirstTimeWarningBoneGeometryMtl && nMtlID != 255 && nMtlID != -1) // 255/-1 are the no-materials
|
||||
{
|
||||
g_GetLog()->LogWarning ("\004CryModel::LoadGeomChunks: Bone geometry material id is invalid: %s", getFilePathCStr());
|
||||
m_bFirstTimeWarningBoneGeometryMtl = false;
|
||||
}
|
||||
|
||||
pmats[j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(m_pPhysicalGeometryManager)
|
||||
{
|
||||
// add a new entry to the map chunk->limb geometry, so that later it can be found by the bones
|
||||
// during the post-initialization mapping of pPhysGeom from Chunk id to the actual geometry pointer
|
||||
IGeometry* pPhysicalGeometry = m_pPhysicalGeometryManager->CreateMesh (
|
||||
// hack: the interface method has short* parameter, that should actually be const void*, because it can accept both char* and short* arrays, depending on the flags
|
||||
&points[0], &indices[0], (short*)&pmats[0],
|
||||
(unsigned)GeomInfo.numFaces(),
|
||||
(GeomInfo.numFaces()<=20 ? mesh_SingleBB : mesh_OBB|mesh_AABB)
|
||||
| mesh_multicontact0 | mesh_uchar_ids | mesh_approx_box | mesh_approx_sphere | mesh_approx_cylinder
|
||||
);
|
||||
assert (pPhysicalGeometry);
|
||||
m_mapLimbPhysGeoms[chunkHeader.ChunkID] =
|
||||
m_pPhysicalGeometryManager->RegisterGeometry (pPhysicalGeometry, pmats.empty() ? 0 : pmats[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns the file path tot he file being currently loaded
|
||||
const char* CryModelGeometryLoader::getFilePathCStr()const
|
||||
{
|
||||
return m_pModel->m_pBody->GetNameCStr();
|
||||
}
|
||||
|
||||
void CryModelGeometryLoader::indicateProgress(const char*szMsg)
|
||||
{
|
||||
//g_GetLog()->UpdateLoadingScreenPlus(/*szMsg?szMsg:*/"\003.");
|
||||
}
|
||||
134
CryAnimation/CryModelGeometryLoader.h
Normal file
134
CryAnimation/CryModelGeometryLoader.h
Normal file
@@ -0,0 +1,134 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Sergiy Migdalskiy
|
||||
//
|
||||
// Contains the geometry load code for CryModel class
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _CRY_MODEL_GEOMETRY_LOADER_HDR_
|
||||
#define _CRY_MODEL_GEOMETRY_LOADER_HDR_
|
||||
|
||||
#include "CryBoneInfo.h"
|
||||
class CryModel;
|
||||
#include "CryBoneHierarchyLoader.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// this is just a helper class for CryModelLoader.
|
||||
// it parses the CGF file and extracts the useful information in the useful form
|
||||
//
|
||||
// NOTE:
|
||||
// to load different LODs, use the same instance of this class. Between LOD
|
||||
// loading, it may preserve important information
|
||||
class CryModelGeometryLoader
|
||||
{
|
||||
// class CryModel is the only client of this class for now
|
||||
public:
|
||||
CryModelGeometryLoader ();
|
||||
~CryModelGeometryLoader ();
|
||||
|
||||
// frees all resources allocated within this class
|
||||
void clear();
|
||||
|
||||
// parses the given file, returns true if successful
|
||||
bool load (CryModel* pModel, CChunkFileReader* pReader, unsigned nLOD, float fScale);
|
||||
|
||||
// returns true if the bone infos have the initial position set
|
||||
bool hasBoneInfoInitPos();
|
||||
protected:
|
||||
// loads the materials; returns true if succeeds
|
||||
bool loadMaterials();
|
||||
|
||||
bool prepare();
|
||||
bool finalize();
|
||||
|
||||
// load the stuff that doesn't need anything else to be already loaded:
|
||||
// Bone name table, bone-light binding table, light and node maps,
|
||||
// calculate the number of morph targets
|
||||
bool loadStage1();
|
||||
|
||||
// loads the stuff that needs the stuff loaded at stage 1
|
||||
bool loadStage2();
|
||||
|
||||
// loads the morph targets - after the other things are known
|
||||
bool loadMorphTargets();
|
||||
|
||||
// corrects the bone ids in the geometry info according to the new numeration of the bones
|
||||
bool rebindBoneIndices();
|
||||
|
||||
// loads the bone name list into m_arrGeomBoneNameTable
|
||||
// m_arrGeomBoneNameTable points to the chunk data places where the actual names are; so no need to deallocate the strings
|
||||
bool loadBoneNameList (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize);
|
||||
|
||||
// loads the bone light binding array
|
||||
bool loadBoneLightBinding (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize);
|
||||
|
||||
// loads the bone light binding array
|
||||
bool loadBoneInitialPos (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize);
|
||||
|
||||
// Attempts to load the material from the given material chunk
|
||||
// loads the material from the material chunk to the given structure.
|
||||
// returns true if the chunk was recognized and the material was loaded
|
||||
bool loadMaterial (CHUNK_HEADER* pMaterialChunk, unsigned nChunkSize, MAT_ENTITY& rMaterial, class CMatEntityNameTokenizer& mt);
|
||||
|
||||
bool loadBoneMesh (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize);
|
||||
bool loadBoneAnim (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize);
|
||||
bool loadBones (const BONEANIM_CHUNK_DESC* pChunk, unsigned nChunkSize);
|
||||
// for LODs > 0, builds the m_arrTempBoneIdToIndex by matching the bone names
|
||||
// against the bone names in the already loaded LOD 0
|
||||
void buildBoneIndicesByNames();
|
||||
// loads the mesh
|
||||
bool loadMesh(const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize);
|
||||
|
||||
// returns the file path tot he file being currently loaded
|
||||
const char* getFilePathCStr()const;
|
||||
|
||||
void indicateProgress (const char* szMsg = NULL);
|
||||
protected:
|
||||
// this will contain the information about the loaded bones, if any
|
||||
CryBoneHierarchyLoader m_BoneLoader;
|
||||
|
||||
// the number of elements in the arrGeomBoneNameTable is the number of bone name entities
|
||||
// this will be the array of bone names, extracted right from the file (the pointers will point into the mapped file)
|
||||
std::vector<const char*> m_arrGeomBoneNameTable;
|
||||
|
||||
// this is the array of bone-light binding
|
||||
unsigned m_numBoneLightBinds;
|
||||
const SBoneLightBind* m_pBoneLightBind;
|
||||
|
||||
// this map contains the bone geometry. The mapping is : [Chunk ID]->[Physical geometry read from that chunk]
|
||||
// the chunks from which the physical geometry is read are the bone mesh chunks.
|
||||
typedef CryBoneInfo::ChunkIdToPhysGeomMap ChunkIdToPhysGeomMap;
|
||||
ChunkIdToPhysGeomMap m_mapLimbPhysGeoms;
|
||||
|
||||
// the map of lights
|
||||
std::map <unsigned, const LIGHT_CHUNK_DESC*> m_mapLights;
|
||||
// the map of object nodes - pointers to the node chunks for ObjectIDs
|
||||
std::map <unsigned, const NODE_CHUNK_DESC*> m_mapObjectNodes;
|
||||
|
||||
TFixedArray<unsigned> m_arrTempBoneIdToIndex;
|
||||
|
||||
// the number of morph target chunks
|
||||
unsigned m_numMorphTargets;
|
||||
|
||||
// the basis number of materials: by this value, the material ids are shifted in the mesh
|
||||
int m_nFirstMat;
|
||||
|
||||
bool m_bFirstTimeWarningBoneGeometryMtl; // if set to true, we can issue this warning and set it to false; when it's false, we don't issue this warning to keep the extra warning number low
|
||||
bool m_bMeshFound; // mesh chunk found in the file
|
||||
bool m_bGeometryFound; // mesh chunk found in the file
|
||||
bool m_bBonesFound; // bone animation chunk found in the file
|
||||
unsigned m_nGeometryChunkID; // the id used to bind the morph targets
|
||||
|
||||
IGeomManager *m_pPhysicalGeometryManager;
|
||||
|
||||
CChunkFileReader_AutoPtr m_pReader;
|
||||
CryModel* m_pModel;
|
||||
unsigned m_nLOD;
|
||||
float m_fScale;
|
||||
};
|
||||
|
||||
#endif
|
||||
650
CryAnimation/CryModelLoader.cpp
Normal file
650
CryAnimation/CryModelLoader.cpp
Normal file
@@ -0,0 +1,650 @@
|
||||
#include "stdafx.h"
|
||||
#include <StlUtils.h>
|
||||
#include <CryCompiledFile.h>
|
||||
#include "ChunkFileReader.h"
|
||||
#include "CryModel.h"
|
||||
#include "CryModelState.h"
|
||||
#include "CryModelLoader.h"
|
||||
#include "CryModelGeometryLoader.h"
|
||||
#include "StringUtils.h"
|
||||
#include "CrySkinMorph.h"
|
||||
#include "CrySkinMorphBuilder.h"
|
||||
#include "ControllerManager.h"
|
||||
#include "CgfUtils.h"
|
||||
#include "CVars.h"
|
||||
#include "CryModelSubmesh.h"
|
||||
using namespace CryStringUtils;
|
||||
|
||||
class CAutoFile
|
||||
{
|
||||
FILE* m_f;
|
||||
public:
|
||||
CAutoFile (const char* szName, const char* szMode)
|
||||
{
|
||||
m_f = g_GetPak()->FOpen (szName, szMode);
|
||||
}
|
||||
~CAutoFile ()
|
||||
{
|
||||
if (m_f)
|
||||
g_GetPak()->FClose (m_f);
|
||||
}
|
||||
|
||||
long GetSize()
|
||||
{
|
||||
if (g_GetPak()->FSeek (m_f, 0, SEEK_END))
|
||||
return -1;
|
||||
|
||||
long nSize = g_GetPak()->FTell (m_f);
|
||||
|
||||
if (g_GetPak()->FSeek(m_f, 0, SEEK_SET))
|
||||
return -1;
|
||||
|
||||
return nSize;
|
||||
}
|
||||
|
||||
bool Read (void* pData, unsigned nSize)
|
||||
{
|
||||
return (1 == g_GetPak()->FRead (pData, nSize, 1, m_f));
|
||||
}
|
||||
|
||||
operator FILE*() {return m_f;}
|
||||
};
|
||||
|
||||
|
||||
CryModelLoader::CryModelLoader (CControllerManager* pControllerManager):
|
||||
m_pControllerManager (pControllerManager),
|
||||
m_fCalFile (NULL),
|
||||
m_nCafFindFileHandle (-1),
|
||||
m_pModel (NULL),
|
||||
m_bLoadFromCCG (false),
|
||||
m_bExtCCG(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CryModelLoader::~CryModelLoader()
|
||||
{
|
||||
// clean up the resources
|
||||
clear();
|
||||
}
|
||||
|
||||
// cleans up the resources allocated during load
|
||||
void CryModelLoader::clear()
|
||||
{
|
||||
m_strGeomFileNameNoExt = "";
|
||||
m_strCalFileName = "";
|
||||
|
||||
if (m_fCalFile)
|
||||
{
|
||||
g_GetPak()->FClose (m_fCalFile);
|
||||
m_fCalFile = NULL;
|
||||
}
|
||||
|
||||
if (m_nCafFindFileHandle != -1)
|
||||
{
|
||||
g_GetPak()->FindClose(m_nCafFindFileHandle);
|
||||
m_nCafFindFileHandle = -1;
|
||||
}
|
||||
|
||||
if (m_pModel)
|
||||
{
|
||||
delete m_pModel;
|
||||
m_pModel = NULL;
|
||||
}
|
||||
|
||||
m_arrLodFiles.clear();
|
||||
m_arrBufferCCG.clear();
|
||||
}
|
||||
|
||||
bool IsValidFile (const char* szFilePath)
|
||||
{
|
||||
FILE* f = g_GetPak()->FOpen(szFilePath, "rb");
|
||||
if (f)
|
||||
{
|
||||
g_GetPak()->FClose (f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CryModel* CryModelLoader::loadNew (CryCharBody* pBody, const string& strGeomFileName, float fScale)
|
||||
{
|
||||
AUTO_PROFILE_SECTION(g_dTimeGeomLoad);
|
||||
// make sure we call done() whenever we return from this function
|
||||
CAutoClearLoader _autoClear (this);
|
||||
|
||||
const char* szExt = FindExtension(strGeomFileName.c_str());
|
||||
m_strGeomFileNameNoExt.assign (strGeomFileName.c_str(), *szExt?szExt-1:szExt);
|
||||
m_bExtCCG = !stricmp(szExt, "ccg");
|
||||
m_fScale = fScale;
|
||||
|
||||
m_bLoadFromCCG = g_GetCVars()->ca_EnableCCG() && IsValidFile (getCCGFilePath().c_str());
|
||||
|
||||
// first, initialize the search of animations. If the file has no animations at all (no cal file, no _..caf files)
|
||||
// then this is not an animated file and must be loaded somewhere else (namely in the 3D Engine)
|
||||
if (!m_bLoadFromCCG)
|
||||
searchAnimations (); // no need to exit if no animations: maybe it's a body part
|
||||
|
||||
g_GetLog()->UpdateLoadingScreen ("\003Loading %s", cutString(m_bLoadFromCCG?getCCGFilePath():strGeomFileName, 40).c_str());
|
||||
// find how many LODs we have
|
||||
if (!(m_bLoadFromCCG?preloadCCG():preloadCGFs()))
|
||||
return NULL;
|
||||
|
||||
m_pModel = new CryModel (pBody, m_pControllerManager);
|
||||
|
||||
if (m_bLoadFromCCG)
|
||||
{
|
||||
if (!loadCCG())
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!loadCGFs())
|
||||
return NULL;
|
||||
|
||||
//m_pModel->buildMorphSkins();
|
||||
|
||||
if (!loadTextures())
|
||||
return NULL;
|
||||
|
||||
m_pModel->m_pDefaultModelState->GetCryModelSubmesh(0)->GenerateRenderArrays (strGeomFileName.c_str());
|
||||
|
||||
if (!loadAnimations())
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
// needed for XBox development
|
||||
extern void exportTestModel(CryGeometryInfo* pGeometry, CLeafBuffer* pLeafBuffer);
|
||||
// on this stage, if m_pCryModel == NULL, it means the loader couldn't load the model
|
||||
if (m_pModel)
|
||||
exportTestModel( m_pModel->getGeometryInfo(), m_pModel->m_pDefaultModelState->m_pLeafBuffers[0]);
|
||||
#endif
|
||||
|
||||
|
||||
if (g_GetCVars()->ca_ZDeleteConstructionData())
|
||||
{
|
||||
m_pModel->clearConstructionData();
|
||||
}
|
||||
else
|
||||
g_GetLog()->LogWarning ("\005The construction data wasn't deleted");
|
||||
|
||||
// return the initialized(loaded) model, forgetting about it so that it doesn't get destructed by the following call to done()
|
||||
return detachModel();
|
||||
}
|
||||
|
||||
|
||||
// tries to find out if there are any animations for this file; if there are some,
|
||||
// prepares to load them and returns true; otherwise returns false
|
||||
bool CryModelLoader::searchAnimations ()
|
||||
{
|
||||
AUTO_PROFILE_SECTION(g_dTimeGeomChunkLoad);
|
||||
// make up the cal file name
|
||||
m_strCalFileName = m_strGeomFileNameNoExt + ".cal";
|
||||
|
||||
// try to find out - if there are any animations for this file. if there are none, then return an error
|
||||
m_fCalFile = g_GetPak()->FOpen(m_strCalFileName.c_str(), "r");
|
||||
m_nCafFindFileHandle = -1;
|
||||
|
||||
if (!m_fCalFile)
|
||||
{
|
||||
// finish making search path
|
||||
string strSeachFilter = m_strGeomFileNameNoExt + "_*.caf";
|
||||
|
||||
// search
|
||||
m_nCafFindFileHandle = g_GetPak()->FindFirst (strSeachFilter.c_str(), &m_fileinfo);
|
||||
}
|
||||
|
||||
return m_fCalFile || m_nCafFindFileHandle != -1;
|
||||
}
|
||||
|
||||
|
||||
// searches for lod models for the given model; returns false in case of some error
|
||||
bool CryModelLoader::preloadCGFs()
|
||||
{
|
||||
AUTO_PROFILE_SECTION (g_dTimeGeomChunkLoadFileIO);
|
||||
CChunkFileReader_AutoPtr pReader = new CChunkFileReader ();
|
||||
// first try to open LOD 0
|
||||
if (!pReader->open (m_strGeomFileNameNoExt + ".cgf"))
|
||||
{
|
||||
g_GetLog()->LogError ("\003CryModelLoader::preloadCGFs(%s): main file not found", m_strGeomFileNameNoExt.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (0 == pReader->numChunksOfType(ChunkType_BoneAnim))
|
||||
return false; // the cgf doesn't contain bone info
|
||||
|
||||
m_arrLodFiles.reinit (1);
|
||||
m_arrLodFiles[0] = pReader;
|
||||
|
||||
for (unsigned nLOD = 1; nLOD < g_nMaxGeomLodLevels; ++nLOD)
|
||||
{
|
||||
pReader = new CChunkFileReader();
|
||||
if (pReader->open(m_strGeomFileNameNoExt + "_lod" + toString(nLOD) + ".cgf"))
|
||||
m_arrLodFiles.push_back(pReader);
|
||||
else
|
||||
break; // we have opened all the LOD files so far
|
||||
|
||||
indicateProgress();
|
||||
}
|
||||
|
||||
// we have LOD 0, so it's optional to have any other and we return true anyway
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// loads the CCG in memory buffer m_arrBufferCCG
|
||||
bool CryModelLoader::preloadCCG()
|
||||
{
|
||||
AUTO_PROFILE_SECTION(g_dTimeGeomChunkLoadFileIO);
|
||||
CAutoFile fIn (getCCGFilePath().c_str(), "rb");
|
||||
if (!fIn)
|
||||
return false;
|
||||
|
||||
long nSize = fIn.GetSize();
|
||||
if (nSize <= 0)
|
||||
return false;
|
||||
|
||||
m_arrBufferCCG.resize(nSize);
|
||||
|
||||
if (!fIn.Read (&m_arrBufferCCG[0], nSize))
|
||||
return false;
|
||||
|
||||
// data has been read - automatically close the file and return success
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// loads animations for already loaded model
|
||||
bool CryModelLoader::loadAnimations()
|
||||
{
|
||||
AUTO_PROFILE_SECTION(g_dTimeAnimLoadBind);
|
||||
|
||||
// the number of animations loaded
|
||||
unsigned numAnimations = 0;
|
||||
|
||||
{
|
||||
AUTO_PROFILE_SECTION (g_dTimeTest1);
|
||||
if(m_fCalFile)
|
||||
numAnimations = loadAnimationsWithCAL ();
|
||||
else
|
||||
if (m_nCafFindFileHandle != -1)
|
||||
numAnimations = loadAnimationsNoCAL ();
|
||||
}
|
||||
|
||||
{
|
||||
AUTO_PROFILE_SECTION(g_dTimeTest2);
|
||||
if (!numAnimations && !m_pModel->numMorphTargets())
|
||||
{
|
||||
g_GetLog()->LogWarning ("\004CryModelLoader::loadAnimations(%s): couldn't find any animations for the model. Standalone character will be useless.", m_strGeomFileNameNoExt.c_str());
|
||||
//return false;
|
||||
}
|
||||
|
||||
if (!m_bLoadFromCCG)
|
||||
{
|
||||
g_GetLog()->UpdateLoadingScreenPlus ("\003 precomputing");
|
||||
m_pModel->LoadPostInitialize (!m_bBoneInitPosInitialized);
|
||||
g_GetLog()->UpdateLoadingScreenPlus ("\003done.");
|
||||
}
|
||||
}
|
||||
|
||||
if (numAnimations)
|
||||
g_GetLog()->UpdateLoadingScreen(" %d animations loaded (total animations: %d)", numAnimations, m_pControllerManager->NumAnimations());
|
||||
|
||||
// m_pControllerManager->LogUsageStats();
|
||||
//m_pModel->shrinkControllerArrays();
|
||||
|
||||
return m_pModel->m_pDefaultModelState
|
||||
&& ((m_pModel->numBoneInfos()
|
||||
&& m_pModel->m_pDefaultModelState->getRootBone())
|
||||
|| m_pModel->numMorphTargets());
|
||||
}
|
||||
|
||||
// loads the animations from the array: pre-allocates the necessary controller arrays
|
||||
// the 0th animation is the default animation
|
||||
unsigned CryModelLoader::loadAnimationArray (const AnimFileArray& arrAnimFiles)
|
||||
{
|
||||
unsigned nAnimID = 0;
|
||||
if (arrAnimFiles.empty())
|
||||
return 0;
|
||||
|
||||
indicateProgress("\003 Anims");
|
||||
{
|
||||
AUTO_PROFILE_SECTION(g_dTimeAnimLoadBindPreallocate);
|
||||
m_pModel->prepareLoadCAFs ((unsigned)arrAnimFiles.size());
|
||||
}
|
||||
|
||||
indicateProgress("\003:");
|
||||
|
||||
// load the default animation - it must be always loaded synchronously
|
||||
|
||||
unsigned nDefAnimFlags = arrAnimFiles[0].nAnimFlags;
|
||||
if (!stricmp(arrAnimFiles[0].strAnimName.c_str(), "default"))
|
||||
nDefAnimFlags |= GlobalAnimation::FLAGS_DEFAULT_ANIMATION;
|
||||
|
||||
if(m_pModel->LoadCAF(arrAnimFiles[0].strFileName.c_str(), m_fScale, nAnimID, arrAnimFiles[0].strAnimName.c_str(), nDefAnimFlags) >= 0)
|
||||
nAnimID++;
|
||||
else
|
||||
if (g_GetCVars()->ca_Debug())
|
||||
g_GetLog()->LogWarning ("\005Default pose for %s was not found, object may not skin as expected", m_strGeomFileNameNoExt.c_str());
|
||||
|
||||
for (unsigned i = 1; i < arrAnimFiles.size(); ++i)
|
||||
{
|
||||
if (m_pModel->LoadCAF(arrAnimFiles[i].strFileName.c_str(), m_fScale, nAnimID, arrAnimFiles[i].strAnimName.c_str(),arrAnimFiles[i].nAnimFlags) >= 0)
|
||||
{
|
||||
nAnimID++;
|
||||
//if((i%10)==0)
|
||||
// indicateProgress();
|
||||
}
|
||||
else
|
||||
g_GetLog()->LogWarning ("\002Animation (Caf) file \"%s\" could not be read (it's an animation of \"%s.cgf\")", arrAnimFiles[i].strFileName.c_str(), m_strGeomFileNameNoExt.c_str());
|
||||
}
|
||||
indicateProgress("\003done;");
|
||||
return nAnimID;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// loads animations for the given file from the given directory, loading
|
||||
// all cal files which begin with the cgf file name and underscope (this
|
||||
// is the convention for the cgf's that don't have cal file associated)
|
||||
// PARAMETERS:
|
||||
// m_strGeomFileNameNoExt - the name of the cgf/cid file without extension
|
||||
// m_nCafFindFileHandle - the search handle opened by _findfirst() for all cal files belonging to this cgf
|
||||
// m_fScale - the scale factor to be applied to all controllers
|
||||
unsigned CryModelLoader::loadAnimationsNoCAL ()
|
||||
{
|
||||
// animation files to load, excluding the default animation
|
||||
AnimFileArray arrAnimFiles;
|
||||
{
|
||||
AUTO_PROFILE_SECTION(g_dTimeAnimLoadBindNoCal);
|
||||
// make search path
|
||||
string strDirName = GetParentDirectory (m_strGeomFileNameNoExt).c_str();
|
||||
|
||||
// load the default pose first
|
||||
string strDefaultPose = (m_strGeomFileNameNoExt + "_default.caf").c_str();
|
||||
|
||||
arrAnimFiles.reserve (64);
|
||||
// we need default animation immediately, but unlikely we'll need it in the future (so we can unload it)
|
||||
arrAnimFiles.push_back (SAnimFile(strDefaultPose, "default", GlobalAnimation::FLAGS_DEFAULT_ANIMATION));
|
||||
|
||||
// the name of the base cgf (before the understrike) + 1
|
||||
unsigned nBaseNameLength = unsigned(m_strGeomFileNameNoExt.length() - strDirName.length());
|
||||
indicateProgress();
|
||||
do
|
||||
{
|
||||
SAnimFile AnimFile;
|
||||
AnimFile.strFileName = strDirName + "\\" + m_fileinfo.name;
|
||||
|
||||
if(!stricmp(AnimFile.strFileName.c_str(), strDefaultPose.c_str()))
|
||||
// skip the default pose as it has already been loaded
|
||||
continue;
|
||||
|
||||
//if (!stricmp(FindExtension(fileinfo.name), "caf")) // actually ,according to the search mask, this should be met automatically
|
||||
char* szExtension = StripFileExtension(m_fileinfo.name);
|
||||
assert (!stricmp(szExtension, "caf"));
|
||||
assert (strlen(m_fileinfo.name) > nBaseNameLength);
|
||||
|
||||
AnimFile.strAnimName = m_fileinfo.name + nBaseNameLength;
|
||||
arrAnimFiles.push_back (AnimFile);
|
||||
}
|
||||
while (g_GetPak()->FindNext( m_nCafFindFileHandle, &m_fileinfo ) != -1);
|
||||
}
|
||||
return loadAnimationArray(arrAnimFiles);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// loads animations for this cgf from the given cal file
|
||||
// does NOT close the file (the file belongs to the calling party)
|
||||
// PARAMETERS
|
||||
// m_strGeomFileNameNoExt - the name of the cgf/cid file without extension
|
||||
// m_fCalFile - the file opened by fopen() for the cal associated with this cgf
|
||||
// m_fScale - the scale factor to be applied to all controllers
|
||||
unsigned CryModelLoader::loadAnimationsWithCAL ()
|
||||
{
|
||||
AnimFileArray arrAnimFiles;
|
||||
{
|
||||
AUTO_PROFILE_SECTION(g_dTimeAnimLoadBindWithCal);
|
||||
// Load cal file and load animations from animations folder
|
||||
// make anim folder name
|
||||
// make search path
|
||||
string strDirName = GetParentDirectory(m_strGeomFileNameNoExt).c_str();
|
||||
string strAnimDirName = GetParentDirectory(strDirName, 2) + "\\animations";
|
||||
// the flags applicable to the currently being loaded animation
|
||||
unsigned nAnimFlags = 0;
|
||||
|
||||
arrAnimFiles.reserve (256);
|
||||
|
||||
indicateProgress();
|
||||
for (int i = 0; m_fCalFile && !g_GetPak()->FEof(m_fCalFile); ++i)
|
||||
{
|
||||
char sBuffer[512]="";
|
||||
g_GetPak()->FGets(sBuffer,512,m_fCalFile);
|
||||
char*szAnimName;
|
||||
char*szFileName;
|
||||
|
||||
if(sBuffer[0] == '/' || sBuffer[0]=='\r' || sBuffer[0]=='\n' || sBuffer[0]==0)
|
||||
continue;
|
||||
|
||||
//if(sscanf(sBuffer, "%s=%s", szAnimName, szFileName) != 2)
|
||||
// continue;
|
||||
szAnimName = strtok (sBuffer, " \t\n\r=");
|
||||
if (!szAnimName)
|
||||
continue;
|
||||
szFileName = strtok(NULL, " \t\n\r=");
|
||||
if (!szFileName || szFileName[0] == '?')
|
||||
{
|
||||
m_pModel->RegisterDummyAnimation(szAnimName);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (szAnimName[0] == '/' && szAnimName[1] == '/')
|
||||
continue; // comment
|
||||
|
||||
{ // remove firsrt '\' and replace '/' with '\'
|
||||
while(szFileName[0]=='/' || szFileName[0]=='\\')
|
||||
memmove(szFileName,szFileName+1,sizeof(szFileName)-1);
|
||||
|
||||
for(char * p = szFileName+strlen(szFileName); p>=szFileName; p--)
|
||||
if(*p == '/')
|
||||
*p = '\\';
|
||||
}
|
||||
|
||||
// process the possible directives
|
||||
if (szAnimName[0] == '$')
|
||||
{
|
||||
const char* szDirective = szAnimName + 1;
|
||||
if (!stricmp(szDirective, "AnimationDir")
|
||||
||!stricmp(szDirective, "AnimDir")
|
||||
||!stricmp(szDirective, "AnimationDirectory")
|
||||
||!stricmp(szDirective, "AnimDirectory"))
|
||||
{
|
||||
strAnimDirName = strDirName + "\\" + szFileName;
|
||||
// delete the trailing slashes
|
||||
while (
|
||||
!strAnimDirName.empty()
|
||||
&& strAnimDirName [strAnimDirName.length()-1] == '\\'
|
||||
)
|
||||
strAnimDirName[strAnimDirName.length()-1] = '\0';
|
||||
}
|
||||
else
|
||||
if (!stricmp (szDirective, "ModelOffsetX"))
|
||||
{
|
||||
float fValue;
|
||||
if (sscanf (szFileName, "%f", &fValue) != 1)
|
||||
g_GetLog ()->LogToFile("\003Warning:directive ModelOffsetX %s couldn't be read in file %s.cal", szFileName, m_strGeomFileNameNoExt.c_str());
|
||||
else
|
||||
m_pModel->m_vModelOffset.x = fValue;
|
||||
}
|
||||
else
|
||||
if (!stricmp (szDirective, "ModelOffsetY"))
|
||||
{
|
||||
float fValue;
|
||||
if (sscanf (szFileName, "%f", &fValue) != 1)
|
||||
g_GetLog ()->LogToFile("\003Warning:directive ModelOffsetY %s couldn't be read in file %s.cal", szFileName, m_strGeomFileNameNoExt.c_str());
|
||||
else
|
||||
m_pModel->m_vModelOffset.y = fValue;
|
||||
}
|
||||
else
|
||||
if (!stricmp (szDirective, "ModelOffsetZ"))
|
||||
{
|
||||
float fValue;
|
||||
if (sscanf (szFileName, "%f", &fValue) != 1)
|
||||
g_GetLog ()->LogToFile("\003Warning:directive ModelOffsetZ %s couldn't be read in file %s.cal", szFileName, m_strGeomFileNameNoExt.c_str());
|
||||
else
|
||||
m_pModel->m_vModelOffset.z = fValue;
|
||||
}
|
||||
else
|
||||
if (!stricmp(szDirective, "AutoUnload"))
|
||||
{
|
||||
switch (CryStringUtils::toYesNoType(szFileName))
|
||||
{
|
||||
case CryStringUtils::nYNT_Yes:
|
||||
nAnimFlags &= ~GlobalAnimation::FLAGS_DISABLE_AUTO_UNLOAD;
|
||||
break;
|
||||
case CryStringUtils::nYNT_No:
|
||||
nAnimFlags |= GlobalAnimation::FLAGS_DISABLE_AUTO_UNLOAD;
|
||||
break;
|
||||
default:
|
||||
g_GetLog()->LogWarning ("\003invalid option for AutoUnload directive (must be yes or no) in file %s.cal", m_strGeomFileNameNoExt.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!stricmp(szDirective, "DelayLoad"))
|
||||
{
|
||||
switch (CryStringUtils::toYesNoType(szFileName))
|
||||
{
|
||||
case CryStringUtils::nYNT_Yes:
|
||||
nAnimFlags &= ~GlobalAnimation::FLAGS_DISABLE_DELAY_LOAD;
|
||||
break;
|
||||
case CryStringUtils::nYNT_No:
|
||||
nAnimFlags |= GlobalAnimation::FLAGS_DISABLE_DELAY_LOAD;
|
||||
break;
|
||||
default:
|
||||
g_GetLog()->LogWarning ("\003invalid option for DelayLoad directive (must be yes or no) in file %s.cal", m_strGeomFileNameNoExt.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
g_GetLog()->LogWarning ("\003Unknown directive %s", szDirective);
|
||||
continue;
|
||||
}
|
||||
|
||||
arrAnimFiles.push_back(SAnimFile (strAnimDirName + "\\" + szFileName, szAnimName,nAnimFlags));
|
||||
}
|
||||
if (arrAnimFiles.empty())
|
||||
return false;
|
||||
}
|
||||
return loadAnimationArray (arrAnimFiles);
|
||||
}
|
||||
|
||||
|
||||
// loads the CCG (including all the LODs in it)
|
||||
bool CryModelLoader::loadCCG()
|
||||
{
|
||||
CCFMemReader Reader (&m_arrBufferCCG[0], (unsigned)m_arrBufferCCG.size());
|
||||
if (Reader.IsEnd())
|
||||
return false;
|
||||
|
||||
string strDirName = GetParentDirectory(m_strGeomFileNameNoExt).c_str();
|
||||
string strAnimDirName = GetParentDirectory(strDirName, 2) + "\\animations";
|
||||
if (!m_pModel->initFromCCG(strDirName, strAnimDirName, Reader, m_fScale))
|
||||
return false;
|
||||
|
||||
// when we load CCG, we always have the init pose initialized,
|
||||
// otherwise we fail to load
|
||||
m_bBoneInitPosInitialized = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// loads the geometry files (LOD files, starting with the main one)
|
||||
bool CryModelLoader::loadCGFs()
|
||||
{
|
||||
unsigned numLODs = (unsigned)m_arrLodFiles.size();
|
||||
m_pModel->m_arrGeomInfo.reinit (numLODs);
|
||||
|
||||
CryModelGeometryLoader GeomLoader;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < numLODs; ++i)
|
||||
{
|
||||
if (!GeomLoader.load (m_pModel, m_arrLodFiles[i], i, m_fScale))
|
||||
{
|
||||
if (i)
|
||||
g_GetLog()->LogWarning ("\003Modes LOD %d can't be loaded. Please reexport the LOD file. Animated object will not be loaded.", i);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
m_bBoneInitPosInitialized = GeomLoader.hasBoneInfoInitPos();
|
||||
}
|
||||
|
||||
if (g_GetCVars()->ca_NoMtlSorting())
|
||||
return true;
|
||||
|
||||
m_pModel->deleteUnusedMaterials();
|
||||
|
||||
unsigned numMtls = (unsigned)m_pModel->m_arrMaterials.size();
|
||||
|
||||
// this will map from the new material id to the old material id after sorting
|
||||
// the materials
|
||||
std::vector<unsigned> arrMapMtlsOld, arrMapMtlsNew;
|
||||
arrMapMtlsOld.resize (numMtls);
|
||||
for (i = 0; i < numMtls; ++i)
|
||||
arrMapMtlsOld[i] = i;
|
||||
|
||||
// initial (identity) mapping created, now tokenize the material names
|
||||
std::vector<CMatEntityNameTokenizer> arrTokenizers;
|
||||
arrTokenizers.resize (numMtls);
|
||||
for (i = 0; i < numMtls; ++i)
|
||||
arrTokenizers[i].tokenize (m_pModel->m_arrMaterials[i].name);
|
||||
|
||||
|
||||
// now sort the indices to receive the correct distribution of materials
|
||||
// thus we'll effectively receive perm[new]=old
|
||||
std::sort (arrMapMtlsOld.begin(), arrMapMtlsOld.end(), CMatEntityIndexSort(&arrTokenizers[0], numMtls));
|
||||
|
||||
//std::swap (arrMapMtlsOld.front(), arrMapMtlsOld.back());
|
||||
|
||||
// form the permutation perm[old]=new
|
||||
arrMapMtlsNew.resize (numMtls);
|
||||
ConstructReversePermutation(&arrMapMtlsOld[0], &arrMapMtlsNew[0], numMtls);
|
||||
|
||||
// resort the material entities in the model
|
||||
RemapMatEntities (&m_pModel->m_arrMaterials[0], numMtls, &arrMapMtlsOld[0]);
|
||||
|
||||
// now remap the material ids in the faces
|
||||
for (i = 0; i < m_pModel->numLODs(); ++i)
|
||||
m_pModel->getGeometryInfo(i)->remapMtlIds(&arrMapMtlsNew[0], numMtls);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CryModelLoader::loadTextures()
|
||||
{
|
||||
AUTO_PROFILE_SECTION(g_dTimeShaderLoad);
|
||||
if (g_GetCVars()->ca_Debug())
|
||||
g_GetLog()->UpdateLoadingScreen("\005 Loading shaders from %s", m_strGeomFileNameNoExt.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
// forgets about the m_pModel (so that it doesn't get deleted upon done()),
|
||||
// and returns it
|
||||
CryModel* CryModelLoader::detachModel()
|
||||
{
|
||||
CryModel* pModel = m_pModel; // this model will be returned
|
||||
m_pModel = NULL; // forget about the model
|
||||
return pModel;
|
||||
}
|
||||
|
||||
void CryModelLoader::indicateProgress(const char*szMsg)
|
||||
{
|
||||
if (szMsg)
|
||||
g_GetLog()->UpdateLoadingScreenPlus(szMsg?szMsg:"\003.");
|
||||
}
|
||||
139
CryAnimation/CryModelLoader.h
Normal file
139
CryAnimation/CryModelLoader.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Sergiy Migdalskiy
|
||||
//
|
||||
// Contains the load code for CryModel class, loads geometry and animation
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef _CRY_MODEL_LOADER_HDR_
|
||||
#define _CRY_MODEL_LOADER_HDR_
|
||||
|
||||
class CryCharBody;
|
||||
class CControllerManager;
|
||||
|
||||
#include "CryAnimationBase.h"
|
||||
#include "ChunkFileReader.h"
|
||||
#include <StlUtils.h>
|
||||
|
||||
class CryModelLoader
|
||||
{
|
||||
public:
|
||||
CryModelLoader (CControllerManager* pControllerManager);
|
||||
~CryModelLoader();
|
||||
|
||||
CryModel* loadNew (CryCharBody* pBody, const string& strGeomFileName, float fScale);
|
||||
|
||||
// cleans up the resources allocated during load
|
||||
void clear();
|
||||
protected:
|
||||
// tries to find out if there are any animations for this file; if there are some,
|
||||
// prepares to load them and returns true; otherwise returns false
|
||||
bool searchAnimations ();
|
||||
|
||||
// searches for lod models for the given model; returns false in case of some error
|
||||
bool preloadCGFs();
|
||||
|
||||
// loads the CCG in memory
|
||||
bool preloadCCG();
|
||||
|
||||
// loads animations for already loaded model
|
||||
bool loadAnimations();
|
||||
|
||||
// loads animations for the given file from the given directory, loading all cal files
|
||||
// which begin with the cgf file name and underscope (this is the convention for the cgf's that don't have cal file associated)
|
||||
unsigned loadAnimationsNoCAL ();
|
||||
|
||||
// loads animations for this cgf from the given cal file
|
||||
// does NOT close the file (the file belongs to the calling party)
|
||||
unsigned loadAnimationsWithCAL ();
|
||||
|
||||
|
||||
// loads the geometry files (LOD files, starting with the main one)
|
||||
bool loadCGFs();
|
||||
|
||||
// loads the CCG (including all the LODs in it)
|
||||
bool loadCCG ();
|
||||
|
||||
// creates the skin objects for the geometry and morph targets
|
||||
bool buildSkins();
|
||||
|
||||
bool loadTextures();
|
||||
|
||||
// information about an animation to load
|
||||
struct SAnimFile
|
||||
{
|
||||
string strFileName;
|
||||
string strAnimName;
|
||||
unsigned nAnimFlags; // combination of GlobalAnimation internal flags
|
||||
SAnimFile (const string& fileName, const char* szAnimName, unsigned animflags):
|
||||
strFileName(fileName), strAnimName(szAnimName), nAnimFlags(animflags) {}
|
||||
SAnimFile():nAnimFlags(0) {}
|
||||
};
|
||||
|
||||
// the animation file array
|
||||
typedef std::vector<SAnimFile> AnimFileArray;
|
||||
// loads the animations fromthe array
|
||||
unsigned loadAnimationArray (const AnimFileArray& arrAnimFiles);
|
||||
|
||||
typedef CAutoClear<CryModelLoader> CAutoClearLoader;
|
||||
|
||||
// forgets about the m_pModel (so that it doesn't get deleted upon done()),
|
||||
// and returns it
|
||||
CryModel* detachModel();
|
||||
|
||||
void indicateProgress(const char*szMsg=NULL);
|
||||
|
||||
string getCCGFilePath()
|
||||
{
|
||||
return
|
||||
m_bExtCCG ? m_strGeomFileNameNoExt + ".ccg"
|
||||
:
|
||||
"CCGF_CACHE\\" + m_strGeomFileNameNoExt+".ccg";
|
||||
}
|
||||
protected:
|
||||
|
||||
// the controller manager for the new model; this remains the same during the whole lifecycle
|
||||
CControllerManager* m_pControllerManager;
|
||||
// the model being loaded
|
||||
CryModel* m_pModel;
|
||||
|
||||
// the file without extension
|
||||
string m_strGeomFileNameNoExt;
|
||||
|
||||
// the name of the cal file
|
||||
string m_strCalFileName;
|
||||
// the CAL file handle, or NULL if none
|
||||
FILE * m_fCalFile;
|
||||
// the handle with which the animations are to be found, -1 by default
|
||||
intptr_t m_nCafFindFileHandle;
|
||||
struct _finddata_t m_fileinfo;
|
||||
|
||||
// different LODs are kept in different files; each file will have its own reader here
|
||||
TFixedArray<CChunkFileReader_AutoPtr> m_arrLodFiles;
|
||||
|
||||
// if the model is loaded from a CCG, then this gets loaded:
|
||||
// the data buffer for the CCG
|
||||
std::vector<char> m_arrBufferCCG;
|
||||
|
||||
// model scale
|
||||
float m_fScale;
|
||||
|
||||
bool m_bExtCCG; // we're loading the CCG by the command-line (we don't transform CGF name)
|
||||
|
||||
// gets set to true if and only if the initial position of bone infos have been set
|
||||
// from the initial pos chunk (i.e. no need to set it from the default animation)
|
||||
// gets initialized in loadLODs()
|
||||
// gets used in loadAnimations()
|
||||
bool m_bBoneInitPosInitialized;
|
||||
|
||||
// set to true when we load from a compiled file (CCG) rather than CGF
|
||||
bool m_bLoadFromCCG;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
23
CryAnimation/CryModelShadowVolume.cpp
Normal file
23
CryAnimation/CryModelShadowVolume.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek CryENGINE source code
|
||||
//
|
||||
// File: CryModelShadowVolume.cpp
|
||||
//
|
||||
// History:
|
||||
// -November 22,2001:Created by Marco Corbetta
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#include "stdafx.h"
|
||||
#include <Cry_Camera.h>
|
||||
#include <I3DEngine.h>
|
||||
#include "..\Cry3DEngine\ShadowVolumeEdge.h" // IEdgeConnectivityBuilder, IEdgeDetector
|
||||
#include <IEdgeConnectivityBuilder.h> // IEdgeConnectivityBuilder
|
||||
#include "IncContHeap.h"
|
||||
#include "CryModel.h"
|
||||
#include "CryModelState.h"
|
||||
#include "CVars.h"
|
||||
#include "CryCharReShadowVolume.h"
|
||||
|
||||
2031
CryAnimation/CryModelState.cpp
Normal file
2031
CryAnimation/CryModelState.cpp
Normal file
File diff suppressed because it is too large
Load Diff
595
CryAnimation/CryModelState.h
Normal file
595
CryAnimation/CryModelState.h
Normal file
@@ -0,0 +1,595 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Crytek Character Animation source code
|
||||
//
|
||||
// History:
|
||||
// Created by Oscar Blasco
|
||||
// Taken over by Vladimir Kajalin, Andrey Honich
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _CRY_MODEL_STATE_HEADER_
|
||||
#define _CRY_MODEL_STATE_HEADER_
|
||||
|
||||
#include "SSEUtils.h"
|
||||
#include "ICryAnimation.h"
|
||||
#include "CryModel.h"
|
||||
#include "MathUtils.h"
|
||||
#include "CryCharReShadowManager.h"
|
||||
#include "CryCharParticleManager.h"
|
||||
#include "CryCharAnimationParams.h"
|
||||
|
||||
class CryCharFxTrail;
|
||||
TYPEDEF_AUTOPTR(CryCharFxTrail);
|
||||
|
||||
struct CryCharMorphParams;
|
||||
class CCryModEffAnimation;
|
||||
TYPEDEF_AUTOPTR(CCryModEffAnimation);
|
||||
|
||||
class CController;
|
||||
|
||||
class CryCharDecalManager;
|
||||
class CBoneLightDynamicBind;
|
||||
|
||||
//forward declarations
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
class CryCharInstance;
|
||||
class CryModel;
|
||||
struct CObjFace;
|
||||
struct SRendParams;
|
||||
class CryModEffMorph;
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// The CryModelState class
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// currently bbox from skin is unsupported
|
||||
#define BBOX_FROM_SKIN 0
|
||||
|
||||
struct aux_bone_info {
|
||||
int iBone;
|
||||
vectorf dir0;
|
||||
float rlen0;
|
||||
quaternionf quat0;
|
||||
};
|
||||
|
||||
struct aux_phys_data {
|
||||
IPhysicalEntity *pPhysEnt;
|
||||
const char *strName;
|
||||
int nBones;
|
||||
aux_bone_info *pauxBoneInfo;
|
||||
};
|
||||
|
||||
class CryModelSubmesh;
|
||||
TYPEDEF_AUTOPTR(CryModelSubmesh);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Contains current state of character ( all unique data for character instance ):
|
||||
// Bones, physical representation, reference to vertex buffers allocated in renderer, shadow volume data.
|
||||
// It performs bone loading, character skinning, some physical calculations, rendering character and shadow volumes.
|
||||
class CryModelState
|
||||
#ifdef _DEBUG
|
||||
// ,public IRendererCallbackClient
|
||||
#endif
|
||||
{
|
||||
friend class CryModel;
|
||||
friend class CryModelGeometryLoader;
|
||||
friend class CryCharInstance;
|
||||
friend class CryModelSubmesh;
|
||||
public:
|
||||
static void initClass();
|
||||
static void deinitClass();
|
||||
|
||||
// this is the array that's returned from the LeafBuffer
|
||||
list2<CMatInfo>* getLeafBufferMaterials();
|
||||
|
||||
CryModelState(CryModel* pMesh);
|
||||
~CryModelState();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ------------------------------- BASIC CONTROL FUNCS -------------------------------- //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// void PushEffector(CCryModEffector *eff); // Puts an effector in list
|
||||
void ProcessSkinning(const Vec3& t,const Matrix44& mtxModel,int nTemplate, int nLod=-1, bool bForceUpdate=false);
|
||||
|
||||
void Render(const struct SRendParams & RendParams, Matrix44& mtxObjMatrix, struct CryCharInstanceRenderParams& rCharParams, const Vec3& t);
|
||||
|
||||
void ProcessAnimations(float deltatime_anim, bool bUpdateBones, CryCharInstance* instance); // Process this model's animations
|
||||
|
||||
CryModelState* MakeCopy(); // Makes an exact copy of this
|
||||
// model and returns it
|
||||
|
||||
int numLODs(); // number of LODs in the 0th submesh
|
||||
|
||||
// copies the given leaf buffers to this instance leaf buffers
|
||||
void CopyLeafBuffers (CLeafBuffer** pLeafBuffers);
|
||||
|
||||
ICryBone * GetBoneByName(const char * szBoneName);
|
||||
bool SetAnimationFrame(const char * szString, int nFrame);
|
||||
|
||||
void BuildPhysicalEntity(IPhysicalEntity *pent,float mass,int surface_idx,float stiffness_scale, float scale,Vec3 offset, int nLod=0);
|
||||
IPhysicalEntity *CreateCharacterPhysics(IPhysicalEntity *pHost, float mass,int surface_idx,float stiffness_scale, float scale,Vec3 offset, int nLod=0);
|
||||
int CreateAuxilaryPhysics(IPhysicalEntity *pHost, float scale,Vec3 offset, int nLod=0);
|
||||
void SynchronizeWithPhysicalEntity(IPhysicalEntity *pent, const Vec3& posMaster,const Quat& qMaster, Vec3 offset,int iDir=-1);
|
||||
// Forces skinning on the next frame
|
||||
void ForceReskin();
|
||||
IPhysicalEntity *RelinquishCharacterPhysics();
|
||||
void SetCharacterPhysParams(float mass,int surface_idx,float scale) { m_fMass=mass; m_iSurfaceIdx=surface_idx; m_fScale=scale; }
|
||||
void ProcessPhysics(float fDeltaTimePhys, int nNeff);
|
||||
IPhysicalEntity *GetCharacterPhysics() { return m_pCharPhysics; }
|
||||
IPhysicalEntity *GetCharacterPhysics(const char *pRootBoneName);
|
||||
IPhysicalEntity *GetCharacterPhysics(int iAuxPhys);
|
||||
void DestroyCharacterPhysics(int iMode=0);
|
||||
|
||||
void ResetNonphysicalBoneRotations (int nLOD, float fBlend);
|
||||
|
||||
bool IsAnimStopped();
|
||||
|
||||
void DumpState();
|
||||
|
||||
void ResetBBoxCache()
|
||||
{
|
||||
m_nLastSkinBBoxUpdateFrameId = 0;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ------------------------------- EFFECTORS FUNCTIONS -------------------------------- //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// RunAnimation:
|
||||
// Searchs for the animation in animations list and starts palying it.
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
bool RunAnimation (const char * szAnimName, const struct CryCharAnimationParams& Params, float fSpeed);
|
||||
bool RunAnimation (int nAnimID, const CryCharAnimationParams& Params, float fSpeed, bool bInternal = false);
|
||||
bool RunMorph (const char* szMorphTarget, const CryCharMorphParams& Params);
|
||||
//! Finds the morph with the given id and sets its relative time.
|
||||
//! Returns false if the operation can't be performed (no morph)
|
||||
bool StopAnimation (int nLayer);
|
||||
void StopAllMorphs();
|
||||
void FreezeAllMorphs();
|
||||
|
||||
|
||||
|
||||
// returns the animation currently being played in the given layer, or -1
|
||||
int GetCurrentAnimation (unsigned nLayer);
|
||||
|
||||
bool RunMorph (int nMorphTargetId, float fBlendInTime, float fBlendOutTime);
|
||||
|
||||
// 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 EnableIdleAnimationRestart (unsigned nLayer, bool bEnable = true);
|
||||
|
||||
// forgets about all default idle animations
|
||||
void ForgetDefaultIdleAnimations();
|
||||
|
||||
// sets the given aniimation to the given layer as the default
|
||||
void SetDefaultIdleAnimation(unsigned nLayer, const char* szAnimName);
|
||||
|
||||
// void ChangeBlendSpeedFactor(float factor, int layer);
|
||||
//
|
||||
// ==================================================================================== //
|
||||
// ResetAllAnimations: //
|
||||
// Stops all running anims and sets the model to the default pose //
|
||||
// ==================================================================================== //
|
||||
void ResetAllAnimations();
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
bool AddImpact(const int partid, vectorf point,vectorf impact,float scale);
|
||||
int TranslatePartIdToDeadBody(int partid);
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
void SetLimbIKGoal(int limbid, float scale, vectorf ptgoal=vectorf(1E10f,0,0), int ik_flags=0, float addlen=0, vectorf goal_normal=vectorf(zero));
|
||||
vectorf GetLimbEndPos(int limbid,float scale);
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// adds all heat sources ( currently head and heart) to the given object via AddHeatSource interface (member function)
|
||||
void UpdateHeatSources (CCObject * pObj, const SRendParams & RendParams);
|
||||
|
||||
// updates the dynamically (via ICryCharInstance at runtime) bound lights
|
||||
void UpdateDynBoundLights (CCObject * pObj, const SRendParams & RendParams);
|
||||
|
||||
// adds a new dynamically bound light
|
||||
CDLight* AddDynBoundLight (ICryCharInstance* pParent,CDLight* pDLight, unsigned nBone, bool bCopyLight);
|
||||
// removes the dynamically bound light
|
||||
void RemoveDynBoundLight (CDLight* pDLight);
|
||||
unsigned numDynBoundLights()const;
|
||||
CDLight* getDynBoundLight(unsigned i);
|
||||
void RemoveAllDynBoundLights();
|
||||
// checks if such light is already bound
|
||||
bool IsDynLightBound (CDLight*pDLight);
|
||||
|
||||
// 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* getGlobalBoundLight (unsigned nIndex);
|
||||
|
||||
|
||||
//! render character's shadow volume
|
||||
void RenderShadowVolumes(const SRendParams *rParams, int nLimitLOD);
|
||||
|
||||
// Set the current time of the given layer, in seconds
|
||||
void SetLayerTime (unsigned nLayer, float fTimeSeconds);
|
||||
float GetLayerTime (unsigned nLayer) const;
|
||||
|
||||
// checks for possible memory corruptions in this object and its children
|
||||
void SelfValidate ()const
|
||||
#ifndef _DEBUG
|
||||
{}
|
||||
#endif
|
||||
;
|
||||
|
||||
unsigned getInstanceNumber()const {return m_nInstanceNumber;}
|
||||
// returns the root bone
|
||||
inline CryBone* getRootBone(){return m_arrBones.empty()?NULL:&m_arrBones[0];}
|
||||
inline const CryBone* getRootBone()const{return m_arrBones.empty()?NULL:&m_arrBones[0];}
|
||||
|
||||
DECLARE_ARRAY_GETTER_METHODS(CryBone, Bone, Bones, m_arrBones)
|
||||
|
||||
// returns the approximate bounding box for this character in the passed in vectors
|
||||
void GetBoundingBox (Vec3& vMin, Vec3& vMax) const;
|
||||
|
||||
Vec3 GetCenter()const;
|
||||
|
||||
// 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 DiscardDecalRequests();
|
||||
|
||||
// Disposes the vertex buffers allocated for this character
|
||||
void DeleteLeafBuffers();
|
||||
|
||||
// Loads the caf file
|
||||
void InitBones(bool bBoneInfoDefPoseNeedInitialization);
|
||||
|
||||
// Creates the bones
|
||||
void CreateBones();
|
||||
|
||||
// Adds a decal to the character
|
||||
void AddDecal (CryEngineDecalInfo& Decal);
|
||||
// cleans up all decals
|
||||
void ClearDecals();
|
||||
void DumpDecals();
|
||||
|
||||
// creates the initial pose, initializes the bones (builds the inverse transform of the global) and IK limb pose
|
||||
void InitDefaultPose(bool bInitBoneInfos, bool bTakePoseFromBoneInfos = false);
|
||||
// sets the default position, getting it from the inverse default matrix in the bone infos
|
||||
void SetPoseFromBoneInfos();
|
||||
|
||||
// given the bone index, (INDEX, NOT ID), returns this bone's parent index
|
||||
int getBoneParentIndex (int nBoneIndex);
|
||||
int getBoneParentIndex (const CryBone* pBone) {return getBoneParentIndex(pBone - getBones());}
|
||||
int getBonePhysParentIndex (int nBoneIndex, int nLod=0); // same, but finds the first ancestor that has physical geometry
|
||||
int getBonePhysChildIndex (int nBoneIndex, int nLod=0);
|
||||
|
||||
unsigned numBoneChildren (int nBoneIndex) {return getBoneInfo(nBoneIndex)->numChildren();}
|
||||
|
||||
//returns the i-th child of the given bone
|
||||
int getBoneChildIndex (int nBone, int i);
|
||||
CryBone* getBoneChild (int nBone, int i);
|
||||
|
||||
//returns the j-th child of i-th child of the given bone
|
||||
int getBoneGrandChildIndex (int nBone, int i, int j);
|
||||
CryBone* getBoneGrandChild (int nBone, int i, int j);
|
||||
|
||||
CryBoneInfo* getBoneInfo(int nBone) {return &GetModel()->getBoneInfo(nBone);}
|
||||
const CryBoneInfo* getBoneInfo(int nBone) const {return &GetModel()->getBoneInfo(nBone);}
|
||||
CryBoneInfo* getBoneInfo(const CryBone* pBone) {return getBoneInfo(pBone - &getBone(0));}
|
||||
const CryBoneInfo* getBoneInfo(const CryBone* pBone) const {return getBoneInfo(pBone - getBones());}
|
||||
|
||||
void setBoneParent();
|
||||
|
||||
ICharInstanceSink* getAnimationEventSink (int nAnimId);
|
||||
void setAnimationEventSink (int nAnimId, ICharInstanceSink* pSink);
|
||||
void removeAllAnimationEvents();
|
||||
void removeAnimationEventSink(ICharInstanceSink * pCharInstanceSink);
|
||||
|
||||
CryModel* getAnimationSet()
|
||||
{
|
||||
SelfValidate();
|
||||
return GetModel();
|
||||
}
|
||||
|
||||
// this is no more than array of AnimEvent's
|
||||
// the class is derived from it to make it easier debugging
|
||||
// the memory allocations with the default mem owner name
|
||||
typedef std::vector<AnimEvent> AnimEventArray;
|
||||
|
||||
AnimEventArray& getAnimEvents(int nAnimId);
|
||||
void addAnimEvent (int nAnimId, int nFrame, AnimSinkEventData UserData);
|
||||
void removeAnimEvent (int nAnimId, int nFrame, AnimSinkEventData UserData);
|
||||
|
||||
const Matrix44* getBoneGlobalMatrices() const {return &m_arrBoneGlobalMatrices[0];}
|
||||
const Matrix44& getBoneMatrixGlobal (int nBone) const {return m_arrBoneGlobalMatrices[nBone];}
|
||||
Matrix44& getBoneMatrixGlobal (int nBone) {return m_arrBoneGlobalMatrices[nBone];}
|
||||
Matrix44& getBoneMatrixGlobal (const CryBone* pBone) { return getBoneMatrixGlobal (pBone - getBones()); }
|
||||
|
||||
// calculates the global matrices
|
||||
// from relative to parent matrices
|
||||
void UpdateBoneMatricesGlobal();
|
||||
|
||||
// Replaces each bone global matrix with the relative matrix.
|
||||
void ConvertBoneGlobalToRelativeMatrices ();
|
||||
|
||||
// Multiplies each bone global matrix with the parent global matrix,
|
||||
// and calculates the relative-to-default-pos matrix. This is essentially
|
||||
// the process opposite to conversion of global matrices to relative form
|
||||
// performed by ConvertBoneGlobalToRelativeMatrices()
|
||||
void UnconvertBoneGlobalFromRelativeForm(bool bNonphysicalOnly, int nLod = 0);
|
||||
|
||||
// draws the skeleton
|
||||
void debugDrawBones(const Matrix44* pModelMatrix = NULL);
|
||||
void debugDrawDynBoundLights(const Matrix44* pModelMatrix);
|
||||
|
||||
void debugDrawBoundingBox (const Matrix44* pModelMatrix, int nBBoxSegments = 1);
|
||||
|
||||
// calculates the mem usage
|
||||
void GetSize(ICrySizer* pSizer);
|
||||
|
||||
|
||||
void setAnimationSpeed (unsigned nLayer, float fSpeed);
|
||||
float getAnimationSpeed (unsigned nLayer);
|
||||
|
||||
const CryAABB& getBBox() const{return m_BBox;}
|
||||
void InitBBox();
|
||||
// notifies the renderer that the character will soon be rendered
|
||||
void PreloadResources ( float fDistance, float fTime, int nFlags);
|
||||
|
||||
// the model that's coherent with the current model state: bones etc. are taken from there
|
||||
CryModel* GetModel();
|
||||
const CryModel* GetModel()const;
|
||||
|
||||
// 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* AddSubmesh (ICryCharModel* pModel, bool bVisible = false);
|
||||
CryModelSubmesh* SetSubmesh (unsigned nSlot, ICryCharModel* pModel, bool bVisible = false);
|
||||
CryModelSubmesh* SetSubmesh (unsigned nSlot, CryModel* pModel, bool bVisible = false);
|
||||
CryModelSubmesh* AddSubmesh (CryModel* pModel, bool bVisible = false);
|
||||
|
||||
// removes submesh from the character
|
||||
void RemoveSubmesh (ICryCharSubmesh* pSubmesh);
|
||||
void RemoveSubmesh (unsigned nSlot);
|
||||
|
||||
// enumeration of submeshes
|
||||
size_t NumSubmeshes() {return m_arrSubmeshes.size();}
|
||||
ICryCharSubmesh* GetSubmesh(unsigned i);
|
||||
CryModelSubmesh* GetCryModelSubmesh(unsigned i);
|
||||
|
||||
CryModel* GetMesh();
|
||||
|
||||
// returns true if there are no submeshes yet
|
||||
bool IsEmpty()const {return m_arrSubmeshes.empty();}
|
||||
|
||||
bool SetShaderTemplateName (const char *TemplName, int Id, const char *ShaderName,IMatInfo *pCustomMaterial,unsigned nFlags);
|
||||
CLeafBuffer* GetLeafBuffer ();
|
||||
|
||||
void SetShaderFloat(const char *Name, float fVal, const char *ShaderName=NULL);
|
||||
|
||||
|
||||
private:
|
||||
// sets the animation speed scale for layers
|
||||
std::vector<float> m_arrLayerSpeedScale;
|
||||
|
||||
|
||||
// this array is used for temporal storage many times on every frame to compute the animations
|
||||
// to apply to the character instance. In order to avoid frequent reallocations, it's here
|
||||
typedef std::vector<CAnimationLayerInfo> ActiveLayerArray;
|
||||
static ActiveLayerArray g_arrActiveLayers;
|
||||
|
||||
// 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 UpdateAnimatedEffectors (float fDeltaTimeSec, ActiveLayerArray& arrActiveLayers);
|
||||
|
||||
// updates the bone matrices using the given array of animations -
|
||||
// applies the animation layers to the bones
|
||||
void UpdateBones (const ActiveLayerArray& arrActiveLayers);
|
||||
|
||||
void ApplyAnimationToBones (CAnimationLayerInfo AnimLayer);
|
||||
void ApplyAnimationsToBones (const CAnimationLayerInfo* pAnims, unsigned numAnims);
|
||||
|
||||
// out of the bone positions, calculates the bounding box for this character and puts it
|
||||
// into m_vBoxMin,m_vBoxMax
|
||||
void UpdateBBox();
|
||||
|
||||
void ValidateBBox();
|
||||
|
||||
|
||||
// renders the decals - adds the render element to the renderer
|
||||
void AddDecalRenderData (CCObject *obj, const SRendParams & RendParams);
|
||||
void AddFxRenderData (CCObject *obj, const SRendParams & RendParams);
|
||||
|
||||
CryCharFxTrail* NewFxTrail (unsigned nSlot, const struct CryCharFxTrailParams& rParams);
|
||||
void RemoveFxTrail(unsigned nSlot);
|
||||
|
||||
// if bForceUpdate , then all the requested buffers are updated immediately (delayed update is not used), every frame
|
||||
void Deform( int nLodToDeform, unsigned nFlags);
|
||||
|
||||
// skins the modelstate into the g_Temp (reserving it appropriately) if necessary
|
||||
// returns the pointer to the deformed vertices (just the geometry info get verts if the model isn't deformable)
|
||||
// returns NULL if can't deform
|
||||
const Vec3* DeformForShadowVolume( int nLodToDeform);
|
||||
|
||||
// skins this model (into the given temporary storage, if needed)
|
||||
// if no storage is provided, then allocates its own storage (g_Temp.data())
|
||||
// the normals must already be valid, they'll be only slightly modified
|
||||
const Vec3* SelfSkin(int nLOD, Vec3*pBuffer = NULL, Vec3dA16* pNormalsA16 = NULL);
|
||||
|
||||
Vec3dA16* SelfNormalSkin(int nLOD, Vec3dA16*pBuffer);
|
||||
|
||||
void DeformFirst();
|
||||
|
||||
void setScale (const Vec3d& vScale);
|
||||
|
||||
// applies necessary offsets to the root bone (only the relative to parent matrix)
|
||||
void AddModelOffsets(CryBone* pBone);
|
||||
|
||||
// the record about animation that may be kept and played back later
|
||||
struct AnimationRecord:public CryCharAnimationParams
|
||||
{
|
||||
AnimationRecord(){}
|
||||
AnimationRecord (int _nAnimId, const CryCharAnimationParams& Params, float _fSpeed):
|
||||
nAnimId(_nAnimId), fSpeed(_fSpeed),
|
||||
CryCharAnimationParams(Params){}
|
||||
|
||||
void assign (int _nAnimId, const CryCharAnimationParams& Params, float _fSpeed)
|
||||
{
|
||||
*static_cast<CryCharAnimationParams*>(this) = Params;
|
||||
nAnimId = _nAnimId;
|
||||
fSpeed = _fSpeed;
|
||||
}
|
||||
|
||||
int nAnimId;
|
||||
float fSpeed;
|
||||
};
|
||||
typedef std::deque<AnimationRecord> AnimationRecordArray;
|
||||
|
||||
|
||||
// effector layer. On some layers, the effectors may not be present (NULL)
|
||||
// On some layers, there may be a default idle animation. This animation is played
|
||||
// back when there's nothing else to play.
|
||||
struct AnimationLayer
|
||||
{
|
||||
CCryModEffAnimation_AutoPtr pEffector;
|
||||
// the default idle animation that gets started upon finish of non-looped animation
|
||||
int nDefaultIdleAnimID;
|
||||
// the blending time between the previous and the default idle animation.
|
||||
float fDefaultAnimBlendTime;
|
||||
// is the animation restart on this layer enabled or not?
|
||||
bool bEnableDefaultIdleAnimRestart;
|
||||
// should the default animation restart with the same phase as the Layer0 animation (if it's not layer 0)
|
||||
bool bKeepLayer0Phase;
|
||||
|
||||
// the queue of animations that must be started upon the end of current animation
|
||||
AnimationRecordArray queDelayed;
|
||||
|
||||
AnimationLayer ();
|
||||
|
||||
// forgets about the default idle animation
|
||||
void ForgetDefaultIdleAnimation();
|
||||
};
|
||||
|
||||
// the number of layers is dynamic. These are the layers
|
||||
typedef std::vector<AnimationLayer> AnimationLayerArray;
|
||||
AnimationLayerArray m_arrAnimationLayers;
|
||||
|
||||
// This is the bone hierarchy. All the bones of the hierarchy are present in this array
|
||||
typedef TFixedArray<CryBone> CryBoneArray;
|
||||
CryBoneArray m_arrBones;
|
||||
typedef TAllocator16<Matrix44> MatrixSSEAllocator;
|
||||
typedef TElementaryArray<Matrix44, MatrixSSEAllocator> MatrixSSEArray;
|
||||
MatrixSSEArray m_arrBoneGlobalMatrices;
|
||||
|
||||
// animation events
|
||||
typedef std::map<int, AnimEventArray> AnimEventMap;
|
||||
AnimEventMap m_mapAnimEvents;
|
||||
|
||||
// This is the global matrix that is passed to the character in ProcessSkinning() by the renderer from EF_ObjectChange()
|
||||
// It is used by the decal manager to calculate the local coordinates of the hit point
|
||||
Matrix44 m_ModelMatrix44;
|
||||
|
||||
CryAABB m_BBox;
|
||||
|
||||
CCryModEffIKSolver *m_pIKEffectors[4];
|
||||
Vec3 m_IKpos0[4];
|
||||
IPhysicalEntity *m_pCharPhysics;
|
||||
aux_phys_data m_auxPhys[8];
|
||||
int m_nAuxPhys;
|
||||
char m_bHasPhysics, m_bPhysicsAwake, m_bPhysicsWasAwake;
|
||||
Vec3 m_vOffset;
|
||||
float m_fScale;
|
||||
float m_fMass;
|
||||
int m_iSurfaceIdx;
|
||||
float m_fPhysBlendTime,m_fPhysBlendMaxTime,m_frPhysBlendMaxTime;
|
||||
|
||||
int m_nLodLevel;
|
||||
|
||||
int GetDamageTableValue (int nId);
|
||||
|
||||
// based on the distance from camera, determines the best LOD
|
||||
// for this character and memorizes it in the m_nLodLevel member
|
||||
void CalculateLOD (float fDistance);
|
||||
|
||||
enum {g_nMaxLods = 3};
|
||||
|
||||
bool IsCharacterActive();
|
||||
|
||||
// morphs the LOD 0 into the given destination (already skinned) buffer
|
||||
// pDstNormlas can be non-NULL, in which case the normals are (fakely) modified
|
||||
//void MorphWithSkin (Vec3d* pDst, Vec3dA16* pDstNormalsA16 = NULL);
|
||||
|
||||
|
||||
// cached bones for heat source calculations
|
||||
//CryBone *m_pBoneHead, *m_pBoneSpine2, *m_pBoneLeftArm;
|
||||
|
||||
CryCharParticleManager* getParticleManager() {return &m_ParticleManager;}
|
||||
|
||||
|
||||
// the lights attached to bones; their properties get
|
||||
// loaded upon CGF load and reside in CryModel
|
||||
TFixedArray<CDLight> m_arrHeatSources;
|
||||
// the external lights that are added by the entity throug 3D Engine
|
||||
typedef std::vector<CBoneLightDynamicBind> DynamicBoundLightArray;
|
||||
DynamicBoundLightArray m_arrDynBoundLights;
|
||||
|
||||
protected:
|
||||
std::vector<ICharInstanceSink*> m_arrSinks;
|
||||
|
||||
CryCharParticleManager m_ParticleManager;
|
||||
|
||||
enum FlagEnum {
|
||||
// if this flag is set, it means the last time the animation was updated,
|
||||
// the bones were not updated. So if skinning is required, then bone update will
|
||||
// need to be called
|
||||
nFlagNeedBoneUpdate = 1,
|
||||
// if this is set, then the animation was applied after the last skinning
|
||||
nFlagNeedReskinLOD = 1<<1,
|
||||
nFlagsNeedReskinAllLODs = nFlagNeedReskinLOD | (nFlagNeedReskinLOD<<1) | (nFlagNeedReskinLOD<<2)
|
||||
|
||||
};
|
||||
// misc. internal flags - combination of FlagEnum values
|
||||
unsigned m_uFlags;
|
||||
#ifdef _DEBUG
|
||||
IController::PQLog m_pqLast;
|
||||
int m_nLastFrameAnimationChanged;
|
||||
#endif
|
||||
|
||||
// this is the count of model states created so far
|
||||
static unsigned g_nInstanceCount;
|
||||
// this is the "slot" allocated for calculating the tangents:
|
||||
// within the sequence of the frames, tangent bases are calculated each n-th frame
|
||||
unsigned m_nInstanceNumber;
|
||||
|
||||
// this is the last frame id when the tangents were updated
|
||||
unsigned m_nLastTangentsUpdatedFrameId;
|
||||
unsigned m_nLastTangentsUpdatedLOD;
|
||||
unsigned m_nLastSkinBBoxUpdateFrameId;
|
||||
|
||||
Vec3d m_vRuntimeScale;
|
||||
|
||||
// this is NULL for objects with even parity of scale matrix (that is, non-flipped)
|
||||
// for flipped objects, it's FrontCull state shader
|
||||
IShader* m_pShaderStateCull;
|
||||
IShader* m_pShaderStateShadowCull;
|
||||
#ifdef _DEBUG
|
||||
// this is true until the first non-default animation is played
|
||||
bool m_bOriginalPose;
|
||||
#endif
|
||||
|
||||
// the submeshes of this model state (all body parts)
|
||||
// the main submesh is the one with index [0], if any
|
||||
// this array may be empty meaning an empty character (no geometry or skeleton)
|
||||
typedef CryModelSubmesh_AutoArray SubmeshArray;
|
||||
SubmeshArray m_arrSubmeshes;
|
||||
|
||||
// the Fx's
|
||||
typedef CryCharFxTrail_AutoArray CryCharFxTrailArray;
|
||||
CryCharFxTrailArray m_arrFxTrails;
|
||||
};
|
||||
|
||||
|
||||
#endif // _MODELSTATE_H
|
||||
839
CryAnimation/CryModelStatePhys.cpp
Normal file
839
CryAnimation/CryModelStatePhys.cpp
Normal file
@@ -0,0 +1,839 @@
|
||||
#include "stdafx.h"
|
||||
#include "CryModel.h"
|
||||
#include "CryModelState.h"
|
||||
#include "CVars.h"
|
||||
|
||||
///////////////////////////////////////////// physics stuff /////////////////////////////////////////////////
|
||||
|
||||
#define FULL_LOD_LEVEL 0
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// finds the first physicalized parent of the given bone (bone given by index)
|
||||
int CryModelState::getBonePhysParentIndex(int iBoneIndex, int nLod)
|
||||
{
|
||||
int iPrevBoneIndex;
|
||||
do {
|
||||
iBoneIndex = getBoneParentIndex(iPrevBoneIndex = iBoneIndex);
|
||||
} while (iBoneIndex!=iPrevBoneIndex && !getBoneInfo(iBoneIndex)->m_PhysInfo[nLod].pPhysGeom);
|
||||
return iBoneIndex==iPrevBoneIndex ? -1 : iBoneIndex;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// finds the first physicalized child (or itself) of the given bone (bone given by index)
|
||||
// returns -1 if it's not physicalized
|
||||
int CryModelState::getBonePhysChildIndex (int iBoneIndex, int nLod)
|
||||
{
|
||||
CryBoneInfo* pBoneInfo = getBoneInfo (iBoneIndex);
|
||||
if (pBoneInfo->m_PhysInfo[nLod].pPhysGeom)
|
||||
return iBoneIndex;
|
||||
unsigned numChildren = pBoneInfo->numChildren();
|
||||
unsigned nFirstChild = pBoneInfo->getFirstChildIndexOffset() + iBoneIndex;
|
||||
for (unsigned nChild = 0; nChild < numChildren; ++nChild)
|
||||
{
|
||||
int nResult = getBonePhysChildIndex(nFirstChild + nChild, nLod);
|
||||
if (nResult >= 0)
|
||||
return nResult;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void CryModelState::BuildPhysicalEntity(IPhysicalEntity *pent,float mass,int surface_idx,float stiffness_scale, float scale,Vec3d offset, int nLod)
|
||||
{
|
||||
pe_type pentype = pent->GetType();
|
||||
int i,j;
|
||||
pe_geomparams gp;
|
||||
pe_articgeomparams agp;
|
||||
pe_geomparams *pgp = pentype==PE_ARTICULATED ? &agp:&gp;
|
||||
pgp->flags = pentype==PE_LIVING ? 0:geom_collides|geom_floats;
|
||||
pgp->bRecalcBBox = 0;
|
||||
float volume;
|
||||
Matrix44 mtx;
|
||||
for (i=0, volume=0; i<(int)numBones(); ++i)
|
||||
if (getBoneInfo(i)->m_PhysInfo[nLod].pPhysGeom)
|
||||
volume += getBoneInfo(i)->m_PhysInfo[nLod].pPhysGeom->V;
|
||||
pgp->density = mass/volume;
|
||||
|
||||
if (surface_idx>=0)
|
||||
pgp->surface_idx = surface_idx;
|
||||
|
||||
pent->Action(&pe_action_remove_all_parts());
|
||||
|
||||
for(i=0;i<(int)numBones();i++) if (getBoneInfo(i)->m_PhysInfo[nLod].pPhysGeom) {
|
||||
mtx = getBoneMatrixGlobal(i);
|
||||
*(Vec3d*)mtx[3] *= scale;
|
||||
*(Vec3d*)mtx[3] += offset;
|
||||
pgp->pMtx4x4T = mtx[0];
|
||||
pgp->flags = /*strstr(getBoneInfo(i)->getNameCStr(),"Hand") ? geom_no_raytrace :*/ geom_collides|geom_floats;
|
||||
agp.idbody = i;
|
||||
pent->AddGeometry(getBoneInfo(i)->m_PhysInfo[nLod].pPhysGeom,pgp,i);
|
||||
getBoneInfo(i)->m_fMass = pgp->density*getBoneInfo(i)->m_PhysInfo[nLod].pPhysGeom->V;
|
||||
}
|
||||
|
||||
if (pentype==PE_ARTICULATED) {
|
||||
CryBone *pBone;
|
||||
CryBoneInfo* pBoneInfo;
|
||||
matrix3x3bf mtx0;
|
||||
for(i=0;i<(int)numBones();i++) if (getBoneInfo(i)->m_PhysInfo[nLod].pPhysGeom) {
|
||||
pe_params_joint pj;
|
||||
int iParts[8]; const char *ptr,*ptr1;
|
||||
pBone = &getBone(i);
|
||||
pBoneInfo = getBoneInfo(i);
|
||||
Matrix44& matBoneGlobal = getBoneMatrixGlobal(i);
|
||||
|
||||
pj.pSelfCollidingParts = iParts;
|
||||
if ((pj.flags = pBoneInfo->m_PhysInfo[nLod].flags)!=-1)
|
||||
pj.flags |= angle0_auto_kd*7;
|
||||
else pj.flags = angle0_locked;
|
||||
pj.op[0] = getBonePhysParentIndex(i,nLod);
|
||||
pj.op[1] = i;
|
||||
pj.pivot = matBoneGlobal.GetTranslationOLD();
|
||||
pj.pivot = (pj.pivot+offset)*scale;
|
||||
pj.nSelfCollidingParts = 0;
|
||||
if ((ptr=strstr(pBoneInfo->getNameCStr(),"Forearm"))) {
|
||||
for(j=0;j<(int)numBones();j++) if (getBoneInfo(j)->m_PhysInfo[nLod].pPhysGeom &&
|
||||
(strstr(getBoneInfo(j)->getNameCStr(),"Pelvis") || strstr(getBoneInfo(j)->getNameCStr(),"Head") ||
|
||||
strstr(getBoneInfo(j)->getNameCStr(),"Spine") ||
|
||||
(ptr1=strstr(getBoneInfo(j)->getNameCStr(),"Thigh")) && ptr[-2]==ptr1[-2] ||
|
||||
strstr(getBoneInfo(j)->getNameCStr(),"Forearm") && i>j))
|
||||
pj.pSelfCollidingParts[pj.nSelfCollidingParts++] = j;
|
||||
}
|
||||
if (pBoneInfo->m_PhysInfo[nLod].flags!=-1) for(j=0;j<3;j++) {
|
||||
pj.limits[0][j] = pBoneInfo->m_PhysInfo[nLod].min[j];
|
||||
pj.limits[1][j] = pBoneInfo->m_PhysInfo[nLod].max[j];
|
||||
pj.bounciness[j] = 0;
|
||||
pj.ks[j] = pBoneInfo->m_PhysInfo[nLod].spring_tension[j]*stiffness_scale;
|
||||
pj.kd[j] = pBoneInfo->m_PhysInfo[nLod].damping[j];
|
||||
if (fabsf(pj.limits[0][j])<3) {
|
||||
pj.qdashpot[j] = 0.2f; pj.kdashpot[j] = 40.0f;
|
||||
} else pj.qdashpot[j] = pj.kdashpot[j] = 0;
|
||||
} else for(j=0;j<3;j++) {
|
||||
pj.limits[0][j]=-1E10f; pj.limits[1][j]=1E10f;
|
||||
pj.bounciness[j]=0; pj.ks[j]=0; pj.kd[j]=stiffness_scale;
|
||||
}
|
||||
pj.pMtx0 = pj.pMtx0T = 0;
|
||||
if (pBoneInfo->m_PhysInfo[nLod].framemtx[0][0]<10 && pBoneInfo->m_PhysInfo[nLod].flags!=-1)
|
||||
pj.pMtx0 = pBoneInfo->m_PhysInfo[nLod].framemtx[0];
|
||||
pent->SetParams(&pj);
|
||||
}
|
||||
}
|
||||
|
||||
//m_vOffset = offset;
|
||||
//m_fScale = scale;
|
||||
m_fMass = mass;
|
||||
m_iSurfaceIdx = surface_idx;
|
||||
}
|
||||
|
||||
IPhysicalEntity *CryModelState::CreateCharacterPhysics(IPhysicalEntity *pHost, float mass,int surface_idx,float stiffness_scale,
|
||||
float scale,Vec3d offset, int nLod)
|
||||
{
|
||||
if (m_pCharPhysics) {
|
||||
GetPhysicalWorld()->DestroyPhysicalEntity(m_pCharPhysics);
|
||||
m_pCharPhysics = 0;
|
||||
}
|
||||
|
||||
if (m_bHasPhysics) {
|
||||
pe_params_pos pp;
|
||||
pp.iSimClass = 4;
|
||||
m_pCharPhysics = GetPhysicalWorld()->CreatePhysicalEntity(PE_ARTICULATED,5.0f,&pp,0);
|
||||
|
||||
pe_params_articulated_body pab;
|
||||
pab.bGrounded = 1;
|
||||
pab.scaleBounceResponse = 0.6f;
|
||||
pab.bAwake = 0;
|
||||
pab.pivot = vectorf(getBoneMatrixGlobal(getBonePhysChildIndex(0))[3])*scale;
|
||||
m_pCharPhysics->SetParams(&pab);
|
||||
|
||||
BuildPhysicalEntity(m_pCharPhysics, mass,surface_idx,stiffness_scale, scale,vectorf(zero), 0);
|
||||
|
||||
if (pHost) {
|
||||
pe_params_foreign_data pfd;
|
||||
pHost->GetParams(&pfd);
|
||||
MARK_UNUSED pfd.iForeignFlags;
|
||||
m_pCharPhysics->SetParams(&pfd);
|
||||
|
||||
pe_params_articulated_body pab1;
|
||||
pab1.pivot.zero();
|
||||
pab1.pHost = pHost;
|
||||
pab1.posHostPivot = vectorf(getBoneMatrixGlobal(getBonePhysChildIndex(0))[3])*scale+offset;
|
||||
m_pCharPhysics->SetParams(&pab1);
|
||||
}
|
||||
|
||||
pe_params_joint pj;
|
||||
pj.op[0] = -1;
|
||||
pj.op[1] = getBonePhysChildIndex(0);
|
||||
pj.flags = all_angles_locked | joint_no_gravity | joint_isolated_accelerations;
|
||||
m_pCharPhysics->SetParams(&pj);
|
||||
}
|
||||
|
||||
m_vOffset = offset;
|
||||
m_fScale = scale;
|
||||
m_fMass = mass;
|
||||
m_iSurfaceIdx = surface_idx;
|
||||
|
||||
return m_pCharPhysics;
|
||||
}
|
||||
|
||||
int CryModelState::CreateAuxilaryPhysics(IPhysicalEntity *pHost, float scale,Vec3d offset, int nLod)
|
||||
{
|
||||
int i,j,k;
|
||||
char strbuf[256],*pspace;
|
||||
|
||||
// Delete aux physics
|
||||
|
||||
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<(int)numBones(); i++)
|
||||
{
|
||||
CryBoneInfo* pBoneInfo = getBoneInfo(i);
|
||||
const char* szBoneName = pBoneInfo->getNameCStr();
|
||||
#if defined(LINUX)
|
||||
if (!strnicmp(szBoneName,"rope",4))
|
||||
#else
|
||||
if (!_strnicoll(szBoneName,"rope",4))
|
||||
#endif
|
||||
{
|
||||
strcpy(strbuf,szBoneName);
|
||||
if (pspace = strchr(strbuf,' '))
|
||||
{
|
||||
*pspace = 0;
|
||||
pBoneInfo->setName(strbuf);
|
||||
}
|
||||
for(j = 0; j < m_nAuxPhys && strcmp(m_auxPhys[j].strName,szBoneName); ++j)
|
||||
continue;
|
||||
if (j == m_nAuxPhys)
|
||||
{
|
||||
if (m_nAuxPhys==SIZEOF_ARRAY(m_auxPhys))
|
||||
break;
|
||||
m_auxPhys[m_nAuxPhys].pPhysEnt = GetPhysicalWorld()->CreatePhysicalEntity(PE_ROPE);
|
||||
m_auxPhys[m_nAuxPhys++].strName = szBoneName;
|
||||
}
|
||||
pBoneInfo->m_nLimbId = 100+j;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < m_nAuxPhys; ++j)
|
||||
{
|
||||
pe_status_pos sp;
|
||||
sp.pos.zero();
|
||||
sp.q.SetIdentity();
|
||||
if (pHost)
|
||||
pHost->GetStatus(&sp);
|
||||
|
||||
pe_params_rope pr;
|
||||
int iLastBone = 0;
|
||||
pr.nSegments = 0;
|
||||
for(i=0;i<(int)numBones();i++) if (!strcmp(m_auxPhys[j].strName,getBoneInfo(i)->getNameCStr()))
|
||||
pr.nSegments++;
|
||||
pr.pPoints = new vectorf[pr.nSegments+1];
|
||||
m_auxPhys[j].pauxBoneInfo = new aux_bone_info[m_auxPhys[j].nBones = pr.nSegments];
|
||||
pr.length = 0;
|
||||
|
||||
for(i=k=0;i<(int)numBones();i++) if (!strcmp(m_auxPhys[j].strName,getBoneInfo(i)->getNameCStr())) {
|
||||
if (k==0 && getBonePhysParentIndex(i,nLod)>=0)
|
||||
pr.idPartTiedTo[0] = getBonePhysParentIndex(i,nLod);
|
||||
pr.pPoints[k] = *(Vec3d*)getBoneMatrixGlobal(i)[3]*scale + offset;
|
||||
pr.pPoints[k+1] = *(Vec3d*)getBoneMatrixGlobal(getBoneChildIndex(i,0))[3]*scale + offset;
|
||||
m_auxPhys[j].pauxBoneInfo[k].iBone = i;
|
||||
m_auxPhys[j].pauxBoneInfo[k].dir0 = pr.pPoints[k+1]-pr.pPoints[k];
|
||||
if (m_auxPhys[j].pauxBoneInfo[k].dir0.len2()>0)
|
||||
m_auxPhys[j].pauxBoneInfo[k].dir0 *=
|
||||
(m_auxPhys[j].pauxBoneInfo[k].rlen0 = 1.0f/m_auxPhys[j].pauxBoneInfo[k].dir0.len());
|
||||
else {
|
||||
m_auxPhys[j].pauxBoneInfo[k].dir0(0,0,1);
|
||||
m_auxPhys[j].pauxBoneInfo[k].rlen0 = 1.0f;
|
||||
}
|
||||
m_auxPhys[j].pauxBoneInfo[k].quat0 = quaternionf(*(const matrix3x3in4x4Tf*)&getBoneMatrixGlobal(i));
|
||||
pr.pPoints[k] = sp.q*pr.pPoints[k]+sp.pos;
|
||||
pr.pPoints[k+1] = sp.q*pr.pPoints[k+1]+sp.pos;
|
||||
pr.length += (pr.pPoints[k+1]-pr.pPoints[k]).len();
|
||||
iLastBone = ++k;
|
||||
}
|
||||
if (!is_unused(pr.idPartTiedTo[0])) {
|
||||
pr.pEntTiedTo[0] = pHost;
|
||||
pr.ptTiedTo[0] = pr.pPoints[0];
|
||||
}
|
||||
if ((i = getBonePhysChildIndex(iLastBone))>=0) {
|
||||
pr.pEntTiedTo[1] = pHost;
|
||||
pr.idPartTiedTo[1] = i;
|
||||
pr.ptTiedTo[1] = pr.pPoints[pr.nSegments];
|
||||
}
|
||||
|
||||
|
||||
m_auxPhys[j].pPhysEnt->SetParams(&pr);
|
||||
delete[] pr.pPoints;
|
||||
}
|
||||
|
||||
m_vOffset = offset;
|
||||
m_fScale = scale;
|
||||
|
||||
return m_nAuxPhys;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Replaces each bone global matrix with the relative matrix.
|
||||
// The root matrix is relative to the world, which is its parent, so it's
|
||||
// obviously both the global and relative (they coincide), so it doesn't change
|
||||
//
|
||||
// ASSUMES: the matrices are unitary and orthogonal, so that Transponse(M) == Inverse(M)
|
||||
// ASSUMES: in each bone global matrix, upon entry, there's the actual global matrix
|
||||
// RETURNS: in each m_matRelativeToParent matrix, there's a matrix relative to the parent
|
||||
void CryModelState::ConvertBoneGlobalToRelativeMatrices()
|
||||
{
|
||||
unsigned i, numBones = this->numBones();
|
||||
|
||||
// scan through all bones from the bottom up (from children to parents),
|
||||
// excluding the root (i>0)
|
||||
for (i = numBones-1; i > 0; --i)
|
||||
{
|
||||
Matrix44 &Mtxrel = getBone(i).m_matRelativeToParent;
|
||||
Matrix44 &mtxBoneGlobal = getBoneMatrixGlobal (i);
|
||||
Matrix44 &mtxParentGlobal = getBoneMatrixGlobal (getBoneParentIndex(i));
|
||||
matrix3x3in4x4Tf &mtxrel((matrix3x3in4x4Tf&)Mtxrel),
|
||||
&mtxparent ((matrix3x3in4x4Tf&)mtxParentGlobal),
|
||||
&mtxchild ((matrix3x3in4x4Tf&)mtxBoneGlobal);
|
||||
|
||||
vectorf dx = mtxBoneGlobal.GetTranslationOLD()-mtxParentGlobal.GetTranslationOLD();
|
||||
(mtxrel = mtxparent.T()) *= mtxchild;
|
||||
Mtxrel.SetTranslationOLD(dx*mtxparent);
|
||||
Mtxrel[0][3] = Mtxrel[1][3] = Mtxrel[2][3] = 0.0f;
|
||||
Mtxrel[3][3] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Multiplies each bone global matrix with the parent global matrix,
|
||||
// and calculates the relative-to-default-pos matrix. This is essentially
|
||||
// the process opposite to conversion of global matrices to relative form
|
||||
// performed by ConvertBoneGlobalToRelativeMatrices()
|
||||
// NOTE:
|
||||
// The root matrix is relative to the world, which is its parent, so it's
|
||||
// obviously both the global and relative (they coincide), so it doesn't change
|
||||
//
|
||||
// PARAMETERS:
|
||||
// bNonphysicalOnly - if set to true, only those bones that have no physical geometry are affected
|
||||
//
|
||||
// ASSUMES: in each m_matRelativeToParent matrix, upon entry, there's a matrix relative to the parent
|
||||
// RETURNS: in each bone global matrix, there's the actual global matrix
|
||||
void CryModelState::UnconvertBoneGlobalFromRelativeForm(bool bNonphysicalOnlyArg, int nLod)
|
||||
{
|
||||
unsigned numBones = this->numBones();
|
||||
bool bNonphysicalOnly = true;
|
||||
// start from 1, since we don't affect the root, which is #0
|
||||
for (unsigned i = 1; i < numBones; ++i)
|
||||
{
|
||||
CryBoneInfo* pBoneInfo = getBoneInfo(i);
|
||||
bool bPhysical = pBoneInfo->getPhysInfo(nLod).pPhysGeom || pBoneInfo->getLimbId()>=100;
|
||||
if (!bNonphysicalOnly || !bPhysical)
|
||||
{
|
||||
assert (pBoneInfo->hasParent());
|
||||
Matrix44& matBoneGlobal = getBoneMatrixGlobal(i);
|
||||
matBoneGlobal = getBone(i).m_matRelativeToParent*getBoneMatrixGlobal(getBoneParentIndex(i));
|
||||
}
|
||||
if (bPhysical) // don't update the 1st physical bone (pelvis) even if bNonphysicalOnlyArg is false
|
||||
bNonphysicalOnly = bNonphysicalOnlyArg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CryModelState::ResetNonphysicalBoneRotations(int nLod, float fBlend)
|
||||
{
|
||||
// set non-physical bones to their default position wrt parent for LODs>0
|
||||
// do it for every bone except the root, parents first
|
||||
unsigned numBones = this->numBones();
|
||||
for (unsigned nBone = 1; nBone < numBones; ++nBone)
|
||||
{
|
||||
CryBoneInfo* pInfo = getBoneInfo(nBone);
|
||||
if (!pInfo->getPhysInfo(nLod).pPhysGeom)
|
||||
{
|
||||
CryBone* pBone = &getBone(nBone);
|
||||
Matrix44& matBoneGlobal = getBoneMatrixGlobal(nBone);
|
||||
if (fBlend>=1.0f)
|
||||
pInfo->getDefRelTransform().buildMatrix(pBone->m_matRelativeToParent);
|
||||
else
|
||||
{
|
||||
IController::PQLog pq;
|
||||
pq.blendPQ(pBone->m_pqTransform, pInfo->getDefRelTransform(), fBlend);
|
||||
pq.buildMatrix(pBone->m_matRelativeToParent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CryModelState::SynchronizeWithPhysicalEntity(IPhysicalEntity *pent, const Vec3& posMaster,const Quat& qMaster, Vec3 offset, int iDir)
|
||||
{
|
||||
if (pent && (iDir==0 || iDir<0 && pent->GetType()!=PE_ARTICULATED && pent->GetType()!=PE_RIGID))
|
||||
{ // copy state _to_ physical entity
|
||||
//m_vOffset = offset;
|
||||
//m_fScale = scale;
|
||||
|
||||
if (!m_bHasPhysics) return;
|
||||
pe_params_part partpos;
|
||||
int i,j;
|
||||
for(i=j=0;i<(int)numBones();i++) if (getBoneInfo(i)->m_PhysInfo[0].pPhysGeom)
|
||||
j=i;
|
||||
if (pent->GetType()==PE_ARTICULATED)
|
||||
offset = -*(Vec3*)getBoneMatrixGlobal(getBonePhysChildIndex(0))[3];//*scale;
|
||||
|
||||
for(i=0;i<(int)numBones();i++) if (getBoneInfo(i)->m_PhysInfo[0].pPhysGeom)
|
||||
{
|
||||
partpos.partid = i;
|
||||
partpos.bRecalcBBox = i==j;
|
||||
Matrix44 mtx = getBoneMatrixGlobal(i);
|
||||
//*(vectorf*)mtx[3] *= scale;
|
||||
*(Vec3d*)mtx[3] += offset;
|
||||
partpos.pMtx4x4T = (float*)&mtx;
|
||||
if (!pent->SetParams(&partpos))
|
||||
break;
|
||||
}
|
||||
} else
|
||||
{ // copy state _from_ physical entity
|
||||
if (!pent && !m_nAuxPhys)
|
||||
return;
|
||||
int i,j,nLod = min(m_pCharPhysics!=pent?1:0,(int)numLODs()-1);
|
||||
float rScale = 1.0f;///scale;
|
||||
pe_status_pos sp;
|
||||
pe_status_joint sj;
|
||||
m_bPhysicsAwake = 0;
|
||||
if (pent)
|
||||
m_bPhysicsAwake = pent->GetStatus(&pe_status_awake());
|
||||
for(j=0;j<m_nAuxPhys;j++)
|
||||
m_bPhysicsAwake |= m_auxPhys[j].pPhysEnt->GetStatus(&pe_status_awake());
|
||||
|
||||
if (!m_bPhysicsAwake && !m_bPhysicsWasAwake)
|
||||
return;
|
||||
|
||||
if (!m_bPhysicsAwake)
|
||||
m_fPhysBlendTime = m_fPhysBlendMaxTime+0.1f;
|
||||
ResetNonphysicalBoneRotations(nLod, m_fPhysBlendTime*m_frPhysBlendMaxTime);
|
||||
|
||||
if (pent)
|
||||
{
|
||||
pe_status_pos partpos;
|
||||
partpos.flags = status_local;
|
||||
for(i=0;i<(int)numBones();i++) if (getBoneInfo(i)->m_PhysInfo[nLod].pPhysGeom)
|
||||
{
|
||||
partpos.partid = i;
|
||||
getBoneMatrixGlobal(i).SetIdentity();
|
||||
partpos.pMtx4x4T = (float*)&getBoneMatrixGlobal(i);
|
||||
pent->GetStatus(&partpos);
|
||||
getBoneMatrixGlobal(i).SetTranslationOLD((getBoneMatrixGlobal(i).GetTranslationOLD()-offset)*rScale);
|
||||
if (m_fPhysBlendTime < m_fPhysBlendMaxTime)
|
||||
break; // if in blending stage, do it only for the root
|
||||
}
|
||||
|
||||
//Matrix33 mtxRel;
|
||||
for(;i<(int)numBones();i++) if (getBoneInfo(i)->m_PhysInfo[nLod].pPhysGeom)
|
||||
{
|
||||
//mtxRel.SetIdentity33();
|
||||
// build the physparent->immediate parent matrix
|
||||
//for(j=getBoneParentIndex(i); j>0 && !getBoneInfo(j)->getPhysInfo(nLod).pPhysGeom; j=getBoneParentIndex(j))
|
||||
// mtxRel = (matrix3x3in4x4Tf&)getBone(j).m_matRelativeToParent * mtxRel;
|
||||
sj.idChildBody = i;
|
||||
pent->GetStatus(&sj);
|
||||
//(matrix3x3in4x4Tf&)getBone(i).m_matRelativeToParent = mtxRel.T()*Matrix33(sj.quat0*GetRotationXYZ<float>(sj.q+sj.qext));
|
||||
(matrix3x3in4x4Tf&)getBone(i).m_matRelativeToParent =
|
||||
Matrix33(getBoneInfo(i)->getqRelPhysParent(nLod) * sj.quat0 * GetRotationXYZ<float>(sj.q+sj.qext));
|
||||
getBone(i).m_matRelativeToParent.SetTranslationOLD(getBoneInfo(i)->getDefRelTransform().vPos);
|
||||
}
|
||||
|
||||
pent->GetStatus(&sp);
|
||||
}
|
||||
else
|
||||
{
|
||||
sp.pos = posMaster;
|
||||
sp.q = qMaster;
|
||||
}
|
||||
|
||||
// additionally copy state from all ropes present in this character
|
||||
pe_params_rope pr;
|
||||
for(j=0;j<m_nAuxPhys;j++)
|
||||
{
|
||||
m_auxPhys[j].pPhysEnt->GetParams(&pr);
|
||||
for(i=0;i<m_auxPhys[j].nBones;i++) {
|
||||
vectorf &pt0 = *(vectorf*)((char*)pr.pPoints+i*pr.iStride),
|
||||
&pt1 = *(vectorf*)((char*)pr.pPoints+(i+1)*pr.iStride),
|
||||
dir = (pt1-pt0)*sp.q;
|
||||
float len = dir.len();
|
||||
if (len>1E-4f) {
|
||||
int nBone = m_auxPhys[j].pauxBoneInfo[i].iBone;
|
||||
CryBone &bone = getBone(nBone);
|
||||
Matrix44& matBoneGlobal = getBoneMatrixGlobal(nBone);
|
||||
|
||||
//(quaternionf(m_auxPhys[j].pauxBoneInfo[i].dir0, dir/len)*m_auxPhys[j].pauxBoneInfo[i].quat0).
|
||||
// getmatrix(matrix3x3in4x4Tf(matBoneGlobal[0])) *= len*m_auxPhys[j].pauxBoneInfo[i].rlen0;
|
||||
//Q2M_IVO
|
||||
//(GetRotationV0V1(m_auxPhys[j].pauxBoneInfo[i].dir0, dir/len)*m_auxPhys[j].pauxBoneInfo[i].quat0).
|
||||
//getmatrix((matrix3x3in4x4Tf&)matBoneGlobal) *= len*m_auxPhys[j].pauxBoneInfo[i].rlen0;
|
||||
*(matrix3x3in4x4Tf*)&matBoneGlobal = (
|
||||
matrix3x3f(
|
||||
GetRotationV0V1(m_auxPhys[j].pauxBoneInfo[i].dir0, dir/len) * m_auxPhys[j].pauxBoneInfo[i].quat0
|
||||
) * len*m_auxPhys[j].pauxBoneInfo[i].rlen0
|
||||
);
|
||||
matBoneGlobal.SetTranslationOLD(((pt0-sp.pos)*sp.q-offset)*rScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnconvertBoneGlobalFromRelativeForm(m_fPhysBlendTime>=m_fPhysBlendMaxTime, nLod);
|
||||
if(m_bPhysicsAwake)
|
||||
ForceReskin();
|
||||
m_bPhysicsWasAwake = m_bPhysicsAwake;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
IPhysicalEntity *CryModelState::RelinquishCharacterPhysics()
|
||||
{
|
||||
if (m_bHasPhysics) {
|
||||
int i, nLod=min(numLODs()-1,1), iRoot=getBonePhysChildIndex(0,0);
|
||||
DestroyCharacterPhysics();
|
||||
|
||||
ConvertBoneGlobalToRelativeMatrices(); // takes into accout all post-animation layers
|
||||
// store death pose (current) matRelative orientation in bone's m_pqTransform
|
||||
for(i=0;i<(int)numBones();i++)
|
||||
{ // '-' since pqTransform.buildMatrix assumes flipped quaternion
|
||||
getBone(i).m_pqTransform.vRotLog = -log(CryQuat((matrix3x3in4x4Tf&)getBone(i).m_matRelativeToParent)).v;
|
||||
//Vec3 vb4 = getBone(i).m_pqTransform.vRotLog;
|
||||
AdjustLogRotationTo(getBoneInfo(i)->getDefRelTransform().vRotLog, getBone(i).m_pqTransform.vRotLog);
|
||||
/*Vec3 vaft = getBone(i).m_pqTransform.vRotLog;
|
||||
if (fabsf(exp(CryQuat(0,vb4.x,vb4.y,vb4.z))|exp(CryQuat(0,vaft.x,vaft.y,vaft.z)))<0.98f)
|
||||
getBone(i).m_pqTransform.vRotLog=vb4;*/
|
||||
getBone(i).m_pqTransform.vPos = getBoneInfo(i)->getDefRelTransform().vPos;
|
||||
}
|
||||
ResetNonphysicalBoneRotations(nLod,1.0f); // reset nonphysical bones matRel to default pose matRel
|
||||
UnconvertBoneGlobalFromRelativeForm(false,nLod); // build matGlobals from matRelativeToParents
|
||||
|
||||
pe_params_articulated_body pab;
|
||||
pab.bGrounded = 0;
|
||||
pab.scaleBounceResponse = 1;
|
||||
pab.bCheckCollisions = 1;
|
||||
pab.bCollisionResp = 1;
|
||||
|
||||
IPhysicalEntity *res = GetPhysicalWorld()->CreatePhysicalEntity(PE_ARTICULATED,&pab);//,&pp,0);
|
||||
BuildPhysicalEntity(res, m_fMass,m_iSurfaceIdx,0, m_fScale,m_vOffset, nLod);
|
||||
|
||||
pe_params_joint pj;
|
||||
pj.bNoUpdate = 1;
|
||||
iRoot = getBonePhysChildIndex(0,nLod);
|
||||
for(i=0;i<3;i++) { pj.ks[i]=0; pj.kd[i]=0.2f; }
|
||||
for(i=numBones()-1;i>=0;i--) if (getBoneInfo(i)->m_PhysInfo[nLod].pPhysGeom) {
|
||||
pj.op[0] = getBonePhysParentIndex(i,nLod);
|
||||
pj.op[1] = i;
|
||||
pj.flags = getBoneInfo(i)->m_PhysInfo[nLod].flags & (all_angles_locked | angle0_limit_reached*7);
|
||||
if (i==iRoot)
|
||||
pj.flags = 0;
|
||||
res->SetParams(&pj);
|
||||
}
|
||||
|
||||
m_fPhysBlendTime = 0.0f;
|
||||
m_fPhysBlendMaxTime = g_GetCVars()->ca_DeathBlendTime();
|
||||
if (m_fPhysBlendMaxTime>0.001f)
|
||||
m_frPhysBlendMaxTime = 1.0f/m_fPhysBlendMaxTime;
|
||||
else
|
||||
{
|
||||
m_fPhysBlendMaxTime = 0.0f;
|
||||
m_frPhysBlendMaxTime = 1.0f;
|
||||
}
|
||||
|
||||
ResetNonphysicalBoneRotations(nLod,0.0f); // restore death pose matRel from m_pqTransform
|
||||
UnconvertBoneGlobalFromRelativeForm(false,nLod); // build matGlobals from matRelativeToParents
|
||||
|
||||
pe_simulation_params sp;
|
||||
sp.gravity.Set(0,0,-7.5f);
|
||||
sp.maxTimeStep = 0.025f;
|
||||
sp.damping = 0.3f;
|
||||
sp.iSimClass = 2;
|
||||
res->SetParams(&sp);
|
||||
|
||||
m_vOffset.zero();
|
||||
m_bPhysicsAwake=m_bPhysicsWasAwake = 1;
|
||||
m_uFlags |= nFlagsNeedReskinAllLODs; // in order to force skinning update during the first death sequence frame
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CryModelState::AddImpact(const int partid,vectorf point,vectorf impact,float scale)
|
||||
{
|
||||
if (m_pCharPhysics)
|
||||
{
|
||||
if ((unsigned int)partid>=(unsigned int)numBones())
|
||||
{
|
||||
//GetLog()->LogToFile("*ERROR* CryModelState::AddImpact: part id %u is out of range [0..%u]", partid, numBones());
|
||||
// sometimes out of range value (like -1) is passed to indicate that no impulse should be added to character
|
||||
return false;
|
||||
}
|
||||
int i;
|
||||
|
||||
CryBone* pImpactBone = &getBone(partid);
|
||||
const char* szImpactBoneName = getBoneInfo(partid)->getNameCStr();
|
||||
|
||||
for(i=0; i<4 && !(m_pIKEffectors[i] && m_pIKEffectors[i]->AffectsBone(partid)); i++);
|
||||
const char *ptr = strstr(szImpactBoneName,"Spine");
|
||||
if (i<4 || strstr(szImpactBoneName,"Pelvis") || ptr && !ptr[5])
|
||||
{
|
||||
if (i<4)
|
||||
{
|
||||
pe_params_articulated_body pab;
|
||||
m_pCharPhysics->GetParams(&pab);
|
||||
if (!pab.pHost)
|
||||
return false;
|
||||
//pe_action_impulse crouch_impulse;
|
||||
//crouch_impulse.impulse(0,0,-impact.len()*10.0f);
|
||||
//pab.pHost->Action(&crouch_impulse);
|
||||
}
|
||||
return false; // don't add impulse to spine and pelvis
|
||||
}
|
||||
if (strstr(szImpactBoneName,"UpperArm") || strstr(szImpactBoneName,"Forearm"))
|
||||
impact *= 0.35f;
|
||||
else if (strstr(szImpactBoneName,"Hand"))
|
||||
impact *= 0.2f;
|
||||
else if (strstr(szImpactBoneName,"Spine1"))
|
||||
impact *= 0.2f;
|
||||
|
||||
pe_action_impulse impulse;
|
||||
impulse.partid = partid;
|
||||
impulse.impulse = impact;
|
||||
impulse.point = point;
|
||||
m_pCharPhysics->Action(&impulse);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pe_action_impulse impulse;
|
||||
impulse.impulse = impact;
|
||||
impulse.point = point;
|
||||
for(int i=0;i<m_nAuxPhys;i++)
|
||||
m_auxPhys[i].pPhysEnt->Action(&impulse);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int CryModelState::TranslatePartIdToDeadBody(int partid)
|
||||
{
|
||||
if ((unsigned int)partid>=(unsigned int)numBones())
|
||||
return -1;
|
||||
|
||||
int nLod = min(numLODs()-1,1);
|
||||
if (getBoneInfo(partid)->m_PhysInfo[nLod].pPhysGeom)
|
||||
return partid;
|
||||
|
||||
return getBonePhysParentIndex(partid,nLod);
|
||||
}
|
||||
|
||||
void CryModelState::SetLimbIKGoal(int limbid, float scale, vectorf ptgoal, int ik_flags, float addlen, vectorf goal_normal)
|
||||
{
|
||||
if (ptgoal.x==1E10) {
|
||||
if (m_pIKEffectors[limbid]) {
|
||||
delete m_pIKEffectors[limbid];
|
||||
m_pIKEffectors[limbid] = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
int i;
|
||||
if (!m_pIKEffectors[limbid]) {
|
||||
for(i=0;i<(int)numBones() && getBoneInfo(i)->m_nLimbId!=limbid;i++);
|
||||
if (i==numBones()) return;
|
||||
m_pIKEffectors[limbid] = new CCryModEffIKSolver(this);
|
||||
m_pIKEffectors[limbid]->SetPrimaryBone(i);
|
||||
}
|
||||
for(i=0;i<3;i++) if (ptgoal[i]!=IK_NOT_USED) ptgoal[i]/=scale;
|
||||
if (limbid==LIMB_LEFT_LEG || limbid==LIMB_RIGHT_LEG)
|
||||
ptgoal.z += m_IKpos0[limbid].z;
|
||||
m_pIKEffectors[limbid]->SetGoal(ptgoal,ik_flags,addlen,goal_normal,m_IKpos0[limbid].z);
|
||||
}
|
||||
|
||||
vectorf CryModelState::GetLimbEndPos(int limbid, float scale)
|
||||
{
|
||||
int i;
|
||||
for(i=0;i<(int)numBones() && getBoneInfo(i)->m_nLimbId!=limbid;i++);
|
||||
if (i==numBones())
|
||||
return vectorf(zero);
|
||||
int limbend = getBoneGrandChildIndex(i,0,0);
|
||||
vectorf res = *(vectorf*)getBoneMatrixGlobal(limbend)[3]*scale;
|
||||
if (limbid==LIMB_LEFT_LEG || limbid==LIMB_RIGHT_LEG)
|
||||
res.z -= m_IKpos0[limbid].z;
|
||||
return res;
|
||||
}
|
||||
|
||||
void CryModelState::ProcessPhysics(float fDeltaTimePhys, int nNeff)
|
||||
{
|
||||
if(g_GetCVars()->ca_NoPhys())
|
||||
return;
|
||||
|
||||
m_uFlags |= nFlagsNeedReskinAllLODs;
|
||||
|
||||
pe_params_joint pj;
|
||||
pe_status_joint sj;
|
||||
pj.bNoUpdate = 1;
|
||||
if (fDeltaTimePhys>0)
|
||||
pj.ranimationTimeStep = 1.0f/(pj.animationTimeStep = fDeltaTimePhys);
|
||||
int i,j,iParent,iRoot;
|
||||
|
||||
if (nNeff>=0)
|
||||
for(i=0;i<4;i++) if (m_pIKEffectors[i])
|
||||
m_pIKEffectors[i]->Tick (fDeltaTimePhys);
|
||||
|
||||
if (m_pCharPhysics && (m_bPhysicsAwake = m_pCharPhysics->GetStatus(&pe_status_awake())))
|
||||
{
|
||||
if (nNeff==0)
|
||||
{ // if there's no animation atm, just read the state from physics verbatim
|
||||
SynchronizeWithPhysicalEntity(m_pCharPhysics, Vec3(0,0,0),Quat(1,0,0,0),m_vOffset);
|
||||
} else
|
||||
{
|
||||
matrix3x3bf mtxid;
|
||||
mtxid.SetIdentity();
|
||||
|
||||
// read angles deltas from physics and set current angles from animation as qext to physics
|
||||
for(i=0; i<(int)numBones(); i++)
|
||||
if (getBoneInfo(i)->getPhysInfo(0).pPhysGeom && (iParent=getBonePhysParentIndex(i)) >= 0)
|
||||
{
|
||||
for (j = 0; j < 4 && !(m_pIKEffectors[j] && m_pIKEffectors[j]->AffectsBone(i)); ++j);
|
||||
if (j<4)
|
||||
continue;
|
||||
sj.idChildBody = i;
|
||||
if (!m_pCharPhysics->GetStatus(&sj))
|
||||
continue;
|
||||
matrix3x3in4x4Tf &matrel((matrix3x3in4x4Tf&)getBone(i).m_matRelativeToParent);
|
||||
matrix3x3f matparent,mtx; matparent.SetIdentity();
|
||||
for(int nCurParent=getBoneParentIndex(i); nCurParent != iParent; nCurParent=getBoneParentIndex(nCurParent))
|
||||
matparent.T() *= ((matrix3x3in4x4Tf&)getBone(nCurParent).m_matRelativeToParent).T();
|
||||
matrix3x3f &mtx0(getBoneInfo(i)->getPhysInfo(0).framemtx[0][0]<10 ? (matrix3x3RMf&)*(&getBoneInfo(i)->getPhysInfo(0).framemtx[0][0]) : mtxid);
|
||||
|
||||
//EULER_IVO
|
||||
//(((mtx=mtx0.T())*=matparent)*=matrel).GetEulerAngles_XYZ(sj.qext);
|
||||
mtx=mtx0.T();
|
||||
mtx*=matparent;
|
||||
mtx*=matrel;
|
||||
sj.qext=Ang3::GetAnglesXYZ(mtx);
|
||||
|
||||
|
||||
//quaternionf(sj.q+sj.qext).getmatrix(matrel);
|
||||
|
||||
//Q2M_IVO
|
||||
//GetRotationXYZ(sj.q+sj.qext).getmatrix(matrel);
|
||||
matrel=matrix3x3f(GetRotationXYZ<float>(sj.q+sj.qext));
|
||||
|
||||
(matrel.T() *= mtx0.T()) *= matparent;
|
||||
|
||||
pj.op[0] = iParent;
|
||||
pj.op[1] = i;
|
||||
pj.qext = sj.qext;
|
||||
m_pCharPhysics->SetParams(&pj);
|
||||
}
|
||||
|
||||
UnconvertBoneGlobalFromRelativeForm(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (nNeff>=0)
|
||||
for(int i=0;i<4;i++) if (m_pIKEffectors[i])
|
||||
m_pIKEffectors[i]->ApplyToBone (nNeff++);
|
||||
|
||||
if (m_pCharPhysics)
|
||||
{
|
||||
iRoot = getBonePhysChildIndex(0);
|
||||
|
||||
if (m_bPhysicsAwake)
|
||||
{
|
||||
matrix3x3bf matrel;
|
||||
pj.q.zero();
|
||||
|
||||
for(i=0; i<(int)numBones(); i++) if (getBoneInfo(i)->m_PhysInfo[0].pPhysGeom && (iParent=getBonePhysParentIndex(i))>=0)
|
||||
{ // now force positions of IK-controlled bones to physics
|
||||
for(j=0; j<4 && !(m_pIKEffectors[j] && m_pIKEffectors[j]->AffectsBone(i)); j++);
|
||||
if (j==4)
|
||||
continue;
|
||||
(matrel = ((matrix3x3in4x4Tf&)getBoneMatrixGlobal(iParent)).T()) *= (matrix3x3in4x4Tf&)getBoneMatrixGlobal(i);
|
||||
if (getBoneInfo(i)->m_PhysInfo[0].framemtx[0][0]<10)
|
||||
matrel.T() *= (matrix3x3RMf&)*(&getBoneInfo(i)->m_PhysInfo[0].framemtx[0][0]);
|
||||
|
||||
//EULER_IVO
|
||||
//matrel.GetEulerAngles_XYZ(pj.qext);
|
||||
pj.qext = Ang3::GetAnglesXYZ(matrel);
|
||||
|
||||
pj.op[0] = iParent;
|
||||
pj.op[1] = i;
|
||||
m_pCharPhysics->SetParams(&pj);
|
||||
}
|
||||
|
||||
//EULER_IVO
|
||||
//((matrix3x3in4x4Tf&)getBoneMatrixGlobal(iRoot)).GetEulerAngles_XYZ(pj.qext);
|
||||
pj.qext = Ang3::GetAnglesXYZ( (matrix3x3in4x4Tf&)getBoneMatrixGlobal(iRoot) );
|
||||
|
||||
pj.op[0] = -1;
|
||||
pj.op[1] = iRoot;
|
||||
m_pCharPhysics->SetParams(&pj);
|
||||
} else
|
||||
SynchronizeWithPhysicalEntity(m_pCharPhysics, Vec3(zero),Quat(1,0,0,0),Vec3(zero), 0);
|
||||
|
||||
pe_params_articulated_body pab;
|
||||
pab.pivot.zero();
|
||||
pab.posHostPivot = m_vOffset+vectorf(getBoneMatrixGlobal(iRoot)[3])*m_fScale;
|
||||
pab.bRecalcJoints = m_bPhysicsAwake;
|
||||
m_pCharPhysics->SetParams(&pab);
|
||||
}
|
||||
|
||||
for(i=0;i<m_nAuxPhys;i++)
|
||||
m_bPhysicsAwake |= m_auxPhys[i].pPhysEnt->GetStatus(&pe_status_awake());
|
||||
|
||||
if (m_bPhysicsAwake)
|
||||
m_uFlags |= nFlagsNeedReskinAllLODs;
|
||||
m_bPhysicsWasAwake = m_bPhysicsAwake;
|
||||
}
|
||||
|
||||
IPhysicalEntity *CryModelState::GetCharacterPhysics(const char *pRootBoneName)
|
||||
{
|
||||
if (!pRootBoneName)
|
||||
return m_pCharPhysics;
|
||||
|
||||
for(int i=0;i<m_nAuxPhys;i++)
|
||||
{
|
||||
#if defined(LINUX)
|
||||
if (!stricmp(m_auxPhys[i].strName,pRootBoneName))
|
||||
#else
|
||||
if (!_stricoll(m_auxPhys[i].strName,pRootBoneName))
|
||||
#endif
|
||||
return m_auxPhys[i].pPhysEnt;
|
||||
}
|
||||
|
||||
return m_pCharPhysics;
|
||||
}
|
||||
|
||||
IPhysicalEntity *CryModelState::GetCharacterPhysics(int iAuxPhys)
|
||||
{
|
||||
if (iAuxPhys<0)
|
||||
return m_pCharPhysics;
|
||||
if (iAuxPhys>=m_nAuxPhys)
|
||||
return 0;
|
||||
return m_auxPhys[iAuxPhys].pPhysEnt;
|
||||
}
|
||||
|
||||
|
||||
void CryModelState::DestroyCharacterPhysics(int iMode)
|
||||
{
|
||||
if (m_pCharPhysics)
|
||||
GetPhysicalWorld()->DestroyPhysicalEntity(m_pCharPhysics,iMode);
|
||||
if (iMode==0)
|
||||
m_pCharPhysics = 0;
|
||||
|
||||
int i;
|
||||
for(i=0;i<m_nAuxPhys;i++) {
|
||||
GetPhysicalWorld()->DestroyPhysicalEntity(m_auxPhys[i].pPhysEnt,iMode);
|
||||
if (iMode==0)
|
||||
delete[] m_auxPhys[i].pauxBoneInfo;
|
||||
}
|
||||
if (iMode==0)
|
||||
m_nAuxPhys = 0;
|
||||
}
|
||||
1673
CryAnimation/CryModelSubmesh.cpp
Normal file
1673
CryAnimation/CryModelSubmesh.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user