NiBSPArrayController works

pull/638/head
scrawl 10 years ago
parent 71782462b7
commit 15f9c1ddcf

@ -60,7 +60,7 @@ void CSVRender::Object::update()
Nif::NIFFilePtr file(new Nif::NIFFile(mVFS->get(path), path));
loader.load(file, mBaseNode);
loader.loadAsSkeleton(file, mBaseNode);
//mObject->setVisibilityFlags (Element_Reference);

@ -4,6 +4,8 @@
#include <OgreSceneManager.h>
#include <OgreSceneNode.h>
#include <osgGA/TrackballManipulator>
#include "../../model/world/data.hpp"
#include "../../model/world/idtable.hpp"
@ -13,6 +15,8 @@ CSVRender::PreviewWidget::PreviewWidget (const VFS::Manager* vfs, CSMWorld::Data
{
//setNavigation (&mOrbit);
setCameraManipulator(new osgGA::TrackballManipulator);
QAbstractItemModel *referenceables =
mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables);

@ -15,7 +15,6 @@
#include <osgQt/GraphicsWindowQt>
#include <osg/GraphicsContext>
#include <osgGA/TrackballManipulator>
#include <osgViewer/ViewerEventHandlers>
namespace CSVRender
@ -72,14 +71,16 @@ SceneWidget::SceneWidget(QWidget *parent, Qt::WindowFlags f)
// Press S to reveal profiling stats
addEventHandler(new osgViewer::StatsHandler);
setCameraManipulator(new osgGA::TrackballManipulator);
// Only render when the camera position changed, or content flagged dirty
//setRunFrameScheme(osgViewer::ViewerBase::ON_DEMAND);
setRunFrameScheme(osgViewer::ViewerBase::CONTINUOUS);
getCamera()->setCullMask(~(0x1));
connect( &mTimer, SIGNAL(timeout()), this, SLOT(update()) );
mTimer.start( 10 );
realize();
}
void SceneWidget::paintEvent(QPaintEvent *event)

