]> git.mxchange.org Git - simgear.git/commitdiff
particles from Tiago_G
authortimoore <timoore>
Fri, 15 Feb 2008 06:44:05 +0000 (06:44 +0000)
committertimoore <timoore>
Fri, 15 Feb 2008 06:44:05 +0000 (06:44 +0000)
simgear/scene/model/Makefile.am
simgear/scene/model/animation.cxx
simgear/scene/model/animation.hxx
simgear/scene/model/model.cxx
simgear/scene/model/particles.cxx [new file with mode: 0644]
simgear/scene/model/particles.hxx [new file with mode: 0644]

index 49c69754b9acd628ff9de9fc5b6eb0b2a1cb5632..db102d9ea50f8a2df3d7dbd83fdfa4dd9f691dad 100644 (file)
@@ -6,6 +6,7 @@ noinst_HEADERS =
 
 include_HEADERS = \
        animation.hxx \
+       particles.hxx \
        location.hxx \
        model.hxx \
        modellib.hxx \
@@ -22,6 +23,7 @@ include_HEADERS = \
 
 libsgmodel_a_SOURCES = \
        animation.cxx \
+       particles.cxx \
        location.cxx \
        model.cxx \
        modellib.cxx \
index 88fd835a4de636f704dcafa00940e4f286c40ef0..1bb11bee4c08b0a63c10a081df4c2e0a1596f502 100644 (file)
@@ -186,7 +186,7 @@ read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
   return expr;
 }
 
-static SGExpressiond*
+SGExpressiond*
 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
            const char* unit, double defMin, double defMax)
 {
index 70ef67263addced098b1a6d5a985b23348081a3b..eec16bc4b89fb2036e3b030d591e45a58498f2d5 100644 (file)
@@ -31,6 +31,9 @@
 #undef max
 #endif
 
+SGExpressiond*
+read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
+           const char* unit, double defMin, double defMax);
 \f
 //////////////////////////////////////////////////////////////////////
 // Base class for animation installers
index becc88c6d824cd128eeb64eb21ecf61feae617dc..1d9606d70fbfc7bfa20ab20fdf385a7a758d0bf3 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "animation.hxx"
 #include "model.hxx"
+#include "particles.hxx"
 
 SG_USING_STD(vector);
 
@@ -250,6 +251,22 @@ sgLoad3DModel( const string &fg_root, const string &path,
     }
   }
 
