# include <simgear_config.h>
#endif
+#include <simgear/math/SGMath.hxx>
+#include <simgear/math/SGGeod.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
+#include <simgear/structure/OSGVersion.hxx>
#include <osgParticle/SmokeTrailEffect>
#include <osgParticle/FireEffect>
#include <osgParticle/SectorPlacer>
#include <osgParticle/ConstantRateCounter>
#include <osgParticle/ParticleSystemUpdater>
+#include <osgParticle/ParticleSystem>
#include <osgParticle/FluidProgram>
#include <osg/Geode>
+#include <osg/Group>
#include <osg/MatrixTransform>
+#include <osg/Node>
#include "particles.hxx"
+#if SG_OSG_VERSION >= 27004
+#define OSG_PARTICLE_FIX 1
+#endif
+
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(q.osg());
+ osg::Matrix om(toOsg(q));
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);
+ const osg::Vec3& zUpWind = Particles::getWindVector();
+ osg::Vec3 w(zUpWind.y(), zUpWind.x(), - zUpWind.z());
wind = om.preMult(w);
//SG_LOG(SG_GENERAL, SG_ALERT, "wind vector:"<<w[0]<<","<<w[1]<<","<<w[2]<<"\n");
//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)
+{
+}
template <typename Object>
class PointerGuard{
Object* _ptr;
};
+osg::Group* Particles::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();
+}
+
+void transformParticles(osgParticle::ParticleSystem* particleSys,
+ const osg::Matrix& mat)
+{
+ const int numParticles = particleSys->numParticles();
+ if (particleSys->areAllParticlesDead())
+ return;
+ for (int i = 0; i < numParticles; ++i) {
+ osgParticle::Particle* P = particleSys->getParticle(i);
+ if (!P->isAlive())
+ continue;
+ P->transformPositionVelocity(mat);
+ }
+}
+
osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot,
const osgDB::ReaderWriter::Options*
osg::Geode* g = new osg::Geode;
align->addChild(g);
g->addDrawable(particleSys);
+#ifndef OSG_PARTICLE_FIX
emitter->setReferenceFrame(osgParticle::Emitter::ABSOLUTE_RF);
+#endif
} else {
+#ifdef OSG_PARTICLE_FIX
+ callback()->particleFrame = new osg::MatrixTransform();
+ osg::Geode* g = new osg::Geode;
+ g->addDrawable(particleSys);
+ callback()->particleFrame->addChild(g);
+ getCommonRoot()->addChild(callback()->particleFrame.get());
+#else
getCommonGeode()->addDrawable(particleSys);
+#endif
}
std::string textureFile;
if (configNode->hasValue("texture")) {
- SG_LOG(SG_GENERAL, SG_ALERT,
- "requested:"<<configNode->getStringValue("texture","")<<"\n");
+ //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");
+ //SG_LOG(SG_GENERAL, SG_ALERT, "found:"<<textureFile<<"\n");
+ //for(unsigned i = 0; i < options->getDatabasePathList().size(); ++i)
+ // SG_LOG(SG_GENERAL, SG_ALERT,
+ // "opts:"<<options->getDatabasePathList()[i]<<"\n");
}
- if (textureFile.empty())
- textureFile="";
-
particleSys->setDefaultAttributes(textureFile,
configNode->getBoolValue("emissive",
true),
shooter->setThetaRange(minTheta, maxTheta);
shooter->setPhiRange(minPhi, maxPhi);
- const SGPropertyNode* speednode = shnode->getChild("speed");
+ const SGPropertyNode* speednode = shnode->getChild("speed-mps");
if (speednode) {
if (speednode->hasValue("value")) {
}
}
- const SGPropertyNode* rotspeednode = shnode->getChild("rotspeed");
+ const SGPropertyNode* rotspeednode = shnode->getChild("rotation-speed");
if (rotspeednode) {
float x1,y1,z1,x2,y2,z2;
}
} //else ModularEmitter uses the default RadialShooter
+
+ const SGPropertyNode* conditionNode = configNode->getChild("condition");
const SGPropertyNode* counternode = configNode->getChild("counter");
- if (counternode) {
+ if (conditionNode || counternode) {
osgParticle::RandomRateCounter* counter
= new osgParticle::RandomRateCounter;
emitter->setCounter(counter);
- float pps, spread;
- const SGPropertyNode* ppsnode = counternode->getChild("pps");
-
- if (ppsnode) {
-
- if (ppsnode->hasValue("value")) {
- pps = ppsnode->getFloatValue("value",0);
- spread = ppsnode->getFloatValue("spread",0);
- counter->setRateRange(pps-spread, pps+spread);
- } else {
- callback()->setupCounterData(ppsnode, modelRoot);
+ float pps = 0.0f, spread = 0.0f;
+
+ if (counternode) {
+ const SGPropertyNode* ppsnode = counternode->getChild("particles-per-sec");
+ if (ppsnode) {
+ if (ppsnode->hasValue("value")) {
+ pps = ppsnode->getFloatValue("value",0);
+ spread = ppsnode->getFloatValue("spread",0);
+ counter->setRateRange(pps-spread, pps+spread);
+ } else {
+ callback()->setupCounterData(ppsnode, modelRoot);
+ }
}
}
- const SGPropertyNode* conditionNode
- = counternode->getChild("condition");
+
if (conditionNode) {
callback()->setupCounterCondition(conditionNode, modelRoot);
callback()->setupCounterCondition(pps, spread);
= 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");
+ = particlenode->getNode("start/color");
if (startcolornode) {
const SGPropertyNode* componentnode
= startcolornode->getChild("red");
0, 3);
}
}
- const SGPropertyNode* endcolornode = particlenode->getChild("endcolor");
+ const SGPropertyNode* endcolornode = particlenode->getNode("end/color");
if (endcolornode) {
const SGPropertyNode* componentnode = endcolornode->getChild("red");
osg::Vec4(r2,g2,b2,a2)));
float startsize=1, endsize=0.1f;
- const SGPropertyNode* startsizenode = particlenode->getChild("startsize");
+ const SGPropertyNode* startsizenode = particlenode->getNode("start/size");
if (startsizenode) {
if (startsizenode->hasValue("value"))
startsize = startsizenode->getFloatValue("value",0);
else
callback()->setupStartSizeData(startsizenode, modelRoot);
}
- const SGPropertyNode* endsizenode = particlenode->getChild("endsize");
+ const SGPropertyNode* endsizenode = particlenode->getNode("end/size");
if (endsizenode) {
if (endsizenode->hasValue("value"))
endsize = endsizenode->getFloatValue("value",0);
osgParticle::FluidProgram *program = new osgParticle::FluidProgram();
if (programnode) {
- std::string fluid = programnode->getStringValue("fluid","air");
+ std::string fluid = programnode->getStringValue("fluid", "air");
- if (fluid=="air")
+ if (fluid=="air")
program->setFluidToAir();
-
else
program->setFluidToWater();
- std::string grav = programnode->getStringValue("gravity","enabled");
-
- if (grav=="enabled") {
-
+ if (programnode->getBoolValue("gravity", true)) {
+#ifdef OSG_PARTICLE_FIX
+ program->setToGravity();
+#else
if (attach == "world")
callback()->setupProgramGravity(true);
else
program->setToGravity();
+#endif
} else
program->setAcceleration(osg::Vec3(0,0,0));
- std::string wind = programnode->getStringValue("wind","enabled");
- if (wind=="enabled")
+ if (programnode->getBoolValue("wind", true))
callback()->setupProgramWind(true);
else
program->setWind(osg::Vec3(0,0,0));
program->setParticleSystem(particleSys);
}
- else { }
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");
void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
//SG_LOG(SG_GENERAL, SG_ALERT, "callback!\n");
+ this->particleSys->setFrozen(_frozen);
+ using namespace osg;
if (shooterValue)
shooter->setInitialSpeedRange(shooterValue->getValue(),
(shooterValue->getValue()
else if (counterCond)
counter->setRateRange(counterStaticValue,
counterStaticValue + counterStaticExtraRange);
- if (counterCond && !counterCond->test())
+ if (!GlobalParticleCallback::getEnabled() || (counterCond && !counterCond->test()))
counter->setRateRange(0, 0);
bool colorchange=false;
for (int i = 0; i < 8; ++i) {
}
}
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])));
+ 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)
particleSys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(startSize, endSize));
if (lifeValue)
particleSys->getDefaultParticleTemplate().setLifeTime(lifeValue->getValue());
- if (program) {
+#ifdef OSG_PARTICLE_FIX
+ 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 = geod.makeZUpFrame();
+ Matrix changeParticleFrame
+ = particleMat * Matrix::inverse(newParticleMat);
+ particleFrame->setMatrix(newParticleMat);
+ transformParticles(particleSys.get(), changeParticleFrame);
+ }
+ }
+ }
+ if (program.valid() && useWind)
+ program->setWind(_wind);
+#else
+ if (program.valid()) {
if (useGravity)
program->setAcceleration(GlobalParticleCallback::getGravityVector());
if (useWind)
program->setWind(GlobalParticleCallback::getWindVector());
}
+#endif
}
} // namespace simgear