]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/model/particles.cxx
Make return type from loadPagedModel explicit.
[simgear.git] / simgear / scene / model / particles.cxx
index ac4fc0bbce7e67f34b299961deaab645c952f5f8..a7fee2ded106e3dbb4c69bd8570bd6f930ddd6f6 100644 (file)
@@ -1,6 +1,6 @@
 // particles.cxx - classes to manage particles
-// started in 2008 by Tiago Gusmão, using animation.hxx as reference
-// Copyright (C) 2008 Tiago Gusmão
+// started in 2008 by Tiago Gusmo, using animation.hxx as reference
+// Copyright (C) 2008 Tiago Gusmo
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License as
@@ -24,6 +24,8 @@
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/props/props.hxx>
 #include <simgear/props/props_io.hxx>
+#include <simgear/scene/util/OsgMath.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"
 
@@ -43,28 +48,42 @@ 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);
+    // 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_GENERAL, SG_ALERT, "wind vector:"<<w[0]<<","<<w[1]<<","<<w[2]<<"\n");
+    // SG_LOG(SG_GENERAL, SG_ALERT,
+    //        "wind vector:" << w[0] << "," <<w[1] << "," << w[2]);
 }
 
 
 //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::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{
@@ -81,9 +100,38 @@ private:
     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());
+        commonRoot->setNodeMask( ~simgear::MODELLIGHT_BIT );
+    }
+    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*
+                                          const osgDB::Options*
                                           options)
 {
     SG_LOG(SG_GENERAL, SG_DEBUG, "Setting up a particle system!\n");
@@ -140,28 +188,27 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
         osg::Geode* g = new osg::Geode;
         align->addChild(g);
         g->addDrawable(particleSys);
-        emitter->setReferenceFrame(osgParticle::Emitter::ABSOLUTE_RF);
     } else {
-        getCommonGeode()->addDrawable(particleSys);
+        callback()->particleFrame = new osg::MatrixTransform();
+        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")) {
-        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),
@@ -233,7 +280,7 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
         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")) {
@@ -245,7 +292,7 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
             }
         }
 
-        const SGPropertyNode* rotspeednode = shnode->getChild("rotspeed");
+        const SGPropertyNode* rotspeednode = shnode->getChild("rotation-speed");
 
         if (rotspeednode) {
             float x1,y1,z1,x2,y2,z2;
@@ -259,27 +306,29 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
         }
     } //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 = 0.0f, spread = 0.0f;
-        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);
+        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);
@@ -292,7 +341,7 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
             = 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");
@@ -328,7 +377,7 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
                                                     0, 3);
             }
         }
-        const SGPropertyNode* endcolornode = particlenode->getChild("endcolor");
+        const SGPropertyNode* endcolornode = particlenode->getNode("end/color");
         if (endcolornode) {
             const SGPropertyNode* componentnode = endcolornode->getChild("red");
 
@@ -368,14 +417,14 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
                                                     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);
@@ -409,27 +458,19 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
     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 (attach == "world")
-                callback()->setupProgramGravity(true);
-            else
-                program->setToGravity();
+        if (programnode->getBoolValue("gravity", true)) {
+            program->setToGravity();
         } 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));
@@ -438,7 +479,6 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
 
         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");
@@ -455,7 +495,9 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
 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()
@@ -466,7 +508,7 @@ void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv)
     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) {
@@ -476,7 +518,7 @@ void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv)
         }
     }
     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)
@@ -485,11 +527,28 @@ void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv)
         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());
+
+    if (particleFrame.valid()) {
+        MatrixList mlist = node->getWorldMatrices();
+        if (!mlist.empty()) {
+            const Matrix& particleMat = particleFrame->getMatrix();
+            Vec3d emitOrigin(mlist[0](3, 0), mlist[0](3, 1), mlist[0](3, 2));
+            Vec3d displace
+                = emitOrigin - Vec3d(particleMat(3, 0), particleMat(3, 1),
+                                     particleMat(3, 2));
+            if (displace * displace > 10000.0 * 10000.0) {
+                // Make new frame for particle system, coincident with
+                // the emitter frame, but oriented with local Z.
+                SGGeod geod = SGGeod::fromCart(toSG(emitOrigin));
+                Matrix newParticleMat = makeZUpFrame(geod);
+                Matrix changeParticleFrame
+                    = particleMat * Matrix::inverse(newParticleMat);
+                particleFrame->setMatrix(newParticleMat);
+                transformParticles(particleSys.get(), changeParticleFrame);
+            }
+        }
     }
+    if (program.valid() && useWind)
+        program->setWind(_wind);
 }
 } // namespace simgear