mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-03-29 22:20:33 +00:00
Merge branch 'particle_vertex_palooza' into 'master'
Support vertex emitters (#6592) Closes #6592 See merge request OpenMW/openmw!1638
This commit is contained in:
commit
9183fa897e
@ -131,6 +131,7 @@
|
|||||||
Feature #6419: Topics shouldn't be greyed out if they can produce another topic reference
|
Feature #6419: Topics shouldn't be greyed out if they can produce another topic reference
|
||||||
Feature #6443: NiStencilProperty is not fully supported
|
Feature #6443: NiStencilProperty is not fully supported
|
||||||
Feature #6534: Shader-based object texture blending
|
Feature #6534: Shader-based object texture blending
|
||||||
|
Feature #6592: Missing support for NiTriShape particle emitters
|
||||||
Task #6201: Remove the "Note: No relevant classes found. No output generated" warnings
|
Task #6201: Remove the "Note: No relevant classes found. No output generated" warnings
|
||||||
Task #6264: Remove the old classes in animation.cpp
|
Task #6264: Remove the old classes in animation.cpp
|
||||||
Task #6553: Simplify interpreter instruction registration
|
Task #6553: Simplify interpreter instruction registration
|
||||||
|
@ -211,6 +211,9 @@ struct NiNode : Node
|
|||||||
enum ControllerFlags {
|
enum ControllerFlags {
|
||||||
ControllerFlag_Active = 0x8
|
ControllerFlag_Active = 0x8
|
||||||
};
|
};
|
||||||
|
enum BSPArrayController {
|
||||||
|
BSPArrayController_AtVertex = 0x10
|
||||||
|
};
|
||||||
|
|
||||||
void read(NIFStream *nif) override
|
void read(NIFStream *nif) override
|
||||||
{
|
{
|
||||||
|
@ -51,8 +51,7 @@ namespace
|
|||||||
|
|
||||||
void getAllNiNodes(const Nif::Node* node, std::vector<int>& outIndices)
|
void getAllNiNodes(const Nif::Node* node, std::vector<int>& outIndices)
|
||||||
{
|
{
|
||||||
const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(node);
|
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(node))
|
||||||
if (ninode)
|
|
||||||
{
|
{
|
||||||
outIndices.push_back(ninode->recIndex);
|
outIndices.push_back(ninode->recIndex);
|
||||||
for (unsigned int i=0; i<ninode->children.length(); ++i)
|
for (unsigned int i=0; i<ninode->children.length(); ++i)
|
||||||
@ -1000,7 +999,8 @@ namespace NifOsg
|
|||||||
osg::ref_ptr<Emitter> handleParticleEmitter(const Nif::NiParticleSystemController* partctrl)
|
osg::ref_ptr<Emitter> handleParticleEmitter(const Nif::NiParticleSystemController* partctrl)
|
||||||
{
|
{
|
||||||
std::vector<int> targets;
|
std::vector<int> targets;
|
||||||
if (partctrl->recType == Nif::RC_NiBSPArrayController)
|
const bool atVertex = (partctrl->flags & Nif::NiNode::BSPArrayController_AtVertex);
|
||||||
|
if (partctrl->recType == Nif::RC_NiBSPArrayController && !atVertex)
|
||||||
{
|
{
|
||||||
getAllNiNodes(partctrl->emitter.getPtr(), targets);
|
getAllNiNodes(partctrl->emitter.getPtr(), targets);
|
||||||
}
|
}
|
||||||
@ -1024,12 +1024,20 @@ namespace NifOsg
|
|||||||
partctrl->lifetime, partctrl->lifetimeRandom);
|
partctrl->lifetime, partctrl->lifetimeRandom);
|
||||||
emitter->setShooter(shooter);
|
emitter->setShooter(shooter);
|
||||||
|
|
||||||
osgParticle::BoxPlacer* placer = new osgParticle::BoxPlacer;
|
if (atVertex && (partctrl->recType == Nif::RC_NiBSPArrayController))
|
||||||
placer->setXRange(-partctrl->offsetRandom.x() / 2.f, partctrl->offsetRandom.x() / 2.f);
|
{
|
||||||
placer->setYRange(-partctrl->offsetRandom.y() / 2.f, partctrl->offsetRandom.y() / 2.f);
|
emitter->setUseGeometryEmitter(true);
|
||||||
placer->setZRange(-partctrl->offsetRandom.z() / 2.f, partctrl->offsetRandom.z() / 2.f);
|
emitter->setGeometryEmitterTarget(partctrl->emitter->recIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
osgParticle::BoxPlacer* placer = new osgParticle::BoxPlacer;
|
||||||
|
placer->setXRange(-partctrl->offsetRandom.x() / 2.f, partctrl->offsetRandom.x() / 2.f);
|
||||||
|
placer->setYRange(-partctrl->offsetRandom.y() / 2.f, partctrl->offsetRandom.y() / 2.f);
|
||||||
|
placer->setZRange(-partctrl->offsetRandom.z() / 2.f, partctrl->offsetRandom.z() / 2.f);
|
||||||
|
emitter->setPlacer(placer);
|
||||||
|
}
|
||||||
|
|
||||||
emitter->setPlacer(placer);
|
|
||||||
return emitter;
|
return emitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,52 @@
|
|||||||
#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/sceneutil/morphgeometry.hpp>
|
||||||
|
#include <components/sceneutil/riggeometry.hpp>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class FindFirstGeometry : public osg::NodeVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FindFirstGeometry()
|
||||||
|
: osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
|
||||||
|
, mGeometry(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply(osg::Node& node) override
|
||||||
|
{
|
||||||
|
if (mGeometry)
|
||||||
|
return;
|
||||||
|
|
||||||
|
traverse(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply(osg::Drawable& drawable) override
|
||||||
|
{
|
||||||
|
if (auto morph = dynamic_cast<SceneUtil::MorphGeometry*>(&drawable))
|
||||||
|
{
|
||||||
|
mGeometry = morph->getSourceGeometry();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (auto rig = dynamic_cast<SceneUtil::RigGeometry*>(&drawable))
|
||||||
|
{
|
||||||
|
mGeometry = rig->getSourceGeometry();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
traverse(drawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply(osg::Geometry& geometry) override
|
||||||
|
{
|
||||||
|
mGeometry = &geometry;
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::Geometry* mGeometry;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
namespace NifOsg
|
namespace NifOsg
|
||||||
{
|
{
|
||||||
@ -264,6 +310,8 @@ void GravityAffector::operate(osgParticle::Particle *particle, double dt)
|
|||||||
|
|
||||||
Emitter::Emitter()
|
Emitter::Emitter()
|
||||||
: osgParticle::Emitter()
|
: osgParticle::Emitter()
|
||||||
|
, mUseGeometryEmitter(false)
|
||||||
|
, mGeometryEmitterTarget(std::nullopt)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,29 +322,19 @@ 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)
|
||||||
|
, mGeometryEmitterTarget(copy.mGeometryEmitterTarget)
|
||||||
|
, mCachedGeometryEmitter(copy.mCachedGeometryEmitter)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Emitter::Emitter(const std::vector<int> &targets)
|
Emitter::Emitter(const std::vector<int> &targets)
|
||||||
: mTargets(targets)
|
: mTargets(targets)
|
||||||
|
, mUseGeometryEmitter(false)
|
||||||
|
, mGeometryEmitterTarget(std::nullopt)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
void Emitter::emitParticles(double dt)
|
||||||
{
|
{
|
||||||
int n = mCounter->numParticlesToCreate(dt);
|
int n = mCounter->numParticlesToCreate(dt);
|
||||||
@ -316,21 +354,53 @@ void Emitter::emitParticles(double dt)
|
|||||||
const osg::Matrix& ltw = getLocalToWorldMatrix();
|
const osg::Matrix& ltw = getLocalToWorldMatrix();
|
||||||
osg::Matrix emitterToPs = ltw * worldToPs;
|
osg::Matrix emitterToPs = ltw * worldToPs;
|
||||||
|
|
||||||
if (!mTargets.empty())
|
osg::ref_ptr<osg::Vec3Array> geometryVertices = nullptr;
|
||||||
|
|
||||||
|
if (mUseGeometryEmitter || !mTargets.empty())
|
||||||
{
|
{
|
||||||
int randomIndex = Misc::Rng::rollClosedProbability() * (mTargets.size() - 1);
|
int recIndex;
|
||||||
int randomRecIndex = mTargets[randomIndex];
|
|
||||||
|
if (mUseGeometryEmitter)
|
||||||
|
{
|
||||||
|
if (!mGeometryEmitterTarget.has_value())
|
||||||
|
return;
|
||||||
|
|
||||||
|
recIndex = mGeometryEmitterTarget.value();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int randomIndex = Misc::Rng::rollClosedProbability() * (mTargets.size() - 1);
|
||||||
|
recIndex = mTargets[randomIndex];
|
||||||
|
}
|
||||||
|
|
||||||
// we could use a map here for faster lookup
|
// we could use a map here for faster lookup
|
||||||
FindGroupByRecIndex visitor(randomRecIndex);
|
FindGroupByRecIndex visitor(recIndex);
|
||||||
getParent(0)->accept(visitor);
|
getParent(0)->accept(visitor);
|
||||||
|
|
||||||
if (!visitor.mFound)
|
if (!visitor.mFound)
|
||||||
{
|
{
|
||||||
Log(Debug::Info) << "Can't find emitter node" << randomRecIndex;
|
Log(Debug::Info) << "Can't find emitter node" << recIndex;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mUseGeometryEmitter)
|
||||||
|
{
|
||||||
|
if (!mCachedGeometryEmitter.lock(geometryVertices))
|
||||||
|
{
|
||||||
|
FindFirstGeometry geometryVisitor;
|
||||||
|
visitor.mFound->accept(geometryVisitor);
|
||||||
|
|
||||||
|
if (geometryVisitor.mGeometry)
|
||||||
|
{
|
||||||
|
if (auto* vertices = dynamic_cast<osg::Vec3Array*>(geometryVisitor.mGeometry->getVertexArray()))
|
||||||
|
{
|
||||||
|
mCachedGeometryEmitter = osg::observer_ptr<osg::Vec3Array>(vertices);
|
||||||
|
geometryVertices = vertices;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
osg::NodePath path = visitor.mFoundPath;
|
osg::NodePath path = visitor.mFoundPath;
|
||||||
path.erase(path.begin());
|
path.erase(path.begin());
|
||||||
emitterToPs = osg::computeLocalToWorld(path) * emitterToPs;
|
emitterToPs = osg::computeLocalToWorld(path) * emitterToPs;
|
||||||
@ -338,12 +408,18 @@ void Emitter::emitParticles(double dt)
|
|||||||
|
|
||||||
emitterToPs.orthoNormalize(emitterToPs);
|
emitterToPs.orthoNormalize(emitterToPs);
|
||||||
|
|
||||||
|
if (mUseGeometryEmitter && (!geometryVertices.valid() || geometryVertices->empty()))
|
||||||
|
return;
|
||||||
|
|
||||||
for (int i=0; i<n; ++i)
|
for (int i=0; i<n; ++i)
|
||||||
{
|
{
|
||||||
osgParticle::Particle* P = getParticleSystem()->createParticle(nullptr);
|
osgParticle::Particle* P = getParticleSystem()->createParticle(nullptr);
|
||||||
if (P)
|
if (P)
|
||||||
{
|
{
|
||||||
mPlacer->place(P);
|
if (mUseGeometryEmitter)
|
||||||
|
P->setPosition((*geometryVertices)[Misc::Rng::rollDice(geometryVertices->getNumElements())]);
|
||||||
|
else if (mPlacer)
|
||||||
|
mPlacer->place(P);
|
||||||
|
|
||||||
mShooter->shoot(P);
|
mShooter->shoot(P);
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#ifndef OPENMW_COMPONENTS_NIFOSG_PARTICLE_H
|
#ifndef OPENMW_COMPONENTS_NIFOSG_PARTICLE_H
|
||||||
#define OPENMW_COMPONENTS_NIFOSG_PARTICLE_H
|
#define OPENMW_COMPONENTS_NIFOSG_PARTICLE_H
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <osgParticle/Particle>
|
#include <osgParticle/Particle>
|
||||||
#include <osgParticle/Shooter>
|
#include <osgParticle/Shooter>
|
||||||
#include <osgParticle/Operator>
|
#include <osgParticle/Operator>
|
||||||
@ -233,9 +235,12 @@ namespace NifOsg
|
|||||||
|
|
||||||
void emitParticles(double dt) override;
|
void emitParticles(double dt) override;
|
||||||
|
|
||||||
void setShooter(osgParticle::Shooter* shooter);
|
void setShooter(osgParticle::Shooter* shooter) { mShooter = shooter; }
|
||||||
void setPlacer(osgParticle::Placer* placer);
|
void setPlacer(osgParticle::Placer* placer) { mPlacer = placer; }
|
||||||
void setCounter(osgParticle::Counter* counter);
|
void setCounter(osgParticle::Counter* counter) { mCounter = counter;}
|
||||||
|
|
||||||
|
void setUseGeometryEmitter(bool useGeometryEmitter) { mUseGeometryEmitter = useGeometryEmitter; }
|
||||||
|
void setGeometryEmitterTarget(std::optional<int> recIndex) { mGeometryEmitterTarget = recIndex; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// NIF Record indices
|
// NIF Record indices
|
||||||
@ -244,6 +249,10 @@ namespace NifOsg
|
|||||||
osg::ref_ptr<osgParticle::Placer> mPlacer;
|
osg::ref_ptr<osgParticle::Placer> mPlacer;
|
||||||
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;
|
||||||
|
std::optional<int> mGeometryEmitterTarget;
|
||||||
|
osg::observer_ptr<osg::Vec3Array> mCachedGeometryEmitter;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user