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"
2004-06-08 14:10:51 +02:00
# include "CLogger.h"
2004-09-24 05:52:03 +02:00
# include "MaterialManager.h"
2004-11-08 23:02:01 +01:00
# include "MeshManager.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
bool CObjectEntry : : BuildRandomVariant ( const CObjectBase : : variation_key & vars , CObjectBase : : variation_key : : const_iterator & vars_it )
2004-05-30 02:46:58 +02:00
{
2005-07-03 03:37:49 +02:00
// vars_it is passed by reference so that the caller's iterator
// can be incremented by the appropriate amount, to point to the
// next object's set of variant choices (for propped models).
2005-04-03 07:02:00 +02:00
CStr chosenTexture ;
CStr chosenModel ;
2005-04-07 06:29:07 +02:00
CStr chosenColor ;
2005-04-03 07:02:00 +02:00
std : : map < CStr , CObjectBase : : Prop > chosenProps ;
2005-05-21 03:40:32 +02:00
std : : multimap < CStr , CObjectBase : : Anim > chosenAnims ;
2005-04-03 07:02:00 +02:00
// For each group in m_Base->m_Variants, take whichever variant is specified
// by 'vars', and then store its data into the 'chosen' variables. If data
// is specified more than once, the last value overrides all previous ones.
for ( std : : vector < std : : vector < CObjectBase : : Variant > > : : iterator grp = m_Base - > m_Variants . begin ( ) ;
grp ! = m_Base - > m_Variants . end ( ) ;
+ + grp )
{
if ( vars_it = = vars . end ( ) )
{
debug_warn ( " BuildRandomVariant is using too many vars " ) ;
return false ;
}
// Get the correct variant
2005-05-04 23:12:57 +02:00
u8 var_id = * vars_it + + ;
2005-09-13 01:37:52 +02:00
if ( var_id > = grp - > size ( ) )
2005-05-04 23:12:57 +02:00
{
2005-07-03 03:37:49 +02:00
LOG ( ERROR , LOG_CATEGORY , " Internal error (BuildRandomVariant: %d not in 0..%d) " , var_id , grp - > size ( ) - 1 ) ;
2005-07-30 22:12:41 +02:00
// Carry on as best we can, by using some arbitrary variant (rather
// than choosing none, else we might end up with no model or texture)
if ( grp - > size ( ) )
var_id = 0 ;
else
// ... unless there aren't any variants in this group, in which
// case just give up and try the next group
continue ;
2005-05-04 23:12:57 +02:00
}
CObjectBase : : Variant & var ( ( * grp ) [ var_id ] ) ;
2005-04-03 07:02:00 +02:00
// Apply its data:
if ( var . m_TextureFilename . Length ( ) )
chosenTexture = var . m_TextureFilename ;
if ( var . m_ModelFilename . Length ( ) )
chosenModel = var . m_ModelFilename ;
2005-04-07 06:29:07 +02:00
if ( var . m_Color . Length ( ) )
chosenColor = var . m_Color ;
2005-04-03 07:02:00 +02:00
for ( std : : vector < CObjectBase : : Prop > : : iterator it = var . m_Props . begin ( ) ; it ! = var . m_Props . end ( ) ; + + it )
chosenProps [ it - > m_PropPointName ] = * it ;
2005-05-21 03:40:32 +02:00
// If one variant defines one animation called e.g. "attack", and this
// variant defines two different animations with the same name, the one
// original should be erased, and replaced by the two new ones.
//
// So, erase all existing animations which are overridden by this variant:
2005-04-03 07:02:00 +02:00
for ( std : : vector < CObjectBase : : Anim > : : iterator it = var . m_Anims . begin ( ) ; it ! = var . m_Anims . end ( ) ; + + it )
2005-05-21 03:40:32 +02:00
chosenAnims . erase ( chosenAnims . lower_bound ( it - > m_AnimName ) , chosenAnims . upper_bound ( it - > m_AnimName ) ) ;
2005-07-30 22:12:41 +02:00
// and then insert the new ones:
2005-05-21 03:40:32 +02:00
for ( std : : vector < CObjectBase : : Anim > : : iterator it = var . m_Anims . begin ( ) ; it ! = var . m_Anims . end ( ) ; + + it )
chosenAnims . insert ( make_pair ( it - > m_AnimName , * it ) ) ;
2004-05-30 02:46:58 +02:00
}
2005-04-03 07:02:00 +02:00
// Copy the chosen data onto this model:
m_TextureName = chosenTexture ;
m_ModelName = chosenModel ;
2005-04-07 06:29:07 +02:00
if ( chosenColor . Length ( ) )
{
std : : stringstream str ;
str < < chosenColor ;
int r , g , b ;
if ( ! ( str > > r > > g > > b ) ) // Any trailing data is ignored
LOG ( ERROR , LOG_CATEGORY , " Invalid RGB colour '%s' " , chosenColor . c_str ( ) ) ;
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
2005-05-21 03:40:32 +02:00
for ( std : : map < CStr , CObjectBase : : Prop > : : iterator it = chosenProps . begin ( ) ; it ! = chosenProps . end ( ) ; + + it )
props . push_back ( it - > second ) ;
2005-07-30 22:12:41 +02:00
// TODO: This is all wrong, since it breaks the order (which vars_it relies on)
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
for ( std : : multimap < CStr , CObjectBase : : Anim > : : iterator it = chosenAnims . begin ( ) ; it ! = chosenAnims . 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
2004-05-30 02:46:58 +02:00
// start up idling
2005-05-21 03:40:32 +02:00
if ( ! m_Model - > SetAnimation ( GetRandomAnimation ( " idle " ) ) )
2005-02-11 13:57:19 +01:00
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
CObjectEntry * oe = g_ObjMan . FindObjectVariation ( prop . m_ModelName , vars , vars_it ) ;
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 ( ) ;
m_Model - > AddProp ( proppoint , propmodel ) ;
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-01-11 21:15:39 +01:00
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?
int id = rand ( ) % ( int ) count ;
std : : advance ( lower , id ) ;
return lower - > second ;
}
2004-10-06 20:46:33 +02:00
}