+  std::vector<SGPropertyNode_ptr> particle_nodes;
+  particle_nodes = props.getChildren("particlesystem");
+  for (unsigned i = 0; i < particle_nodes.size(); ++i)
+  {
+    if(i==0)
+    {
+      if (texturepath.extension() != "")
+          texturepath = texturepath.dir();
+
+      options->setDatabasePath(texturepath.str());
+      if (!externalTexturePath.str().empty())
+          options->getDatabasePathList().push_back(externalTexturePath.str());
+    }
+    alignmainmodel.get()->addChild(SGParticles::appendParticles(particle_nodes[i], prop_root, options.get()));
+  }
+
   if (data) {
     alignmainmodel->setUserData(data);
     data->modelLoaded(path, &props, alignmainmodel.get());
diff --git a/simgear/scene/model/particles.cxx b/simgear/scene/model/particles.cxx
new file mode 100644 (file)
index 0000000..918abb5
--- /dev/null
@@ -0,0 +1,486 @@
+// particles.cxx - classes to manage particles
+// started in 2008 by Tiago Gusmão, using animation.hxx as reference
+
+#ifdef HAVE_CONFIG_H
+#  include <simgear_config.h>
+#endif
+
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/props/props.hxx>
+#include <simgear/props/props_io.hxx>
+
+#include <osgParticle/SmokeTrailEffect>
+#include <osgParticle/FireEffect>
+#include <osgParticle/ConnectedParticleSystem>
+#include <osgParticle/MultiSegmentPlacer>
+#include <osgParticle/SectorPlacer>
+#include <osgParticle/ConstantRateCounter>
+#include <osgParticle/ParticleSystemUpdater>
+#include <osgParticle/FluidProgram>
+
+#include <osg/Geode>
+#include <osg/MatrixTransform>
+
+#include "particles.hxx"
+
+//static members
+osg::Vec3 SGGlobalParticleCallback::gravity;
+osg::Vec3 SGGlobalParticleCallback::wind;
+
+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)
+{
+    SG_LOG(SG_GENERAL, SG_DEBUG, "Setting up a particle system!\n");
+
+    osgParticle::ParticleSystem *particleSys;
+
+    //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
+
+    getPSU()->addParticleSystem(particleSys); 
+
+    getPSU()->setUpdateCallback(new SGGlobalParticleCallback(modelRoot));
+
+    //contains counter, placer and shooter by default
+    osgParticle::ModularEmitter *emitter = new osgParticle::ModularEmitter;
+
+    emitter->setParticleSystem(particleSys);
+
+    // Set up the alignment node ("stolen" from animation.cxx)
+    osg::MatrixTransform *align = new osg::MatrixTransform;
+    osg::Matrix res_matrix;
+    res_matrix.makeRotate(
+        configNode->getFloatValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
+        osg::Vec3(0, 1, 0),
+        configNode->getFloatValue("offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
+        osg::Vec3(1, 0, 0),
+        configNode->getFloatValue("offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
+        osg::Vec3(0, 0, 1));
+
+    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);
+
+    align->setName("particle align");
+
+    //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);
+
+    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;
+        align->addChild(g);
+        g->addDrawable(particleSys);
+        emitter->setReferenceFrame(osgParticle::Emitter::ABSOLUTE_RF);
+    } 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());
+        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");
+
+    }
+
+    if(textureFile.empty())
+        textureFile="";
+
+    particleSys->setDefaultAttributes(textureFile,configNode->getBoolValue("emissive", true),
+        configNode->getBoolValue("lighting", false));
+
+    std::string alignstr = configNode->getStringValue("align","billboard");
+
+    if(alignstr == "fixed")
+        particleSys->setParticleAlignment(osgParticle::ParticleSystem::FIXED);
+
+    const SGPropertyNode* placernode = configNode->getChild("placer");
+
+    if(placernode) {
+        std::string emitterType = placernode->getStringValue("type", "point");
+
+        if (emitterType == "sector") {
+            osgParticle::SectorPlacer *splacer = new  osgParticle::SectorPlacer;
+            float minRadius, maxRadius, minPhi, maxPhi;
+
+            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;
+
+            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();
+                float x,y,z;
+
+                for (unsigned i = 0; i < segments.size(); ++i) {
+                    x = segments[i]->getFloatValue("x-m",0);
+                    y = segments[i]->getFloatValue("y-m",0);
+                    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 the default placer in ModularEmitter is used (PointPlacer)
+    }
+
+    const SGPropertyNode* shnode = configNode->getChild("shooter");
+
+    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;
+        minPhi = shnode->getFloatValue("phi-min-deg",0)* 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);
+
+        shooter->setThetaRange(minTheta, maxTheta);
+        shooter->setPhiRange(minPhi, maxPhi);
+
+        const SGPropertyNode* speednode = shnode->getChild("speed");
+
+        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);
+            }
+        }
+
+        const SGPropertyNode* rotspeednode = shnode->getChild("rotspeed");
+
+        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;
+            z1 = rotspeednode->getFloatValue("z-min-deg-sec",0) * SG_DEGREES_TO_RADIANS;
+            x2 = rotspeednode->getFloatValue("x-max-deg-sec",0) * SG_DEGREES_TO_RADIANS;
+            y2 = rotspeednode->getFloatValue("y-max-deg-sec",0) * SG_DEGREES_TO_RADIANS;
+            z2 = rotspeednode->getFloatValue("z-max-deg-sec",0) * SG_DEGREES_TO_RADIANS;
+            shooter->setInitialRotationalSpeedRange(osg::Vec3f(x1,y1,z1), osg::Vec3f(x2,y2,z2));
+        }
+    } //else ModularEmitter uses the default RadialShooter
+
+    const SGPropertyNode* counternode = configNode->getChild("counter");
+
+    if(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 {
+
+                if(!callback)
+                    callback = new SGParticles();
+
+                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);
+        }
+    } //TODO: else perhaps set higher values than default? 
+
+    const SGPropertyNode* particlenode = configNode->getChild("particle");
+
+    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"))
+                    r1 = componentnode->getFloatValue("value",0);
+                else {
+
+                    if(!callback)
+                        callback = new SGParticles();
+
+                    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);
+                }
+            }
+
+            componentnode = startcolornode->getChild("blue");
+            if(componentnode) {
+
+                if(componentnode->hasValue("value"))
+                    b1 = componentnode->getFloatValue("value",0);
+                else {
+
+                    if(!callback)
+                        callback = new SGParticles();
+
+                    callback->setupColorComponent(componentnode, modelRoot, 0, 2);
+                }
+            }
+
+            componentnode = startcolornode->getChild("alpha");
+
+            if(componentnode) {
+
+                if(componentnode->hasValue("value"))
+                    a1 = componentnode->getFloatValue("value",0);
+                else {
+
+                    if(!callback)
+                        callback = new SGParticles();
+
+                    callback->setupColorComponent(componentnode, modelRoot, 0, 3);
+                }
+            }
+        }
+
+        const SGPropertyNode* endcolornode = particlenode->getChild("endcolor");
+        if(endcolornode) {
+            const SGPropertyNode* componentnode = endcolornode->getChild("red");
+
+            if(componentnode) {
+
+                if(componentnode->hasValue("value"))
+                    r2 = componentnode->getFloatValue("value",0);
+
+                else {
+
+                    if(!callback)
+                        callback = new SGParticles();
+
+                    callback->setupColorComponent(componentnode, modelRoot, 1, 0);
+                }
+            }
+
+            componentnode = endcolornode->getChild("green");
+
+            if(componentnode) {
+
+                if(componentnode->hasValue("value"))
+                    g2 = componentnode->getFloatValue("value",0);
+
+                else {
+
+                    if(!callback)
+                        callback = new SGParticles();
+
+                    callback->setupColorComponent(componentnode, modelRoot, 1, 1);
+                }
+            }
+
+            componentnode = endcolornode->getChild("blue");
+
+            if(componentnode) {
+
+                if(componentnode->hasValue("value"))
+                    b2 = componentnode->getFloatValue("value",0);
+
+                else {
+
+                    if(!callback)
+                        callback = new SGParticles();
+
+                    callback->setupColorComponent(componentnode, modelRoot, 1, 2);
+                }
+            }
+
+            componentnode = endcolornode->getChild("alpha");
+            if(componentnode) {
+
+                if(componentnode->hasValue("value"))
+                    a2 = componentnode->getFloatValue("value",0);
+
+                else {
+
+                    if(!callback)
+                        callback = new SGParticles();
+
+                    callback->setupColorComponent(componentnode, modelRoot, 1, 3);
+                }
+            }
+        }
+
+        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"))
+                startsize = startsizenode->getFloatValue("value",0);
+
+            else {
+
+                if(!callback)
+                    callback = new SGParticles();
+
+                callback->setupStartSizeData(startsizenode, modelRoot);
+            }
+        }
+
+        const SGPropertyNode* endsizenode = particlenode->getChild("endsize");
+        if(endsizenode) {
+
+            if(endsizenode->hasValue("value"))
+                endsize = endsizenode->getFloatValue("value",0);
+
+            else {
+
+                if(!callback)
+                    callback = new SGParticles();
+
+                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"))
+                life =  lifenode->getFloatValue("value",0);
+
+            else {
+
+                if(!callback)
+                    callback = new SGParticles();
+
+                callback->setupLifeData(lifenode, modelRoot);
+            }
+        }
+
+        particle.setLifeTime(life);
+
+        if(particlenode->hasValue("radius-m"))
+            particle.setRadius(particlenode->getFloatValue("radius-m",0));
+
+        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);
+        }
+        //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) {
+        std::string fluid = programnode->getStringValue("fluid","air");
+
+        if(fluid=="air") 
+            program->setFluidToAir();
+
+        else
+            program->setFluidToWater();
+
+        std::string grav = programnode->getStringValue("gravity","enabled");
+
+        if(grav=="enabled") {
+
+            if(attach == "world") {
+
+                if(!callback)
+                    callback = new SGParticles();
+
+                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
+            program->setWind(osg::Vec3(0,0,0));
+
+        align->addChild(program);
+
+        program->setParticleSystem(particleSys);
+    }
+    else {  }
+
+    if(callback) {  //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);
+    }
+
+    return align;
+}
diff --git a/simgear/scene/model/particles.hxx b/simgear/scene/model/particles.hxx
new file mode 100644 (file)
index 0000000..bdf99a7
--- /dev/null
@@ -0,0 +1,327 @@
+// particles.hxx - classes to manage particles
+// started in 2008 by Tiago Gusmão, using animation.hxx as reference
+
+#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 <osg/Node>
+#include <osg/NodeVisitor>
+#include <osg/Texture2D>
+#include <osgParticle/SmokeTrailEffect>
+#include <osgParticle/Particle>
+#include <osgParticle/ModularEmitter>
+#include <osgParticle/ParticleSystemUpdater>
+#include <osgParticle/ParticleSystem>
+#include <osg/MatrixTransform>
+#include <osgDB/ReaderWriter>
+#include <osgDB/FileNameUtils>
+#include <osgDB/FileUtils>
+#include <osgDB/ReadFile>
+#include <osg/Notify>
+#include <osg/Vec3>
+
+
+#include <simgear/scene/util/SGNodeMasks.hxx>
+#include <simgear/props/props.hxx>
+#include <simgear/props/condition.hxx>
+#include <simgear/structure/SGExpression.hxx>
+#include <simgear/math/SGQuat.hxx>
+#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"
+
+class SGGlobalParticleCallback : public osg::NodeCallback 
+{
+public:
+    SGGlobalParticleCallback(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");
+    }
+
+    static const osg::Vec3 &getGravityVector()
+    {
+        return gravity;
+    }
+
+    static const osg::Vec3 &getWindVector()
+    {
+        return wind;
+    }
+
+
+    static osg::Vec3 gravity;
+    static osg::Vec3 wind;
+private:
+
+    const SGPropertyNode* modelRoot;
+};
+
+
+
+class SGParticles : 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();
+      }
+
+private:
+    float shooterExtraRange;
+    float counterExtraRange;
+    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;
+    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;
+};
+
+
+/*
+class CustomModularEmitter : public osgParticle::ModularEmitter, public osg::Observer
+{
+public:
+
+virtual void objectDeleted (void *)
+{
+SG_LOG(SG_GENERAL, SG_ALERT, "object deleted!\n");
+
+SGParticles::getCommonRoot()->removeChild(this->getParticleSystem()->getParent(0));
+SGParticles::getPSU()->removeParticleSystem(this->getParticleSystem());
+}
+
+
+//   ~CustomModularEmitter()
+//   {
+//     SG_LOG(SG_GENERAL, SG_ALERT, "object deleted!\n");
+//     
+//     SGParticles::getCommonRoot()->removeChild(this->getParticleSystem()->getParent(0));
+//     SGParticles::getPSU()->removeParticleSystem(this->getParticleSystem());
+//   }
+};
+*/