From 5a1ed52d7c79813a74d7900f52abafbab858bb64 Mon Sep 17 00:00:00 2001 From: James Turner Date: Sun, 16 Aug 2020 12:34:01 +0100 Subject: [PATCH] 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. --- simgear/scene/model/SGReaderWriterXML.cxx | 9 +- simgear/scene/model/particles.cxx | 443 ++++++++++++++++------ simgear/scene/model/particles.hxx | 243 +++--------- 3 files changed, 382 insertions(+), 313 deletions(-) diff --git a/simgear/scene/model/SGReaderWriterXML.cxx b/simgear/scene/model/SGReaderWriterXML.cxx index 91f5bd11..53d71583 100644 --- a/simgear/scene/model/SGReaderWriterXML.cxx +++ b/simgear/scene/model/SGReaderWriterXML.cxx @@ -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 particle_nodes; particle_nodes = props->getChildren("particlesystem"); for (unsigned i = 0; i < particle_nodes.size(); ++i) { @@ -503,9 +504,9 @@ sgLoad3DModel_internal(const SGPath& path, options2->setDatabasePath(texturepath.utf8Str()); } - group->addChild(Particles::appendParticles(particle_nodes[i], - prop_root, - options2.get())); + group->addChild(particlesManager->appendParticles(particle_nodes[i], + prop_root, + options2.get())); } } diff --git a/simgear/scene/model/particles.cxx b/simgear/scene/model/particles.cxx index 1b055127..33616150 100644 --- a/simgear/scene/model/particles.cxx +++ b/simgear/scene/model/particles.cxx @@ -17,9 +17,12 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // -#ifdef HAVE_CONFIG_H -# include -#endif +#include + +#include "particles.hxx" + + +#include #include #include @@ -42,53 +45,122 @@ #include #include -#include "particles.hxx" + +#include 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, - // "wind vector:" << w[0] << "," < 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 _updater; + osg::ref_ptr _commonRoot; + osg::ref_ptr _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 static_instance; + +ParticlesGlobalManager* ParticlesGlobalManager::instance() +{ + std::lock_guard g(static_managerLock); + if (!static_instance) { + static_instance.reset(new ParticlesGlobalManager); + } + + return static_instance.get(); } - -//static members -osg::Vec3 GlobalParticleCallback::gravity; -osg::Vec3 GlobalParticleCallback::wind; -bool GlobalParticleCallback::enabled = true; -SGConstPropertyNode_ptr GlobalParticleCallback::enabledNode = 0; - -osg::ref_ptr Particles::commonRoot; -osg::ref_ptr Particles::psu = new osgParticle::ParticleSystemUpdater; -osg::ref_ptr Particles::commonGeode = new osg::Geode; -osg::Vec3 Particles::_wind; -bool Particles::_frozen = false; - -Particles::Particles() : - useGravity(false), - useWind(false) +void ParticlesGlobalManager::clear() { + std::lock_guard 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 g(d->_lock); + return d->_enabled; +} + +bool ParticlesGlobalManager::isFrozen() const +{ + std::lock_guard g(d->_lock); + return d->_frozen; +} + +osg::Vec3 ParticlesGlobalManager::getWindVector() const +{ + std::lock_guard g(d->_lock); + return d->_wind; } template class PointerGuard{ public: - PointerGuard() : _ptr(0) {} Object* get() { return _ptr; } Object* operator () () { @@ -97,24 +169,9 @@ public: return _ptr; } 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, const osg::Matrix& mat) { @@ -129,10 +186,182 @@ void transformParticles(osgParticle::ParticleSystem* particleSys, } } -osg::Group * Particles::appendParticles(const SGPropertyNode* configNode, - SGPropertyNode* modelRoot, - const osgDB::Options* - options) +void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv) +{ + auto globalManager = ParticlesGlobalManager::instance(); + + //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 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 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 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 g(d->_lock); + return d->internalGetCommonRoot(); +} + +osg::ref_ptr ParticlesGlobalManager::appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options) { SG_LOG(SG_PARTICLES, SG_DEBUG, "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) << std::noboolalpha); - osgParticle::ParticleSystem *particleSys; + osg::ref_ptr particleSys; //create a generic particle system std::string type = configNode->getStringValue("type", "normal"); @@ -160,11 +389,10 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode, particleSys = new osgParticle::ParticleSystem; else particleSys = new osgParticle::ConnectedParticleSystem; + //may not be used depending on the configuration PointerGuard callback; - getPSU()->addParticleSystem(particleSys); - getPSU()->setUpdateCallback(new GlobalParticleCallback(modelRoot)); //contains counter, placer and shooter by default 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) // XXX Order of rotations is probably not correct. - osg::MatrixTransform *align = new osg::MatrixTransform; + osg::ref_ptr align = new osg::MatrixTransform; osg::Matrix res_matrix; res_matrix.makeRotate( 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/z-m", 0.0)); align->setMatrix(res_matrix * tmat); - align->setName("particle align"); - //if (dynamic_cast(emitter)==0) SG_LOG(SG_PARTICLES, SG_ALERT, "observer error\n"); - //align->addObserver(dynamic_cast(emitter)); - align->addChild(emitter); //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; g->addDrawable(particleSys); callback()->particleFrame->addChild(g); - getCommonRoot()->addChild(callback()->particleFrame.get()); } std::string textureFile; if (configNode->hasValue("texture")) { @@ -505,66 +728,38 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode, emitter->setUpdateCallback(callback.get()); } + // touch shared data now (and not before) + + { + std::lock_guard 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; } -void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv) +void ParticlesGlobalManager::setSwitchNode(const SGPropertyNode* n) { - //SG_LOG(SG_PARTICLES, SG_ALERT, "callback!\n"); - this->particleSys->setFrozen(_frozen); - - 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 (!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); + std::lock_guard g(d->_lock); + d->_enabledNode = n; } + +void ParticlesGlobalManager::setFrozen(bool b) +{ + std::lock_guard g(d->_lock); + d->_frozen = b; +} + } // namespace simgear diff --git a/simgear/scene/model/particles.hxx b/simgear/scene/model/particles.hxx index cdbb1f21..ee928ff7 100644 --- a/simgear/scene/model/particles.hxx +++ b/simgear/scene/model/particles.hxx @@ -1,5 +1,5 @@ // particles.hxx - classes to manage particles -// Copyright (C) 2008 Tiago Gusmão +// Copyright (C) 2008 Tiago Gusm�o // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -53,133 +53,37 @@ class ParticleSystemUpdater; #include -// 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 { -class GlobalParticleCallback : public osg::NodeCallback -{ -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 ParticlesGlobalManager; class Particles : public osg::NodeCallback { public: - Particles(); + Particles() = default; - static osg::Group * appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options); - - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + void operator()(osg::Node* node, osg::NodeVisitor* nv) override; void setupShooterSpeedData(const SGPropertyNode* configNode, - 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); - } + SGPropertyNode* modelRoot); void setupCounterData(const SGPropertyNode* configNode, - 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); - } + SGPropertyNode* modelRoot); void setupCounterCondition(const SGPropertyNode* configNode, - SGPropertyNode* modelRoot) - { - counterCond = sgReadCondition(modelRoot, configNode); - } - + SGPropertyNode* modelRoot); void setupCounterCondition(float counterStaticValue, - float counterStaticExtraRange) - { - this->counterStaticValue = counterStaticValue; - this->counterStaticExtraRange = counterStaticExtraRange; - } + float counterStaticExtraRange); void setupStartSizeData(const SGPropertyNode* configNode, - SGPropertyNode* modelRoot) - { - startSizeValue = read_value(configNode, modelRoot, "-m", - -SGLimitsd::max(), SGLimitsd::max()); - if(!startSizeValue) - { - SG_LOG(SG_GENERAL, SG_ALERT, "startSizeValue error!\n"); - } - } + SGPropertyNode* modelRoot); void setupEndSizeData(const SGPropertyNode* configNode, - SGPropertyNode* modelRoot) - { - endSizeValue = read_value(configNode, modelRoot, "-m", - -SGLimitsd::max(), SGLimitsd::max()); - if(!endSizeValue){ - SG_LOG(SG_GENERAL, SG_ALERT, "startSizeValue error!\n"); - } - } + SGPropertyNode* modelRoot); void setupLifeData(const SGPropertyNode* configNode, - SGPropertyNode* modelRoot) - { - lifeValue = read_value(configNode, modelRoot, "-m", - -SGLimitsd::max(), SGLimitsd::max()); - if(!lifeValue){ - SG_LOG(SG_GENERAL, SG_ALERT, "lifeValue error!\n"); - } - } + SGPropertyNode* modelRoot); void setupStaticSizeData(float startSize, float endSize) { @@ -212,60 +116,14 @@ public: //component: r=0, g=1, ... ; color: 0=start color, 1=end color void setupColorComponent(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, int color, - 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 - } + int component); - void 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 setupStaticColorComponent(float r1, float g1, float b1, float a1, + float r2, float g2, float b2, float 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: + friend class ParticlesGlobalManager; + float shooterExtraRange; float counterExtraRange; SGSharedPtr shooterValue; @@ -285,39 +143,54 @@ protected: osg::ref_ptr particleSys; osg::ref_ptr program; osg::ref_ptr particleFrame; - - bool useGravity; - bool useWind; - static bool _frozen; - static osg::ref_ptr psu; - static osg::ref_ptr commonRoot; - static osg::ref_ptr commonGeode; - static osg::Vec3 _wind; + + bool useGravity = false; + bool useWind = false; }; -} -#endif - -/* -class CustomModularEmitter : public osgParticle::ModularEmitter, public osg::Observer +class ParticlesGlobalManager { public: + ~ParticlesGlobalManager(); -virtual void objectDeleted (void *) -{ -SG_LOG(SG_GENERAL, SG_ALERT, "object deleted!\n"); + static ParticlesGlobalManager* instance(); + static void clear(); -SGParticles::getCommonRoot()->removeChild(this->getParticleSystem()->getParent(0)); -SGParticles::getPSU()->removeParticleSystem(this->getParticleSystem()); -} + bool isEnabled() const; + osg::ref_ptr appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options); -// ~CustomModularEmitter() -// { -// SG_LOG(SG_GENERAL, SG_ALERT, "object deleted!\n"); -// -// SGParticles::getCommonRoot()->removeChild(this->getParticleSystem()->getParent(0)); -// SGParticles::getPSU()->removeParticleSystem(this->getParticleSystem()); -// } + osg::Group* getCommonRoot(); + + osg::Geode* getCommonGeode(); + + osgParticle::ParticleSystemUpdater* getPSU(); + + 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 d; }; -*/ + +} // namespace simgear + +#endif