mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-05-08 11:38:19 +03:00
Merge branch 'smooth_my_nodes' into 'master'
Better support BSPArrayController See merge request OpenMW/openmw!1858
This commit is contained in:
commit
d8eb9d6818
4 changed files with 93 additions and 14 deletions
|
@ -212,6 +212,7 @@ struct NiNode : Node
|
||||||
ControllerFlag_Active = 0x8
|
ControllerFlag_Active = 0x8
|
||||||
};
|
};
|
||||||
enum BSPArrayController {
|
enum BSPArrayController {
|
||||||
|
BSPArrayController_AtNode = 0x8,
|
||||||
BSPArrayController_AtVertex = 0x10
|
BSPArrayController_AtVertex = 0x10
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,21 @@
|
||||||
|
|
||||||
namespace
|
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)
|
void getAllNiNodes(const Nif::Node* node, std::vector<int>& outIndices)
|
||||||
{
|
{
|
||||||
|
@ -1072,10 +1087,10 @@ namespace NifOsg
|
||||||
partctrl->verticalDir, partctrl->verticalAngle,
|
partctrl->verticalDir, partctrl->verticalAngle,
|
||||||
partctrl->lifetime, partctrl->lifetimeRandom);
|
partctrl->lifetime, partctrl->lifetimeRandom);
|
||||||
emitter->setShooter(shooter);
|
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);
|
emitter->setGeometryEmitterTarget(partctrl->emitter->recIndex);
|
||||||
}
|
}
|
||||||
else
|
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
|
// 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!
|
// actually causes the emitter to stop firing. Convenient, because MW behaves this way too!
|
||||||
emitterNode->addChild(emitterPair.second);
|
emitterNode->addChild(emitterPair.second);
|
||||||
|
|
||||||
|
DisableOptimizer disableOptimizer;
|
||||||
|
emitterNode->accept(disableOptimizer);
|
||||||
}
|
}
|
||||||
mEmitterQueue.clear();
|
mEmitterQueue.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "particle.hpp"
|
#include "particle.hpp"
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <osg/Version>
|
#include <osg/Version>
|
||||||
#include <osg/MatrixTransform>
|
#include <osg/MatrixTransform>
|
||||||
|
@ -11,6 +12,7 @@
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
#include <components/nif/controlled.hpp>
|
#include <components/nif/controlled.hpp>
|
||||||
#include <components/nif/data.hpp>
|
#include <components/nif/data.hpp>
|
||||||
|
#include <components/nif/node.hpp>
|
||||||
#include <components/sceneutil/morphgeometry.hpp>
|
#include <components/sceneutil/morphgeometry.hpp>
|
||||||
#include <components/sceneutil/riggeometry.hpp>
|
#include <components/sceneutil/riggeometry.hpp>
|
||||||
|
|
||||||
|
@ -56,6 +58,44 @@ namespace
|
||||||
|
|
||||||
osg::Geometry* mGeometry;
|
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
|
namespace NifOsg
|
||||||
|
@ -310,7 +350,7 @@ void GravityAffector::operate(osgParticle::Particle *particle, double dt)
|
||||||
|
|
||||||
Emitter::Emitter()
|
Emitter::Emitter()
|
||||||
: osgParticle::Emitter()
|
: osgParticle::Emitter()
|
||||||
, mUseGeometryEmitter(false)
|
, mFlags(0)
|
||||||
, mGeometryEmitterTarget(std::nullopt)
|
, mGeometryEmitterTarget(std::nullopt)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -322,7 +362,7 @@ Emitter::Emitter(const Emitter ©, const osg::CopyOp ©op)
|
||||||
, mShooter(copy.mShooter)
|
, mShooter(copy.mShooter)
|
||||||
// need a deep copy because the remainder is stored in the object
|
// 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)))
|
, mCounter(static_cast<osgParticle::Counter*>(copy.mCounter->clone(osg::CopyOp::DEEP_COPY_ALL)))
|
||||||
, mUseGeometryEmitter(copy.mUseGeometryEmitter)
|
, mFlags(copy.mFlags)
|
||||||
, mGeometryEmitterTarget(copy.mGeometryEmitterTarget)
|
, mGeometryEmitterTarget(copy.mGeometryEmitterTarget)
|
||||||
, mCachedGeometryEmitter(copy.mCachedGeometryEmitter)
|
, mCachedGeometryEmitter(copy.mCachedGeometryEmitter)
|
||||||
{
|
{
|
||||||
|
@ -330,7 +370,7 @@ Emitter::Emitter(const Emitter ©, const osg::CopyOp ©op)
|
||||||
|
|
||||||
Emitter::Emitter(const std::vector<int> &targets)
|
Emitter::Emitter(const std::vector<int> &targets)
|
||||||
: mTargets(targets)
|
: mTargets(targets)
|
||||||
, mUseGeometryEmitter(false)
|
, mFlags(0)
|
||||||
, mGeometryEmitterTarget(std::nullopt)
|
, mGeometryEmitterTarget(std::nullopt)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -356,11 +396,13 @@ void Emitter::emitParticles(double dt)
|
||||||
|
|
||||||
osg::ref_ptr<osg::Vec3Array> geometryVertices = nullptr;
|
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;
|
int recIndex;
|
||||||
|
|
||||||
if (mUseGeometryEmitter)
|
if (useGeometryEmitter)
|
||||||
{
|
{
|
||||||
if (!mGeometryEmitterTarget.has_value())
|
if (!mGeometryEmitterTarget.has_value())
|
||||||
return;
|
return;
|
||||||
|
@ -383,7 +425,7 @@ void Emitter::emitParticles(double dt)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mUseGeometryEmitter)
|
if (useGeometryEmitter)
|
||||||
{
|
{
|
||||||
if (!mCachedGeometryEmitter.lock(geometryVertices))
|
if (!mCachedGeometryEmitter.lock(geometryVertices))
|
||||||
{
|
{
|
||||||
|
@ -403,12 +445,30 @@ void Emitter::emitParticles(double dt)
|
||||||
|
|
||||||
osg::NodePath path = visitor.mFoundPath;
|
osg::NodePath path = visitor.mFoundPath;
|
||||||
path.erase(path.begin());
|
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);
|
emitterToPs.orthoNormalize(emitterToPs);
|
||||||
|
|
||||||
if (mUseGeometryEmitter && (!geometryVertices.valid() || geometryVertices->empty()))
|
if (useGeometryEmitter && (!geometryVertices.valid() || geometryVertices->empty()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (int i=0; i<n; ++i)
|
for (int i=0; i<n; ++i)
|
||||||
|
@ -416,7 +476,7 @@ void Emitter::emitParticles(double dt)
|
||||||
osgParticle::Particle* P = getParticleSystem()->createParticle(nullptr);
|
osgParticle::Particle* P = getParticleSystem()->createParticle(nullptr);
|
||||||
if (P)
|
if (P)
|
||||||
{
|
{
|
||||||
if (mUseGeometryEmitter)
|
if (useGeometryEmitter)
|
||||||
P->setPosition((*geometryVertices)[Misc::Rng::rollDice(geometryVertices->getNumElements())]);
|
P->setPosition((*geometryVertices)[Misc::Rng::rollDice(geometryVertices->getNumElements())]);
|
||||||
else if (mPlacer)
|
else if (mPlacer)
|
||||||
mPlacer->place(P);
|
mPlacer->place(P);
|
||||||
|
|
|
@ -241,9 +241,8 @@ namespace NifOsg
|
||||||
void setShooter(osgParticle::Shooter* shooter) { mShooter = shooter; }
|
void setShooter(osgParticle::Shooter* shooter) { mShooter = shooter; }
|
||||||
void setPlacer(osgParticle::Placer* placer) { mPlacer = placer; }
|
void setPlacer(osgParticle::Placer* placer) { mPlacer = placer; }
|
||||||
void setCounter(osgParticle::Counter* counter) { mCounter = counter;}
|
void setCounter(osgParticle::Counter* counter) { mCounter = counter;}
|
||||||
|
|
||||||
void setUseGeometryEmitter(bool useGeometryEmitter) { mUseGeometryEmitter = useGeometryEmitter; }
|
|
||||||
void setGeometryEmitterTarget(std::optional<int> recIndex) { mGeometryEmitterTarget = recIndex; }
|
void setGeometryEmitterTarget(std::optional<int> recIndex) { mGeometryEmitterTarget = recIndex; }
|
||||||
|
void setFlags(int flags) { mFlags = flags; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// NIF Record indices
|
// NIF Record indices
|
||||||
|
@ -253,7 +252,8 @@ namespace NifOsg
|
||||||
osg::ref_ptr<osgParticle::Shooter> mShooter;
|
osg::ref_ptr<osgParticle::Shooter> mShooter;
|
||||||
osg::ref_ptr<osgParticle::Counter> mCounter;
|
osg::ref_ptr<osgParticle::Counter> mCounter;
|
||||||
|
|
||||||
bool mUseGeometryEmitter;
|
int mFlags;
|
||||||
|
|
||||||
std::optional<int> mGeometryEmitterTarget;
|
std::optional<int> mGeometryEmitterTarget;
|
||||||
osg::observer_ptr<osg::Vec3Array> mCachedGeometryEmitter;
|
osg::observer_ptr<osg::Vec3Array> mCachedGeometryEmitter;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue