2004-05-30 02:46:58 +02:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: Model.cpp
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
2004-06-03 20:38:14 +02:00
# include "precompiled.h"
2004-05-30 02:46:58 +02:00
# include "Model.h"
2004-12-12 20:43:55 +01:00
# include "ModelDef.h"
2004-05-30 02:46:58 +02:00
# include "Quaternion.h"
# include "Bound.h"
# include "SkeletonAnim.h"
# include "SkeletonAnimDef.h"
# include "SkeletonAnimManager.h"
2004-11-08 23:02:01 +01:00
# include "MeshManager.h"
2004-12-16 13:01:47 +01:00
# include "lib/res/ogl_tex.h"
2005-04-03 07:02:00 +02:00
# include "lib/res/h_mgr.h"
2005-05-20 19:09:47 +02:00
# include "Profile.h"
2004-05-30 02:46:58 +02:00
2005-02-11 13:57:19 +01:00
# include "ps/CLogger.h"
# define LOG_CATEGORY "graphics"
2004-05-30 02:46:58 +02:00
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Constructor
CModel : : CModel ( )
2005-04-07 06:29:07 +02:00
: m_Flags ( 0 ) , m_Anim ( 0 ) , m_AnimTime ( 0 ) ,
2004-11-08 23:15:06 +01:00
m_BoneMatrices ( 0 ) , m_InvBoneMatrices ( 0 ) , m_BoneMatricesValid ( false )
2004-05-30 02:46:58 +02:00
{
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Destructor
CModel : : ~ CModel ( )
{
ReleaseData ( ) ;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ReleaseData: delete anything allocated by the model
void CModel : : ReleaseData ( )
{
delete [ ] m_BoneMatrices ;
delete [ ] m_InvBoneMatrices ;
for ( size_t i = 0 ; i < m_Props . size ( ) ; i + + ) {
delete m_Props [ i ] . m_Model ;
}
m_Props . clear ( ) ;
2004-12-12 19:40:00 +01:00
m_pModelDef = CModelDefPtr ( ) ;
2004-12-16 13:01:47 +01:00
Handle h = m_Texture . GetHandle ( ) ;
tex_free ( h ) ;
m_Texture . SetHandle ( 0 ) ;
2004-05-30 02:46:58 +02:00
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// InitModel: setup model from given geometry
2004-12-12 19:40:00 +01:00
bool CModel : : InitModel ( CModelDefPtr modeldef )
2004-05-30 02:46:58 +02:00
{
// clean up any existing data first
ReleaseData ( ) ;
m_pModelDef = modeldef ;
2005-08-09 17:55:44 +02:00
size_t numBones = modeldef - > GetNumBones ( ) ;
if ( numBones ! = 0 ) {
2004-05-30 02:46:58 +02:00
// allocate matrices for bone transformations
m_BoneMatrices = new CMatrix3D [ numBones ] ;
m_InvBoneMatrices = new CMatrix3D [ numBones ] ;
// store default pose until animation assigned
CBoneState * defpose = modeldef - > GetBones ( ) ;
for ( uint i = 0 ; i < numBones ; i + + ) {
CMatrix3D & m = m_BoneMatrices [ i ] ;
m . SetIdentity ( ) ;
m . Rotate ( defpose [ i ] . m_Rotation ) ;
m . Translate ( defpose [ i ] . m_Translation ) ;
m . GetInverse ( m_InvBoneMatrices [ i ] ) ;
}
m_BoneMatricesValid = true ;
}
return true ;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SkinPoint: skin the given point using the given blend and bonestate data
static CVector3D SkinPoint ( const CVector3D & pos , const SVertexBlend & blend ,
const CBoneState * bonestates )
{
CVector3D result ( 0 , 0 , 0 ) ;
for ( int i = 0 ; i < SVertexBlend : : SIZE & & blend . m_Bone [ i ] ! = 0xff ; i + + ) {
CMatrix3D m ;
m . SetIdentity ( ) ;
m . Rotate ( bonestates [ blend . m_Bone [ i ] ] . m_Rotation ) ;
m . Translate ( bonestates [ blend . m_Bone [ i ] ] . m_Translation ) ;
CVector3D tmp = m . Transform ( pos ) ;
result + = tmp * blend . m_Weight [ i ] ;
}
return result ;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SkinPoint: skin the given point using the given blend and matrix data
static CVector3D SkinPoint ( const CVector3D & pos , const SVertexBlend & blend ,
const CMatrix3D * bonestates )
2004-06-08 14:10:51 +02:00
{
CVector3D result , tmp ;
// must have at least one valid bone if we're using SkinPoint
2005-06-28 06:06:25 +02:00
debug_assert ( blend . m_Bone [ 0 ] ! = 0xff ) ;
2004-06-08 14:10:51 +02:00
const CMatrix3D & m = bonestates [ blend . m_Bone [ 0 ] ] ;
m . Transform ( pos , result ) ;
result * = blend . m_Weight [ 0 ] ;
2004-06-07 22:00:56 +02:00
for ( int i = 1 ; i < SVertexBlend : : SIZE & & blend . m_Bone [ i ] ! = 0xff ; i + + ) {
2004-06-08 14:10:51 +02:00
const CMatrix3D & m = bonestates [ blend . m_Bone [ i ] ] ;
2004-06-07 22:00:56 +02:00
m . Transform ( pos , tmp ) ;
2004-05-30 02:46:58 +02:00
result + = tmp * blend . m_Weight [ i ] ;
}
return result ;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CalcBound: calculate the world space bounds of this model
void CModel : : CalcBounds ( )
{
2005-02-10 00:19:48 +01:00
// Need to calculate the object bounds first, if that hasn't already been done
if ( ! m_Anim )
CalcObjectBounds ( ) ;
else
{
if ( m_Anim - > m_ObjectBounds . IsEmpty ( ) )
CalcAnimatedObjectBound ( m_Anim - > m_AnimDef , m_Anim - > m_ObjectBounds ) ;
2005-06-28 06:06:25 +02:00
debug_assert ( ! m_Anim - > m_ObjectBounds . IsEmpty ( ) ) ; // (if this happens, it'll be recalculating the bounds every time)
2005-02-10 00:19:48 +01:00
m_ObjectBounds = m_Anim - > m_ObjectBounds ;
}
2004-05-30 02:46:58 +02:00
m_ObjectBounds . Transform ( GetTransform ( ) , m_Bounds ) ;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CalcObjectBounds: calculate object space bounds of this model, based solely on vertex positions
void CModel : : CalcObjectBounds ( )
{
m_ObjectBounds . SetEmpty ( ) ;
2005-08-09 17:55:44 +02:00
size_t numverts = m_pModelDef - > GetNumVertices ( ) ;
2004-05-30 02:46:58 +02:00
SModelVertex * verts = m_pModelDef - > GetVertices ( ) ;
2005-08-09 17:55:44 +02:00
for ( size_t i = 0 ; i < numverts ; i + + ) {
2004-05-30 02:46:58 +02:00
m_ObjectBounds + = verts [ i ] . m_Coords ;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CalcAnimatedObjectBound: calculate bounds encompassing all vertex positions for given animation
void CModel : : CalcAnimatedObjectBound ( CSkeletonAnimDef * anim , CBound & result )
{
result . SetEmpty ( ) ;
2005-02-10 00:19:48 +01:00
// Set the current animation on which to perform calculations (if it's necessary)
if ( anim ! = m_Anim - > m_AnimDef )
{
CSkeletonAnim dummyanim ;
dummyanim . m_AnimDef = anim ;
if ( ! SetAnimation ( & dummyanim ) ) return ;
}
2004-05-30 02:46:58 +02:00
2005-08-09 17:55:44 +02:00
size_t numverts = m_pModelDef - > GetNumVertices ( ) ;
2004-05-30 02:46:58 +02:00
SModelVertex * verts = m_pModelDef - > GetVertices ( ) ;
2005-02-10 00:19:48 +01:00
// Remove any transformations, so that we calculate the bounding box
// at the origin. The box is later re-transformed onto the object, without
// having to recalculate the size of the box.
CMatrix3D transform , oldtransform = GetTransform ( ) ;
transform . SetIdentity ( ) ;
SetTransform ( transform ) ;
2005-05-01 21:09:13 +02:00
// Following seems to stomp over the current animation time - which, unsurprisingly,
// introduces artefacts in the currently playing animation. Save it here and restore it
// at the end.
float AnimTime = m_AnimTime ;
2004-05-30 02:46:58 +02:00
// iterate through every frame of the animation
2005-08-09 17:55:44 +02:00
for ( size_t j = 0 ; j < anim - > GetNumFrames ( ) ; j + + ) {
2004-05-30 02:46:58 +02:00
// extend bounds by vertex positions at the frame
2005-08-09 17:55:44 +02:00
for ( size_t i = 0 ; i < numverts ; i + + ) {
2004-05-30 02:46:58 +02:00
CVector3D tmp = SkinPoint ( verts [ i ] . m_Coords , verts [ i ] . m_Blend , GetBoneMatrices ( ) ) ;
result + = tmp ;
}
// advance to next frame
m_AnimTime + = anim - > GetFrameTime ( ) ;
m_BoneMatricesValid = false ;
}
2005-02-10 00:19:48 +01:00
SetTransform ( oldtransform ) ;
2005-05-01 21:09:13 +02:00
m_AnimTime = AnimTime ;
2004-05-30 02:46:58 +02:00
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BuildAnimation: load raw animation frame animation from given file, and build a
// animation specific to this model
2005-05-21 03:40:32 +02:00
CSkeletonAnim * CModel : : BuildAnimation ( const char * filename , const char * name , float speed , double actionpos , double actionpos2 )
2004-05-30 02:46:58 +02:00
{
CSkeletonAnimDef * def = g_SkelAnimMan . GetAnimation ( filename ) ;
2005-05-21 03:40:32 +02:00
if ( ! def ) return NULL ;
2005-05-27 02:38:30 +02:00
CSkeletonAnim * anim = new CSkeletonAnim ;
2005-05-21 03:40:32 +02:00
anim - > m_Name = name ;
2005-05-27 02:38:30 +02:00
anim - > m_AnimDef = def ;
anim - > m_Speed = speed ;
anim - > m_ActionPos = ( float ) ( actionpos /* * anim->m_AnimDef->GetDuration() */ / speed ) ;
anim - > m_ActionPos2 = ( float ) ( actionpos2 /* * anim->m_AnimDef->GetDuration() */ / speed ) ;
2005-02-10 00:19:48 +01:00
anim - > m_ObjectBounds . SetEmpty ( ) ;
InvalidateBounds ( ) ;
2004-05-30 02:46:58 +02:00
return anim ;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Update: update this model by the given time, in seconds
void CModel : : Update ( float time )
{
if ( m_Anim & & m_BoneMatrices ) {
2005-05-27 02:38:30 +02:00
// adjust for animation speed
float animtime = time * m_AnimSpeed ;
2004-05-30 02:46:58 +02:00
// update animation time, but don't calculate bone matrices - do that (lazily) when
// something requests them; that saves some calculation work for offscreen models,
// and also assures the world space, inverted bone matrices (required for normal
// skinning) are up to date with respect to m_Transform
2005-05-21 03:40:32 +02:00
m_AnimTime + = animtime ;
2004-05-30 02:46:58 +02:00
2005-05-27 02:38:30 +02:00
float duration = m_Anim - > m_AnimDef - > GetDuration ( ) ;
2005-05-21 03:40:32 +02:00
if ( m_AnimTime > duration ) {
2005-05-27 02:38:30 +02:00
if ( m_Flags & MODELFLAG_NOLOOPANIMATION )
{
SetAnimation ( m_NextAnim ) ;
}
else
m_AnimTime = ( float ) fmod ( m_AnimTime , duration ) ;
2004-05-30 02:46:58 +02:00
}
// mark vertices as dirty
SetDirty ( RENDERDATA_UPDATE_VERTICES ) ;
// mark matrices as dirty
2005-05-21 03:40:32 +02:00
m_BoneMatricesValid = false ;
2004-05-30 02:46:58 +02:00
}
// update props
2005-05-21 03:40:32 +02:00
for ( uint i = 0 ; i < m_Props . size ( ) ; i + + ) {
2004-05-30 02:46:58 +02:00
m_Props [ i ] . m_Model - > Update ( time ) ;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// GenerateBoneMatrices: calculate necessary bone transformation matrices for skinning
void CModel : : GenerateBoneMatrices ( )
{
if ( ! m_Anim | | ! m_BoneMatrices ) return ;
2005-05-20 19:09:47 +02:00
PROFILE ( " generating bone matrices " ) ;
2005-06-28 06:06:25 +02:00
debug_assert ( m_pModelDef - > GetNumBones ( ) = = m_Anim - > m_AnimDef - > GetNumKeys ( ) ) ;
2005-02-11 13:57:19 +01:00
2004-05-30 02:46:58 +02:00
m_Anim - > m_AnimDef - > BuildBoneMatrices ( m_AnimTime , m_BoneMatrices ) ;
const CMatrix3D & transform = GetTransform ( ) ;
2005-08-09 17:55:44 +02:00
for ( size_t i = 0 ; i < m_pModelDef - > GetNumBones ( ) ; i + + ) {
2004-06-07 22:00:56 +02:00
m_BoneMatrices [ i ] . Concatenate ( transform ) ;
m_BoneMatrices [ i ] . GetInverse ( m_InvBoneMatrices [ i ] ) ;
2004-05-30 02:46:58 +02:00
}
// update transform of boned props
// TODO, RC - ugh, we'll be doing this twice (for boned props, at least) - once here,
// and once again in SetTransform; better to just do it in Update?
2004-06-19 16:39:24 +02:00
for ( size_t j = 0 ; j < m_Props . size ( ) ; j + + ) {
const Prop & prop = m_Props [ j ] ;
2004-05-30 02:46:58 +02:00
if ( prop . m_Point - > m_BoneIndex ! = 0xff ) {
CMatrix3D proptransform = prop . m_Point - > m_Transform ; ;
if ( prop . m_Point - > m_BoneIndex ! = 0xff ) {
proptransform . Concatenate ( m_BoneMatrices [ prop . m_Point - > m_BoneIndex ] ) ;
2004-06-07 22:00:56 +02:00
} else {
2004-06-08 14:10:51 +02:00
proptransform . Concatenate ( transform ) ;
2004-06-07 22:00:56 +02:00
}
2004-05-30 02:46:58 +02:00
prop . m_Model - > SetTransform ( proptransform ) ;
}
}
m_BoneMatricesValid = true ;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SetAnimation: set the given animation as the current animation on this model;
// return false on error, else true
2005-05-27 02:38:30 +02:00
bool CModel : : SetAnimation ( CSkeletonAnim * anim , bool once , float speed , CSkeletonAnim * next )
2004-05-30 02:46:58 +02:00
{
2005-02-11 13:57:19 +01:00
m_Anim = NULL ; // in case something fails
if ( anim ) {
2004-11-11 08:09:32 +01:00
m_Flags & = ~ MODELFLAG_NOLOOPANIMATION ;
2005-05-21 03:40:32 +02:00
if ( once )
2005-05-27 02:38:30 +02:00
{
2004-11-11 08:09:32 +01:00
m_Flags | = MODELFLAG_NOLOOPANIMATION ;
2005-05-27 02:38:30 +02:00
m_NextAnim = next ;
}
2004-11-11 08:09:32 +01:00
2004-05-30 02:46:58 +02:00
if ( ! m_BoneMatrices ) {
// not boned, can't animate
return false ;
}
2005-05-21 03:40:32 +02:00
if ( anim - > m_AnimDef - > GetNumKeys ( ) ! = m_pModelDef - > GetNumBones ( ) ) {
2005-02-10 00:19:48 +01:00
// mismatch between model's skeleton and animation's skeleton
2005-02-11 13:57:19 +01:00
LOG ( ERROR , LOG_CATEGORY , " Mismatch between model's skeleton and animation's skeleton (%d model bones != %d animation keys) " ,
m_pModelDef - > GetNumBones ( ) , anim - > m_AnimDef - > GetNumKeys ( ) ) ;
2004-05-30 02:46:58 +02:00
return false ;
}
2005-02-10 00:19:48 +01:00
// reset the cached bounds when the animation is changed
m_ObjectBounds . SetEmpty ( ) ;
InvalidateBounds ( ) ;
2004-05-30 02:46:58 +02:00
// start anim from beginning
2005-05-27 02:38:30 +02:00
m_AnimTime = 0 ;
// Adjust speed by animation base rate.
m_AnimSpeed = speed * anim - > m_Speed ;
2004-05-30 02:46:58 +02:00
}
2005-02-11 13:57:19 +01:00
m_Anim = anim ;
2004-05-30 02:46:58 +02:00
return true ;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AddProp: add a prop to the model on the given point
2005-05-21 03:40:32 +02:00
void CModel : : AddProp ( SPropPoint * point , CModel * model )
2004-05-30 02:46:58 +02:00
{
// position model according to prop point position
model - > SetTransform ( point - > m_Transform ) ;
// check if we're already using this point, and replace
// model on it if so
uint i ;
for ( i = 0 ; i < m_Props . size ( ) ; i + + ) {
if ( m_Props [ i ] . m_Point = = point ) {
2004-12-12 19:40:00 +01:00
delete m_Props [ i ] . m_Model ;
2004-05-30 02:46:58 +02:00
m_Props [ i ] . m_Model = model ;
return ;
}
}
// not using point; add new prop
Prop prop ;
prop . m_Point = point ;
prop . m_Model = model ;
m_Props . push_back ( prop ) ;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// RemoveProp: remove a prop from the given point
void CModel : : RemoveProp ( SPropPoint * point )
{
typedef std : : vector < Prop > : : iterator Iter ;
for ( Iter iter = m_Props . begin ( ) ; iter ! = m_Props . end ( ) ; + + iter ) {
const Prop & prop = * iter ;
if ( prop . m_Point = = point ) {
2005-05-10 09:13:25 +02:00
delete prop . m_Model ;
2004-05-30 02:46:58 +02:00
m_Props . erase ( iter ) ;
return ;
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Clone: return a clone of this model
CModel * CModel : : Clone ( ) const
{
2005-05-21 03:40:32 +02:00
CModel * clone = new CModel ;
clone - > m_ObjectBounds = m_ObjectBounds ;
2004-05-30 02:46:58 +02:00
clone - > InitModel ( m_pModelDef ) ;
clone - > SetTexture ( m_Texture ) ;
2005-05-21 03:40:32 +02:00
if ( m_Texture . GetHandle ( ) )
h_add_ref ( m_Texture . GetHandle ( ) ) ;
2005-03-22 18:09:36 +01:00
clone - > SetMaterial ( m_Material ) ;
2004-05-30 02:46:58 +02:00
clone - > SetAnimation ( m_Anim ) ;
2004-10-06 20:45:59 +02:00
clone - > SetFlags ( m_Flags ) ;
2004-05-30 02:46:58 +02:00
for ( uint i = 0 ; i < m_Props . size ( ) ; i + + ) {
// eek! TODO, RC - need to investigate shallow clone here
2005-04-07 06:29:07 +02:00
clone - > AddProp ( m_Props [ i ] . m_Point , m_Props [ i ] . m_Model - > Clone ( ) ) ;
2004-05-30 02:46:58 +02:00
}
return clone ;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SetTransform: set the transform on this object, and reorientate props accordingly
void CModel : : SetTransform ( const CMatrix3D & transform )
{
// call base class to set transform on this object
CRenderableObject : : SetTransform ( transform ) ;
2004-10-06 20:45:59 +02:00
m_BoneMatricesValid = false ;
2005-02-10 00:19:48 +01:00
InvalidateBounds ( ) ;
2004-05-30 02:46:58 +02:00
// now set transforms on props
2005-08-09 23:26:40 +02:00
const CMatrix3D * bonematrices = GetBoneMatrices ( ) ; // TODO2: this or m_BoneMatrices? // (GetBoneMatrices updates m_BoneMatrices (when necessary) and returns it)
2004-05-30 02:46:58 +02:00
for ( size_t i = 0 ; i < m_Props . size ( ) ; i + + ) {
const Prop & prop = m_Props [ i ] ;
CMatrix3D proptransform = prop . m_Point - > m_Transform ; ;
if ( prop . m_Point - > m_BoneIndex ! = 0xff ) {
proptransform . Concatenate ( m_BoneMatrices [ prop . m_Point - > m_BoneIndex ] ) ;
2004-06-07 22:00:56 +02:00
} else {
2004-06-08 14:10:51 +02:00
proptransform . Concatenate ( transform ) ;
2004-06-07 22:00:56 +02:00
}
2004-05-30 02:46:58 +02:00
prop . m_Model - > SetTransform ( proptransform ) ;
}
}
2004-10-30 23:57:46 +02:00
2005-04-07 06:29:07 +02:00
//////////////////////////////////////////////////////////////////////////
2004-10-30 23:57:46 +02:00
void CModel : : SetMaterial ( const CMaterial & material )
{
2005-03-22 18:09:36 +01:00
m_Material = material ;
if ( m_Material . GetTexture ( ) . Trim ( PS_TRIM_BOTH ) . Length ( ) > 0 )
{
}
2004-10-30 23:57:46 +02:00
}
2005-04-07 06:29:07 +02:00
void CModel : : SetPlayerID ( int id )
{
m_Material . SetPlayerColor ( id ) ;
for ( std : : vector < Prop > : : iterator it = m_Props . begin ( ) ; it ! = m_Props . end ( ) ; + + it )
it - > m_Model - > SetPlayerID ( id ) ;
}
void CModel : : SetPlayerColor ( CColor & colour )
{
m_Material . SetPlayerColor ( colour ) ;
}