forked from mirror/openmw-tes3mp
Transform world space particles when attaching to a node
This commit is contained in:
parent
961aba5e2b
commit
322fcdc2d3
5 changed files with 123 additions and 33 deletions
|
@ -5,6 +5,10 @@
|
||||||
#include <osg/Group>
|
#include <osg/Group>
|
||||||
#include <osg/PositionAttitudeTransform>
|
#include <osg/PositionAttitudeTransform>
|
||||||
|
|
||||||
|
#include <osg/ShapeDrawable>
|
||||||
|
#include <osg/Shape>
|
||||||
|
#include <osg/Geode>
|
||||||
|
|
||||||
#include "../../model/world/data.hpp"
|
#include "../../model/world/data.hpp"
|
||||||
#include "../../model/world/ref.hpp"
|
#include "../../model/world/ref.hpp"
|
||||||
#include "../../model/world/refidcollection.hpp"
|
#include "../../model/world/refidcollection.hpp"
|
||||||
|
@ -14,6 +18,22 @@
|
||||||
|
|
||||||
#include "elements.hpp"
|
#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()
|
void CSVRender::Object::clear()
|
||||||
{
|
{
|
||||||
|
@ -44,15 +64,11 @@ void CSVRender::Object::update()
|
||||||
error = 2;
|
error = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mBaseNode->removeChildren(0, mBaseNode->getNumChildren());
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
/*
|
mBaseNode->addChild(createErrorCube());
|
||||||
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);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -60,10 +76,7 @@ void CSVRender::Object::update()
|
||||||
{
|
{
|
||||||
std::string path = "meshes\\" + model;
|
std::string path = "meshes\\" + model;
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> loaded = mResourceSystem->getSceneManager()->getInstance(path);
|
mResourceSystem->getSceneManager()->createInstance(path, mBaseNode);
|
||||||
|
|
||||||
mBaseNode->removeChildren(0, mBaseNode->getNumChildren());
|
|
||||||
mBaseNode->addChild(loaded);
|
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
|
@ -73,7 +86,7 @@ void CSVRender::Object::update()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Object::adjust()
|
void CSVRender::Object::adjustTransform()
|
||||||
{
|
{
|
||||||
if (mReferenceId.empty())
|
if (mReferenceId.empty())
|
||||||
return;
|
return;
|
||||||
|
@ -120,8 +133,8 @@ CSVRender::Object::Object (CSMWorld::Data& data, osg::Group* parentNode,
|
||||||
mReferenceableId = getReference().mRefID;
|
mReferenceableId = getReference().mRefID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adjustTransform();
|
||||||
update();
|
update();
|
||||||
adjust();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CSVRender::Object::~Object()
|
CSVRender::Object::~Object()
|
||||||
|
@ -140,8 +153,8 @@ bool CSVRender::Object::referenceableDataChanged (const QModelIndex& topLeft,
|
||||||
|
|
||||||
if (index!=-1 && index>=topLeft.row() && index<=bottomRight.row())
|
if (index!=-1 && index>=topLeft.row() && index<=bottomRight.row())
|
||||||
{
|
{
|
||||||
|
adjustTransform();
|
||||||
update();
|
update();
|
||||||
adjust();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,8 +173,8 @@ bool CSVRender::Object::referenceableAboutToBeRemoved (const QModelIndex& parent
|
||||||
// Deletion of referenceable-type objects is handled outside of Object.
|
// Deletion of referenceable-type objects is handled outside of Object.
|
||||||
if (!mReferenceId.empty())
|
if (!mReferenceId.empty())
|
||||||
{
|
{
|
||||||
|
adjustTransform();
|
||||||
update();
|
update();
|
||||||
adjust();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,6 +197,8 @@ bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft,
|
||||||
int columnIndex =
|
int columnIndex =
|
||||||
references.findColumnIndex (CSMWorld::Columns::ColumnId_ReferenceableId);
|
references.findColumnIndex (CSMWorld::Columns::ColumnId_ReferenceableId);
|
||||||
|
|
||||||
|
adjustTransform();
|
||||||
|
|
||||||
if (columnIndex>=topLeft.column() && columnIndex<=bottomRight.row())
|
if (columnIndex>=topLeft.column() && columnIndex<=bottomRight.row())
|
||||||
{
|
{
|
||||||
mReferenceableId =
|
mReferenceableId =
|
||||||
|
@ -192,8 +207,6 @@ bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft,
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
adjust();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,10 +52,11 @@ namespace CSVRender
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
/// Update model
|
/// Update model
|
||||||
|
/// @note Make sure adjustTransform() was called first so world space particles get positioned correctly
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
/// Adjust position, orientation and scale
|
/// Adjust position, orientation and scale
|
||||||
void adjust();
|
void adjustTransform();
|
||||||
|
|
||||||
/// Throws an exception if *this was constructed with referenceable
|
/// Throws an exception if *this was constructed with referenceable
|
||||||
const CSMWorld::CellRef& getReference() const;
|
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.
|
// 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,
|
void handleParticleInitialState(const Nif::Node* nifNode, osgParticle::ParticleSystem* partsys, const Nif::NiParticleSystemController* partctrl)
|
||||||
osgParticle::ParticleProcessor::ReferenceFrame rf)
|
|
||||||
{
|
{
|
||||||
// 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;
|
const Nif::NiAutoNormalParticlesData *particledata = NULL;
|
||||||
if(nifNode->recType == Nif::RC_NiAutoNormalParticles)
|
if(nifNode->recType == Nif::RC_NiAutoNormalParticles)
|
||||||
particledata = static_cast<const Nif::NiAutoNormalParticles*>(nifNode)->data.getPtr();
|
particledata = static_cast<const Nif::NiAutoNormalParticles*>(nifNode)->data.getPtr();
|
||||||
|
@ -941,9 +935,11 @@ namespace NifOsg
|
||||||
|
|
||||||
osgParticle::Particle* created = partsys->createParticle(&particletemplate);
|
osgParticle::Particle* created = partsys->createParticle(&particletemplate);
|
||||||
created->setLifeTime(std::max(0.f, particle.lifespan));
|
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()));
|
// Note this position and velocity is not correct for a particle system with absolute reference frame,
|
||||||
created->setPosition(particledata->vertices.at(particle.vertex) * particletransform);
|
// 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);
|
osg::Vec4f partcolor (1.f,1.f,1.f,1.f);
|
||||||
if (particle.vertex < int(particledata->colors.size()))
|
if (particle.vertex < int(particledata->colors.size()))
|
||||||
|
@ -1014,7 +1010,13 @@ namespace NifOsg
|
||||||
? osgParticle::ParticleProcessor::RELATIVE_RF
|
? osgParticle::ParticleProcessor::RELATIVE_RF
|
||||||
: osgParticle::ParticleProcessor::ABSOLUTE_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);
|
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.
|
// 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.
|
// 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);
|
FindRecIndexVisitor find (partctrl->emitter->recIndex);
|
||||||
rootNode->accept(find);
|
rootNode->accept(find);
|
||||||
if (!find.mFound)
|
if (!find.mFound)
|
||||||
|
|
|
@ -1,12 +1,63 @@
|
||||||
#include "scenemanager.hpp"
|
#include "scenemanager.hpp"
|
||||||
|
|
||||||
#include <osg/Node>
|
#include <osg/Node>
|
||||||
|
#include <osg/Geode>
|
||||||
|
#include <osg/UserDataContainer>
|
||||||
|
|
||||||
|
#include <osgParticle/ParticleSystem>
|
||||||
|
|
||||||
#include <components/nifosg/nifloader.hpp>
|
#include <components/nifosg/nifloader.hpp>
|
||||||
#include <components/nif/niffile.hpp>
|
#include <components/nif/niffile.hpp>
|
||||||
|
|
||||||
#include <components/sceneutil/clone.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
|
namespace Resource
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -40,10 +91,25 @@ namespace Resource
|
||||||
return it->second;
|
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);
|
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);
|
osg::ref_ptr<const osg::Node> getTemplate(const std::string& name);
|
||||||
|
|
||||||
/// Create an instance of the given scene template
|
/// 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:
|
private:
|
||||||
const VFS::Manager* mVFS;
|
const VFS::Manager* mVFS;
|
||||||
|
|
Loading…
Reference in a new issue