forked from mirror/openmw-tes3mp
NiBSPArrayController works
This commit is contained in:
parent
71782462b7
commit
15f9c1ddcf
6 changed files with 193 additions and 42 deletions
|
@ -60,7 +60,7 @@ void CSVRender::Object::update()
|
||||||
|
|
||||||
Nif::NIFFilePtr file(new Nif::NIFFile(mVFS->get(path), path));
|
Nif::NIFFilePtr file(new Nif::NIFFile(mVFS->get(path), path));
|
||||||
|
|
||||||
loader.load(file, mBaseNode);
|
loader.loadAsSkeleton(file, mBaseNode);
|
||||||
|
|
||||||
//mObject->setVisibilityFlags (Element_Reference);
|
//mObject->setVisibilityFlags (Element_Reference);
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include <OgreSceneManager.h>
|
#include <OgreSceneManager.h>
|
||||||
#include <OgreSceneNode.h>
|
#include <OgreSceneNode.h>
|
||||||
|
|
||||||
|
#include <osgGA/TrackballManipulator>
|
||||||
|
|
||||||
#include "../../model/world/data.hpp"
|
#include "../../model/world/data.hpp"
|
||||||
#include "../../model/world/idtable.hpp"
|
#include "../../model/world/idtable.hpp"
|
||||||
|
|
||||||
|
@ -13,6 +15,8 @@ CSVRender::PreviewWidget::PreviewWidget (const VFS::Manager* vfs, CSMWorld::Data
|
||||||
{
|
{
|
||||||
//setNavigation (&mOrbit);
|
//setNavigation (&mOrbit);
|
||||||
|
|
||||||
|
setCameraManipulator(new osgGA::TrackballManipulator);
|
||||||
|
|
||||||
QAbstractItemModel *referenceables =
|
QAbstractItemModel *referenceables =
|
||||||
mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables);
|
mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables);
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#include <osgQt/GraphicsWindowQt>
|
#include <osgQt/GraphicsWindowQt>
|
||||||
#include <osg/GraphicsContext>
|
#include <osg/GraphicsContext>
|
||||||
|
|
||||||
#include <osgGA/TrackballManipulator>
|
|
||||||
#include <osgViewer/ViewerEventHandlers>
|
#include <osgViewer/ViewerEventHandlers>
|
||||||
|
|
||||||
namespace CSVRender
|
namespace CSVRender
|
||||||
|
@ -72,14 +71,16 @@ SceneWidget::SceneWidget(QWidget *parent, Qt::WindowFlags f)
|
||||||
// Press S to reveal profiling stats
|
// Press S to reveal profiling stats
|
||||||
addEventHandler(new osgViewer::StatsHandler);
|
addEventHandler(new osgViewer::StatsHandler);
|
||||||
|
|
||||||
setCameraManipulator(new osgGA::TrackballManipulator);
|
|
||||||
|
|
||||||
// Only render when the camera position changed, or content flagged dirty
|
// Only render when the camera position changed, or content flagged dirty
|
||||||
//setRunFrameScheme(osgViewer::ViewerBase::ON_DEMAND);
|
//setRunFrameScheme(osgViewer::ViewerBase::ON_DEMAND);
|
||||||
setRunFrameScheme(osgViewer::ViewerBase::CONTINUOUS);
|
setRunFrameScheme(osgViewer::ViewerBase::CONTINUOUS);
|
||||||
|
|
||||||
|
getCamera()->setCullMask(~(0x1));
|
||||||
|
|
||||||
connect( &mTimer, SIGNAL(timeout()), this, SLOT(update()) );
|
connect( &mTimer, SIGNAL(timeout()), this, SLOT(update()) );
|
||||||
mTimer.start( 10 );
|
mTimer.start( 10 );
|
||||||
|
|
||||||
|
realize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneWidget::paintEvent(QPaintEvent *event)
|
void SceneWidget::paintEvent(QPaintEvent *event)
|
||||||
|
|
|
@ -64,6 +64,18 @@ namespace
|
||||||
return toMatrix(node->trafo);
|
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)
|
osg::BlendFunc::BlendFuncMode getBlendMode(int mode)
|
||||||
{
|
{
|
||||||
switch(mode)
|
switch(mode)
|
||||||
|
@ -328,13 +340,6 @@ namespace NifOsg
|
||||||
toSetup->mFunction = boost::shared_ptr<ControllerFunction>(new ControllerFunction(ctrl, 1 /*autoPlay*/));
|
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,
|
void Loader::handleNode(const Nif::Node* nifNode, osg::Group* parentNode, bool createSkeleton,
|
||||||
std::map<int, int> boundTextures, int animflags, int particleflags, bool collisionNode)
|
std::map<int, int> boundTextures, int animflags, int particleflags, bool collisionNode)
|
||||||
{
|
{
|
||||||
|
@ -356,6 +361,10 @@ namespace NifOsg
|
||||||
transformNode = new osg::MatrixTransform(toMatrix(nifNode->trafo));
|
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));
|
transformNode->setUserData(new RecIndexHolder(nifNode->recIndex));
|
||||||
|
|
||||||
if (nifNode->recType == Nif::RC_NiBSAnimationNode)
|
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)
|
void Loader::handleParticleSystem(const Nif::Node *nifNode, osg::Group *parentNode, int animflags, int particleflags)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<ParticleSystem> partsys (new ParticleSystem);
|
osg::ref_ptr<ParticleSystem> partsys (new ParticleSystem);
|
||||||
|
@ -582,8 +565,6 @@ namespace NifOsg
|
||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO: add special handling for NiBSPArrayController
|
|
||||||
|
|
||||||
const Nif::NiParticleSystemController* partctrl = NULL;
|
const Nif::NiParticleSystemController* partctrl = NULL;
|
||||||
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)
|
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)
|
||||||
{
|
{
|
||||||
|
@ -598,6 +579,12 @@ namespace NifOsg
|
||||||
return;
|
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::ReferenceFrame rf = (particleflags & Nif::NiNode::ParticleFlag_LocalSpace)
|
||||||
? osgParticle::ParticleProcessor::RELATIVE_RF
|
? osgParticle::ParticleProcessor::RELATIVE_RF
|
||||||
: osgParticle::ParticleProcessor::ABSOLUTE_RF;
|
: osgParticle::ParticleProcessor::ABSOLUTE_RF;
|
||||||
|
@ -638,7 +625,7 @@ namespace NifOsg
|
||||||
|
|
||||||
// ---- emitter
|
// ---- emitter
|
||||||
|
|
||||||
osg::ref_ptr<osgParticle::ModularEmitter> emitter = new osgParticle::ModularEmitter;
|
osg::ref_ptr<Emitter> emitter = new Emitter(targets);
|
||||||
emitter->setParticleSystem(partsys);
|
emitter->setParticleSystem(partsys);
|
||||||
emitter->setReferenceFrame(osgParticle::ParticleProcessor::RELATIVE_RF);
|
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.
|
// 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.
|
||||||
|
|
||||||
FindEmitterNode find (partctrl->emitter->recIndex);
|
FindRecIndexVisitor find (partctrl->emitter->recIndex);
|
||||||
mRootNode->accept(find);
|
mRootNode->accept(find);
|
||||||
if (!find.mFound)
|
if (!find.mFound)
|
||||||
{
|
{
|
||||||
|
@ -676,6 +663,9 @@ namespace NifOsg
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
osg::Group* emitterNode = find.mFound;
|
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);
|
emitterNode->addChild(emitter);
|
||||||
|
|
||||||
osg::ref_ptr<ParticleSystemController> callback(new ParticleSystemController(partctrl));
|
osg::ref_ptr<ParticleSystemController> callback(new ParticleSystemController(partctrl));
|
||||||
|
@ -852,6 +842,7 @@ namespace NifOsg
|
||||||
geometry = new osg::Geometry;
|
geometry = new osg::Geometry;
|
||||||
|
|
||||||
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
|
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);
|
triShapeToGeometry(triShape, geometry, geode, boundTextures, animflags);
|
||||||
|
|
||||||
geode->addDrawable(geometry);
|
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)
|
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);
|
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);
|
osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);
|
||||||
triShapeToGeometry(triShape, geometry, geode, boundTextures, animflags);
|
triShapeToGeometry(triShape, geometry, geode, boundTextures, animflags);
|
||||||
|
|
||||||
|
@ -969,7 +961,7 @@ namespace NifOsg
|
||||||
case Nif::RC_NiVertexColorProperty:
|
case Nif::RC_NiVertexColorProperty:
|
||||||
case Nif::RC_NiSpecularProperty:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case Nif::RC_NiAlphaProperty:
|
case Nif::RC_NiAlphaProperty:
|
||||||
|
|
|
@ -191,4 +191,92 @@ void GravityAffector::operate(osgParticle::Particle *particle, double dt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Emitter::Emitter()
|
||||||
|
: osgParticle::Emitter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Emitter::Emitter(const Emitter ©, const osg::CopyOp ©op)
|
||||||
|
: 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/Particle>
|
||||||
#include <osgParticle/Shooter>
|
#include <osgParticle/Shooter>
|
||||||
#include <osgParticle/Operator>
|
#include <osgParticle/Operator>
|
||||||
|
#include <osgParticle/ModularEmitter>
|
||||||
|
|
||||||
#include <osg/NodeCallback>
|
#include <osg/NodeCallback>
|
||||||
|
|
||||||
|
@ -151,6 +152,71 @@ namespace NifOsg
|
||||||
osg::Vec3f mCachedWorldPositionDirection;
|
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
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue