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/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…
Reference in a new issue