2011-03-13 12:22:05 -07:00
/* Copyright (C) 2011 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"
2011-03-13 12:22:05 -07:00
# include "graphics/Decal.h"
# include "graphics/MaterialManager.h"
# include "graphics/MeshManager.h"
# include "graphics/Model.h"
# include "graphics/ModelDef.h"
# include "graphics/ObjectBase.h"
# include "graphics/ObjectManager.h"
2011-04-03 12:15:15 -07:00
# include "graphics/ParticleManager.h"
2011-03-13 12:22:05 -07:00
# include "graphics/SkeletonAnim.h"
# include "graphics/TextureManager.h"
2007-05-17 17:14:26 -07:00
# include "lib/rand.h"
2011-03-13 12:22:05 -07:00
# include "ps/CLogger.h"
# include "ps/Game.h"
# include "ps/World.h"
# include "renderer/Renderer.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 ) :
2011-04-03 12:15:15 -07:00
m_Base ( base ) , m_Color ( 1.0f , 1.0f , 1.0f , 1.0f ) , m_Model ( NULL ) , m_Outdated ( false )
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 ) ;
}
2011-03-13 12:22:05 -07:00
if ( variation . decal . m_SizeX & & variation . decal . m_SizeZ )
{
CTextureProperties textureProps ( m_TextureName ) ;
// Decals should be transparent, so clamp to the border (default 0,0,0,0)
textureProps . SetWrap ( GL_CLAMP_TO_BORDER ) ;
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
SDecal decal ( texture ,
variation . decal . m_SizeX , variation . decal . m_SizeZ ,
variation . decal . m_Angle , variation . decal . m_OffsetX , variation . decal . m_OffsetZ ,
m_Base - > m_Properties . m_FloatOnWater ) ;
2011-04-05 13:32:03 -07:00
m_Model = new CModelDecal ( objectManager . GetTerrain ( ) , decal ) ;
2011-03-13 12:22:05 -07:00
return true ;
}
2011-04-03 12:15:15 -07:00
if ( ! variation . particles . empty ( ) )
{
m_Model = new CModelParticleEmitter ( g_Renderer . GetParticleManager ( ) . LoadEmitterType ( variation . particles ) ) ;
return true ;
}
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:
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 )
{
2011-03-23 06:36:20 -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
2011-03-13 12:22:05 -07:00
CModel * model = new CModel ( objectManager . GetSkeletonAnimManager ( ) ) ;
2004-10-06 11:46:33 -07:00
delete m_Model ;
2011-03-13 12:22:05 -07:00
m_Model = model ;
2012-04-03 11:44:46 -07:00
model - > SetMaterial ( g_Renderer . GetMaterialManager ( ) . LoadMaterial ( m_Base - > m_Material ) ) ;
model - > GetMaterial ( ) . SetObjectColor ( m_Color ) ;
2011-03-13 12:22:05 -07:00
model - > InitModel ( modeldef ) ;
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
2012-04-03 11:44:46 -07:00
model - > GetMaterial ( ) . SetDiffuseTexture ( texture ) ;
2010-09-10 14:02:10 -07:00
2004-05-29 17:46:58 -07:00
// calculate initial object space bounds, based on vertex positions
2011-11-24 22:36:13 -08:00
model - > CalcStaticObjectBounds ( ) ;
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
{
2011-03-13 12:22:05 -07:00
CSkeletonAnim * anim = model - > BuildAnimation ( it - > second . m_FileName , name , it - > second . m_Speed , it - > second . m_ActionPos , it - > second . m_ActionPos2 ) ;
2005-06-02 11:04:20 -07:00
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
2011-03-13 12:22:05 -07:00
model - > SetAnimation ( anim ) ;
2006-03-16 19:59:49 -08:00
}
else
{
// start up idling
2011-03-13 12:22:05 -07:00
if ( ! model - > SetAnimation ( GetRandomAnimation ( " idle " ) ) )
2011-03-23 06:36:20 -07:00
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 " )
{
2011-03-21 10:53:13 -07:00
m_ProjectileModelName = prop . m_ModelName ;
2010-04-17 04:34:40 -07:00
continue ;
}
2011-03-21 10:53:13 -07:00
CObjectEntry * oe = objectManager . FindObjectVariation ( prop . m_ModelName . c_str ( ) , selections ) ;
2005-05-10 00:13:25 -07:00
if ( ! oe )
2005-04-02 21:02:00 -08:00
{
2011-03-21 10:53:13 -07:00
LOGERROR ( L " Failed to build prop model \" %ls \" on actor \" %ls \" " , prop . m_ModelName . 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
2010-11-20 12:16:06 -08:00
const SPropPoint * proppoint = modeldef - > FindPropPoint ( ppn . c_str ( ) ) ;
2010-06-04 17:49:14 -07:00
if ( proppoint )
2005-05-10 00:13:25 -07:00
{
2011-03-13 12:22:05 -07:00
CModelAbstract * propmodel = oe - > m_Model - > Clone ( ) ;
2010-06-04 17:49:14 -07:00
if ( isAmmo )
2011-03-13 12:22:05 -07:00
model - > AddAmmoProp ( proppoint , propmodel , oe ) ;
2005-04-02 21:02:00 -08:00
else
2011-03-13 12:22:05 -07:00
model - > AddProp ( proppoint , propmodel , oe ) ;
if ( propmodel - > ToCModel ( ) )
propmodel - > ToCModel ( ) - > SetAnimation ( oe - > GetRandomAnimation ( " idle " ) ) ;
2004-05-29 17:46:58 -07:00
}
2010-06-04 17:49:14 -07:00
else
2011-03-23 06:36:20 -07:00
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 )
{
2011-03-13 12:22:05 -07:00
model - > SetFlags ( model - > GetFlags ( ) | MODELFLAG_CASTSHADOWS ) ;
2004-10-06 11:46:33 -07: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
}