@ -64,6 +64,18 @@ namespace
return toMatrix(node->trafo);
}
void getAllNiNodes(const Nif::Node* node, std::vector<int>& outIndices)
{
const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(node);
if (ninode)
{
outIndices.push_back(ninode->recIndex);
for (unsigned int i=0; i<ninode->children.length(); ++i)
if (!ninode->children[i].empty())
getAllNiNodes(ninode->children[i].getPtr(), outIndices);
}
}
osg::BlendFunc::BlendFuncMode getBlendMode(int mode)
{
switch(mode)
@ -328,13 +340,6 @@ namespace NifOsg
toSetup->mFunction = boost::shared_ptr<ControllerFunction>(new ControllerFunction(ctrl, 1 /*autoPlay*/));
}
class RecIndexHolder : public osg::Referenced
{
public:
RecIndexHolder(int index) : mIndex(index) {}
int mIndex;
};
void Loader::handleNode(const Nif::Node* nifNode, osg::Group* parentNode, bool createSkeleton,
std::map<int, int> boundTextures, int animflags, int particleflags, bool collisionNode)
{
@ -356,6 +361,10 @@ namespace NifOsg
transformNode = new osg::MatrixTransform(toMatrix(nifNode->trafo));
}
// UserData used for a variety of features:
// - finding the correct emitter node for a particle system
// - establishing connections to the animated collision shapes, which are handled in a separate loader
// - finding a random child NiNode in NiBspArrayController
transformNode->setUserData(new RecIndexHolder(nifNode->recIndex));
if (nifNode->recType == Nif::RC_NiBSAnimationNode)
@ -543,32 +552,6 @@ namespace NifOsg
}
}
class FindEmitterNode : public osg::NodeVisitor
{
public:
FindEmitterNode(int recIndex)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mFound(NULL)
, mRecIndex(recIndex)
{
}
virtual void apply(osg::Node &searchNode)
{
if (searchNode.getUserData())
{
RecIndexHolder* holder = static_cast<RecIndexHolder*>(searchNode.getUserData());
if (holder->mIndex == mRecIndex)
mFound = static_cast<osg::Group*>(&searchNode);
}
traverse(searchNode);
}
osg::Group* mFound;
private:
int mRecIndex;
};
void Loader::handleParticleSystem(const Nif::Node *nifNode, osg::Group *parentNode, int animflags, int particleflags)
{
osg::ref_ptr<ParticleSystem> partsys (new ParticleSystem);
@ -582,8 +565,6 @@ namespace NifOsg
else
return;
// TODO: add special handling for NiBSPArrayController
const Nif::NiParticleSystemController* partctrl = NULL;
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)
{
@ -598,6 +579,12 @@ namespace NifOsg
return;
}
std::vector<int> targets;
if (partctrl->recType == Nif::RC_NiBSPArrayController)
{
getAllNiNodes(partctrl->emitter.getPtr(), targets);
}
osgParticle::ParticleProcessor::ReferenceFrame rf = (particleflags & Nif::NiNode::ParticleFlag_LocalSpace)
? osgParticle::ParticleProcessor::RELATIVE_RF
: osgParticle::ParticleProcessor::ABSOLUTE_RF;
@ -638,7 +625,7 @@ namespace NifOsg
// ---- emitter
osg::ref_ptr<osgParticle::ModularEmitter> emitter = new osgParticle::ModularEmitter;
osg::ref_ptr<Emitter> emitter = new Emitter(targets);
emitter->setParticleSystem(partsys);
emitter->setReferenceFrame(osgParticle::ParticleProcessor::RELATIVE_RF);
@ -668,7 +655,7 @@ 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.
FindEmitterNode find (partctrl->emitter->recIndex);
FindRecIndexVisitor find (partctrl->emitter->recIndex);
mRootNode->accept(find);
if (!find.mFound)
{
@ -676,6 +663,9 @@ namespace NifOsg
return;
}
osg::Group* emitterNode = find.mFound;
// Emitter attached to the emitter node. Note one side effect of the emitter using the CullVisitor is that hiding its node
// actually causes the emitter to stop firing. Convenient, because MW behaves this way too!
emitterNode->addChild(emitter);
osg::ref_ptr<ParticleSystemController> callback(new ParticleSystemController(partctrl));
@ -852,6 +842,7 @@ namespace NifOsg
geometry = new osg::Geometry;
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
geode->setName(triShape->name); // name will be used for part filtering
triShapeToGeometry(triShape, geometry, geode, boundTextures, animflags);
geode->addDrawable(geometry);
@ -862,6 +853,7 @@ namespace NifOsg
void Loader::handleSkinnedTriShape(const Nif::NiTriShape *triShape, osg::Group *parentNode, const std::map<int, int>& boundTextures, int animflags)
{
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
geode->setName(triShape->name); // name will be used for part filtering
osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);
triShapeToGeometry(triShape, geometry, geode, boundTextures, animflags);
@ -969,7 +961,7 @@ namespace NifOsg
case Nif::RC_NiVertexColorProperty:
case Nif::RC_NiSpecularProperty:
{
// TODO: handle these in handleTriShape so we know whether vertex colors are available
// Handled in handleTriShape so we know whether vertex colors are available
break;
}
case Nif::RC_NiAlphaProperty:

