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:
parent
fd191b51ce
commit
5a1ed52d7c
@ -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;
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,12 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "particles.hxx"
|
||||
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
@ -42,53 +45,122 @@
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/Node>
|
||||
|
||||
#include "particles.hxx"
|
||||
|
||||
#include <simgear/scene/model/animation.hxx>
|
||||
|
||||
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] << "," <<w[1] << "," << w[2]);
|
||||
class ParticlesGlobalManager::ParticlesGlobalManagerPrivate : public osg::NodeCallback
|
||||
{
|
||||
public:
|
||||
ParticlesGlobalManagerPrivate() : _updater(new osgParticle::ParticleSystemUpdater),
|
||||
_commonGeode(new osg::Geode)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(osg::Node* node, osg::NodeVisitor* nv) override
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
//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)
|
||||
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>
|
||||
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<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,
|
||||
"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<osgParticle::ParticleSystem> 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<Particles> 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<osg::MatrixTransform> 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<CustomModularEmitter*>(emitter)==0) SG_LOG(SG_PARTICLES, SG_ALERT, "observer error\n");
|
||||
//align->addObserver(dynamic_cast<CustomModularEmitter*>(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<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;
|
||||
}
|
||||
|
||||
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<std::mutex> g(d->_lock);
|
||||
d->_enabledNode = n;
|
||||
}
|
||||
|
||||
void ParticlesGlobalManager::setFrozen(bool b)
|
||||
{
|
||||
std::lock_guard<std::mutex> g(d->_lock);
|
||||
d->_frozen = b;
|
||||
}
|
||||
|
||||
} // namespace simgear
|
||||
|
@ -1,5 +1,5 @@
|
||||
// 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
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
@ -53,133 +53,37 @@ class ParticleSystemUpdater;
|
||||
#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
|
||||
{
|
||||
|
||||
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<SGExpressiond> shooterValue;
|
||||
@ -286,38 +144,53 @@ protected:
|
||||
osg::ref_ptr<osgParticle::FluidProgram> program;
|
||||
osg::ref_ptr<osg::MatrixTransform> particleFrame;
|
||||
|
||||
bool useGravity;
|
||||
bool useWind;
|
||||
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;
|
||||
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<osg::Group> 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<ParticlesGlobalManagerPrivate> d;
|
||||
};
|
||||
*/
|
||||
|
||||
} // namespace simgear
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user