From 322fcdc2d318a1c9c0325d2b7cb7dd5be2359ed3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Mar 2015 00:30:49 +0100 Subject: [PATCH] Transform world space particles when attaching to a node --- apps/opencs/view/render/object.cpp | 47 ++++++++++++------- apps/opencs/view/render/object.hpp | 3 +- components/nifosg/nifloader.cpp | 25 +++++----- components/resource/scenemanager.cpp | 70 +++++++++++++++++++++++++++- components/resource/scenemanager.hpp | 11 ++++- 5 files changed, 123 insertions(+), 33 deletions(-) diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index afed837f8..ec184a563 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -5,6 +5,10 @@ #include #include +#include +#include +#include + #include "../../model/world/data.hpp" #include "../../model/world/ref.hpp" #include "../../model/world/refidcollection.hpp" @@ -14,6 +18,22 @@ #include "elements.hpp" +namespace +{ + + osg::ref_ptr createErrorCube() + { + osg::ref_ptr shape(new osg::Box(osg::Vec3f(0,0,0), 50.f)); + osg::ref_ptr shapedrawable(new osg::ShapeDrawable); + shapedrawable->setShape(shape); + + osg::ref_ptr geode (new osg::Geode); + geode->addDrawable(shapedrawable); + return geode; + } + +} + void CSVRender::Object::clear() { @@ -44,15 +64,11 @@ void CSVRender::Object::update() error = 2; } + mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); + if (error) { - /* - Ogre::Entity* entity = mBase->getCreator()->createEntity (Ogre::SceneManager::PT_CUBE); - entity->setMaterialName("BaseWhite"); /// \todo adjust material according to error - entity->setVisibilityFlags (Element_Reference); - - mBase->attachObject (entity); - */ + mBaseNode->addChild(createErrorCube()); } else { @@ -60,10 +76,7 @@ void CSVRender::Object::update() { std::string path = "meshes\\" + model; - osg::ref_ptr loaded = mResourceSystem->getSceneManager()->getInstance(path); - - mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); - mBaseNode->addChild(loaded); + mResourceSystem->getSceneManager()->createInstance(path, mBaseNode); } catch (std::exception& e) { @@ -73,7 +86,7 @@ void CSVRender::Object::update() } } -void CSVRender::Object::adjust() +void CSVRender::Object::adjustTransform() { if (mReferenceId.empty()) return; @@ -120,8 +133,8 @@ CSVRender::Object::Object (CSMWorld::Data& data, osg::Group* parentNode, mReferenceableId = getReference().mRefID; } + adjustTransform(); update(); - adjust(); } CSVRender::Object::~Object() @@ -140,8 +153,8 @@ bool CSVRender::Object::referenceableDataChanged (const QModelIndex& topLeft, if (index!=-1 && index>=topLeft.row() && index<=bottomRight.row()) { + adjustTransform(); update(); - adjust(); return true; } @@ -160,8 +173,8 @@ bool CSVRender::Object::referenceableAboutToBeRemoved (const QModelIndex& parent // Deletion of referenceable-type objects is handled outside of Object. if (!mReferenceId.empty()) { + adjustTransform(); update(); - adjust(); return true; } } @@ -184,6 +197,8 @@ bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft, int columnIndex = references.findColumnIndex (CSMWorld::Columns::ColumnId_ReferenceableId); + adjustTransform(); + if (columnIndex>=topLeft.column() && columnIndex<=bottomRight.row()) { mReferenceableId = @@ -192,8 +207,6 @@ bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft, update(); } - adjust(); - return true; } diff --git a/apps/opencs/view/render/object.hpp b/apps/opencs/view/render/object.hpp index e40253a60..4cea62133 100644 --- a/apps/opencs/view/render/object.hpp +++ b/apps/opencs/view/render/object.hpp @@ -52,10 +52,11 @@ namespace CSVRender void clear(); /// Update model + /// @note Make sure adjustTransform() was called first so world space particles get positioned correctly void update(); /// Adjust position, orientation and scale - void adjust(); + void adjustTransform(); /// Throws an exception if *this was constructed with referenceable const CSMWorld::CellRef& getReference() const; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 3b68df562..7e1c27b0e 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -915,14 +915,8 @@ namespace NifOsg } // Load the initial state of the particle system, i.e. the initial particles and their positions, velocity and colors. - void handleParticleInitialState(const Nif::Node* nifNode, osgParticle::ParticleSystem* partsys, const Nif::NiParticleSystemController* partctrl, - osgParticle::ParticleProcessor::ReferenceFrame rf) + void handleParticleInitialState(const Nif::Node* nifNode, osgParticle::ParticleSystem* partsys, const Nif::NiParticleSystemController* partctrl) { - // TODO: also take into account the transform by placement in the scene (should be done post-load) - osg::Matrix particletransform; - if (rf == osgParticle::ParticleProcessor::ABSOLUTE_RF) - particletransform = getWorldTransform(nifNode); - const Nif::NiAutoNormalParticlesData *particledata = NULL; if(nifNode->recType == Nif::RC_NiAutoNormalParticles) particledata = static_cast(nifNode)->data.getPtr(); @@ -941,9 +935,11 @@ namespace NifOsg osgParticle::Particle* created = partsys->createParticle(&particletemplate); created->setLifeTime(std::max(0.f, particle.lifespan)); - osg::Vec4f adjustedVelocity = osg::Vec4f(particle.velocity, 0.f) * particletransform; - created->setVelocity(osg::Vec3f(adjustedVelocity.x(), adjustedVelocity.y(), adjustedVelocity.z())); - created->setPosition(particledata->vertices.at(particle.vertex) * particletransform); + + // Note this position and velocity is not correct for a particle system with absolute reference frame, + // which can not be done in this loader since we are not attached to the scene yet. Will be fixed up post-load in the SceneManager. + created->setVelocity(particle.velocity); + created->setPosition(particledata->vertices.at(particle.vertex)); osg::Vec4f partcolor (1.f,1.f,1.f,1.f); if (particle.vertex < int(particledata->colors.size())) @@ -1014,7 +1010,13 @@ namespace NifOsg ? osgParticle::ParticleProcessor::RELATIVE_RF : osgParticle::ParticleProcessor::ABSOLUTE_RF; - handleParticleInitialState(nifNode, partsys, partctrl, rf); + // HACK: ParticleSystem has no setReferenceFrame method + if (rf == osgParticle::ParticleProcessor::ABSOLUTE_RF) + { + partsys->getOrCreateUserDataContainer()->addDescription("worldspace"); + } + + handleParticleInitialState(nifNode, partsys, partctrl); partsys->setQuota(partctrl->numParticles); @@ -1030,7 +1032,6 @@ namespace NifOsg // This seems to be true for all NIF files in the game that I've checked, suggesting that NIFs work similar to OSG with regards to update order. // If something ever violates this assumption, the worst that could happen is the culling being one frame late, which wouldn't be a disaster. - // Creating emitters will need to be changed when cloning a scenegraph is implemented, the particleSystem pointer would become invalid FindRecIndexVisitor find (partctrl->emitter->recIndex); rootNode->accept(find); if (!find.mFound) diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index b6a09f3a7..c7fd5065e 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -1,12 +1,63 @@ #include "scenemanager.hpp" #include +#include +#include + +#include #include #include #include +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;igetNumDrawables();++i) + { + if (osgParticle::ParticleSystem* partsys = dynamic_cast(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)); + } + } + } + } + + traverse(node); + } + + void transformInitialParticles(osgParticle::ParticleSystem* partsys, osg::Node* node) + { + osg::Matrix worldMat = node->getWorldMatrices()[0]; + for (int i=0; inumParticles(); ++i) + { + partsys->getParticle(i)->transformPositionVelocity(worldMat); + } + } + }; + +} + namespace Resource { @@ -40,10 +91,25 @@ namespace Resource return it->second; } - osg::ref_ptr SceneManager::getInstance(const std::string &name) + osg::ref_ptr SceneManager::createInstance(const std::string &name) { osg::ref_ptr scene = getTemplate(name); - return osg::clone(scene.get(), SceneUtil::CopyOp()); + osg::ref_ptr cloned = osg::clone(scene.get(), SceneUtil::CopyOp()); + return cloned; + } + + osg::ref_ptr SceneManager::createInstance(const std::string &name, osg::Group* parentNode) + { + osg::ref_ptr cloned = createInstance(name); + attachTo(cloned, parentNode); + return cloned; + } + + void SceneManager::attachTo(osg::Node *instance, osg::Group *parentNode) const + { + parentNode->addChild(instance); + InitWorldSpaceParticlesVisitor visitor; + instance->accept(visitor); } } diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 7922f3a49..76b69be6e 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -25,7 +25,16 @@ namespace Resource osg::ref_ptr getTemplate(const std::string& name); /// Create an instance of the given scene template - osg::ref_ptr getInstance(const std::string& name); + osg::ref_ptr createInstance(const std::string& name); + + /// Create an instance of the given scene template and immediately attach it to a parent node + osg::ref_ptr createInstance(const std::string& name, osg::Group* parentNode); + + /// Attach the given scene instance to the given parent node + /// @note You should have the parentNode in its intended position before calling this method, + /// so that world space particles of the \a instance get transformed correctly. + /// @note Assumes the given instance was not attached to any parents before. + void attachTo(osg::Node* instance, osg::Group* parentNode) const; private: const VFS::Manager* mVFS;