@ -191,4 +191,92 @@ void GravityAffector::operate(osgParticle::Particle *particle, double dt)
}
}
Emitter::Emitter()
: osgParticle::Emitter()
{
}
Emitter::Emitter(const Emitter &copy, const osg::CopyOp &copyop)
: osgParticle::Emitter(copy, copyop)
, mTargets(copy.mTargets)
{
}
Emitter::Emitter(const std::vector<int> &targets)
: mTargets(targets)
{
}
void Emitter::setShooter(osgParticle::Shooter *shooter)
{
mShooter = shooter;
}
void Emitter::setPlacer(osgParticle::Placer *placer)
{
mPlacer = placer;
}
void Emitter::setCounter(osgParticle::Counter *counter)
{
mCounter = counter;
}
void Emitter::emitParticles(double dt)
{
osg::Matrix worldToPs;
osg::MatrixList worldMats = getParticleSystem()->getWorldMatrices();
if (!worldMats.empty())
{
const osg::Matrix psToWorld = worldMats[0];
worldToPs = osg::Matrix::inverse(psToWorld);
}
const osg::Matrix& ltw = getLocalToWorldMatrix();
const osg::Matrix& previous_ltw = getPreviousLocalToWorldMatrix();
const osg::Matrix emitterToPs = ltw * worldToPs;
const osg::Matrix prevEmitterToPs = previous_ltw * worldToPs;
int n = mCounter->numParticlesToCreate(dt);
osg::Matrix transform;
if (!mTargets.empty())
{
int randomRecIndex = mTargets[(std::rand() / (static_cast<double>(RAND_MAX)+1.0)) * mTargets.size()];
// we could use a map here for faster lookup
FindRecIndexVisitor visitor(randomRecIndex);
getParent(0)->accept(visitor);
if (!visitor.mFound)
{
std::cerr << "Emitter: Can't find emitter node" << randomRecIndex << std::endl;
return;
}
osg::NodePath path = visitor.mFoundPath;
path.erase(path.begin());
transform = osg::computeLocalToWorld(path);
}
for (int i=0; i<n; ++i)
{
osgParticle::Particle* P = getParticleSystem()->createParticle(0);
if (P)
{
mPlacer->place(P);
P->transformPositionVelocity(transform);
mShooter->shoot(P);
// Now need to transform the position and velocity because we having a moving model.
// (is this actually how MW works?)
float r = ((float)rand()/(float)RAND_MAX);
P->transformPositionVelocity(emitterToPs, prevEmitterToPs, r);
//P->transformPositionVelocity(ltw);
}
}
}
}

@ -4,6 +4,7 @@
#include <osgParticle/Particle>
#include <osgParticle/Shooter>
#include <osgParticle/Operator>
#include <osgParticle/ModularEmitter>
#include <osg/NodeCallback>
@ -151,6 +152,71 @@ namespace NifOsg
osg::Vec3f mCachedWorldPositionDirection;
};
class RecIndexHolder : public osg::Referenced
{
public:
RecIndexHolder(int index) : mIndex(index) {}
int mIndex;
};
// NodeVisitor to find a child node with the given record index, stored in the node's user data.
class FindRecIndexVisitor : public osg::NodeVisitor
{
public:
FindRecIndexVisitor(int recIndex)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mFound(NULL)
, mRecIndex(recIndex)
{
}
virtual void apply(osg::Node &searchNode)
{
if (searchNode.getUserData())
{
RecIndexHolder* holder = static_cast<RecIndexHolder*>(searchNode.getUserData());
if (holder->mIndex == mRecIndex)
{
mFound = static_cast<osg::Group*>(&searchNode);
mFoundPath = getNodePath();
return;
}
}
traverse(searchNode);
}
osg::Group* mFound;
osg::NodePath mFoundPath;
private:
int mRecIndex;
};
// Subclass emitter to support randomly choosing one of the child node's transforms for the emit position of new particles.
class Emitter : public osgParticle::Emitter
{
public:
Emitter(const std::vector<int>& targets);
Emitter();
Emitter(const Emitter& copy, const osg::CopyOp& copyop);
META_Object(NifOsg, NifOsg::Emitter)
virtual void emitParticles(double dt);
void setShooter(osgParticle::Shooter* shooter);
void setPlacer(osgParticle::Placer* placer);
void setCounter(osgParticle::Counter* counter);
private:
// NIF Record indices
std::vector<int> mTargets;
osg::ref_ptr<osgParticle::Placer> mPlacer;
osg::ref_ptr<osgParticle::Shooter> mShooter;
osg::ref_ptr<osgParticle::Counter> mCounter;
};
}
#endif

Loading…
Cancel
Save