1
0
forked from 0ad/0ad
0ad/source/graphics/ObjectManager.cpp
Ykkrosh 310f3466a8 # Hotloading of actor XML files.
Stop ignoring actor XML animation speeds.
Add decentralised registration of hotloaders.
Move player ID storage into CModel, to simplify CUnit.
Remove obsolete unit ID allocation code.
Remove some material junk.

This was SVN commit r7605.
2010-06-03 01:29:43 +00:00

199 lines
5.4 KiB
C++

/* Copyright (C) 2010 Wildfire Games.
* 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/>.
*/
#include "precompiled.h"
#include "ObjectManager.h"
#include "graphics/ObjectBase.h"
#include "graphics/ObjectEntry.h"
#include "ps/CLogger.h"
#include "ps/Game.h"
#include "ps/Profile.h"
#include "ps/Filesystem.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpVisual.h"
template<typename T, typename S>
static void delete_pair_2nd(std::pair<T,S> v)
{
delete v.second;
}
template<typename T>
struct second_equals
{
T x;
second_equals(const T& x) : x(x) {}
template<typename S> bool operator()(const S& v) { return v.second == x; }
};
bool CObjectManager::ObjectKey::operator< (const CObjectManager::ObjectKey& a) const
{
if (ActorName < a.ActorName)
return true;
else if (ActorName > a.ActorName)
return false;
else
return ActorVariation < a.ActorVariation;
}
static LibError ReloadChangedFileCB(void* param, const VfsPath& path)
{
return static_cast<CObjectManager*>(param)->ReloadChangedFile(path);
}
CObjectManager::CObjectManager(CMeshManager& meshManager, CSkeletonAnimManager& skeletonAnimManager, CSimulation2& simulation)
: m_MeshManager(meshManager), m_SkeletonAnimManager(skeletonAnimManager), m_Simulation(simulation)
{
RegisterFileReloadFunc(ReloadChangedFileCB, this);
}
CObjectManager::~CObjectManager()
{
UnloadObjects();
UnregisterFileReloadFunc(ReloadChangedFileCB, this);
}
CObjectBase* CObjectManager::FindObjectBase(const wchar_t* objectname)
{
debug_assert(objectname[0] != '\0');
// See if the base type has been loaded yet:
std::map<CStrW, CObjectBase*>::iterator it = m_ObjectBases.find(objectname);
if (it != m_ObjectBases.end())
return it->second;
// Not already loaded, so try to load it:
CObjectBase* obj = new CObjectBase(*this);
VfsPath pathname(VfsPath(L"art/actors/")/objectname);
if (obj->Load(pathname))
{
m_ObjectBases[objectname] = obj;
return obj;
}
else
delete obj;
LOGERROR(L"CObjectManager::FindObjectBase(): Cannot find object '%ls'", objectname);
return 0;
}
CObjectEntry* CObjectManager::FindObject(const wchar_t* objname)
{
std::vector<std::set<CStr> > selections; // TODO - should this really be empty?
return FindObjectVariation(objname, selections);
}
CObjectEntry* CObjectManager::FindObjectVariation(const wchar_t* objname, const std::vector<std::set<CStr> >& selections)
{
CObjectBase* base = FindObjectBase(objname);
if (! base)
return NULL;
return FindObjectVariation(base, selections);
}
CObjectEntry* CObjectManager::FindObjectVariation(CObjectBase* base, const std::vector<std::set<CStr> >& selections)
{
PROFILE("object variation loading");
// Look to see whether this particular variation has already been loaded
std::vector<u8> choices = base->CalculateVariationKey(selections);
ObjectKey key (base->m_Pathname.string(), choices);
std::map<ObjectKey, CObjectEntry*>::iterator it = m_Objects.find(key);
if (it != m_Objects.end())
return it->second;
// If it hasn't been loaded, load it now
CObjectEntry* obj = new CObjectEntry(base); // TODO: type ?
// TODO (for some efficiency): use the pre-calculated choices for this object,
// which has already worked out what to do for props, instead of passing the
// selections into BuildVariation and having it recalculate the props' choices.
if (! obj->BuildVariation(selections, choices, *this))
{
DeleteObject(obj);
return NULL;
}
m_Objects[key] = obj;
return obj;
}
void CObjectManager::DeleteObject(CObjectEntry* entry)
{
std::map<ObjectKey, CObjectEntry*>::iterator it;
while (m_Objects.end() != (it = find_if(m_Objects.begin(), m_Objects.end(), second_equals<CObjectEntry*>(entry))))
m_Objects.erase(it);
delete entry;
}
void CObjectManager::UnloadObjects()
{
std::for_each(
m_Objects.begin(),
m_Objects.end(),
delete_pair_2nd<ObjectKey, CObjectEntry*>
);
m_Objects.clear();
std::for_each(
m_ObjectBases.begin(),
m_ObjectBases.end(),
delete_pair_2nd<CStr, CObjectBase*>
);
m_ObjectBases.clear();
}
LibError CObjectManager::ReloadChangedFile(const VfsPath& path)
{
for (std::map<CStrW, CObjectBase*>::iterator it = m_ObjectBases.begin(); it != m_ObjectBases.end(); ++it)
{
if (it->second->m_Pathname == path)
{
it->second->Reload();
// Slightly ugly hack: The graphics system doesn't preserve enough information to regenerate the
// object with all correct variations, and we don't want to waste space storing it just for the
// rare occurrence of hotloading, so we'll tell the component (which does preserve the information)
// to do the reloading itself
const std::map<entity_id_t, IComponent*>& cmps = m_Simulation.GetEntitiesWithInterface(IID_Visual);
for (std::map<entity_id_t, IComponent*>::const_iterator eit = cmps.begin(); eit != cmps.end(); ++eit)
static_cast<ICmpVisual*>(eit->second)->Hotload(it->first);
}
}
return INFO::OK;
}