Transform world space particles when attaching to a node

c++11
scrawl 10 years ago
parent 961aba5e2b
commit 322fcdc2d3

@ -5,6 +5,10 @@
#include <osg/Group>
#include <osg/PositionAttitudeTransform>
#include <osg/ShapeDrawable>
#include <osg/Shape>
#include <osg/Geode>
#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<osg::Geode> createErrorCube()
{
osg::ref_ptr<osg::Box> shape(new osg::Box(osg::Vec3f(0,0,0), 50.f));
osg::ref_ptr<osg::ShapeDrawable> shapedrawable(new osg::ShapeDrawable);
shapedrawable->setShape(shape);
osg::ref_ptr<osg::Geode> 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<osg::Node> 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;
}

@ -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;

@ -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<const Nif::NiAutoNormalParticles*>(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)

@ -1,12 +1,63 @@
#include "scenemanager.hpp"
#include <osg/Node>
#include <osg/Geode>
#include <osg/UserDataContainer>
#include <osgParticle/ParticleSystem>
#include <components/nifosg/nifloader.hpp>
#include <components/nif/niffile.hpp>
#include <components/sceneutil/clone.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));
}
}
}
}
traverse(node);
}
void transformInitialParticles(osgParticle::ParticleSystem* partsys, osg::Node* node)
{
osg::Matrix worldMat = node->getWorldMatrices()[0];
for (int i=0; i<partsys->numParticles(); ++i)
{
partsys->getParticle(i)->transformPositionVelocity(worldMat);
}
}
};
}
namespace Resource
{
@ -40,10 +91,25 @@ namespace Resource
return it->second;
}
osg::ref_ptr<osg::Node> SceneManager::getInstance(const std::string &name)
osg::ref_ptr<osg::Node> SceneManager::createInstance(const std::string &name)
{
osg::ref_ptr<const osg::Node> scene = getTemplate(name);
return osg::clone(scene.get(), SceneUtil::CopyOp());
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;
}
void SceneManager::attachTo(osg::Node *instance, osg::Group *parentNode) const
{
parentNode->addChild(instance);
InitWorldSpaceParticlesVisitor visitor;
instance->accept(visitor);
}
}

@ -25,7 +25,16 @@ namespace Resource
osg::ref_ptr<const osg::Node> getTemplate(const std::string& name);
/// Create an instance of the given scene template
osg::ref_ptr<osg::Node> getInstance(const std::string& name);
osg::ref_ptr<osg::Node> createInstance(const std::string& name);
/// Create an instance of the given scene template and immediately attach it to a parent node
osg::ref_ptr<osg::Node> 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;

Loading…
Cancel
Save