You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw-tes3mp/components/resource/scenemanager.cpp

201 lines
6.4 KiB
C++

#include "scenemanager.hpp"
#include <osg/Node>
#include <osg/Geode>
#include <osg/UserDataContainer>
#include <osgParticle/ParticleSystem>
#include <osgUtil/IncrementalCompileOperation>
#include <osgDB/SharedStateManager>
#include <osgDB/Registry>
#include <components/nifosg/nifloader.hpp>
#include <components/nif/niffile.hpp>
#include <components/vfs/manager.hpp>
#include <components/sceneutil/clone.hpp>
#include <components/sceneutil/util.hpp>
namespace
{
class InitWorldSpaceParticlesVisitor : public osg::NodeVisitor
{
public:
InitWorldSpaceParticlesVisitor()
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
{
}
void apply(osg::Node& node)
{
if (osg::Geode* geode = node.asGeode())
{
for (unsigned int i=0;i<geode->getNumDrawables();++i)
{
if (osgParticle::ParticleSystem* partsys = dynamic_cast<osgParticle::ParticleSystem*>(geode->getDrawable(i)))
{
// HACK: ParticleSystem has no getReferenceFrame()
if (partsys->getUserDataContainer()
&& partsys->getUserDataContainer()->getNumDescriptions() > 0
&& partsys->getUserDataContainer()->getDescriptions()[0] == "worldspace")
{
// HACK: Ignore the InverseWorldMatrix transform the geode is attached to
if (geode->getNumParents() && geode->getParent(0)->getNumParents())
transformInitialParticles(partsys, geode->getParent(0)->getParent(0));
}
geode->setNodeMask((1<<10)); //MWRender::Mask_ParticleSystem
}
}
}
traverse(node);
}
void transformInitialParticles(osgParticle::ParticleSystem* partsys, osg::Node* node)
{
osg::MatrixList mats = node->getWorldMatrices();
if (mats.empty())
return;
osg::Matrix worldMat = mats[0];
worldMat.orthoNormalize(worldMat); // scale is already applied on the particle node
for (int i=0; i<partsys->numParticles(); ++i)
{
partsys->getParticle(i)->transformPositionVelocity(worldMat);
}
// transform initial bounds to worldspace
osg::BoundingSphere sphere(partsys->getInitialBound());
SceneUtil::transformBoundingSphere(worldMat, sphere);
osg::BoundingBox box;
box.expandBy(sphere);
partsys->setInitialBound(box);
}
};
}
namespace Resource
{
SceneManager::SceneManager(const VFS::Manager *vfs, Resource::TextureManager* textureManager)
: mVFS(vfs)
, mTextureManager(textureManager)
{
}
SceneManager::~SceneManager()
{
// this has to be defined in the .cpp file as we can't delete incomplete types
}
osg::ref_ptr<const osg::Node> SceneManager::getTemplate(const std::string &name)
{
std::string normalized = name;
mVFS->normalizeFilename(normalized);
Index::iterator it = mIndex.find(normalized);
if (it == mIndex.end())
{
// TODO: add support for non-NIF formats
osg::ref_ptr<osg::Node> loaded;
try
{
Files::IStreamPtr file = mVFS->get(normalized);
loaded = NifOsg::Loader::load(Nif::NIFFilePtr(new Nif::NIFFile(file, normalized)), mTextureManager);
}
catch (std::exception& e)
{
std::cerr << "Failed to load '" << name << "': " << e.what() << ", using marker_error.nif instead" << std::endl;
Files::IStreamPtr file = mVFS->get("meshes/marker_error.nif");
loaded = NifOsg::Loader::load(Nif::NIFFilePtr(new Nif::NIFFile(file, normalized)), mTextureManager);
}
osgDB::Registry::instance()->getOrCreateSharedStateManager()->share(loaded.get());
// TODO: run SharedStateManager::prune on unload
if (mIncrementalCompileOperation)
mIncrementalCompileOperation->add(loaded);
mIndex[normalized] = loaded;
return loaded;
}
else
return it->second;
}
osg::ref_ptr<osg::Node> SceneManager::createInstance(const std::string &name)
{
osg::ref_ptr<const osg::Node> scene = getTemplate(name);
osg::ref_ptr<osg::Node> cloned = osg::clone(scene.get(), SceneUtil::CopyOp());
return cloned;
}
osg::ref_ptr<osg::Node> SceneManager::createInstance(const std::string &name, osg::Group* parentNode)
{
osg::ref_ptr<osg::Node> cloned = createInstance(name);
attachTo(cloned, parentNode);
return cloned;
}
osg::ref_ptr<const NifOsg::KeyframeHolder> SceneManager::getKeyframes(const std::string &name)
{
std::string normalized = name;
mVFS->normalizeFilename(normalized);
KeyframeIndex::iterator it = mKeyframeIndex.find(normalized);
if (it == mKeyframeIndex.end())
{
Files::IStreamPtr file = mVFS->get(normalized);
osg::ref_ptr<NifOsg::KeyframeHolder> loaded (new NifOsg::KeyframeHolder);
NifOsg::Loader::loadKf(Nif::NIFFilePtr(new Nif::NIFFile(file, normalized)), *loaded.get());
mKeyframeIndex[normalized] = loaded;
return loaded;
}
else
return it->second;
}
void SceneManager::attachTo(osg::Node *instance, osg::Group *parentNode) const
{
parentNode->addChild(instance);
notifyAttached(instance);
}
void SceneManager::releaseGLObjects(osg::State *state)
{
for (Index::iterator it = mIndex.begin(); it != mIndex.end(); ++it)
{
it->second->releaseGLObjects(state);
}
}
void SceneManager::setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation *ico)
{
mIncrementalCompileOperation = ico;
}
void SceneManager::notifyAttached(osg::Node *node) const
{
InitWorldSpaceParticlesVisitor visitor;
node->accept(visitor);
}
const VFS::Manager* SceneManager::getVFS() const
{
return mVFS;
}
Resource::TextureManager* SceneManager::getTextureManager()
{
return mTextureManager;
}
}