2010-06-04 17:49:14 -07:00
/* Copyright (C) 2010 Wildfire Games.
2009-04-18 10:00:33 -07:00
* This file is part of 0 A . D .
*
* 0 A . D . is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 2 of the License , or
* ( at your option ) any later version .
*
* 0 A . D . is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with 0 A . D . If not , see < http : //www.gnu.org/licenses/>.
*/
2004-06-03 11:38:14 -07:00
# include "precompiled.h"
2004-05-29 17:46:58 -07:00
# include "ObjectEntry.h"
# include "ObjectManager.h"
2005-03-22 09:09:36 -08:00
# include "ObjectBase.h"
2004-05-29 17:46:58 -07:00
# include "Model.h"
# include "ModelDef.h"
2006-06-01 19:10:27 -07:00
# include "ps/CLogger.h"
2004-09-23 20:52:03 -07:00
# include "MaterialManager.h"
2004-11-08 14:02:01 -08:00
# include "MeshManager.h"
2006-04-23 15:22:18 -07:00
# include "SkeletonAnim.h"
2010-09-10 14:02:10 -07:00
# include "TextureManager.h"
# include "renderer/Renderer.h"
2004-05-29 17:46:58 -07:00
2007-05-17 17:14:26 -07:00
# include "lib/rand.h"
2004-12-30 15:01:09 -08:00
2005-05-17 22:32:09 -07:00
# include <sstream>
2004-08-15 13:57:31 -07:00
2010-06-04 17:49:14 -07:00
CObjectEntry : : CObjectEntry ( CObjectBase * base ) :
m_Base ( base ) , m_Color ( 1.0f , 1.0f , 1.0f , 1.0f ) , m_Model ( NULL )
2004-05-29 17:46:58 -07:00
{
}
2005-05-20 18:40:32 -07:00
template < typename T , typename S > static void delete_pair_2nd ( std : : pair < T , S > v ) { delete v . second ; }
2004-05-29 17:46:58 -07:00
CObjectEntry : : ~ CObjectEntry ( )
{
2005-05-20 18:40:32 -07:00
std : : for_each ( m_Animations . begin ( ) , m_Animations . end ( ) , delete_pair_2nd < CStr , CSkeletonAnim * > ) ;
2004-05-29 17:46:58 -07:00
delete m_Model ;
}
2005-07-02 18:37:49 -07:00
2007-01-16 19:25:20 -08:00
bool CObjectEntry : : BuildVariation ( const std : : vector < std : : set < CStr > > & selections ,
2007-01-07 17:56:46 -08:00
const std : : vector < u8 > & variationKey ,
CObjectManager & objectManager )
2006-03-16 19:59:49 -08:00
{
2006-04-13 20:14:43 -07:00
CObjectBase : : Variation variation = m_Base - > BuildVariation ( variationKey ) ;
2004-05-29 17:46:58 -07:00
2005-04-02 21:02:00 -08:00
// Copy the chosen data onto this model:
2006-04-13 20:14:43 -07:00
m_TextureName = variation . texture ;
m_ModelName = variation . model ;
2005-04-02 21:02:00 -08:00
2007-02-01 06:46:14 -08:00
if ( ! variation . color . empty ( ) )
2005-04-06 21:29:07 -07:00
{
std : : stringstream str ;
2006-04-13 20:14:43 -07:00
str < < variation . color ;
2005-04-06 21:29:07 -07:00
int r , g , b ;
if ( ! ( str > > r > > g > > b ) ) // Any trailing data is ignored
2010-06-04 17:49:14 -07:00
LOGERROR ( L " Actor '%ls' has invalid RGB colour '%hs' " , m_Base - > m_ShortName . c_str ( ) , variation . color . c_str ( ) ) ;
2005-04-06 21:29:07 -07:00
else
m_Color = CColor ( r / 255.0f , g / 255.0f , b / 255.0f , 1.0f ) ;
}
2005-05-20 18:40:32 -07:00
std : : vector < CObjectBase : : Prop > props ;
2005-04-02 21:02:00 -08:00
2009-02-18 02:36:27 -08:00
for ( std : : multimap < CStr , CObjectBase : : Prop > : : iterator it = variation . props . begin ( ) ; it ! = variation . props . end ( ) ; + + it )
2005-05-20 18:40:32 -07:00
props . push_back ( it - > second ) ;
2005-04-02 21:02:00 -08:00
// Build the model:
2007-02-25 13:11:57 -08:00
/*
2004-05-29 17:46:58 -07:00
// remember the old model so we can replace any models using it later on
2005-04-02 21:02:00 -08:00
CModelDefPtr oldmodeldef = m_Model ? m_Model - > GetModelDef ( ) : CModelDefPtr ( ) ;
2007-02-25 13:11:57 -08:00
*/
2004-06-07 13:03:10 -07:00
2004-12-12 10:40:00 -08:00
// try and create a model
2007-01-07 17:56:46 -08:00
CModelDefPtr modeldef ( objectManager . GetMeshManager ( ) . GetMesh ( m_ModelName ) ) ;
2004-12-12 10:40:00 -08:00
if ( ! modeldef )
{
2010-06-04 17:49:14 -07:00
LOGERROR ( L " CObjectEntry::BuildVariation(): Model %ls failed to load " , m_ModelName . string ( ) . c_str ( ) ) ;
2004-05-29 17:46:58 -07:00
return false ;
}
2004-06-07 13:03:10 -07:00
2004-10-06 11:46:33 -07:00
// delete old model, create new
delete m_Model ;
2007-03-01 10:52:53 -08:00
m_Model = new CModel ( objectManager . GetSkeletonAnimManager ( ) ) ;
2005-03-22 09:09:36 -08:00
m_Model - > SetMaterial ( g_MaterialManager . LoadMaterial ( m_Base - > m_Material ) ) ;
2004-05-29 17:46:58 -07:00
m_Model - > InitModel ( modeldef ) ;
2005-04-06 21:29:07 -07:00
m_Model - > SetPlayerColor ( m_Color ) ;
2004-06-07 13:03:10 -07:00
2010-09-10 14:02:10 -07:00
CTextureProperties textureProps ( m_TextureName ) ;
textureProps . SetWrap ( GL_CLAMP_TO_EDGE ) ;
CTexturePtr texture = g_Renderer . GetTextureManager ( ) . CreateTexture ( textureProps ) ;
texture - > Prefetch ( ) ; // if we've loaded this model we're probably going to render it soon, so prefetch its texture
m_Model - > SetTexture ( texture ) ;
2004-05-29 17:46:58 -07:00
// calculate initial object space bounds, based on vertex positions
m_Model - > CalcObjectBounds ( ) ;
2004-06-07 13:03:10 -07:00
2005-05-20 18:40:32 -07:00
// load the animations
2006-04-13 20:14:43 -07:00
for ( std : : multimap < CStr , CObjectBase : : Anim > : : iterator it = variation . anims . begin ( ) ; it ! = variation . anims . end ( ) ; + + it )
2004-05-29 17:46:58 -07:00
{
2005-05-20 18:40:32 -07:00
CStr name = it - > first . LowerCase ( ) ;
2004-05-29 17:46:58 -07:00
2005-05-20 18:40:32 -07: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 06:31:47 -08:00
2007-02-01 06:46:14 -08:00
if ( ! it - > second . m_FileName . empty ( ) )
2005-06-02 11:04:20 -07:00
{
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-29 17:46:58 -07:00
}
2005-05-20 18:40:32 -07:00
2006-03-16 19:59:49 -08: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
2010-06-04 17:49:14 -07:00
if ( ! m_Model - > SetAnimation ( GetRandomAnimation ( " idle " ) ) )
LOGERROR ( L " Failed to set idle animation in model \" %ls \" " , m_ModelName . string ( ) . c_str ( ) ) ;
2006-03-16 19:59:49 -08:00
}
2004-06-07 13:03:10 -07:00
2004-05-29 17:46:58 -07:00
// build props - TODO, RC - need to fix up bounds here
2005-04-02 21:02:00 -08:00
// TODO: Make sure random variations get handled correctly when a prop fails
2005-05-20 18:40:32 -07:00
for ( size_t p = 0 ; p < props . size ( ) ; p + + )
2005-04-02 21:02:00 -08:00
{
2005-05-20 18:40:32 -07:00
const CObjectBase : : Prop & prop = props [ p ] ;
2010-04-17 04:34:40 -07:00
// Pluck out the special attachpoint 'projectile'
if ( prop . m_PropPointName = = " projectile " )
{
m_ProjectileModelName = prop . m_ModelName . string ( ) ;
continue ;
}
2009-11-03 13:46:35 -08:00
CObjectEntry * oe = objectManager . FindObjectVariation ( prop . m_ModelName . string ( ) . c_str ( ) , selections ) ;
2005-05-10 00:13:25 -07:00
if ( ! oe )
2005-04-02 21:02:00 -08:00
{
2010-06-04 17:49:14 -07:00
LOGERROR ( L " Failed to build prop model \" %ls \" on actor \" %ls \" " , prop . m_ModelName . string ( ) . c_str ( ) , m_Base - > m_ShortName . c_str ( ) ) ;
2005-05-10 00:13:25 -07:00
continue ;
}
2010-06-04 17:49:14 -07:00
// If we don't have a projectile but this prop does (e.g. it's our rider), then
// use that as our projectile too
if ( m_ProjectileModelName . empty ( ) & & ! oe - > m_ProjectileModelName . empty ( ) )
m_ProjectileModelName = oe - > m_ProjectileModelName ;
CStr ppn = prop . m_PropPointName ;
bool isAmmo = false ;
// Handle the special attachpoint 'loaded-<proppoint>'
if ( ppn . Find ( " loaded- " ) = = 0 )
2005-05-10 00:13:25 -07:00
{
2010-06-04 17:49:14 -07:00
ppn = prop . m_PropPointName . substr ( 7 ) ;
isAmmo = true ;
2005-05-10 00:13:25 -07:00
}
2010-06-04 17:49:14 -07:00
SPropPoint * proppoint = modeldef - > FindPropPoint ( ppn . c_str ( ) ) ;
if ( proppoint )
2005-05-10 00:13:25 -07:00
{
2010-06-04 17:49:14 -07:00
CModel * propmodel = oe - > m_Model - > Clone ( ) ;
if ( isAmmo )
m_Model - > AddAmmoProp ( proppoint , propmodel , oe ) ;
2005-04-02 21:02:00 -08:00
else
2010-06-04 17:49:14 -07:00
m_Model - > AddProp ( proppoint , propmodel , oe ) ;
propmodel - > SetAnimation ( oe - > GetRandomAnimation ( " idle " ) ) ;
2004-05-29 17:46:58 -07:00
}
2010-06-04 17:49:14 -07:00
else
LOGERROR ( L " Failed to find matching prop point called \" %hs \" in model \" %ls \" for actor \" %ls \" " , ppn . c_str ( ) , m_ModelName . string ( ) . c_str ( ) , m_Base - > m_ShortName . c_str ( ) ) ;
2004-05-29 17:46:58 -07:00
}
2004-10-06 11:46:33 -07:00
// setup flags
2005-04-02 21:02:00 -08:00
if ( m_Base - > m_Properties . m_CastShadows )
{
2004-10-06 11:46:33 -07:00
m_Model - > SetFlags ( m_Model - > GetFlags ( ) | MODELFLAG_CASTSHADOWS ) ;
}
2004-05-29 17:46:58 -07:00
// replace any units using old model to now use new model; also reprop models, if necessary
2004-10-06 11:46:33 -07:00
// FIXME, RC - ugh, doesn't recurse correctly through props
2005-04-02 21:02:00 -08: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-29 17:46:58 -07:00
CModel * unitmodel = units [ i ] - > GetModel ( ) ;
2005-04-02 21:02:00 -08:00
if ( unitmodel - > GetModelDef ( ) = = oldmodeldef )
{
2004-05-29 17:46:58 -07:00
unitmodel - > InitModel ( m_Model - > GetModelDef ( ) ) ;
2004-10-06 11:46:33 -07:00
unitmodel - > SetFlags ( m_Model - > GetFlags ( ) ) ;
2004-06-07 13:03:10 -07:00
2005-04-02 21:02:00 -08: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-29 17:46:58 -07:00
}
2004-10-06 11:46:33 -07:00
2005-04-02 21:02:00 -08: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 11:46:33 -07:00
delete prop . m_Model ;
2005-04-02 21:02:00 -08:00
prop . m_Model = m_Model - > Clone ( ) ;
2004-10-06 11:46:33 -07:00
}
}
}
2004-05-29 17:46:58 -07:00
}
2005-04-02 21:02:00 -08:00
*/
2004-05-29 17:46:58 -07:00
return true ;
}
2010-06-04 17:49:14 -07:00
CSkeletonAnim * CObjectEntry : : GetRandomAnimation ( const CStr & animationName ) const
2005-03-22 09:09:36 -08:00
{
2010-06-04 17:49:14 -07:00
SkeletonAnimMap : : const_iterator lower = m_Animations . lower_bound ( animationName ) ;
SkeletonAnimMap : : const_iterator upper = m_Animations . upper_bound ( animationName ) ;
2005-05-20 18:40:32 -07:00
size_t count = std : : distance ( lower , upper ) ;
if ( count = = 0 )
return NULL ;
2010-06-04 17:49:14 -07:00
size_t id = rand ( 0 , count ) ;
std : : advance ( lower , id ) ;
return lower - > second ;
}
std : : vector < CSkeletonAnim * > CObjectEntry : : GetAnimations ( const CStr & animationName ) const
{
std : : vector < CSkeletonAnim * > anims ;
SkeletonAnimMap : : const_iterator lower = m_Animations . lower_bound ( animationName ) ;
SkeletonAnimMap : : const_iterator upper = m_Animations . upper_bound ( animationName ) ;
for ( SkeletonAnimMap : : const_iterator it = lower ; it ! = upper ; + + it )
anims . push_back ( it - > second ) ;
return anims ;
2004-10-06 11:46:33 -07:00
}