mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 21:49:55 +00:00
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));
|
||||
|
||||
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 ©, 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/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…
Reference in a new issue