// particles.cxx - classes to manage particles
// started in 2008 by Tiago Gusmão, using animation.hxx as reference
+// 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
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#include "particles.hxx"
-//static members
-osg::Vec3 SGGlobalParticleCallback::gravity;
-osg::Vec3 SGGlobalParticleCallback::wind;
+namespace simgear
+{
+void GlobalParticleCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
+{
+ SGQuatd q
+ = SGQuatd::fromLonLatDeg(modelRoot->getFloatValue("/position/longitude-deg",0),
+ modelRoot->getFloatValue("/position/latitude-deg",0));
+ osg::Matrix om(q.osg());
+ osg::Vec3 v(0,0,9.81);
+ gravity = om.preMult(v);
+
+ osg::Vec3 w(-modelRoot->getFloatValue("/environment/wind-from-north-fps",0) * SG_FEET_TO_METER,
+ -modelRoot->getFloatValue("/environment/wind-from-east-fps",0) * SG_FEET_TO_METER, 0);
+ wind = om.preMult(w);
+
+ //SG_LOG(SG_GENERAL, SG_ALERT, "wind vector:"<<w[0]<<","<<w[1]<<","<<w[2]<<"\n");
+}
-osg::ref_ptr<osg::Group> SGParticles::commonRoot;
-osg::ref_ptr<osgParticle::ParticleSystemUpdater> SGParticles::psu = new osgParticle::ParticleSystemUpdater;
-osg::ref_ptr<osg::Geode> SGParticles::commonGeode = new osg::Geode;;
-osg::Group * SGParticles::appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::ReaderWriter::Options* options)
+//static members
+osg::Vec3 GlobalParticleCallback::gravity;
+osg::Vec3 GlobalParticleCallback::wind;
+
+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;;
+
+template <typename Object>
+class PointerGuard{
+public:
+ PointerGuard() : _ptr(0) {}
+ Object* get() { return _ptr; }
+ Object* operator () ()
+ {
+ if (!_ptr)
+ _ptr = new Object;
+ return _ptr;
+ }
+private:
+ Object* _ptr;
+};
+
+osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
+ SGPropertyNode* modelRoot,
+ const osgDB::ReaderWriter::Options*
+ options)
{
SG_LOG(SG_GENERAL, SG_DEBUG, "Setting up a particle system!\n");
//create a generic particle system
std::string type = configNode->getStringValue("type", "normal");
- if (type == "normal")particleSys= new osgParticle::ParticleSystem;
- else particleSys= new osgParticle::ConnectedParticleSystem;
- SGParticles *callback=NULL; //may not be used depending on the configuration
+ if (type == "normal")
+ 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 SGGlobalParticleCallback(modelRoot));
-
+ getPSU()->setUpdateCallback(new GlobalParticleCallback(modelRoot));
//contains counter, placer and shooter by default
- osgParticle::ModularEmitter *emitter = new osgParticle::ModularEmitter;
+ osgParticle::ModularEmitter* emitter = new osgParticle::ModularEmitter;
emitter->setParticleSystem(particleSys);
// Set up the alignment node ("stolen" from animation.cxx)
+ // XXX Order of rotations is probably not correct.
osg::MatrixTransform *align = new osg::MatrixTransform;
osg::Matrix res_matrix;
res_matrix.makeRotate(
osg::Matrix tmat;
tmat.makeTranslate(configNode->getFloatValue("offsets/x-m", 0.0),
- configNode->getFloatValue("offsets/y-m", 0.0),
- configNode->getFloatValue("offsets/z-m", 0.0));
- align->setMatrix(res_matrix*tmat);
+ 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_GENERAL, SG_ALERT, "observer error\n");
+ //if (dynamic_cast<CustomModularEmitter*>(emitter)==0) SG_LOG(SG_GENERAL, 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
std::string name = configNode->getStringValue("name", "");
- if(!name.empty()) align->setName(name);
-
+ if (!name.empty())
+ align->setName(name);
std::string attach = configNode->getStringValue("attach", "world");
-
if (attach == "local") { //local means attached to the model and not the world
- osg::Geode *g = new osg::Geode;
+ osg::Geode* g = new osg::Geode;
align->addChild(g);
g->addDrawable(particleSys);
emitter->setReferenceFrame(osgParticle::Emitter::ABSOLUTE_RF);
- } else
+ } else {
getCommonGeode()->addDrawable(particleSys);
-
+ }
std::string textureFile;
-
- if(configNode->hasValue("texture")) {
- SG_LOG(SG_GENERAL, SG_ALERT, "requested:"<<configNode->getStringValue("texture","")<<"\n");
- textureFile= osgDB::findFileInPath(configNode->getStringValue("texture",""),
- options->getDatabasePathList());
+ if (configNode->hasValue("texture")) {
+ SG_LOG(SG_GENERAL, SG_ALERT,
+ "requested:"<<configNode->getStringValue("texture","")<<"\n");
+ textureFile= osgDB::findFileInPath(configNode->getStringValue("texture",
+ ""),
+ options->getDatabasePathList());
SG_LOG(SG_GENERAL, SG_ALERT, "found:"<<textureFile<<"\n");
- for(int i=0;i<options->getDatabasePathList().size();++i)
- SG_LOG(SG_GENERAL, SG_ALERT, "opts:"<<options->getDatabasePathList()[i]<<"\n");
+ for(int i = 0; i < options->getDatabasePathList().size(); ++i)
+ SG_LOG(SG_GENERAL, SG_ALERT,
+ "opts:"<<options->getDatabasePathList()[i]<<"\n");
}
- if(textureFile.empty())
+ if (textureFile.empty())
textureFile="";
- particleSys->setDefaultAttributes(textureFile,configNode->getBoolValue("emissive", true),
- configNode->getBoolValue("lighting", false));
+ particleSys->setDefaultAttributes(textureFile,
+ configNode->getBoolValue("emissive",
+ true),
+ configNode->getBoolValue("lighting",
+ false));
- std::string alignstr = configNode->getStringValue("align","billboard");
+ std::string alignstr = configNode->getStringValue("align", "billboard");
- if(alignstr == "fixed")
+ if (alignstr == "fixed")
particleSys->setParticleAlignment(osgParticle::ParticleSystem::FIXED);
const SGPropertyNode* placernode = configNode->getChild("placer");
- if(placernode) {
+ if (placernode) {
std::string emitterType = placernode->getStringValue("type", "point");
if (emitterType == "sector") {
minRadius = placernode->getFloatValue("radius-min-m",0);
maxRadius = placernode->getFloatValue("radius-max-m",1);
- minPhi = placernode->getFloatValue("phi-min-deg",0) * SG_DEGREES_TO_RADIANS;
- maxPhi = placernode->getFloatValue("phi-max-deg",360.0f) * SG_DEGREES_TO_RADIANS;
+ minPhi = (placernode->getFloatValue("phi-min-deg",0)
+ * SG_DEGREES_TO_RADIANS);
+ maxPhi = (placernode->getFloatValue("phi-max-deg",360.0f)
+ * SG_DEGREES_TO_RADIANS);
splacer->setRadiusRange(minRadius, maxRadius);
splacer->setPhiRange(minPhi, maxPhi);
emitter->setPlacer(splacer);
} else if (emitterType == "segments") {
- std::vector<SGPropertyNode_ptr> segments = placernode->getChildren("vertex");
-
- if(segments.size()>1) {
- osgParticle::MultiSegmentPlacer *msplacer = new osgParticle::MultiSegmentPlacer();
+ std::vector<SGPropertyNode_ptr> segments
+ = placernode->getChildren("vertex");
+ if (segments.size()>1) {
+ osgParticle::MultiSegmentPlacer *msplacer
+ = new osgParticle::MultiSegmentPlacer();
float x,y,z;
for (unsigned i = 0; i < segments.size(); ++i) {
z = segments[i]->getFloatValue("z-m",0);
msplacer->addVertex(x, y, z);
}
-
emitter->setPlacer(msplacer);
+ } else {
+ SG_LOG(SG_GENERAL, SG_ALERT,
+ "Detected particle system using segment(s) with less than 2 vertices\n");
}
- else SG_LOG(SG_GENERAL, SG_ALERT, "Detected particle system using segment(s) with less than 2 vertices\n");
} //else the default placer in ModularEmitter is used (PointPlacer)
}
const SGPropertyNode* shnode = configNode->getChild("shooter");
- if(shnode) {
+ if (shnode) {
float minTheta, maxTheta, minPhi, maxPhi, speed, spread;
- minTheta = shnode->getFloatValue("theta-min-deg",0) * SG_DEGREES_TO_RADIANS;
- maxTheta = shnode->getFloatValue("theta-max-deg",360.0f) * SG_DEGREES_TO_RADIANS;
+ minTheta = (shnode->getFloatValue("theta-min-deg",0)
+ * SG_DEGREES_TO_RADIANS);
+ maxTheta = (shnode->getFloatValue("theta-max-deg",360.0f)
+ * SG_DEGREES_TO_RADIANS);
minPhi = shnode->getFloatValue("phi-min-deg",0)* SG_DEGREES_TO_RADIANS;
- maxPhi = shnode->getFloatValue("phi-max-deg",360.0f)* SG_DEGREES_TO_RADIANS;
+ maxPhi = (shnode->getFloatValue("phi-max-deg",360.0f)
+ * SG_DEGREES_TO_RADIANS);
osgParticle::RadialShooter *shooter = new osgParticle::RadialShooter;
emitter->setShooter(shooter);
const SGPropertyNode* speednode = shnode->getChild("speed");
- if(speednode) {
- if(speednode->hasValue("value")) {
+ if (speednode) {
+ if (speednode->hasValue("value")) {
speed = speednode->getFloatValue("value",0);
spread = speednode->getFloatValue("spread",0);
shooter->setInitialSpeedRange(speed-spread, speed+spread);
} else {
-
- if(!callback)
- callback = new SGParticles();
-
- callback->setupShooterSpeedData(speednode, modelRoot);
+ callback()->setupShooterSpeedData(speednode, modelRoot);
}
}
const SGPropertyNode* rotspeednode = shnode->getChild("rotspeed");
- if(rotspeednode) {
+ if (rotspeednode) {
float x1,y1,z1,x2,y2,z2;
x1 = rotspeednode->getFloatValue("x-min-deg-sec",0) * SG_DEGREES_TO_RADIANS;
y1 = rotspeednode->getFloatValue("y-min-deg-sec",0) * SG_DEGREES_TO_RADIANS;
const SGPropertyNode* counternode = configNode->getChild("counter");
- if(counternode) {
- osgParticle::RandomRateCounter *counter = new osgParticle::RandomRateCounter;
+ if (counternode) {
+ osgParticle::RandomRateCounter* counter
+ = new osgParticle::RandomRateCounter;
emitter->setCounter(counter);
float pps, spread;
const SGPropertyNode* ppsnode = counternode->getChild("pps");
- if(ppsnode) {
+ if (ppsnode) {
- if(ppsnode->hasValue("value")) {
+ if (ppsnode->hasValue("value")) {
pps = ppsnode->getFloatValue("value",0);
spread = ppsnode->getFloatValue("spread",0);
counter->setRateRange(pps-spread, pps+spread);
} else {
-
- if(!callback)
- callback = new SGParticles();
-
- callback->setupCounterData(ppsnode, modelRoot);
+ callback()->setupCounterData(ppsnode, modelRoot);
}
}
- const SGPropertyNode* conditionNode = counternode->getChild("condition");
-
- if(conditionNode) {
-
- if(!callback)
- callback = new SGParticles();
- callback->setupCounterCondition(conditionNode, modelRoot);
- callback->setupCounterCondition(pps, spread);
+ const SGPropertyNode* conditionNode
+ = counternode->getChild("condition");
+ if (conditionNode) {
+ callback()->setupCounterCondition(conditionNode, modelRoot);
+ callback()->setupCounterCondition(pps, spread);
}
} //TODO: else perhaps set higher values than default?
const SGPropertyNode* particlenode = configNode->getChild("particle");
-
- if(particlenode) {
- osgParticle::Particle &particle = particleSys->getDefaultParticleTemplate();
+ if (particlenode) {
+ osgParticle::Particle &particle
+ = particleSys->getDefaultParticleTemplate();
float r1=0, g1=0, b1=0, a1=1, r2=0, g2=0, b2=0, a2=1;
-
- const SGPropertyNode* startcolornode = particlenode->getChild("startcolor");
-
- if(startcolornode) {
- const SGPropertyNode* componentnode = startcolornode->getChild("red");
-
- if(componentnode) {
-
- if(componentnode->hasValue("value"))
+ const SGPropertyNode* startcolornode
+ = particlenode->getChild("startcolor");
+ if (startcolornode) {
+ const SGPropertyNode* componentnode
+ = startcolornode->getChild("red");
+ if (componentnode) {
+ if (componentnode->hasValue("value"))
r1 = componentnode->getFloatValue("value",0);
- else {
-
- if(!callback)
- callback = new SGParticles();
-
- callback->setupColorComponent(componentnode, modelRoot, 0, 0);
- }
+ else
+ callback()->setupColorComponent(componentnode, modelRoot,
+ 0, 0);
}
-
componentnode = startcolornode->getChild("green");
- if(componentnode) {
-
- if(componentnode->hasValue("value"))
- g1 = componentnode->getFloatValue("value",0);
-
- else {
-
- if(!callback)
- callback = new SGParticles();
-
- callback->setupColorComponent(componentnode, modelRoot, 0, 1);
- }
+ if (componentnode) {
+ if (componentnode->hasValue("value"))
+ g1 = componentnode->getFloatValue("value", 0);
+ else
+ callback()->setupColorComponent(componentnode, modelRoot,
+ 0, 1);
}
-
componentnode = startcolornode->getChild("blue");
- if(componentnode) {
-
- if(componentnode->hasValue("value"))
+ if (componentnode) {
+ if (componentnode->hasValue("value"))
b1 = componentnode->getFloatValue("value",0);
- else {
-
- if(!callback)
- callback = new SGParticles();
-
- callback->setupColorComponent(componentnode, modelRoot, 0, 2);
- }
+ else
+ callback()->setupColorComponent(componentnode, modelRoot,
+ 0, 2);
}
-
componentnode = startcolornode->getChild("alpha");
-
- if(componentnode) {
-
- if(componentnode->hasValue("value"))
+ if (componentnode) {
+ if (componentnode->hasValue("value"))
a1 = componentnode->getFloatValue("value",0);
- else {
-
- if(!callback)
- callback = new SGParticles();
-
- callback->setupColorComponent(componentnode, modelRoot, 0, 3);
- }
+ else
+ callback()->setupColorComponent(componentnode, modelRoot,
+ 0, 3);
}
}
-
const SGPropertyNode* endcolornode = particlenode->getChild("endcolor");
- if(endcolornode) {
+ if (endcolornode) {
const SGPropertyNode* componentnode = endcolornode->getChild("red");
- if(componentnode) {
-
- if(componentnode->hasValue("value"))
+ if (componentnode) {
+ if (componentnode->hasValue("value"))
r2 = componentnode->getFloatValue("value",0);
-
- else {
-
- if(!callback)
- callback = new SGParticles();
-
- callback->setupColorComponent(componentnode, modelRoot, 1, 0);
- }
+ else
+ callback()->setupColorComponent(componentnode, modelRoot,
+ 1, 0);
}
-
componentnode = endcolornode->getChild("green");
-
- if(componentnode) {
-
- if(componentnode->hasValue("value"))
+ if (componentnode) {
+ if (componentnode->hasValue("value"))
g2 = componentnode->getFloatValue("value",0);
-
- else {
-
- if(!callback)
- callback = new SGParticles();
-
- callback->setupColorComponent(componentnode, modelRoot, 1, 1);
- }
+ else
+ callback()->setupColorComponent(componentnode, modelRoot,
+ 1, 1);
}
-
componentnode = endcolornode->getChild("blue");
-
- if(componentnode) {
-
- if(componentnode->hasValue("value"))
+ if (componentnode) {
+ if (componentnode->hasValue("value"))
b2 = componentnode->getFloatValue("value",0);
-
- else {
-
- if(!callback)
- callback = new SGParticles();
-
- callback->setupColorComponent(componentnode, modelRoot, 1, 2);
- }
+ else
+ callback()->setupColorComponent(componentnode, modelRoot,
+ 1, 2);
}
-
componentnode = endcolornode->getChild("alpha");
- if(componentnode) {
-
- if(componentnode->hasValue("value"))
+ if (componentnode) {
+ if (componentnode->hasValue("value"))
a2 = componentnode->getFloatValue("value",0);
-
- else {
-
- if(!callback)
- callback = new SGParticles();
-
- callback->setupColorComponent(componentnode, modelRoot, 1, 3);
- }
+ else
+ callback()->setupColorComponent(componentnode, modelRoot,
+ 1, 3);
}
}
-
- particle.setColorRange(osgParticle::rangev4( osg::Vec4(r1,g1,b1,a1), osg::Vec4(r2,g2,b2,a2)));
+ particle.setColorRange(osgParticle::rangev4(osg::Vec4(r1,g1,b1,a1),
+ osg::Vec4(r2,g2,b2,a2)));
float startsize=1, endsize=0.1f;
const SGPropertyNode* startsizenode = particlenode->getChild("startsize");
-
- if(startsizenode) {
-
- if(startsizenode->hasValue("value"))
+ if (startsizenode) {
+ if (startsizenode->hasValue("value"))
startsize = startsizenode->getFloatValue("value",0);
-
- else {
-
- if(!callback)
- callback = new SGParticles();
-
- callback->setupStartSizeData(startsizenode, modelRoot);
- }
+ else
+ callback()->setupStartSizeData(startsizenode, modelRoot);
}
-
const SGPropertyNode* endsizenode = particlenode->getChild("endsize");
- if(endsizenode) {
-
- if(endsizenode->hasValue("value"))
+ if (endsizenode) {
+ if (endsizenode->hasValue("value"))
endsize = endsizenode->getFloatValue("value",0);
-
- else {
-
- if(!callback)
- callback = new SGParticles();
-
- callback->setupEndSizeData(endsizenode, modelRoot);
- }
+ else
+ callback()->setupEndSizeData(endsizenode, modelRoot);
}
-
particle.setSizeRange(osgParticle::rangef(startsize, endsize));
-
float life=5;
const SGPropertyNode* lifenode = particlenode->getChild("life-sec");
-
- if(lifenode) {
-
- if(lifenode->hasValue("value"))
+ if (lifenode) {
+ if (lifenode->hasValue("value"))
life = lifenode->getFloatValue("value",0);
-
- else {
-
- if(!callback)
- callback = new SGParticles();
-
- callback->setupLifeData(lifenode, modelRoot);
- }
+ else
+ callback()->setupLifeData(lifenode, modelRoot);
}
particle.setLifeTime(life);
-
- if(particlenode->hasValue("radius-m"))
+ if (particlenode->hasValue("radius-m"))
particle.setRadius(particlenode->getFloatValue("radius-m",0));
-
- if(particlenode->hasValue("mass-kg"))
+ if (particlenode->hasValue("mass-kg"))
particle.setMass(particlenode->getFloatValue("mass-kg",0));
-
- if(callback) {
- callback->setupStaticColorComponent(r1, g1, b1, a1, r2, g2, b2, a2);
- callback->setupStaticSizeData(startsize, endsize);
+ if (callback.get()) {
+ callback.get()->setupStaticColorComponent(r1, g1, b1, a1,
+ r2, g2, b2, a2);
+ callback.get()->setupStaticSizeData(startsize, endsize);
}
//particle.setColorRange(osgParticle::rangev4( osg::Vec4(r1, g1, b1, a1), osg::Vec4(r2, g2, b2, a2)));
}
const SGPropertyNode* programnode = configNode->getChild("program");
osgParticle::FluidProgram *program = new osgParticle::FluidProgram();
- if(programnode) {
+ if (programnode) {
std::string fluid = programnode->getStringValue("fluid","air");
- if(fluid=="air")
+ if (fluid=="air")
program->setFluidToAir();
else
std::string grav = programnode->getStringValue("gravity","enabled");
- if(grav=="enabled") {
-
- if(attach == "world") {
-
- if(!callback)
- callback = new SGParticles();
+ if (grav=="enabled") {
- callback->setupProgramGravity(true);
- } else
+ if (attach == "world")
+ callback()->setupProgramGravity(true);
+ else
program->setToGravity();
-
} else
program->setAcceleration(osg::Vec3(0,0,0));
std::string wind = programnode->getStringValue("wind","enabled");
- if(wind=="enabled") {
- if(!callback)
- callback = new SGParticles();
- callback->setupProgramWind(true);
- } else
+ if (wind=="enabled")
+ callback()->setupProgramWind(true);
+ else
program->setWind(osg::Vec3(0,0,0));
align->addChild(program);
}
else { }
- if(callback) { //this means we want property-driven changes
+ if (callback.get()) { //this means we want property-driven changes
SG_LOG(SG_GENERAL, SG_DEBUG, "setting up particle system user data and callback\n");
//setup data and callback
- callback->setGeneralData(dynamic_cast<osgParticle::RadialShooter*>(emitter->getShooter()), dynamic_cast<osgParticle::RandomRateCounter*>(emitter->getCounter()), particleSys, program);
- emitter->setUpdateCallback(callback);
+ callback.get()->setGeneralData(dynamic_cast<osgParticle::RadialShooter*>(emitter->getShooter()),
+ dynamic_cast<osgParticle::RandomRateCounter*>(emitter->getCounter()),
+ particleSys, program);
+ emitter->setUpdateCallback(callback.get());
}
return align;
}
+
+void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv)
+{
+ //SG_LOG(SG_GENERAL, SG_ALERT, "callback!\n");
+
+ 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 (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( osg::Vec4(staticColorComponents[0], staticColorComponents[1], staticColorComponents[2], staticColorComponents[3]), osg::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 (program) {
+ if (useGravity)
+ program->setAcceleration(GlobalParticleCallback::getGravityVector());
+ if (useWind)
+ program->setWind(GlobalParticleCallback::getWindVector());
+ }
+}
+} // namespace simgear
// particles.hxx - classes to manage particles
-// started in 2008 by Tiago Gusmão, using animation.hxx as reference
+// 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
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
#ifndef _SG_PARTICLES_HXX
#define _SG_PARTICLES_HXX 1
-#endif
-
-#ifndef __cplusplus
-# error This library requires C++
-#endif
#include <osg/ref_ptr>
#include <osg/Group>
#include "animation.hxx"
-class SGGlobalParticleCallback : public osg::NodeCallback
+namespace simgear
+{
+
+class GlobalParticleCallback : public osg::NodeCallback
{
public:
- SGGlobalParticleCallback(const SGPropertyNode* modelRoot)
+ GlobalParticleCallback(const SGPropertyNode* modelRoot)
{
this->modelRoot=modelRoot;
}
- virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
- {
- SGQuat<float> q = SGQuat<float>::fromLonLatDeg(modelRoot->getFloatValue("/position/longitude-deg",0), modelRoot->getFloatValue("/position/latitude-deg",0));
-
- SGMatrix<float> m(q);
- osg::Matrix om(m.data());
- osg::Vec3 v(0,0,9.81);
- gravity = om.postMult(v);
-
- osg::Vec3 w(-modelRoot->getFloatValue("/environment/wind-from-north-fps",0) * SG_FEET_TO_METER,
- -modelRoot->getFloatValue("/environment/wind-from-east-fps",0) * SG_FEET_TO_METER, 0);
- wind = om.postMult(w);
-
- //SG_LOG(SG_GENERAL, SG_ALERT, "wind vector:"<<w[0]<<","<<w[1]<<","<<w[2]<<"\n");
- }
-
+ virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+
static const osg::Vec3 &getGravityVector()
{
return gravity;
-class SGParticles : public osg::NodeCallback
+class Particles : public osg::NodeCallback
{
public:
- SGParticles( ) :
- shooterValue(NULL),
- counterValue(NULL),
- startSizeValue(NULL),
- endSizeValue(NULL),
- lifeValue(NULL),
- refFrame(NULL),
- program(NULL),
- useGravity(false),
- useWind(false),
- counterCond(NULL)
- {
- memset(colorComponents, 0, sizeof(colorComponents));
- }
-
- static osg::Group * appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::ReaderWriter::Options* options);
-
- virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
- {
- //SG_LOG(SG_GENERAL, SG_ALERT, "callback!\n");
-
- 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(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( osg::Vec4(staticColorComponents[0], staticColorComponents[1], staticColorComponents[2], staticColorComponents[3]), osg::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(program){
- if(useGravity)
- program->setAcceleration(SGGlobalParticleCallback::getGravityVector());
- if(useWind)
- program->setWind(SGGlobalParticleCallback::getWindVector());
- }
- }
-
- inline 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);
- }
-
- inline 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);
- }
-
- inline void setupCounterCondition(const SGPropertyNode* configNode, SGPropertyNode* modelRoot)
- {
- counterCond = sgReadCondition(modelRoot, configNode);
- }
-
- inline void setupCounterCondition(float counterStaticValue, float counterStaticExtraRange)
- {
- this->counterStaticValue = counterStaticValue;
- this->counterStaticExtraRange = counterStaticExtraRange;
- }
-
- inline 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");
- }
- }
-
- inline 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");
- }
- }
-
- inline 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");
- }
- }
-
- inline void setupStaticSizeData(float startSize, float endSize)
- {
- this->startSize=startSize;
- this->endSize=endSize;
- }
-
-
- inline void setGeneralData(osgParticle::RadialShooter * shooter, osgParticle::RandomRateCounter * counter, osgParticle::ParticleSystem * particleSys, osgParticle::FluidProgram *program)
- {
- this->shooter = shooter;
- this->counter = counter;
- this->particleSys = particleSys;
- this->program = program;
- }
-
- inline void setupProgramGravity(bool useGravity)
- {
- this->useGravity = useGravity;
- }
-
- inline void setupProgramWind(bool useWind)
- {
- this->useWind = useWind;
- }
-
- //component: r=0, g=1, ... ; color: 0=start color, 1=end color
- inline 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
- }
-
- inline 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;
- }
-
- static inline osg::Group * getCommonRoot()
- {
- if(!commonRoot.valid())
- {
- SG_LOG(SG_GENERAL, SG_DEBUG, "Particle common root called!\n");
- 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());
- }
- return commonRoot.get();
- }
-
- static inline osg::Geode * getCommonGeode()
- {
- return commonGeode.get();
- }
-
- static inline osgParticle::ParticleSystemUpdater * getPSU()
- {
- return psu.get();
- }
+ Particles() :
+ shooterValue(NULL),
+ counterValue(NULL),
+ startSizeValue(NULL),
+ endSizeValue(NULL),
+ lifeValue(NULL),
+ refFrame(NULL),
+ program(NULL),
+ useGravity(false),
+ useWind(false),
+ counterCond(NULL)
+ {
+ memset(colorComponents, 0, sizeof(colorComponents));
+ }
-private:
+ static osg::Group * appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::ReaderWriter::Options* options);
+
+ virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+
+ 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);
+ }
+
+ 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);
+ }
+
+ void setupCounterCondition(const SGPropertyNode* configNode,
+ SGPropertyNode* modelRoot)
+ {
+ counterCond = sgReadCondition(modelRoot, configNode);
+ }
+
+ void setupCounterCondition(float counterStaticValue,
+ float counterStaticExtraRange)
+ {
+ this->counterStaticValue = counterStaticValue;
+ this->counterStaticExtraRange = 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");
+ }
+ }
+
+ 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");
+ }
+ }
+
+ 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");
+ }
+ }
+
+ void setupStaticSizeData(float startSize, float endSize)
+ {
+ this->startSize=startSize;
+ this->endSize=endSize;
+ }
+
+
+ void setGeneralData(osgParticle::RadialShooter* shooter,
+ osgParticle::RandomRateCounter* counter,
+ osgParticle::ParticleSystem* particleSys,
+ osgParticle::FluidProgram* program)
+ {
+ this->shooter = shooter;
+ this->counter = counter;
+ this->particleSys = particleSys;
+ this->program = program;
+ }
+
+ void setupProgramGravity(bool useGravity)
+ {
+ this->useGravity = useGravity;
+ }
+
+ void setupProgramWind(bool useWind)
+ {
+ this->useWind = useWind;
+ }
+
+ //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
+ }
+
+ 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;
+ }
+
+ static osg::Group * getCommonRoot()
+ {
+ if(!commonRoot.valid())
+ {
+ SG_LOG(SG_GENERAL, SG_DEBUG, "Particle common root called!\n");
+ 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());
+ }
+ return commonRoot.get();
+ }
+
+ static osg::Geode * getCommonGeode()
+ {
+ return commonGeode.get();
+ }
+
+ static osgParticle::ParticleSystemUpdater * getPSU()
+ {
+ return psu.get();
+ }
+
+protected:
float shooterExtraRange;
float counterExtraRange;
- SGExpressiond * shooterValue;
- SGExpressiond * counterValue;
- SGExpressiond * colorComponents[8];
- SGExpressiond * startSizeValue;
- SGExpressiond * endSizeValue;
- SGExpressiond * lifeValue;
- SGCondition * counterCond;
- osg::MatrixTransform *refFrame;
+ SGExpressiond* shooterValue;
+ SGExpressiond* counterValue;
+ SGExpressiond* colorComponents[8];
+ SGExpressiond* startSizeValue;
+ SGExpressiond* endSizeValue;
+ SGExpressiond* lifeValue;
+ SGCondition* counterCond;
+ osg::MatrixTransform* refFrame;
float staticColorComponents[8];
float startSize;
float endSize;
float counterStaticValue;
float counterStaticExtraRange;
- osgParticle::RadialShooter * shooter;
- osgParticle::RandomRateCounter * counter;
- osgParticle::ParticleSystem * particleSys;
- osgParticle::FluidProgram * program;
+ osgParticle::RadialShooter* shooter;
+ osgParticle::RandomRateCounter* counter;
+ osgParticle::ParticleSystem* particleSys;
+ osgParticle::FluidProgram* program;
bool useGravity;
bool useWind;
static osg::ref_ptr<osgParticle::ParticleSystemUpdater> psu;
static osg::ref_ptr<osg::Group> commonRoot;
static osg::ref_ptr<osg::Geode> commonGeode;
};
+}
+#endif
/*