# Added support for COLLADA skeletal animations.
Moved COLLADA-loading code into separate class, since it now handles both PMD and PSA. Desingletonised CSkeletonAnimManager, moved into CGameView. Made Atlas load its icons with buffered IO, for possible efficiency. This was SVN commit r4934.
This commit is contained in:
parent
ea29a1caeb
commit
fa45d214b3
236
source/graphics/ColladaManager.cpp
Normal file
236
source/graphics/ColladaManager.cpp
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
#include "ColladaManager.h"
|
||||||
|
|
||||||
|
#include "graphics/ModelDef.h"
|
||||||
|
#include "lib/res/file/vfs.h"
|
||||||
|
#include "lib/res/handle.h"
|
||||||
|
#include "ps/CLogger.h"
|
||||||
|
#include "ps/CStr.h"
|
||||||
|
#include "ps/CVFSFile.h"
|
||||||
|
#include "ps/DllLoader.h"
|
||||||
|
|
||||||
|
namespace Collada
|
||||||
|
{
|
||||||
|
#include "collada/DLL.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct VFSOutputCB
|
||||||
|
{
|
||||||
|
VFSOutputCB(Handle hf) : hf(hf) {}
|
||||||
|
void operator() (const char* data, unsigned int length)
|
||||||
|
{
|
||||||
|
FileIOBuf buf = (FileIOBuf)data;
|
||||||
|
const ssize_t ret = vfs_io(hf, length, &buf);
|
||||||
|
// TODO: handle errors sensibly
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle hf;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ColladaLog(int severity, const char* text)
|
||||||
|
{
|
||||||
|
LOG(severity == LOG_INFO ? NORMAL :
|
||||||
|
severity == LOG_WARNING ? WARNING : ERROR,
|
||||||
|
"collada", "%s", text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColladaOutput(void* cb_data, const char* data, unsigned int length)
|
||||||
|
{
|
||||||
|
VFSOutputCB* cb = static_cast<VFSOutputCB*>(cb_data);
|
||||||
|
(*cb)(data, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CColladaManagerImpl
|
||||||
|
{
|
||||||
|
DllLoader dll;
|
||||||
|
|
||||||
|
void (*set_logger)(Collada::LogFn logger);
|
||||||
|
int (*convert_dae_to_pmd)(const char* dae, Collada::OutputFn pmd_writer, void* cb_data);
|
||||||
|
int (*convert_dae_to_psa)(const char* dae, Collada::OutputFn psa_writer, void* cb_data);
|
||||||
|
|
||||||
|
public:
|
||||||
|
CColladaManagerImpl()
|
||||||
|
: dll("Collada")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~CColladaManagerImpl()
|
||||||
|
{
|
||||||
|
if (dll.IsLoaded())
|
||||||
|
set_logger(NULL); // unregister the log handler
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Convert(const CStr& daeFilename, const CStr& pmdFilename, CColladaManager::FileType type)
|
||||||
|
{
|
||||||
|
// To avoid always loading the DLL when it's usually not going to be
|
||||||
|
// used (and to do the same on Linux where delay-loading won't help),
|
||||||
|
// and to avoid compile-time dependencies (because it's a minor pain
|
||||||
|
// to get all the right libraries to build the COLLADA DLL), we load
|
||||||
|
// it dynamically when it is required, instead of using the exported
|
||||||
|
// functions and binding at link-time.
|
||||||
|
if (! dll.IsLoaded())
|
||||||
|
{
|
||||||
|
if (! dll.LoadDLL())
|
||||||
|
{
|
||||||
|
LOG_ONCE(ERROR, "collada", "Failed to load COLLADA conversion DLL");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dll.LoadSymbol("set_logger", set_logger);
|
||||||
|
dll.LoadSymbol("convert_dae_to_pmd", convert_dae_to_pmd);
|
||||||
|
dll.LoadSymbol("convert_dae_to_psa", convert_dae_to_psa);
|
||||||
|
}
|
||||||
|
catch (PSERROR_DllLoader&)
|
||||||
|
{
|
||||||
|
LOG(ERROR, "collada", "Failed to load symbols from COLLADA conversion DLL");
|
||||||
|
dll.Unload();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_logger(ColladaLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to null-terminate the buffer, so do it (possibly inefficiently)
|
||||||
|
// by converting to a CStr
|
||||||
|
CStr daeData;
|
||||||
|
|
||||||
|
{
|
||||||
|
CVFSFile daeFile;
|
||||||
|
if (daeFile.Load(daeFilename) != PSRETURN_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
daeData = daeFile.GetAsString();
|
||||||
|
|
||||||
|
// scope closes daeFile - necessary if we don't use FILE_LONG_LIVED
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the output file
|
||||||
|
|
||||||
|
Handle hf = vfs_open(pmdFilename, FILE_WRITE|FILE_NO_AIO);
|
||||||
|
if (hf < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Do the conversion
|
||||||
|
|
||||||
|
VFSOutputCB cb (hf);
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case CColladaManager::PMD: convert_dae_to_pmd(daeData.c_str(), ColladaOutput, static_cast<void*>(&cb)); break;
|
||||||
|
case CColladaManager::PSA: convert_dae_to_psa(daeData.c_str(), ColladaOutput, static_cast<void*>(&cb)); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
vfs_close(hf);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CColladaManager::CColladaManager()
|
||||||
|
: m(new CColladaManagerImpl())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CColladaManager::~CColladaManager()
|
||||||
|
{
|
||||||
|
delete m;
|
||||||
|
}
|
||||||
|
|
||||||
|
CStr CColladaManager::GetLoadableFilename(const CStr &sourceName, FileType type)
|
||||||
|
{
|
||||||
|
const char* extn = NULL;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case PMD: extn = ".pmd"; break;
|
||||||
|
case PSA: extn = ".psa"; break;
|
||||||
|
// no other alternatives
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
If there is a .dae file:
|
||||||
|
* Calculate a hash to identify it.
|
||||||
|
* Look for a cached .pmd file matching that hash.
|
||||||
|
* If it exists, load it. Else, convert the .dae into .pmd and load it.
|
||||||
|
Otherwise, if there is a (non-cache) .pmd file:
|
||||||
|
* Load it.
|
||||||
|
Else, fail.
|
||||||
|
|
||||||
|
The hash calculation ought to be fast, since normally (during development)
|
||||||
|
the .dae file will exist but won't have changed recently and so the cache
|
||||||
|
would be used. Hence, just hash the file's size, mtime, and the converter
|
||||||
|
version number (so updates of the converter can cause regeneration of .pmds)
|
||||||
|
instead of the file's actual contents.
|
||||||
|
|
||||||
|
TODO (maybe): The .dae -> .pmd conversion may fail (e.g. if the .dae is
|
||||||
|
invalid or unsupported), but it may take a long time to start the conversion
|
||||||
|
then realise it's not going to work. That will delay the loading of the game
|
||||||
|
every time, which is annoying, so maybe it should cache the error message
|
||||||
|
until the .dae is updated and fixed. (Alternatively, avoid having that many
|
||||||
|
broken .daes in the game.)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// (TODO: the comments and variable names say "pmd" but actually they can
|
||||||
|
// be "psa" too.)
|
||||||
|
|
||||||
|
CStr dae = sourceName + ".dae";
|
||||||
|
if (! vfs_exists(dae))
|
||||||
|
{
|
||||||
|
// No .dae - got to use the .pmd, assuming there is one
|
||||||
|
return sourceName + extn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is a .dae - see if there's an up-to-date cached copy
|
||||||
|
|
||||||
|
struct stat fileStat;
|
||||||
|
if (vfs_stat(dae, &fileStat) < 0)
|
||||||
|
{
|
||||||
|
// This shouldn't occur for any sensible reasons
|
||||||
|
LOG(ERROR, "collada", "Failed to stat DAE file '%s'", dae.c_str());
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a struct of all the data we want to hash.
|
||||||
|
// (Use ints and not time_t/off_t because we don't care about overflow
|
||||||
|
// but do care about the fields not being 64-bit aligned)
|
||||||
|
// (Remove the lowest bit of mtime because some things round it to a
|
||||||
|
// resolution of 2 seconds)
|
||||||
|
struct { int version; int mtime; int size; } hashSource
|
||||||
|
= { COLLADA_CONVERTER_VERSION, (int)fileStat.st_mtime & ~1, (int)fileStat.st_size };
|
||||||
|
cassert(sizeof(hashSource) == sizeof(int) * 3); // no padding, because that would be bad
|
||||||
|
|
||||||
|
// Calculate the hash, convert to hex
|
||||||
|
u32 hash = fnv_hash(static_cast<void*>(&hashSource), sizeof(hashSource));
|
||||||
|
char hashString[9];
|
||||||
|
sprintf(hashString, "%08x", hash);
|
||||||
|
|
||||||
|
// realDaePath is "mods/whatever/art/meshes/whatever.dae"
|
||||||
|
char realDaePath[PATH_MAX];
|
||||||
|
vfs_realpath(dae, realDaePath);
|
||||||
|
|
||||||
|
// cachedPmdVfsPath is "cache/mods/whatever/art/meshes/whatever_{hash}.pmd"
|
||||||
|
CStr cachedPmdVfsPath = "cache/";
|
||||||
|
cachedPmdVfsPath += realDaePath;
|
||||||
|
// Remove the .dae extension (which will certainly be there)
|
||||||
|
cachedPmdVfsPath = cachedPmdVfsPath.substr(0, cachedPmdVfsPath.length()-4);
|
||||||
|
// Add a _hash.pmd extension
|
||||||
|
cachedPmdVfsPath += "_";
|
||||||
|
cachedPmdVfsPath += hashString;
|
||||||
|
cachedPmdVfsPath += extn;
|
||||||
|
|
||||||
|
// If it's not in the cache, we'll have to create it first
|
||||||
|
if (! vfs_exists(cachedPmdVfsPath))
|
||||||
|
{
|
||||||
|
if (! m->Convert(dae, cachedPmdVfsPath, type))
|
||||||
|
return ""; // failed to convert
|
||||||
|
}
|
||||||
|
|
||||||
|
return cachedPmdVfsPath;
|
||||||
|
}
|
32
source/graphics/ColladaManager.h
Normal file
32
source/graphics/ColladaManager.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#ifndef COLLADAMANAGER_H__
|
||||||
|
#define COLLADAMANAGER_H__
|
||||||
|
|
||||||
|
class CStr8;
|
||||||
|
|
||||||
|
class CColladaManagerImpl;
|
||||||
|
|
||||||
|
class CColladaManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum FileType { PMD, PSA };
|
||||||
|
|
||||||
|
CColladaManager();
|
||||||
|
~CColladaManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the VFS path to a PMD/PSA file for the given source file.
|
||||||
|
* Performs a (cached) conversion from COLLADA if necessary.
|
||||||
|
*
|
||||||
|
* @param sourceName path and name, minus extension, of file to load.
|
||||||
|
* One of either "sourceName.pmd" or "sourceName.dae" should exist.
|
||||||
|
*
|
||||||
|
* @return full VFS path (including extension) of file to load; or empty
|
||||||
|
* string if there was a problem and it could not be loaded.
|
||||||
|
*/
|
||||||
|
CStr8 GetLoadableFilename(const CStr8& sourceName, FileType type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
CColladaManagerImpl* m;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COLLADAMANAGER_H__
|
@ -4,11 +4,13 @@
|
|||||||
|
|
||||||
#include "graphics/Camera.h"
|
#include "graphics/Camera.h"
|
||||||
#include "graphics/CinemaTrack.h"
|
#include "graphics/CinemaTrack.h"
|
||||||
|
#include "graphics/ColladaManager.h"
|
||||||
#include "graphics/HFTracer.h"
|
#include "graphics/HFTracer.h"
|
||||||
#include "graphics/LightEnv.h"
|
#include "graphics/LightEnv.h"
|
||||||
#include "graphics/Model.h"
|
#include "graphics/Model.h"
|
||||||
#include "graphics/ObjectManager.h"
|
#include "graphics/ObjectManager.h"
|
||||||
#include "graphics/Patch.h"
|
#include "graphics/Patch.h"
|
||||||
|
#include "graphics/SkeletonAnimManager.h"
|
||||||
#include "graphics/Terrain.h"
|
#include "graphics/Terrain.h"
|
||||||
#include "graphics/TextureManager.h"
|
#include "graphics/TextureManager.h"
|
||||||
#include "graphics/Unit.h"
|
#include "graphics/Unit.h"
|
||||||
@ -57,7 +59,9 @@ class CGameViewImpl : public CJSObject<CGameViewImpl>, boost::noncopyable
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CGameViewImpl(CGame* game)
|
CGameViewImpl(CGame* game)
|
||||||
: Game(game), MeshManager(), ObjectManager(MeshManager),
|
: Game(game),
|
||||||
|
ColladaManager(), MeshManager(ColladaManager), SkeletonAnimManager(ColladaManager),
|
||||||
|
ObjectManager(MeshManager, SkeletonAnimManager),
|
||||||
ViewCamera(),
|
ViewCamera(),
|
||||||
CullCamera(),
|
CullCamera(),
|
||||||
LockCullCamera(false),
|
LockCullCamera(false),
|
||||||
@ -79,7 +83,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
CGame* Game;
|
CGame* Game;
|
||||||
|
CColladaManager ColladaManager;
|
||||||
CMeshManager MeshManager;
|
CMeshManager MeshManager;
|
||||||
|
CSkeletonAnimManager SkeletonAnimManager;
|
||||||
CObjectManager ObjectManager;
|
CObjectManager ObjectManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,140 +2,25 @@
|
|||||||
|
|
||||||
#include "MeshManager.h"
|
#include "MeshManager.h"
|
||||||
|
|
||||||
|
#include "graphics/ColladaManager.h"
|
||||||
#include "graphics/ModelDef.h"
|
#include "graphics/ModelDef.h"
|
||||||
#include "ps/CLogger.h"
|
#include "ps/CLogger.h"
|
||||||
#include "ps/FileUnpacker.h" // to get access to its CError
|
#include "ps/FileUnpacker.h" // to get access to its CError
|
||||||
#include "ps/CVFSFile.h"
|
#include "ps/Profile.h"
|
||||||
#include "ps/DllLoader.h"
|
|
||||||
#include "lib/res/file/vfs.h"
|
|
||||||
|
|
||||||
namespace Collada
|
|
||||||
{
|
|
||||||
#include "collada/DLL.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <boost/weak_ptr.hpp>
|
|
||||||
|
|
||||||
#define LOG_CATEGORY "mesh"
|
#define LOG_CATEGORY "mesh"
|
||||||
|
|
||||||
void ColladaLog(int severity, const char* text)
|
// TODO: should this cache models while they're not actively in the game?
|
||||||
{
|
// (Currently they'll probably be deleted when the reference count drops to 0,
|
||||||
LOG(severity==LOG_INFO ? NORMAL : severity==LOG_WARNING ? WARNING : ERROR,
|
// even if it's quite possible that they'll get reloaded very soon.)
|
||||||
"collada", "%s", text);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VFSOutputCB
|
CMeshManager::CMeshManager(CColladaManager& colladaManager)
|
||||||
{
|
: m_ColladaManager(colladaManager)
|
||||||
VFSOutputCB(Handle hf) : hf(hf) {}
|
|
||||||
void operator() (const char* data, unsigned int length)
|
|
||||||
{
|
|
||||||
FileIOBuf buf = (FileIOBuf)data;
|
|
||||||
const ssize_t ret = vfs_io(hf, length, &buf);
|
|
||||||
// TODO: handle errors sensibly
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle hf;
|
|
||||||
};
|
|
||||||
|
|
||||||
void ColladaOutput(void* cb_data, const char* data, unsigned int length)
|
|
||||||
{
|
|
||||||
VFSOutputCB* cb = static_cast<VFSOutputCB*>(cb_data);
|
|
||||||
(*cb)(data, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef STL_HASH_MAP<CStr, boost::weak_ptr<CModelDef>, CStr_hash_compare> mesh_map;
|
|
||||||
class CMeshManagerImpl
|
|
||||||
{
|
|
||||||
DllLoader dll;
|
|
||||||
|
|
||||||
void (*set_logger)(Collada::LogFn logger);
|
|
||||||
int (*convert_dae_to_pmd)(const char* dae, Collada::OutputFn pmd_writer, void* cb_data);
|
|
||||||
|
|
||||||
public:
|
|
||||||
mesh_map MeshMap;
|
|
||||||
|
|
||||||
CMeshManagerImpl()
|
|
||||||
: dll("Collada")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~CMeshManagerImpl()
|
|
||||||
{
|
|
||||||
if (dll.IsLoaded())
|
|
||||||
set_logger(NULL); // unregister the log handler
|
|
||||||
}
|
|
||||||
|
|
||||||
CModelDefPtr Convert(const CStr& daeFilename, const CStr& pmdFilename, const CStr& name)
|
|
||||||
{
|
|
||||||
// To avoid always loading the DLL when it's usually not going to be
|
|
||||||
// used (and to do the same on Linux where delay-loading won't help),
|
|
||||||
// and to avoid compile-time dependencies (because it's a minor pain
|
|
||||||
// to get all the right libraries to build the COLLADA DLL), we load
|
|
||||||
// it dynamically when it is required, instead of using the exported
|
|
||||||
// functions and binding at link-time.
|
|
||||||
if (! dll.IsLoaded())
|
|
||||||
{
|
|
||||||
if (! dll.LoadDLL())
|
|
||||||
{
|
|
||||||
LOG_ONCE(ERROR, LOG_CATEGORY, "Failed to load COLLADA conversion DLL");
|
|
||||||
return CModelDefPtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
dll.LoadSymbol("set_logger", set_logger);
|
|
||||||
dll.LoadSymbol("convert_dae_to_pmd", convert_dae_to_pmd);
|
|
||||||
}
|
|
||||||
catch (PSERROR_DllLoader&)
|
|
||||||
{
|
|
||||||
LOG(ERROR, LOG_CATEGORY, "Failed to load symbols from COLLADA conversion DLL");
|
|
||||||
dll.Unload();
|
|
||||||
return CModelDefPtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
set_logger(ColladaLog);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to null-terminate the buffer, so do it (possibly inefficiently)
|
|
||||||
// by converting to a CStr
|
|
||||||
CStr daeData;
|
|
||||||
|
|
||||||
{
|
|
||||||
CVFSFile daeFile;
|
|
||||||
if (daeFile.Load(daeFilename) != PSRETURN_OK)
|
|
||||||
return CModelDefPtr();
|
|
||||||
|
|
||||||
daeData = daeFile.GetAsString();
|
|
||||||
|
|
||||||
// scope closes daeFile - necessary if we don't use FILE_LONG_LIVED
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare the output file
|
|
||||||
Handle hf = vfs_open(pmdFilename, FILE_WRITE|FILE_NO_AIO);
|
|
||||||
if (hf < 0)
|
|
||||||
return CModelDefPtr();
|
|
||||||
|
|
||||||
// Do the conversion
|
|
||||||
VFSOutputCB cb (hf);
|
|
||||||
convert_dae_to_pmd(daeData.c_str(), ColladaOutput, static_cast<void*>(&cb));
|
|
||||||
|
|
||||||
vfs_close(hf);
|
|
||||||
|
|
||||||
// Now load the PMD that was just created
|
|
||||||
CModelDefPtr model (CModelDef::Load(pmdFilename, name));
|
|
||||||
MeshMap[name] = model;
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
CMeshManager::CMeshManager()
|
|
||||||
: m(new CMeshManagerImpl())
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CMeshManager::~CMeshManager()
|
CMeshManager::~CMeshManager()
|
||||||
{
|
{
|
||||||
delete m;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CModelDefPtr CMeshManager::GetMesh(const CStr& filename)
|
CModelDefPtr CMeshManager::GetMesh(const CStr& filename)
|
||||||
@ -148,91 +33,25 @@ CModelDefPtr CMeshManager::GetMesh(const CStr& filename)
|
|||||||
name = filename;
|
name = filename;
|
||||||
|
|
||||||
// Find the mesh if it's already been loaded and cached
|
// Find the mesh if it's already been loaded and cached
|
||||||
mesh_map::iterator iter = m->MeshMap.find(name);
|
mesh_map::iterator iter = m_MeshMap.find(name);
|
||||||
if (iter != m->MeshMap.end() && !iter->second.expired())
|
if (iter != m_MeshMap.end() && !iter->second.expired())
|
||||||
return CModelDefPtr(iter->second);
|
return CModelDefPtr(iter->second);
|
||||||
|
|
||||||
/*
|
PROFILE( "load mesh" );
|
||||||
|
|
||||||
If there is a .dae file:
|
CStr pmdFilename = m_ColladaManager.GetLoadableFilename(name, CColladaManager::PMD);
|
||||||
* Calculate a hash to identify it.
|
|
||||||
* Look for a cached .pmd file matching that hash.
|
|
||||||
* If it exists, load it. Else, convert the .dae into .pmd and load it.
|
|
||||||
Otherwise, if there is a (non-cache) .pmd file:
|
|
||||||
* Load it.
|
|
||||||
Else, fail.
|
|
||||||
|
|
||||||
The hash calculation ought to be fast, since normally (during development)
|
if (pmdFilename.empty())
|
||||||
the .dae file will exist but won't have changed recently and so the cache
|
{
|
||||||
would be used. Hence, just hash the file's size, mtime, and the converter
|
LOG(ERROR, LOG_CATEGORY, "Could not load mesh '%s'", filename.c_str());
|
||||||
version number (so updates will cause regeneration of .pmds) instead of
|
return CModelDefPtr();
|
||||||
its contents.
|
}
|
||||||
|
|
||||||
TODO (maybe): The .dae -> .pmd conversion may fail (e.g. if the .dae is
|
|
||||||
invalid or unsupported), but it may take a long time to start the conversion
|
|
||||||
then realise it's not going to work. That will delay the loading of the game
|
|
||||||
every time, which is annoying, so maybe it should cache the error messge
|
|
||||||
until the .dae is updated and fixed. (Alternatively, avoid having many
|
|
||||||
broken .daes in the game's data files.)
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CStr dae = name+".dae";
|
CModelDefPtr model (CModelDef::Load(pmdFilename, name));
|
||||||
if (! vfs_exists(dae))
|
m_MeshMap[name] = model;
|
||||||
{
|
return model;
|
||||||
// No .dae - got to use the .pmd, assuming there is one
|
|
||||||
CModelDefPtr model (CModelDef::Load(name+".pmd", name));
|
|
||||||
m->MeshMap[name] = model;
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
// There is a .dae - see if there's an up-to-date cached copy
|
|
||||||
|
|
||||||
struct stat fileStat;
|
|
||||||
if (vfs_stat(dae, &fileStat) < 0)
|
|
||||||
{
|
|
||||||
// This shouldn't occur for any sensible reasons
|
|
||||||
LOG(ERROR, LOG_CATEGORY, "Failed to stat DAE file '%s'", filename.c_str());
|
|
||||||
return CModelDefPtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build a struct of all the data we want to hash.
|
|
||||||
// (Use ints and not time_t/off_t because we don't care about overflow
|
|
||||||
// but do care about the fields not being 64-bit aligned)
|
|
||||||
struct { int version; int mtime; int size; } hashSource
|
|
||||||
= { COLLADA_CONVERTER_VERSION, fileStat.st_mtime & ~1, fileStat.st_size };
|
|
||||||
cassert(sizeof(hashSource) == sizeof(int) * 3); // no padding, because that would be bad
|
|
||||||
// Calculate the hash, convert to hex
|
|
||||||
u32 hash = fnv_hash(static_cast<void*>(&hashSource), sizeof(hashSource));
|
|
||||||
char hashString[9];
|
|
||||||
sprintf(hashString, "%08x", hash);
|
|
||||||
|
|
||||||
char realDaePath[PATH_MAX];
|
|
||||||
vfs_realpath(dae, realDaePath);
|
|
||||||
// realDaePath is "mods/whatever/art/meshes/whatever.dae"
|
|
||||||
|
|
||||||
CStr cachedPmdVfsPath = "cache/";
|
|
||||||
cachedPmdVfsPath += realDaePath;
|
|
||||||
// Remove the .dae extension (which will certainly be there)
|
|
||||||
cachedPmdVfsPath = cachedPmdVfsPath.substr(0, cachedPmdVfsPath.length()-4);
|
|
||||||
// Add a _hash.pmd extension
|
|
||||||
cachedPmdVfsPath += "_";
|
|
||||||
cachedPmdVfsPath += hashString;
|
|
||||||
cachedPmdVfsPath += ".pmd";
|
|
||||||
|
|
||||||
// If it's cached, load and return that copy
|
|
||||||
if (vfs_exists(cachedPmdVfsPath))
|
|
||||||
{
|
|
||||||
CModelDefPtr model (CModelDef::Load(cachedPmdVfsPath, name));
|
|
||||||
m->MeshMap[name] = model;
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not in the cache, so create it
|
|
||||||
|
|
||||||
return m->Convert(dae, cachedPmdVfsPath, name);
|
|
||||||
}
|
}
|
||||||
catch (PSERROR_File&)
|
catch (PSERROR_File&)
|
||||||
{
|
{
|
||||||
|
@ -1,25 +1,28 @@
|
|||||||
#ifndef __H_MESHMANAGER_H__
|
#ifndef __H_MESHMANAGER_H__
|
||||||
#define __H_MESHMANAGER_H__
|
#define __H_MESHMANAGER_H__
|
||||||
|
|
||||||
|
#include "ps/CStr.h"
|
||||||
|
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
|
#include <boost/weak_ptr.hpp>
|
||||||
|
|
||||||
class CModelDef;
|
class CModelDef;
|
||||||
typedef boost::shared_ptr<CModelDef> CModelDefPtr;
|
typedef boost::shared_ptr<CModelDef> CModelDefPtr;
|
||||||
|
|
||||||
class CStr8;
|
class CColladaManager;
|
||||||
|
|
||||||
class CMeshManagerImpl;
|
class CMeshManager : boost::noncopyable
|
||||||
|
|
||||||
class CMeshManager
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CMeshManager();
|
CMeshManager(CColladaManager& colladaManager);
|
||||||
~CMeshManager();
|
~CMeshManager();
|
||||||
|
|
||||||
CModelDefPtr GetMesh(const CStr8& filename);
|
CModelDefPtr GetMesh(const CStr& filename);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CMeshManagerImpl* m;
|
typedef STL_HASH_MAP<CStr, boost::weak_ptr<CModelDef>, CStr_hash_compare> mesh_map;
|
||||||
|
mesh_map m_MeshMap;
|
||||||
|
CColladaManager& m_ColladaManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -26,10 +26,11 @@
|
|||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
CModel::CModel()
|
CModel::CModel(CSkeletonAnimManager& skeletonAnimManager)
|
||||||
: m_Parent(NULL), m_Flags(0), m_Anim(NULL), m_AnimTime(0),
|
: m_Parent(NULL), m_Flags(0), m_Anim(NULL), m_AnimTime(0),
|
||||||
m_BoneMatrices(NULL), m_InverseBindBoneMatrices(NULL),
|
m_BoneMatrices(NULL), m_InverseBindBoneMatrices(NULL),
|
||||||
m_PositionValid(false), m_ShadingColor(1,1,1,1)
|
m_PositionValid(false), m_ShadingColor(1,1,1,1),
|
||||||
|
m_SkeletonAnimManager(skeletonAnimManager)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +202,7 @@ void CModel::CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result)
|
|||||||
// animation specific to this model
|
// animation specific to this model
|
||||||
CSkeletonAnim* CModel::BuildAnimation(const char* filename, const char* name, float speed, double actionpos, double actionpos2)
|
CSkeletonAnim* CModel::BuildAnimation(const char* filename, const char* name, float speed, double actionpos, double actionpos2)
|
||||||
{
|
{
|
||||||
CSkeletonAnimDef* def=g_SkelAnimMan.GetAnimation(filename);
|
CSkeletonAnimDef* def = m_SkeletonAnimManager.GetAnimation(filename);
|
||||||
if (!def) return NULL;
|
if (!def) return NULL;
|
||||||
|
|
||||||
|
|
||||||
@ -463,7 +464,7 @@ void CModel::RemoveProp(SPropPoint* point)
|
|||||||
// Clone: return a clone of this model
|
// Clone: return a clone of this model
|
||||||
CModel* CModel::Clone() const
|
CModel* CModel::Clone() const
|
||||||
{
|
{
|
||||||
CModel* clone = new CModel;
|
CModel* clone = new CModel(m_SkeletonAnimManager);
|
||||||
clone->m_ObjectBounds = m_ObjectBounds;
|
clone->m_ObjectBounds = m_ObjectBounds;
|
||||||
clone->InitModel(m_pModelDef);
|
clone->InitModel(m_pModelDef);
|
||||||
clone->SetTexture(m_Texture);
|
clone->SetTexture(m_Texture);
|
||||||
|
@ -20,6 +20,7 @@ struct SPropPoint;
|
|||||||
class CObjectEntry;
|
class CObjectEntry;
|
||||||
class CSkeletonAnim;
|
class CSkeletonAnim;
|
||||||
class CSkeletonAnimDef;
|
class CSkeletonAnimDef;
|
||||||
|
class CSkeletonAnimManager;
|
||||||
|
|
||||||
#define MODELFLAG_CASTSHADOWS (1<<0)
|
#define MODELFLAG_CASTSHADOWS (1<<0)
|
||||||
#define MODELFLAG_NOLOOPANIMATION (1<<1)
|
#define MODELFLAG_NOLOOPANIMATION (1<<1)
|
||||||
@ -27,7 +28,7 @@ class CSkeletonAnimDef;
|
|||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// CModel: basically, a mesh object - holds the texturing and skinning
|
// CModel: basically, a mesh object - holds the texturing and skinning
|
||||||
// information for a model in game
|
// information for a model in game
|
||||||
class CModel : public CRenderableObject
|
class CModel : public CRenderableObject, boost::noncopyable
|
||||||
{
|
{
|
||||||
friend class CUnitAnimation;
|
friend class CUnitAnimation;
|
||||||
// HACK - we should probably move the rest of this class's animation state
|
// HACK - we should probably move the rest of this class's animation state
|
||||||
@ -44,7 +45,7 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// constructor
|
// constructor
|
||||||
CModel();
|
CModel(CSkeletonAnimManager& skeletonAnimManager);
|
||||||
// destructor
|
// destructor
|
||||||
~CModel();
|
~CModel();
|
||||||
|
|
||||||
@ -201,6 +202,9 @@ private:
|
|||||||
|
|
||||||
// modulating color
|
// modulating color
|
||||||
CColor m_ShadingColor;
|
CColor m_ShadingColor;
|
||||||
|
|
||||||
|
// manager object which can load animations for us
|
||||||
|
CSkeletonAnimManager& m_SkeletonAnimManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -82,7 +82,7 @@ bool CObjectEntry::BuildVariation(const std::vector<std::set<CStr> >& selections
|
|||||||
|
|
||||||
// delete old model, create new
|
// delete old model, create new
|
||||||
delete m_Model;
|
delete m_Model;
|
||||||
m_Model = new CModel;
|
m_Model = new CModel(objectManager.GetSkeletonAnimManager());
|
||||||
m_Model->SetTexture((const char*) m_TextureName);
|
m_Model->SetTexture((const char*) m_TextureName);
|
||||||
m_Model->SetMaterial(g_MaterialManager.LoadMaterial(m_Base->m_Material));
|
m_Model->SetMaterial(g_MaterialManager.LoadMaterial(m_Base->m_Material));
|
||||||
m_Model->InitModel(modeldef);
|
m_Model->InitModel(modeldef);
|
||||||
|
@ -34,8 +34,8 @@ bool operator< (const CObjectManager::ObjectKey& a, const CObjectManager::Object
|
|||||||
return a.ActorVariation < b.ActorVariation;
|
return a.ActorVariation < b.ActorVariation;
|
||||||
}
|
}
|
||||||
|
|
||||||
CObjectManager::CObjectManager(CMeshManager& meshManager)
|
CObjectManager::CObjectManager(CMeshManager& meshManager, CSkeletonAnimManager& skeletonAnimManager)
|
||||||
: m_MeshManager(meshManager)
|
: m_MeshManager(meshManager), m_SkeletonAnimManager(skeletonAnimManager)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ class CMatrix3D;
|
|||||||
class CMeshManager;
|
class CMeshManager;
|
||||||
class CObjectBase;
|
class CObjectBase;
|
||||||
class CObjectEntry;
|
class CObjectEntry;
|
||||||
|
class CSkeletonAnimManager;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// CObjectManager: manager class for all possible actor types
|
// CObjectManager: manager class for all possible actor types
|
||||||
@ -28,10 +29,14 @@ public:
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
// constructor, destructor
|
// constructor, destructor
|
||||||
CObjectManager(CMeshManager& meshManager);
|
CObjectManager(CMeshManager& meshManager, CSkeletonAnimManager& skeletonAnimManager);
|
||||||
~CObjectManager();
|
~CObjectManager();
|
||||||
|
|
||||||
|
// Provide access to the manager classes for meshes and animations - they're
|
||||||
|
// needed when objects are being created and so this seems like a convenient
|
||||||
|
// place to centralise access.
|
||||||
CMeshManager& GetMeshManager() const { return m_MeshManager; }
|
CMeshManager& GetMeshManager() const { return m_MeshManager; }
|
||||||
|
CSkeletonAnimManager& GetSkeletonAnimManager() const { return m_SkeletonAnimManager; }
|
||||||
|
|
||||||
void UnloadObjects();
|
void UnloadObjects();
|
||||||
|
|
||||||
@ -49,6 +54,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
CMeshManager& m_MeshManager;
|
CMeshManager& m_MeshManager;
|
||||||
|
CSkeletonAnimManager& m_SkeletonAnimManager;
|
||||||
|
|
||||||
std::map<ObjectKey, CObjectEntry*> m_Objects;
|
std::map<ObjectKey, CObjectEntry*> m_Objects;
|
||||||
std::map<CStr, CObjectBase*> m_ObjectBases;
|
std::map<CStr, CObjectBase*> m_ObjectBases;
|
||||||
|
@ -8,18 +8,21 @@
|
|||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
#include "lib/res/res.h"
|
|
||||||
#include "Model.h"
|
|
||||||
#include "ps/CLogger.h"
|
|
||||||
#include "SkeletonAnimManager.h"
|
#include "SkeletonAnimManager.h"
|
||||||
|
|
||||||
|
#include "graphics/ColladaManager.h"
|
||||||
|
#include "graphics/Model.h"
|
||||||
|
#include "graphics/SkeletonAnimDef.h"
|
||||||
|
#include "lib/res/res.h"
|
||||||
|
#include "ps/CLogger.h"
|
||||||
#include "ps/FileUnpacker.h"
|
#include "ps/FileUnpacker.h"
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#define LOG_CATEGORY "graphics"
|
#define LOG_CATEGORY "graphics"
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// CSkeletonAnimManager constructor
|
// CSkeletonAnimManager constructor
|
||||||
CSkeletonAnimManager::CSkeletonAnimManager()
|
CSkeletonAnimManager::CSkeletonAnimManager(CColladaManager& colladaManager)
|
||||||
|
: m_ColladaManager(colladaManager)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,48 +31,55 @@ CSkeletonAnimManager::CSkeletonAnimManager()
|
|||||||
CSkeletonAnimManager::~CSkeletonAnimManager()
|
CSkeletonAnimManager::~CSkeletonAnimManager()
|
||||||
{
|
{
|
||||||
typedef std::map<CStr,CSkeletonAnimDef*>::iterator Iter;
|
typedef std::map<CStr,CSkeletonAnimDef*>::iterator Iter;
|
||||||
for (Iter i=m_Animations.begin();i!=m_Animations.end();++i) {
|
for (Iter i = m_Animations.begin(); i != m_Animations.end(); ++i)
|
||||||
delete i->second;
|
delete i->second;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// GetAnimation: return a given animation by filename; return null if filename
|
// GetAnimation: return a given animation by filename; return null if filename
|
||||||
// doesn't refer to valid animation file
|
// doesn't refer to valid animation file
|
||||||
CSkeletonAnimDef* CSkeletonAnimManager::GetAnimation(const char* filename)
|
CSkeletonAnimDef* CSkeletonAnimManager::GetAnimation(const CStr& filename)
|
||||||
{
|
{
|
||||||
// already loaded?
|
// Strip a three-letter file extension (if there is one) from the filename
|
||||||
CStr fname(filename);
|
CStr name;
|
||||||
std::map<CStr,CSkeletonAnimDef*>::iterator iter=m_Animations.find(fname);
|
if (filename.length() > 4 && filename[filename.length()-4] == '.')
|
||||||
if (iter!=m_Animations.end()) {
|
name = filename.substr(0, filename.length()-4);
|
||||||
// yes - return it
|
else
|
||||||
|
name = filename;
|
||||||
|
|
||||||
|
// Find if it's already been loaded
|
||||||
|
std::map<CStr, CSkeletonAnimDef*>::iterator iter = m_Animations.find(name);
|
||||||
|
if (iter != m_Animations.end())
|
||||||
return iter->second;
|
return iter->second;
|
||||||
|
|
||||||
|
CSkeletonAnimDef* def = NULL;
|
||||||
|
|
||||||
|
// Find the file to load
|
||||||
|
CStr psaFilename = m_ColladaManager.GetLoadableFilename(name, CColladaManager::PSA);
|
||||||
|
|
||||||
|
if (psaFilename.empty())
|
||||||
|
{
|
||||||
|
LOG(ERROR, LOG_CATEGORY, "Could not load animation '%s'", filename.c_str());
|
||||||
|
def = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
def = CSkeletonAnimDef::Load(psaFilename);
|
||||||
|
}
|
||||||
|
catch (PSERROR_File&)
|
||||||
|
{
|
||||||
|
// ignore errors (they'll be logged elsewhere)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// already failed to load?
|
if (def)
|
||||||
std::set<CStr>::iterator setiter=m_BadAnimationFiles.find(fname);
|
LOG(NORMAL, LOG_CATEGORY, "CSkeletonAnimManager::GetAnimation(%s): Loaded successfully", filename.c_str());
|
||||||
if (setiter!=m_BadAnimationFiles.end()) {
|
else
|
||||||
// yes - return null
|
LOG(ERROR, LOG_CATEGORY, "CSkeletonAnimManager::GetAnimation(%s): Failed loading, marked file as bad", filename.c_str());
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try and load it now
|
// Add to map
|
||||||
CSkeletonAnimDef* def;
|
m_Animations[name] = def; // NULL if failed to load - we won't try loading it again
|
||||||
try {
|
return def;
|
||||||
def=CSkeletonAnimDef::Load(filename);
|
|
||||||
} catch (PSERROR_File&) {
|
|
||||||
def=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!def) {
|
|
||||||
LOG(ERROR, LOG_CATEGORY, "CSkeletonAnimManager::GetAnimation(%s): Failed loading, marked file as bad", filename);
|
|
||||||
// add this file as bad
|
|
||||||
m_BadAnimationFiles.insert(fname);
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
LOG(NORMAL, LOG_CATEGORY, "CSkeletonAnimManager::GetAnimation(%s): Loaded successfully", filename);
|
|
||||||
// add mapping for this file
|
|
||||||
m_Animations[fname]=def;
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -11,34 +11,32 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include "SkeletonAnimDef.h"
|
|
||||||
#include "ps/Singleton.h"
|
|
||||||
|
|
||||||
|
class CColladaManager;
|
||||||
// access to sole CSkeletonAnimManager object
|
class CSkeletonAnimDef;
|
||||||
#define g_SkelAnimMan CSkeletonAnimManager::GetSingleton()
|
class CStr8;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// CSkeletonAnimManager : owner class of all skeleton anims - manages creation,
|
// CSkeletonAnimManager : owner class of all skeleton anims - manages creation,
|
||||||
// loading and destruction of animation data
|
// loading and destruction of animation data
|
||||||
class CSkeletonAnimManager : public Singleton<CSkeletonAnimManager>
|
class CSkeletonAnimManager : boost::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// constructor, destructor
|
// constructor, destructor
|
||||||
CSkeletonAnimManager();
|
CSkeletonAnimManager(CColladaManager& colladaManager);
|
||||||
~CSkeletonAnimManager();
|
~CSkeletonAnimManager();
|
||||||
|
|
||||||
// return a given animation by filename; return null if filename doesn't
|
// return a given animation by filename; return null if filename doesn't
|
||||||
// refer to valid animation file
|
// refer to valid animation file
|
||||||
CSkeletonAnimDef* GetAnimation(const char* filename);
|
CSkeletonAnimDef* GetAnimation(const CStr8& filename);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CSkeletonAnimDef* LoadAnimation(const char* filename);
|
CSkeletonAnimDef* LoadAnimation(const char* filename);
|
||||||
|
|
||||||
// map of all known animations
|
// map of all known animations. Value is NULL if it failed to load.
|
||||||
std::map<CStr,CSkeletonAnimDef*> m_Animations;
|
std::map<CStr8, CSkeletonAnimDef*> m_Animations;
|
||||||
// set of bad animation names - prevents multiple reloads of bad files
|
|
||||||
std::set<CStr> m_BadAnimationFiles;
|
CColladaManager& m_ColladaManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "lib/res/file/trace.h"
|
#include "lib/res/file/trace.h"
|
||||||
#include "lib/res/h_mgr.h"
|
#include "lib/res/h_mgr.h"
|
||||||
|
|
||||||
|
#include "graphics/ColladaManager.h"
|
||||||
#include "graphics/MeshManager.h"
|
#include "graphics/MeshManager.h"
|
||||||
#include "graphics/ModelDef.h"
|
#include "graphics/ModelDef.h"
|
||||||
|
|
||||||
@ -95,6 +96,7 @@ class TestMeshManager : public CxxTest::TestSuite
|
|||||||
TS_ASSERT_OK(vfs_opt_rebuild_main_archive(MOD_PATH"/trace.txt", MOD_PATH"/test%02d.zip"));
|
TS_ASSERT_OK(vfs_opt_rebuild_main_archive(MOD_PATH"/trace.txt", MOD_PATH"/test%02d.zip"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CColladaManager* colladaManager;
|
||||||
CMeshManager* meshManager;
|
CMeshManager* meshManager;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -102,12 +104,14 @@ public:
|
|||||||
void setUp()
|
void setUp()
|
||||||
{
|
{
|
||||||
initVfs();
|
initVfs();
|
||||||
meshManager = new CMeshManager();
|
colladaManager = new CColladaManager();
|
||||||
|
meshManager = new CMeshManager(*colladaManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tearDown()
|
void tearDown()
|
||||||
{
|
{
|
||||||
delete meshManager;
|
delete meshManager;
|
||||||
|
delete colladaManager;
|
||||||
deinitVfs();
|
deinitVfs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,6 +167,18 @@ public:
|
|||||||
if (modeldef) TS_ASSERT_STR_EQUALS(modeldef->GetName(), testBase);
|
if (modeldef) TS_ASSERT_STR_EQUALS(modeldef->GetName(), testBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_load_dae_caching()
|
||||||
|
{
|
||||||
|
copyFile(srcDAE, testDAE);
|
||||||
|
|
||||||
|
CStr daeName1 = colladaManager->GetLoadableFilename(testBase, CColladaManager::PMD);
|
||||||
|
CStr daeName2 = colladaManager->GetLoadableFilename(testBase, CColladaManager::PMD);
|
||||||
|
TS_ASSERT(daeName1.length());
|
||||||
|
TS_ASSERT_STR_EQUALS(daeName1, daeName2);
|
||||||
|
// TODO: it'd be nice to test that it isn't doing the DAE->PMD conversion
|
||||||
|
// again, but there doesn't seem to be an easy way to check that
|
||||||
|
}
|
||||||
|
|
||||||
void test_load_nonexistent_pmd()
|
void test_load_nonexistent_pmd()
|
||||||
{
|
{
|
||||||
CModelDefPtr modeldef = meshManager->GetMesh(testPMD);
|
CModelDefPtr modeldef = meshManager->GetMesh(testPMD);
|
||||||
|
@ -36,7 +36,6 @@
|
|||||||
#include "graphics/MapReader.h"
|
#include "graphics/MapReader.h"
|
||||||
#include "graphics/MaterialManager.h"
|
#include "graphics/MaterialManager.h"
|
||||||
#include "graphics/ParticleEngine.h"
|
#include "graphics/ParticleEngine.h"
|
||||||
#include "graphics/SkeletonAnimManager.h"
|
|
||||||
#include "graphics/TextureManager.h"
|
#include "graphics/TextureManager.h"
|
||||||
|
|
||||||
#include "renderer/Renderer.h"
|
#include "renderer/Renderer.h"
|
||||||
@ -710,9 +709,6 @@ static void InitRenderer()
|
|||||||
// create the material manager
|
// create the material manager
|
||||||
new CMaterialManager;
|
new CMaterialManager;
|
||||||
|
|
||||||
// create actor related stuff
|
|
||||||
new CSkeletonAnimManager;
|
|
||||||
|
|
||||||
MICROLOG(L"init renderer");
|
MICROLOG(L"init renderer");
|
||||||
g_Renderer.Open(g_xres,g_yres,g_bpp);
|
g_Renderer.Open(g_xres,g_yres,g_bpp);
|
||||||
|
|
||||||
@ -811,8 +807,6 @@ void Shutdown(uint flags)
|
|||||||
|
|
||||||
// destroy actor related stuff
|
// destroy actor related stuff
|
||||||
TIMER_BEGIN("shutdown actor stuff");
|
TIMER_BEGIN("shutdown actor stuff");
|
||||||
delete &g_SkelAnimMan;
|
|
||||||
|
|
||||||
delete &g_MaterialManager;
|
delete &g_MaterialManager;
|
||||||
TIMER_END("shutdown actor stuff");
|
TIMER_END("shutdown actor stuff");
|
||||||
|
|
||||||
|
@ -372,6 +372,11 @@ void CProfileNode::Frame()
|
|||||||
static long get_memory_alloc_count()
|
static long get_memory_alloc_count()
|
||||||
{
|
{
|
||||||
#if HAVE_VC_DEBUG_ALLOC
|
#if HAVE_VC_DEBUG_ALLOC
|
||||||
|
// TODO: it's probably better to use _CrtSetAllocHook to increment a
|
||||||
|
// user-visible counter. (I didn't know that existed when I wrote this.)
|
||||||
|
|
||||||
|
// Find the number of allocations that have ever occurred, by doing a dummy
|
||||||
|
// allocation and checking its request number
|
||||||
static long bias = 0; // so we can subtract the allocations caused by this function
|
static long bias = 0; // so we can subtract the allocations caused by this function
|
||||||
void* p = malloc(1);
|
void* p = malloc(1);
|
||||||
long requestNumber = 0;
|
long requestNumber = 0;
|
||||||
@ -381,6 +386,9 @@ static long get_memory_alloc_count()
|
|||||||
++bias;
|
++bias;
|
||||||
return requestNumber - bias;
|
return requestNumber - bias;
|
||||||
#else
|
#else
|
||||||
|
// TODO: support other compilers if it's easy.
|
||||||
|
// TODO: don't show this column of data when we don't have sensible values
|
||||||
|
// to display.
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -450,7 +458,7 @@ void CProfileManager::StartScript( const char* name )
|
|||||||
current->Call();
|
current->Call();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* CProfileManager::InternString( CStr8 intern )
|
const char* CProfileManager::InternString( const CStr8& intern )
|
||||||
{
|
{
|
||||||
std::map<CStr8, const char*>::iterator it = m_internedStrings.find( intern );
|
std::map<CStr8, const char*>::iterator it = m_internedStrings.find( intern );
|
||||||
if( it != m_internedStrings.end() )
|
if( it != m_internedStrings.end() )
|
||||||
|
@ -134,7 +134,7 @@ public:
|
|||||||
// Resets absolutely everything
|
// Resets absolutely everything
|
||||||
void StructuralReset();
|
void StructuralReset();
|
||||||
|
|
||||||
const char* InternString( CStr8 intern );
|
const char* InternString( const CStr8& intern );
|
||||||
|
|
||||||
inline const CProfileNode* GetCurrent() { return( current ); }
|
inline const CProfileNode* GetCurrent() { return( current ); }
|
||||||
inline const CProfileNode* GetRoot() { return( root ); }
|
inline const CProfileNode* GetRoot() { return( root ); }
|
||||||
@ -150,11 +150,13 @@ class CProfileSample
|
|||||||
public:
|
public:
|
||||||
CProfileSample( const char* name )
|
CProfileSample( const char* name )
|
||||||
{
|
{
|
||||||
g_Profiler.Start( name );
|
if (CProfileManager::IsInitialised())
|
||||||
|
g_Profiler.Start( name );
|
||||||
}
|
}
|
||||||
~CProfileSample()
|
~CProfileSample()
|
||||||
{
|
{
|
||||||
g_Profiler.Stop();
|
if (CProfileManager::IsInitialised())
|
||||||
|
g_Profiler.Stop();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class LeakReporter : public CxxTest::GlobalFixture
|
|||||||
|
|
||||||
// Send output to stdout as well as the debug window, so it works during
|
// Send output to stdout as well as the debug window, so it works during
|
||||||
// the normal build process as well as when debugging the test .exe
|
// the normal build process as well as when debugging the test .exe
|
||||||
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE|_CRTDBG_MODE_DEBUG);
|
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
|
||||||
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
|
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ void ToolButtonBar::AddToolButton(const wxString& shortLabel, const wxString& lo
|
|||||||
wxFileName iconPath (_T("tools/atlas/toolbar/"));
|
wxFileName iconPath (_T("tools/atlas/toolbar/"));
|
||||||
iconPath.MakeAbsolute(Datafile::GetDataDirectory());
|
iconPath.MakeAbsolute(Datafile::GetDataDirectory());
|
||||||
iconPath.SetFullName(iconPNGFilename);
|
iconPath.SetFullName(iconPNGFilename);
|
||||||
wxFileInputStream fstr (iconPath.GetFullPath());
|
wxFFileInputStream fstr (iconPath.GetFullPath());
|
||||||
if (! fstr.Ok())
|
if (! fstr.Ok())
|
||||||
{
|
{
|
||||||
wxLogError(_("Failed to open toolbar icon file '%s'"), iconPath.GetFullPath().c_str());
|
wxLogError(_("Failed to open toolbar icon file '%s'"), iconPath.GetFullPath().c_str());
|
||||||
|
@ -86,7 +86,7 @@ public:
|
|||||||
wxFileName iconPath (_T("tools/atlas/toolbar/"));
|
wxFileName iconPath (_T("tools/atlas/toolbar/"));
|
||||||
iconPath.MakeAbsolute(Datafile::GetDataDirectory());
|
iconPath.MakeAbsolute(Datafile::GetDataDirectory());
|
||||||
iconPath.SetFullName(iconPNGFilename);
|
iconPath.SetFullName(iconPNGFilename);
|
||||||
wxFileInputStream fstr (iconPath.GetFullPath());
|
wxFFileInputStream fstr (iconPath.GetFullPath());
|
||||||
if (! fstr.Ok())
|
if (! fstr.Ok())
|
||||||
{
|
{
|
||||||
wxLogError(_("Failed to open toolbar icon file '%s'"), iconPath.GetFullPath().c_str());
|
wxLogError(_("Failed to open toolbar icon file '%s'"), iconPath.GetFullPath().c_str());
|
||||||
|
@ -841,7 +841,7 @@ wxImage CinematicSidebar::LoadIcon(const wxString& filename)
|
|||||||
wxFileName iconPath (_T("tools/atlas/buttons/"));
|
wxFileName iconPath (_T("tools/atlas/buttons/"));
|
||||||
iconPath.MakeAbsolute(Datafile::GetDataDirectory());
|
iconPath.MakeAbsolute(Datafile::GetDataDirectory());
|
||||||
iconPath.SetFullName(filename);
|
iconPath.SetFullName(filename);
|
||||||
wxFileInputStream fstr (iconPath.GetFullPath());
|
wxFFileInputStream fstr (iconPath.GetFullPath());
|
||||||
if (! fstr.Ok())
|
if (! fstr.Ok())
|
||||||
{
|
{
|
||||||
wxLogError(_("Failed to open cinematic icon file '%s'"), iconPath.GetFullPath().c_str());
|
wxLogError(_("Failed to open cinematic icon file '%s'"), iconPath.GetFullPath().c_str());
|
||||||
|
@ -4,11 +4,13 @@
|
|||||||
|
|
||||||
#include "View.h"
|
#include "View.h"
|
||||||
|
|
||||||
|
#include "graphics/ColladaManager.h"
|
||||||
#include "graphics/Model.h"
|
#include "graphics/Model.h"
|
||||||
#include "graphics/ObjectManager.h"
|
#include "graphics/ObjectManager.h"
|
||||||
#include "graphics/Patch.h"
|
#include "graphics/Patch.h"
|
||||||
#include "graphics/SkeletonAnim.h"
|
#include "graphics/SkeletonAnim.h"
|
||||||
#include "graphics/SkeletonAnimDef.h"
|
#include "graphics/SkeletonAnimDef.h"
|
||||||
|
#include "graphics/SkeletonAnimManager.h"
|
||||||
#include "graphics/Terrain.h"
|
#include "graphics/Terrain.h"
|
||||||
#include "graphics/TextureEntry.h"
|
#include "graphics/TextureEntry.h"
|
||||||
#include "graphics/TextureManager.h"
|
#include "graphics/TextureManager.h"
|
||||||
@ -25,7 +27,8 @@
|
|||||||
struct ActorViewerImpl : public Scene, boost::noncopyable
|
struct ActorViewerImpl : public Scene, boost::noncopyable
|
||||||
{
|
{
|
||||||
ActorViewerImpl()
|
ActorViewerImpl()
|
||||||
: Unit(NULL), MeshManager(), ObjectManager(MeshManager)
|
: Unit(NULL), ColladaManager(), MeshManager(ColladaManager), SkeletonAnimManager(ColladaManager),
|
||||||
|
ObjectManager(MeshManager, SkeletonAnimManager)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +44,9 @@ struct ActorViewerImpl : public Scene, boost::noncopyable
|
|||||||
|
|
||||||
CTerrain Terrain;
|
CTerrain Terrain;
|
||||||
|
|
||||||
|
CColladaManager ColladaManager;
|
||||||
CMeshManager MeshManager;
|
CMeshManager MeshManager;
|
||||||
|
CSkeletonAnimManager SkeletonAnimManager;
|
||||||
CObjectManager ObjectManager;
|
CObjectManager ObjectManager;
|
||||||
|
|
||||||
// Simplistic implementation of the Scene interface
|
// Simplistic implementation of the Scene interface
|
||||||
|
Loading…
Reference in New Issue
Block a user