mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-26 09:35:28 +00:00
Merge branch 'smooth_my_nodes' into 'master'
Better support BSPArrayController See merge request OpenMW/openmw!1858
This commit is contained in:
commit
d8eb9d6818
@ -212,6 +212,7 @@ struct NiNode : Node
|
||||
ControllerFlag_Active = 0x8
|
||||
};
|
||||
enum BSPArrayController {
|
||||
BSPArrayController_AtNode = 0x8,
|
||||
BSPArrayController_AtVertex = 0x10
|
||||
};
|
||||
|
||||
|
@ -50,6 +50,21 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
struct DisableOptimizer : osg::NodeVisitor
|
||||
{
|
||||
DisableOptimizer(osg::NodeVisitor::TraversalMode mode = TRAVERSE_ALL_CHILDREN) : osg::NodeVisitor(mode) {}
|
||||
|
||||
void apply(osg::Node &node) override
|
||||
{
|
||||
node.setDataVariance(osg::Object::DYNAMIC);
|
||||
traverse(node);
|
||||
}
|
||||
|
||||
void apply(osg::Drawable &node) override
|
||||
{
|
||||
traverse(node);
|
||||
}
|
||||
};
|
||||
|
||||
void getAllNiNodes(const Nif::Node* node, std::vector<int>& outIndices)
|
||||
{
|
||||
@ -1072,10 +1087,10 @@ namespace NifOsg
|
||||
partctrl->verticalDir, partctrl->verticalAngle,
|
||||
partctrl->lifetime, partctrl->lifetimeRandom);
|
||||
emitter->setShooter(shooter);
|
||||
emitter->setFlags(partctrl->flags);
|
||||
|
||||
if (atVertex && (partctrl->recType == Nif::RC_NiBSPArrayController))
|
||||
if (partctrl->recType == Nif::RC_NiBSPArrayController && atVertex)
|
||||
{
|
||||
emitter->setUseGeometryEmitter(true);
|
||||
emitter->setGeometryEmitterTarget(partctrl->emitter->recIndex);
|
||||
}
|
||||
else
|
||||
@ -1107,6 +1122,9 @@ namespace NifOsg
|
||||
// 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(emitterPair.second);
|
||||
|
||||
DisableOptimizer disableOptimizer;
|
||||
emitterNode->accept(disableOptimizer);
|
||||
}
|
||||
mEmitterQueue.clear();
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "particle.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
#include <osg/Version>
|
||||
#include <osg/MatrixTransform>
|
||||
@ -11,6 +12,7 @@
|
||||
#include <components/misc/rng.hpp>
|
||||
#include <components/nif/controlled.hpp>
|
||||
#include <components/nif/data.hpp>
|
||||
#include <components/nif/node.hpp>
|
||||
#include <components/sceneutil/morphgeometry.hpp>
|
||||
#include <components/sceneutil/riggeometry.hpp>
|
||||
|
||||
@ -56,6 +58,44 @@ namespace
|
||||
|
||||
osg::Geometry* mGeometry;
|
||||
};
|
||||
|
||||
class LocalToWorldAccumulator : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
LocalToWorldAccumulator(osg::Matrix& matrix) : osg::NodeVisitor(), mMatrix(matrix) {}
|
||||
|
||||
virtual void apply(osg::Transform& transform)
|
||||
{
|
||||
if (&transform != mLastAppliedTransform)
|
||||
{
|
||||
mLastAppliedTransform = &transform;
|
||||
mLastMatrix = mMatrix;
|
||||
}
|
||||
transform.computeLocalToWorldMatrix(mMatrix, this);
|
||||
}
|
||||
|
||||
void accumulate(const osg::NodePath& path)
|
||||
{
|
||||
if (path.empty())
|
||||
return;
|
||||
|
||||
size_t i = path.size();
|
||||
|
||||
for (auto rit = path.rbegin(); rit != path.rend(); rit++, --i)
|
||||
{
|
||||
const osg::Camera* camera = (*rit)->asCamera();
|
||||
if (camera && (camera->getReferenceFrame() != osg::Transform::RELATIVE_RF || camera->getParents().empty()))
|
||||
break;
|
||||
}
|
||||
|
||||
for(; i < path.size(); ++i)
|
||||
path[i]->accept(*this);
|
||||
}
|
||||
|
||||
osg::Matrix& mMatrix;
|
||||
std::optional<osg::Matrix> mLastMatrix;
|
||||
osg::Transform* mLastAppliedTransform = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
namespace NifOsg
|
||||
@ -310,7 +350,7 @@ void GravityAffector::operate(osgParticle::Particle *particle, double dt)
|
||||
|
||||
Emitter::Emitter()
|
||||
: osgParticle::Emitter()
|
||||
, mUseGeometryEmitter(false)
|
||||
, mFlags(0)
|
||||
, mGeometryEmitterTarget(std::nullopt)
|
||||
{
|
||||
}
|
||||
@ -322,7 +362,7 @@ Emitter::Emitter(const Emitter ©, const osg::CopyOp ©op)
|
||||
, mShooter(copy.mShooter)
|
||||
// need a deep copy because the remainder is stored in the object
|
||||
, mCounter(static_cast<osgParticle::Counter*>(copy.mCounter->clone(osg::CopyOp::DEEP_COPY_ALL)))
|
||||
, mUseGeometryEmitter(copy.mUseGeometryEmitter)
|
||||
, mFlags(copy.mFlags)
|
||||
, mGeometryEmitterTarget(copy.mGeometryEmitterTarget)
|
||||
, mCachedGeometryEmitter(copy.mCachedGeometryEmitter)
|
||||
{
|
||||
@ -330,7 +370,7 @@ Emitter::Emitter(const Emitter ©, const osg::CopyOp ©op)
|
||||
|
||||
Emitter::Emitter(const std::vector<int> &targets)
|
||||
: mTargets(targets)
|
||||
, mUseGeometryEmitter(false)
|
||||
, mFlags(0)
|
||||
, mGeometryEmitterTarget(std::nullopt)
|
||||
{
|
||||
}
|
||||
@ -356,11 +396,13 @@ void Emitter::emitParticles(double dt)
|
||||
|
||||
osg::ref_ptr<osg::Vec3Array> geometryVertices = nullptr;
|
||||
|
||||
if (mUseGeometryEmitter || !mTargets.empty())
|
||||
const bool useGeometryEmitter = mFlags & Nif::NiNode::BSPArrayController_AtVertex;
|
||||
|
||||
if (useGeometryEmitter || !mTargets.empty())
|
||||
{
|
||||
int recIndex;
|
||||
|
||||
if (mUseGeometryEmitter)
|
||||
if (useGeometryEmitter)
|
||||
{
|
||||
if (!mGeometryEmitterTarget.has_value())
|
||||
return;
|
||||
@ -383,7 +425,7 @@ void Emitter::emitParticles(double dt)
|
||||
return;
|
||||
}
|
||||
|
||||
if (mUseGeometryEmitter)
|
||||
if (useGeometryEmitter)
|
||||
{
|
||||
if (!mCachedGeometryEmitter.lock(geometryVertices))
|
||||
{
|
||||
@ -403,12 +445,30 @@ void Emitter::emitParticles(double dt)
|
||||
|
||||
osg::NodePath path = visitor.mFoundPath;
|
||||
path.erase(path.begin());
|
||||
emitterToPs = osg::computeLocalToWorld(path) * emitterToPs;
|
||||
if (!useGeometryEmitter && (mFlags & Nif::NiNode::BSPArrayController_AtNode) && path.size())
|
||||
{
|
||||
osg::Matrix current;
|
||||
|
||||
LocalToWorldAccumulator accum(current);
|
||||
accum.accumulate(path);
|
||||
|
||||
osg::Matrix parent = accum.mLastMatrix.value_or(current);
|
||||
|
||||
auto p1 = parent.getTrans();
|
||||
auto p2 = current.getTrans();
|
||||
current.setTrans((p2 - p1) * Misc::Rng::rollClosedProbability() + p1);
|
||||
|
||||
emitterToPs = current * emitterToPs;
|
||||
}
|
||||
else
|
||||
{
|
||||
emitterToPs = osg::computeLocalToWorld(path) * emitterToPs;
|
||||
}
|
||||
}
|
||||
|
||||
emitterToPs.orthoNormalize(emitterToPs);
|
||||
|
||||
if (mUseGeometryEmitter && (!geometryVertices.valid() || geometryVertices->empty()))
|
||||
if (useGeometryEmitter && (!geometryVertices.valid() || geometryVertices->empty()))
|
||||
return;
|
||||
|
||||
for (int i=0; i<n; ++i)
|
||||
@ -416,7 +476,7 @@ void Emitter::emitParticles(double dt)
|
||||
osgParticle::Particle* P = getParticleSystem()->createParticle(nullptr);
|
||||
if (P)
|
||||
{
|
||||
if (mUseGeometryEmitter)
|
||||
if (useGeometryEmitter)
|
||||
P->setPosition((*geometryVertices)[Misc::Rng::rollDice(geometryVertices->getNumElements())]);
|
||||
else if (mPlacer)
|
||||
mPlacer->place(P);
|
||||
|
@ -241,9 +241,8 @@ namespace NifOsg
|
||||
void setShooter(osgParticle::Shooter* shooter) { mShooter = shooter; }
|
||||
void setPlacer(osgParticle::Placer* placer) { mPlacer = placer; }
|
||||
void setCounter(osgParticle::Counter* counter) { mCounter = counter;}
|
||||
|
||||
void setUseGeometryEmitter(bool useGeometryEmitter) { mUseGeometryEmitter = useGeometryEmitter; }
|
||||
void setGeometryEmitterTarget(std::optional<int> recIndex) { mGeometryEmitterTarget = recIndex; }
|
||||
void setFlags(int flags) { mFlags = flags; }
|
||||
|
||||
private:
|
||||
// NIF Record indices
|
||||
@ -253,7 +252,8 @@ namespace NifOsg
|
||||
osg::ref_ptr<osgParticle::Shooter> mShooter;
|
||||
osg::ref_ptr<osgParticle::Counter> mCounter;
|
||||
|
||||
bool mUseGeometryEmitter;
|
||||
int mFlags;
|
||||
|
||||
std::optional<int> mGeometryEmitterTarget;
|
||||
osg::observer_ptr<osg::Vec3Array> mCachedGeometryEmitter;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user