2004-06-03 20:38:14 +02:00
# include "precompiled.h"
2004-05-30 02:46:58 +02:00
# include "ObjectEntry.h"
# include "ObjectManager.h"
2005-03-22 18:09:36 +01:00
# include "ObjectBase.h"
2004-05-30 02:46:58 +02:00
# include "Model.h"
# include "ModelDef.h"
2006-06-02 04:10:27 +02:00
# include "ps/CLogger.h"
2004-09-24 05:52:03 +02:00
# include "MaterialManager.h"
2004-11-08 23:02:01 +01:00
# include "MeshManager.h"
2006-04-24 00:22:18 +02:00
# include "SkeletonAnim.h"
2004-05-30 02:46:58 +02:00
# include "UnitManager.h"
2004-12-12 20:43:55 +01:00
# include "Unit.h"
2004-05-30 02:46:58 +02:00
2005-09-02 20:38:25 +02:00
# include "ps/XML/Xeromyces.h"
# include "ps/XML/XMLWriter.h"
2005-08-12 19:06:53 +02:00
# include "lib/res/file/vfs.h"
2004-12-31 00:01:09 +01:00
2005-05-18 07:32:09 +02:00
# include <sstream>
2004-08-15 22:57:31 +02:00
2005-05-18 07:32:09 +02:00
# define LOG_CATEGORY "graphics"
2005-03-18 23:30:23 +01:00
2005-03-22 18:09:36 +01:00
CObjectEntry : : CObjectEntry ( int type , CObjectBase * base )
2005-10-07 17:24:29 +02:00
: m_Base ( base ) , m_Color ( 1.0f , 1.0f , 1.0f , 1.0f ) ,
m_ProjectileModel ( NULL ) , m_AmmunitionModel ( NULL ) , m_AmmunitionPoint ( NULL ) , m_Model ( NULL ) , m_Type ( type )
2004-05-30 02:46:58 +02:00
{
}
2005-05-21 03:40:32 +02:00
template < typename T , typename S > static void delete_pair_2nd ( std : : pair < T , S > v ) { delete v . second ; }
2004-05-30 02:46:58 +02:00
CObjectEntry : : ~ CObjectEntry ( )
{
2005-05-21 03:40:32 +02:00
std : : for_each ( m_Animations . begin ( ) , m_Animations . end ( ) , delete_pair_2nd < CStr , CSkeletonAnim * > ) ;
2004-05-30 02:46:58 +02:00
delete m_Model ;
}
2005-07-03 03:37:49 +02:00
2006-04-14 05:14:43 +02:00
bool CObjectEntry : : BuildVariation ( const std : : vector < std : : set < CStrW > > & selections , const std : : vector < u8 > & variationKey )
2006-03-17 04:59:49 +01:00
{
2006-04-14 05:14:43 +02:00
CObjectBase : : Variation variation = m_Base - > BuildVariation ( variationKey ) ;
2004-05-30 02:46:58 +02:00
2005-04-03 07:02:00 +02:00
// Copy the chosen data onto this model:
2006-04-14 05:14:43 +02:00
m_TextureName = variation . texture ;
m_ModelName = variation . model ;
2005-04-03 07:02:00 +02:00
2006-04-14 05:14:43 +02:00
if ( variation . color . Length ( ) )
2005-04-07 06:29:07 +02:00
{
std : : stringstream str ;
2006-04-14 05:14:43 +02:00
str < < variation . color ;
2005-04-07 06:29:07 +02:00
int r , g , b ;
if ( ! ( str > > r > > g > > b ) ) // Any trailing data is ignored
2006-04-14 05:14:43 +02:00
LOG ( ERROR , LOG_CATEGORY , " Invalid RGB colour '%s' " , variation . color . c_str ( ) ) ;
2005-04-07 06:29:07 +02:00
else
m_Color = CColor ( r / 255.0f , g / 255.0f , b / 255.0f , 1.0f ) ;
}
2005-05-21 03:40:32 +02:00
std : : vector < CObjectBase : : Prop > props ;
2005-04-03 07:02:00 +02:00
2006-04-14 05:14:43 +02:00
for ( std : : map < CStr , CObjectBase : : Prop > : : iterator it = variation . props . begin ( ) ; it ! = variation . props . end ( ) ; + + it )
2005-05-21 03:40:32 +02:00
props . push_back ( it - > second ) ;
2005-04-03 07:02:00 +02:00
// Build the model:
2004-05-30 02:46:58 +02:00
// get the root directory of this object
2005-04-03 07:02:00 +02:00
CStr dirname = g_ObjMan . m_ObjectTypes [ m_Type ] . m_Name ;
2004-05-30 02:46:58 +02:00
// remember the old model so we can replace any models using it later on
2005-04-03 07:02:00 +02:00
CModelDefPtr oldmodeldef = m_Model ? m_Model - > GetModelDef ( ) : CModelDefPtr ( ) ;
2004-06-07 22:03:10 +02:00
2005-04-03 07:02:00 +02:00
const char * modelfilename = m_ModelName ;
2004-08-11 22:18:30 +02:00
2004-12-12 19:40:00 +01:00
// try and create a model
CModelDefPtr modeldef ( g_MeshManager . GetMesh ( modelfilename ) ) ;
if ( ! modeldef )
{
2004-08-15 22:57:31 +02:00
LOG ( ERROR , LOG_CATEGORY , " CObjectEntry::BuildModel(): Model %s failed to load " , modelfilename ) ;
2004-05-30 02:46:58 +02:00
return false ;
}
2004-06-07 22:03:10 +02:00
2004-10-06 20:46:33 +02:00
// delete old model, create new
delete m_Model ;
2005-04-03 07:02:00 +02:00
m_Model = new CModel ;
2004-05-30 02:46:58 +02:00
m_Model - > SetTexture ( ( const char * ) m_TextureName ) ;
2005-03-22 18:09:36 +01:00
m_Model - > SetMaterial ( g_MaterialManager . LoadMaterial ( m_Base - > m_Material ) ) ;
2004-05-30 02:46:58 +02:00
m_Model - > InitModel ( modeldef ) ;
2005-04-07 06:29:07 +02:00
m_Model - > SetPlayerColor ( m_Color ) ;
2004-06-07 22:03:10 +02:00
2004-05-30 02:46:58 +02:00
// calculate initial object space bounds, based on vertex positions
m_Model - > CalcObjectBounds ( ) ;
2004-06-07 22:03:10 +02:00
2005-05-21 03:40:32 +02:00
// load the animations
2006-04-14 05:14:43 +02:00
for ( std : : multimap < CStr , CObjectBase : : Anim > : : iterator it = variation . anims . begin ( ) ; it ! = variation . anims . end ( ) ; + + it )
2004-05-30 02:46:58 +02:00
{
2005-05-21 03:40:32 +02:00
CStr name = it - > first . LowerCase ( ) ;
2004-05-30 02:46:58 +02:00
2005-05-21 03:40:32 +02:00
// TODO: Use consistent names everywhere, then remove this translation section.
// (It's just mapping the names used in actors onto the names used by code.)
if ( name = = " attack " ) name = " melee " ;
else if ( name = = " chop " ) name = " gather " ;
else if ( name = = " decay " ) name = " corpse " ;
2005-01-12 15:31:47 +01:00
2005-06-02 20:04:20 +02:00
if ( it - > second . m_FileName . Length ( ) )
{
CSkeletonAnim * anim = m_Model - > BuildAnimation ( it - > second . m_FileName , name , it - > second . m_Speed , it - > second . m_ActionPos , it - > second . m_ActionPos2 ) ;
if ( anim )
m_Animations . insert ( std : : make_pair ( name , anim ) ) ;
}
2004-05-30 02:46:58 +02:00
}
2005-05-21 03:40:32 +02:00
2006-03-17 04:59:49 +01:00
// ensure there's always an idle animation
if ( m_Animations . find ( " idle " ) = = m_Animations . end ( ) )
{
CSkeletonAnim * anim = new CSkeletonAnim ( ) ;
anim - > m_Name = " idle " ;
anim - > m_AnimDef = NULL ;
anim - > m_Speed = 0.f ;
anim - > m_ActionPos = 0.f ;
anim - > m_ActionPos2 = 0.f ;
m_Animations . insert ( std : : make_pair ( " idle " , anim ) ) ;
// Ignore errors, since they're probably saying this is a non-animated model
m_Model - > SetAnimation ( anim ) ;
}
else
{
// start up idling
if ( ! m_Model - > SetAnimation ( GetRandomAnimation ( " idle " ) ) )
LOG ( ERROR , LOG_CATEGORY , " Failed to set idle animation in model \" %s \" " , modelfilename ) ;
}
2004-06-07 22:03:10 +02:00
2004-05-30 02:46:58 +02:00
// build props - TODO, RC - need to fix up bounds here
2005-04-03 07:02:00 +02:00
// TODO: Make sure random variations get handled correctly when a prop fails
2005-05-21 03:40:32 +02:00
for ( size_t p = 0 ; p < props . size ( ) ; p + + )
2005-04-03 07:02:00 +02:00
{
2005-05-21 03:40:32 +02:00
const CObjectBase : : Prop & prop = props [ p ] ;
2005-05-10 09:13:25 +02:00
2006-03-17 04:59:49 +01:00
CObjectEntry * oe = g_ObjMan . FindObjectVariation ( prop . m_ModelName , selections ) ;
2005-05-10 09:13:25 +02:00
if ( ! oe )
2005-04-03 07:02:00 +02:00
{
2005-05-10 09:13:25 +02:00
LOG ( ERROR , LOG_CATEGORY , " Failed to build prop model \" %s \" on actor \" %s \" " , ( const char * ) prop . m_ModelName , ( const char * ) m_Base - > m_ShortName ) ;
continue ;
}
// Pluck out the special attachpoint 'projectile'
if ( prop . m_PropPointName = = " projectile " )
{
m_ProjectileModel = oe - > m_Model ;
}
// Also the other special attachpoint 'loaded-<proppoint>'
else if ( ( prop . m_PropPointName . Length ( ) > 7 ) & & ( prop . m_PropPointName . Left ( 7 ) = = " loaded- " ) )
{
CStr ppn = prop . m_PropPointName . GetSubstring ( 7 , prop . m_PropPointName . Length ( ) - 7 ) ;
m_AmmunitionModel = oe - > m_Model ;
m_AmmunitionPoint = modeldef - > FindPropPoint ( ( const char * ) ppn ) ;
if ( ! m_AmmunitionPoint )
LOG ( ERROR , LOG_CATEGORY , " Failed to find matching prop point called \" %s \" in model \" %s \" on actor \" %s \" " , ( const char * ) ppn , modelfilename , ( const char * ) prop . m_ModelName ) ;
}
else
{
SPropPoint * proppoint = modeldef - > FindPropPoint ( ( const char * ) prop . m_PropPointName ) ;
if ( proppoint )
2005-04-03 07:02:00 +02:00
{
CModel * propmodel = oe - > m_Model - > Clone ( ) ;
2006-04-24 00:22:18 +02:00
m_Model - > AddProp ( proppoint , propmodel , oe ) ;
2005-05-21 03:40:32 +02:00
propmodel - > SetAnimation ( oe - > GetRandomAnimation ( " idle " ) ) ;
2005-04-03 07:02:00 +02:00
}
else
2005-05-10 09:13:25 +02:00
LOG ( ERROR , LOG_CATEGORY , " Failed to find matching prop point called \" %s \" in model \" %s \" on actor \" %s \" " , ( const char * ) prop . m_PropPointName , modelfilename , ( const char * ) prop . m_ModelName ) ;
2004-05-30 02:46:58 +02:00
}
}
2004-10-06 20:46:33 +02:00
// setup flags
2005-04-03 07:02:00 +02:00
if ( m_Base - > m_Properties . m_CastShadows )
{
2004-10-06 20:46:33 +02:00
m_Model - > SetFlags ( m_Model - > GetFlags ( ) | MODELFLAG_CASTSHADOWS ) ;
}
2004-05-30 02:46:58 +02:00
// replace any units using old model to now use new model; also reprop models, if necessary
2004-10-06 20:46:33 +02:00
// FIXME, RC - ugh, doesn't recurse correctly through props
2005-04-03 07:02:00 +02:00
/*
// (PT: Removed this, since I'm not entirely sure what it's useful for, and it
// gets a bit confusing with randomised actors)
const std : : vector < CUnit * > & units = g_UnitMan . GetUnits ( ) ;
for ( size_t i = 0 ; i < units . size ( ) ; + + i )
{
2004-05-30 02:46:58 +02:00
CModel * unitmodel = units [ i ] - > GetModel ( ) ;
2005-04-03 07:02:00 +02:00
if ( unitmodel - > GetModelDef ( ) = = oldmodeldef )
{
2004-05-30 02:46:58 +02:00
unitmodel - > InitModel ( m_Model - > GetModelDef ( ) ) ;
2004-10-06 20:46:33 +02:00
unitmodel - > SetFlags ( m_Model - > GetFlags ( ) ) ;
2004-06-07 22:03:10 +02:00
2005-04-03 07:02:00 +02:00
const std : : vector < CModel : : Prop > & newprops = m_Model - > GetProps ( ) ;
for ( size_t j = 0 ; j < newprops . size ( ) ; j + + )
unitmodel - > AddProp ( newprops [ j ] . m_Point , newprops [ j ] . m_Model - > Clone ( ) ) ;
2004-05-30 02:46:58 +02:00
}
2004-10-06 20:46:33 +02:00
2005-04-03 07:02:00 +02:00
std : : vector < CModel : : Prop > & mdlprops = unitmodel - > GetProps ( ) ;
for ( size_t j = 0 ; j < mdlprops . size ( ) ; j + + )
{
CModel : : Prop & prop = mdlprops [ j ] ;
if ( prop . m_Model )
{
if ( prop . m_Model - > GetModelDef ( ) = = oldmodeldef )
{
2004-10-06 20:46:33 +02:00
delete prop . m_Model ;
2005-04-03 07:02:00 +02:00
prop . m_Model = m_Model - > Clone ( ) ;
2004-10-06 20:46:33 +02:00
}
}
}
2004-05-30 02:46:58 +02:00
}
2005-04-03 07:02:00 +02:00
*/
2004-05-30 02:46:58 +02:00
return true ;
}
2005-05-21 03:40:32 +02:00
CSkeletonAnim * CObjectEntry : : GetRandomAnimation ( const CStr & animationName )
2005-03-22 18:09:36 +01:00
{
2005-05-21 03:40:32 +02:00
SkeletonAnimMap : : iterator lower = m_Animations . lower_bound ( animationName ) ;
SkeletonAnimMap : : iterator upper = m_Animations . upper_bound ( animationName ) ;
size_t count = std : : distance ( lower , upper ) ;
if ( count = = 0 )
{
// LOG(WARNING, LOG_CATEGORY, "Failed to find animation '%s' for actor '%s'", animationName.c_str(), m_ModelName.c_str());
return NULL ;
}
else
{
// TODO: Do we care about network synchronisation of random animations?
2006-03-17 04:59:49 +01:00
int id = rand ( 0 , ( int ) count ) ;
2005-05-21 03:40:32 +02:00
std : : advance ( lower , id ) ;
return lower - > second ;
}
2004-10-06 20:46:33 +02:00
}