Particles: rewrite global manager for thread-safety

Avoid re-registering the global node callback each time a new particle
animation is loaded, and use mutexes to protected all shared data.
This commit is contained in:
James Turner 2020-08-16 12:34:01 +01:00 committed by Automatic Release Builder
parent fd191b51ce
commit 5a1ed52d7c
3 changed files with 382 additions and 313 deletions

View File

@ -490,7 +490,8 @@ sgLoad3DModel_internal(const SGPath& path,
} }
} }
if (GlobalParticleCallback::getEnabled()){//dbOptions->getPluginStringData("SimGear::PARTICLESYSTEM") != "OFF") { auto particlesManager = ParticlesGlobalManager::instance();
if (particlesManager->isEnabled()) { //dbOptions->getPluginStringData("SimGear::PARTICLESYSTEM") != "OFF") {
std::vector<SGPropertyNode_ptr> particle_nodes; std::vector<SGPropertyNode_ptr> particle_nodes;
particle_nodes = props->getChildren("particlesystem"); particle_nodes = props->getChildren("particlesystem");
for (unsigned i = 0; i < particle_nodes.size(); ++i) { for (unsigned i = 0; i < particle_nodes.size(); ++i) {
@ -503,7 +504,7 @@ sgLoad3DModel_internal(const SGPath& path,
options2->setDatabasePath(texturepath.utf8Str()); options2->setDatabasePath(texturepath.utf8Str());
} }
group->addChild(Particles::appendParticles(particle_nodes[i], group->addChild(particlesManager->appendParticles(particle_nodes[i],
prop_root, prop_root,
options2.get())); options2.get()));
} }

View File

@ -17,9 +17,12 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// //
#ifdef HAVE_CONFIG_H
#include <simgear_config.h> #include <simgear_config.h>
#endif
#include "particles.hxx"
#include <mutex>
#include <simgear/misc/sg_path.hxx> #include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx> #include <simgear/props/props.hxx>
@ -42,53 +45,122 @@
#include <osg/MatrixTransform> #include <osg/MatrixTransform>
#include <osg/Node> #include <osg/Node>
#include "particles.hxx"
#include <simgear/scene/model/animation.hxx>
namespace simgear namespace simgear
{ {
void GlobalParticleCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
enabled = !enabledNode || enabledNode->getBoolValue();
if (!enabled)
return;
SGQuatd q
= SGQuatd::fromLonLatDeg(modelRoot->getFloatValue("/position/longitude-deg",0),
modelRoot->getFloatValue("/position/latitude-deg",0));
osg::Matrix om(toOsg(q));
osg::Vec3 v(0,0,9.81);
gravity = om.preMult(v);
// NOTE: THIS WIND COMPUTATION DOESN'T SEEM TO AFFECT PARTICLES
const osg::Vec3& zUpWind = Particles::getWindVector();
osg::Vec3 w(zUpWind.y(), zUpWind.x(), -zUpWind.z());
wind = om.preMult(w);
// SG_LOG(SG_PARTICLES, SG_ALERT, class ParticlesGlobalManager::ParticlesGlobalManagerPrivate : public osg::NodeCallback
// "wind vector:" << w[0] << "," <<w[1] << "," << w[2]); {
public:
ParticlesGlobalManagerPrivate() : _updater(new osgParticle::ParticleSystemUpdater),
_commonGeode(new osg::Geode)
{
} }
void operator()(osg::Node* node, osg::NodeVisitor* nv) override
//static members
osg::Vec3 GlobalParticleCallback::gravity;
osg::Vec3 GlobalParticleCallback::wind;
bool GlobalParticleCallback::enabled = true;
SGConstPropertyNode_ptr GlobalParticleCallback::enabledNode = 0;
osg::ref_ptr<osg::Group> Particles::commonRoot;
osg::ref_ptr<osgParticle::ParticleSystemUpdater> Particles::psu = new osgParticle::ParticleSystemUpdater;
osg::ref_ptr<osg::Geode> Particles::commonGeode = new osg::Geode;
osg::Vec3 Particles::_wind;
bool Particles::_frozen = false;
Particles::Particles() :
useGravity(false),
useWind(false)
{ {
std::lock_guard<std::mutex> g(_lock);
_enabled = !_enabledNode || _enabledNode->getBoolValue();
if (!_enabled)
return;
const auto q = SGQuatd::fromLonLatDeg(_longitudeNode->getFloatValue(), _latitudeNode->getFloatValue());
osg::Matrix om(toOsg(q));
osg::Vec3 v(0, 0, 9.81);
_gravity = om.preMult(v);
// NOTE: THIS WIND COMPUTATION DOESN'T SEEM TO AFFECT PARTICLES
// const osg::Vec3& zUpWind = _wind;
// osg::Vec3 w(zUpWind.y(), zUpWind.x(), -zUpWind.z());
// _localWind = om.preMult(w);
}
// only call this with the lock held!
osg::Group* internalGetCommonRoot()
{
if (!_commonRoot.valid()) {
SG_LOG(SG_PARTICLES, SG_DEBUG, "Particle common root called.");
_commonRoot = new osg::Group;
_commonRoot->setName("common particle system root");
_commonGeode->setName("common particle system geode");
_commonRoot->addChild(_commonGeode);
_commonRoot->addChild(_updater);
_commonRoot->setNodeMask(~simgear::MODELLIGHT_BIT);
}
return _commonRoot.get();
}
std::mutex _lock;
bool _frozen = false;
osg::ref_ptr<osgParticle::ParticleSystemUpdater> _updater;
osg::ref_ptr<osg::Group> _commonRoot;
osg::ref_ptr<osg::Geode> _commonGeode;
osg::Vec3 _wind;
bool _globalCallbackRegistered = false;
bool _enabled = true;
osg::Vec3 _gravity;
// osg::Vec3 _localWind;
SGConstPropertyNode_ptr _enabledNode;
SGConstPropertyNode_ptr _longitudeNode, _latitudeNode;
};
static std::mutex static_managerLock;
static std::unique_ptr<ParticlesGlobalManager> static_instance;
ParticlesGlobalManager* ParticlesGlobalManager::instance()
{
std::lock_guard<std::mutex> g(static_managerLock);
if (!static_instance) {
static_instance.reset(new ParticlesGlobalManager);
}
return static_instance.get();
}
void ParticlesGlobalManager::clear()
{
std::lock_guard<std::mutex> g(static_managerLock);
static_instance.reset();
}
ParticlesGlobalManager::ParticlesGlobalManager() : d(new ParticlesGlobalManagerPrivate)
{
}
ParticlesGlobalManager::~ParticlesGlobalManager()
{
if (d->_globalCallbackRegistered) {
// is this actually necessary? possibly not
d->_updater->setUpdateCallback(nullptr);
}
}
bool ParticlesGlobalManager::isEnabled() const
{
std::lock_guard<std::mutex> g(d->_lock);
return d->_enabled;
}
bool ParticlesGlobalManager::isFrozen() const
{
std::lock_guard<std::mutex> g(d->_lock);
return d->_frozen;
}
osg::Vec3 ParticlesGlobalManager::getWindVector() const
{
std::lock_guard<std::mutex> g(d->_lock);
return d->_wind;
} }
template <typename Object> template <typename Object>
class PointerGuard{ class PointerGuard{
public: public:
PointerGuard() : _ptr(0) {}
Object* get() { return _ptr; } Object* get() { return _ptr; }
Object* operator () () Object* operator () ()
{ {
@ -97,24 +169,9 @@ public:
return _ptr; return _ptr;
} }
private: private:
Object* _ptr; Object* _ptr = nullptr;
}; };
osg::Group* Particles::getCommonRoot()
{
if(!commonRoot.valid())
{
SG_LOG(SG_PARTICLES, SG_DEBUG, "Particle common root called.");
commonRoot = new osg::Group;
commonRoot.get()->setName("common particle system root");
commonGeode.get()->setName("common particle system geode");
commonRoot.get()->addChild(commonGeode.get());
commonRoot.get()->addChild(psu.get());
commonRoot->setNodeMask( ~simgear::MODELLIGHT_BIT );
}
return commonRoot.get();
}
void transformParticles(osgParticle::ParticleSystem* particleSys, void transformParticles(osgParticle::ParticleSystem* particleSys,
const osg::Matrix& mat) const osg::Matrix& mat)
{ {
@ -129,10 +186,182 @@ void transformParticles(osgParticle::ParticleSystem* particleSys,
} }
} }
osg::Group * Particles::appendParticles(const SGPropertyNode* configNode, void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv)
SGPropertyNode* modelRoot, {
const osgDB::Options* auto globalManager = ParticlesGlobalManager::instance();
options)
//SG_LOG(SG_PARTICLES, SG_ALERT, "callback!\n");
particleSys->setFrozen(globalManager->isFrozen());
using namespace osg;
if (shooterValue)
shooter->setInitialSpeedRange(shooterValue->getValue(),
(shooterValue->getValue() + shooterExtraRange));
if (counterValue)
counter->setRateRange(counterValue->getValue(),
counterValue->getValue() + counterExtraRange);
else if (counterCond)
counter->setRateRange(counterStaticValue,
counterStaticValue + counterStaticExtraRange);
if (!globalManager->isEnabled() || (counterCond && !counterCond->test()))
counter->setRateRange(0, 0);
bool colorchange = false;
for (int i = 0; i < 8; ++i) {
if (colorComponents[i]) {
staticColorComponents[i] = colorComponents[i]->getValue();
colorchange = true;
}
}
if (colorchange)
particleSys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4(Vec4(staticColorComponents[0], staticColorComponents[1], staticColorComponents[2], staticColorComponents[3]), Vec4(staticColorComponents[4], staticColorComponents[5], staticColorComponents[6], staticColorComponents[7])));
if (startSizeValue)
startSize = startSizeValue->getValue();
if (endSizeValue)
endSize = endSizeValue->getValue();
if (startSizeValue || endSizeValue)
particleSys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(startSize, endSize));
if (lifeValue)
particleSys->getDefaultParticleTemplate().setLifeTime(lifeValue->getValue());
if (particleFrame.valid()) {
MatrixList mlist = node->getWorldMatrices();
if (!mlist.empty()) {
const Matrix& particleMat = particleFrame->getMatrix();
Vec3d emitOrigin(mlist[0](3, 0), mlist[0](3, 1), mlist[0](3, 2));
Vec3d displace = emitOrigin - Vec3d(particleMat(3, 0), particleMat(3, 1),
particleMat(3, 2));
if (displace * displace > 10000.0 * 10000.0) {
// Make new frame for particle system, coincident with
// the emitter frame, but oriented with local Z.
SGGeod geod = SGGeod::fromCart(toSG(emitOrigin));
Matrix newParticleMat = makeZUpFrame(geod);
Matrix changeParticleFrame = particleMat * Matrix::inverse(newParticleMat);
particleFrame->setMatrix(newParticleMat);
transformParticles(particleSys.get(), changeParticleFrame);
}
}
}
if (program.valid() && useWind)
program->setWind(globalManager->getWindVector());
}
void Particles::setupShooterSpeedData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
shooterValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if (!shooterValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: shooter property error!\n");
}
shooterExtraRange = configNode->getFloatValue("extrarange", 0);
}
void Particles::setupCounterData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
counterValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if (!counterValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "counter property error!\n");
}
counterExtraRange = configNode->getFloatValue("extrarange", 0);
}
void Particles::Particles::setupCounterCondition(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
counterCond = sgReadCondition(modelRoot, configNode);
}
void Particles::setupCounterCondition(float aCounterStaticValue,
float aCounterStaticExtraRange)
{
counterStaticValue = aCounterStaticValue;
counterStaticExtraRange = aCounterStaticExtraRange;
}
void Particles::setupStartSizeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
startSizeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if (!startSizeValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: startSizeValue error!\n");
}
}
void Particles::setupEndSizeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
endSizeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if (!endSizeValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: startSizeValue error!\n");
}
}
void Particles::setupLifeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
lifeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if (!lifeValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: lifeValue error!\n");
}
}
void Particles::setupColorComponent(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot, int color,
int component)
{
SGSharedPtr<SGExpressiond> colorValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(),
SGLimitsd::max());
if (!colorValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: color property error!\n");
}
colorComponents[(color * 4) + component] = colorValue;
//number of color components = 4
}
void Particles::setupStaticColorComponent(float r1, float g1, float b1, float a1,
float r2, float g2, float b2, float a2)
{
staticColorComponents[0] = r1;
staticColorComponents[1] = g1;
staticColorComponents[2] = b1;
staticColorComponents[3] = a1;
staticColorComponents[4] = r2;
staticColorComponents[5] = g2;
staticColorComponents[6] = b2;
staticColorComponents[7] = a2;
}
void ParticlesGlobalManager::setWindVector(const osg::Vec3& wind)
{
std::lock_guard<std::mutex> g(d->_lock);
d->_wind = wind;
}
void ParticlesGlobalManager::setWindFrom(const double from_deg, const double speed_kt)
{
double map_rad = -from_deg * SG_DEGREES_TO_RADIANS;
double speed_mps = speed_kt * SG_KT_TO_MPS;
std::lock_guard<std::mutex> g(d->_lock);
d->_wind[0] = cos(map_rad) * speed_mps;
d->_wind[1] = sin(map_rad) * speed_mps;
d->_wind[2] = 0.0;
}
osg::Group* ParticlesGlobalManager::getCommonRoot()
{
std::lock_guard<std::mutex> g(d->_lock);
return d->internalGetCommonRoot();
}
osg::ref_ptr<osg::Group> ParticlesGlobalManager::appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options)
{ {
SG_LOG(SG_PARTICLES, SG_DEBUG, SG_LOG(SG_PARTICLES, SG_DEBUG,
"Setting up a particle system." << std::boolalpha "Setting up a particle system." << std::boolalpha
@ -152,7 +381,7 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
<< "\n Wind: " << configNode->getChild("program")->getBoolValue("wind", true) << "\n Wind: " << configNode->getChild("program")->getBoolValue("wind", true)
<< std::noboolalpha); << std::noboolalpha);
osgParticle::ParticleSystem *particleSys; osg::ref_ptr<osgParticle::ParticleSystem> particleSys;
//create a generic particle system //create a generic particle system
std::string type = configNode->getStringValue("type", "normal"); std::string type = configNode->getStringValue("type", "normal");
@ -160,11 +389,10 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
particleSys = new osgParticle::ParticleSystem; particleSys = new osgParticle::ParticleSystem;
else else
particleSys = new osgParticle::ConnectedParticleSystem; particleSys = new osgParticle::ConnectedParticleSystem;
//may not be used depending on the configuration //may not be used depending on the configuration
PointerGuard<Particles> callback; PointerGuard<Particles> callback;
getPSU()->addParticleSystem(particleSys);
getPSU()->setUpdateCallback(new GlobalParticleCallback(modelRoot));
//contains counter, placer and shooter by default //contains counter, placer and shooter by default
osgParticle::ModularEmitter* emitter = new osgParticle::ModularEmitter; osgParticle::ModularEmitter* emitter = new osgParticle::ModularEmitter;
@ -172,7 +400,7 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
// Set up the alignment node ("stolen" from animation.cxx) // Set up the alignment node ("stolen" from animation.cxx)
// XXX Order of rotations is probably not correct. // XXX Order of rotations is probably not correct.
osg::MatrixTransform *align = new osg::MatrixTransform; osg::ref_ptr<osg::MatrixTransform> align = new osg::MatrixTransform;
osg::Matrix res_matrix; osg::Matrix res_matrix;
res_matrix.makeRotate( res_matrix.makeRotate(
configNode->getFloatValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS, configNode->getFloatValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
@ -187,12 +415,8 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
configNode->getFloatValue("offsets/y-m", 0.0), configNode->getFloatValue("offsets/y-m", 0.0),
configNode->getFloatValue("offsets/z-m", 0.0)); configNode->getFloatValue("offsets/z-m", 0.0));
align->setMatrix(res_matrix * tmat); align->setMatrix(res_matrix * tmat);
align->setName("particle align"); align->setName("particle align");
//if (dynamic_cast<CustomModularEmitter*>(emitter)==0) SG_LOG(SG_PARTICLES, SG_ALERT, "observer error\n");
//align->addObserver(dynamic_cast<CustomModularEmitter*>(emitter));
align->addChild(emitter); align->addChild(emitter);
//this name can be used in the XML animation as if it was a submodel //this name can be used in the XML animation as if it was a submodel
@ -209,7 +433,6 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
osg::Geode* g = new osg::Geode; osg::Geode* g = new osg::Geode;
g->addDrawable(particleSys); g->addDrawable(particleSys);
callback()->particleFrame->addChild(g); callback()->particleFrame->addChild(g);
getCommonRoot()->addChild(callback()->particleFrame.get());
} }
std::string textureFile; std::string textureFile;
if (configNode->hasValue("texture")) { if (configNode->hasValue("texture")) {
@ -505,66 +728,38 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
emitter->setUpdateCallback(callback.get()); emitter->setUpdateCallback(callback.get());
} }
// touch shared data now (and not before)
{
std::lock_guard<std::mutex> g(d->_lock);
d->_updater->addParticleSystem(particleSys);
if (attach != "local") {
d->internalGetCommonRoot()->addChild(callback()->particleFrame);
}
if (!d->_globalCallbackRegistered) {
SG_LOG(SG_PARTICLES, SG_INFO, "Registering global particles callback");
d->_globalCallbackRegistered = true;
d->_longitudeNode = modelRoot->getNode("/position/longitude-deg", true);
d->_latitudeNode = modelRoot->getNode("/position/latitude-deg", true);
d->_updater->setUpdateCallback(d.get());
}
}
return align; return align;
} }
void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv) void ParticlesGlobalManager::setSwitchNode(const SGPropertyNode* n)
{ {
//SG_LOG(SG_PARTICLES, SG_ALERT, "callback!\n"); std::lock_guard<std::mutex> g(d->_lock);
this->particleSys->setFrozen(_frozen); d->_enabledNode = n;
}
using namespace osg; void ParticlesGlobalManager::setFrozen(bool b)
if (shooterValue) {
shooter->setInitialSpeedRange(shooterValue->getValue(), std::lock_guard<std::mutex> g(d->_lock);
(shooterValue->getValue() d->_frozen = b;
+ shooterExtraRange));
if (counterValue)
counter->setRateRange(counterValue->getValue(),
counterValue->getValue() + counterExtraRange);
else if (counterCond)
counter->setRateRange(counterStaticValue,
counterStaticValue + counterStaticExtraRange);
if (!GlobalParticleCallback::getEnabled() || (counterCond && !counterCond->test()))
counter->setRateRange(0, 0);
bool colorchange=false;
for (int i = 0; i < 8; ++i) {
if (colorComponents[i]) {
staticColorComponents[i] = colorComponents[i]->getValue();
colorchange=true;
} }
}
if (colorchange)
particleSys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4( Vec4(staticColorComponents[0], staticColorComponents[1], staticColorComponents[2], staticColorComponents[3]), Vec4(staticColorComponents[4], staticColorComponents[5], staticColorComponents[6], staticColorComponents[7])));
if (startSizeValue)
startSize = startSizeValue->getValue();
if (endSizeValue)
endSize = endSizeValue->getValue();
if (startSizeValue || endSizeValue)
particleSys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(startSize, endSize));
if (lifeValue)
particleSys->getDefaultParticleTemplate().setLifeTime(lifeValue->getValue());
if (particleFrame.valid()) {
MatrixList mlist = node->getWorldMatrices();
if (!mlist.empty()) {
const Matrix& particleMat = particleFrame->getMatrix();
Vec3d emitOrigin(mlist[0](3, 0), mlist[0](3, 1), mlist[0](3, 2));
Vec3d displace
= emitOrigin - Vec3d(particleMat(3, 0), particleMat(3, 1),
particleMat(3, 2));
if (displace * displace > 10000.0 * 10000.0) {
// Make new frame for particle system, coincident with
// the emitter frame, but oriented with local Z.
SGGeod geod = SGGeod::fromCart(toSG(emitOrigin));
Matrix newParticleMat = makeZUpFrame(geod);
Matrix changeParticleFrame
= particleMat * Matrix::inverse(newParticleMat);
particleFrame->setMatrix(newParticleMat);
transformParticles(particleSys.get(), changeParticleFrame);
}
}
}
if (program.valid() && useWind)
program->setWind(_wind);
}
} // namespace simgear } // namespace simgear

View File

@ -1,5 +1,5 @@
// particles.hxx - classes to manage particles // particles.hxx - classes to manage particles
// Copyright (C) 2008 Tiago Gusmão // Copyright (C) 2008 Tiago Gusm<EFBFBD>o
// //
// This program is free software; you can redistribute it and/or // This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as // modify it under the terms of the GNU General Public License as
@ -53,133 +53,37 @@ class ParticleSystemUpdater;
#include <simgear/math/SGMatrix.hxx> #include <simgear/math/SGMatrix.hxx>
// Has anyone done anything *really* stupid, like making min and max macros?
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
#include "animation.hxx"
namespace simgear namespace simgear
{ {
class GlobalParticleCallback : public osg::NodeCallback class ParticlesGlobalManager;
{
public:
GlobalParticleCallback(const SGPropertyNode* modelRoot)
{
this->modelRoot=modelRoot;
}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
static const osg::Vec3 &getGravityVector()
{
return gravity;
}
static const osg::Vec3 &getWindVector()
{
return wind;
}
static void setSwitch(const SGPropertyNode* n)
{
enabledNode = n;
}
static bool getEnabled()
{
return enabled;
}
private:
static osg::Vec3 gravity;
static osg::Vec3 wind;
SGConstPropertyNode_ptr modelRoot;
static SGConstPropertyNode_ptr enabledNode;
static bool enabled;
};
class Particles : public osg::NodeCallback class Particles : public osg::NodeCallback
{ {
public: public:
Particles(); Particles() = default;
static osg::Group * appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options); void operator()(osg::Node* node, osg::NodeVisitor* nv) override;
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
void setupShooterSpeedData(const SGPropertyNode* configNode, void setupShooterSpeedData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) SGPropertyNode* modelRoot);
{
shooterValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!shooterValue){
SG_LOG(SG_GENERAL, SG_ALERT, "shooter property error!\n");
}
shooterExtraRange = configNode->getFloatValue("extrarange",0);
}
void setupCounterData(const SGPropertyNode* configNode, void setupCounterData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) SGPropertyNode* modelRoot);
{
counterValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!counterValue){
SG_LOG(SG_GENERAL, SG_ALERT, "counter property error!\n");
}
counterExtraRange = configNode->getFloatValue("extrarange",0);
}
void setupCounterCondition(const SGPropertyNode* configNode, void setupCounterCondition(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) SGPropertyNode* modelRoot);
{
counterCond = sgReadCondition(modelRoot, configNode);
}
void setupCounterCondition(float counterStaticValue, void setupCounterCondition(float counterStaticValue,
float counterStaticExtraRange) float counterStaticExtraRange);
{
this->counterStaticValue = counterStaticValue;
this->counterStaticExtraRange = counterStaticExtraRange;
}
void setupStartSizeData(const SGPropertyNode* configNode, void setupStartSizeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) SGPropertyNode* modelRoot);
{
startSizeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!startSizeValue)
{
SG_LOG(SG_GENERAL, SG_ALERT, "startSizeValue error!\n");
}
}
void setupEndSizeData(const SGPropertyNode* configNode, void setupEndSizeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) SGPropertyNode* modelRoot);
{
endSizeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!endSizeValue){
SG_LOG(SG_GENERAL, SG_ALERT, "startSizeValue error!\n");
}
}
void setupLifeData(const SGPropertyNode* configNode, void setupLifeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) SGPropertyNode* modelRoot);
{
lifeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!lifeValue){
SG_LOG(SG_GENERAL, SG_ALERT, "lifeValue error!\n");
}
}
void setupStaticSizeData(float startSize, float endSize) void setupStaticSizeData(float startSize, float endSize)
{ {
@ -212,60 +116,14 @@ public:
//component: r=0, g=1, ... ; color: 0=start color, 1=end color //component: r=0, g=1, ... ; color: 0=start color, 1=end color
void setupColorComponent(const SGPropertyNode* configNode, void setupColorComponent(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot, int color, SGPropertyNode* modelRoot, int color,
int component) int component);
{
SGExpressiond *colorValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(),
SGLimitsd::max());
if(!colorValue){
SG_LOG(SG_GENERAL, SG_ALERT, "color property error!\n");
}
colorComponents[(color*4)+component] = colorValue;
//number of color components = 4
}
void setupStaticColorComponent(float r1, float g1, float b1, float a1, void setupStaticColorComponent(float r1, float g1, float b1, float a1,
float r2, float g2, float b2, float a2) float r2, float g2, float b2, float a2);
{
staticColorComponents[0] = r1;
staticColorComponents[1] = g1;
staticColorComponents[2] = b1;
staticColorComponents[3] = a1;
staticColorComponents[4] = r2;
staticColorComponents[5] = g2;
staticColorComponents[6] = b2;
staticColorComponents[7] = a2;
}
static osg::Group * getCommonRoot();
static osg::Geode * getCommonGeode()
{
return commonGeode.get();
}
static osgParticle::ParticleSystemUpdater * getPSU()
{
return psu.get();
}
static void setFrozen(bool e) { _frozen = e; }
/**
* Set and get the wind vector for particles in the
* atmosphere. This vector is in the Z-up Y-north frame, and the
* magnitude is the velocity in meters per second.
*/
static void setWindVector(const osg::Vec3& wind) { _wind = wind; }
static void setWindFrom(const double from_deg, const double speed_kt) {
double map_rad = -from_deg * SG_DEGREES_TO_RADIANS;
double speed_mps = speed_kt * SG_KT_TO_MPS;
_wind[0] = cos(map_rad) * speed_mps;
_wind[1] = sin(map_rad) * speed_mps;
_wind[2] = 0.0;
}
static const osg::Vec3& getWindVector() { return _wind; }
protected: protected:
friend class ParticlesGlobalManager;
float shooterExtraRange; float shooterExtraRange;
float counterExtraRange; float counterExtraRange;
SGSharedPtr<SGExpressiond> shooterValue; SGSharedPtr<SGExpressiond> shooterValue;
@ -286,38 +144,53 @@ protected:
osg::ref_ptr<osgParticle::FluidProgram> program; osg::ref_ptr<osgParticle::FluidProgram> program;
osg::ref_ptr<osg::MatrixTransform> particleFrame; osg::ref_ptr<osg::MatrixTransform> particleFrame;
bool useGravity; bool useGravity = false;
bool useWind; bool useWind = false;
static bool _frozen;
static osg::ref_ptr<osgParticle::ParticleSystemUpdater> psu;
static osg::ref_ptr<osg::Group> commonRoot;
static osg::ref_ptr<osg::Geode> commonGeode;
static osg::Vec3 _wind;
}; };
}
#endif
class ParticlesGlobalManager
/*
class CustomModularEmitter : public osgParticle::ModularEmitter, public osg::Observer
{ {
public: public:
~ParticlesGlobalManager();
virtual void objectDeleted (void *) static ParticlesGlobalManager* instance();
{ static void clear();
SG_LOG(SG_GENERAL, SG_ALERT, "object deleted!\n");
SGParticles::getCommonRoot()->removeChild(this->getParticleSystem()->getParent(0)); bool isEnabled() const;
SGParticles::getPSU()->removeParticleSystem(this->getParticleSystem());
}
osg::ref_ptr<osg::Group> appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options);
// ~CustomModularEmitter() osg::Group* getCommonRoot();
// {
// SG_LOG(SG_GENERAL, SG_ALERT, "object deleted!\n"); osg::Geode* getCommonGeode();
//
// SGParticles::getCommonRoot()->removeChild(this->getParticleSystem()->getParent(0)); osgParticle::ParticleSystemUpdater* getPSU();
// SGParticles::getPSU()->removeParticleSystem(this->getParticleSystem());
// } void setFrozen(bool e);
}; bool isFrozen() const;
void setSwitchNode(const SGPropertyNode* n);
/**
* Set and get the wind vector for particles in the
* atmosphere. This vector is in the Z-up Y-north frame, and the
* magnitude is the velocity in meters per second.
*/ */
void setWindVector(const osg::Vec3& wind);
void setWindFrom(const double from_deg, const double speed_kt);
osg::Vec3 getWindVector() const;
private:
ParticlesGlobalManager();
class ParticlesGlobalManagerPrivate;
// because Private inherits NodeCallback, we need to own it
// via an osg::ref_ptr
osg::ref_ptr<ParticlesGlobalManagerPrivate> d;
};
} // namespace simgear
#endif