00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #ifdef HAVE_CONFIG_H
00021 # include <simgear_config.h>
00022 #endif
00023
00024 #include <simgear/math/SGMath.hxx>
00025 #include <simgear/math/SGGeod.hxx>
00026 #include <simgear/misc/sg_path.hxx>
00027 #include <simgear/props/props.hxx>
00028 #include <simgear/props/props_io.hxx>
00029 #include <simgear/structure/OSGVersion.hxx>
00030
00031 #include <osgParticle/SmokeTrailEffect>
00032 #include <osgParticle/FireEffect>
00033 #include <osgParticle/ConnectedParticleSystem>
00034 #include <osgParticle/MultiSegmentPlacer>
00035 #include <osgParticle/SectorPlacer>
00036 #include <osgParticle/ConstantRateCounter>
00037 #include <osgParticle/ParticleSystemUpdater>
00038 #include <osgParticle/ParticleSystem>
00039 #include <osgParticle/FluidProgram>
00040
00041 #include <osg/Geode>
00042 #include <osg/Group>
00043 #include <osg/MatrixTransform>
00044 #include <osg/Node>
00045
00046 #include "particles.hxx"
00047
00048 #if SG_OSG_VERSION >= 27004
00049 #define OSG_PARTICLE_FIX 1
00050 #endif
00051
00052 namespace simgear
00053 {
00054 void GlobalParticleCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
00055 {
00056 enabled = !enabledNode || enabledNode->getBoolValue();
00057 if (!enabled)
00058 return;
00059 SGQuatd q
00060 = SGQuatd::fromLonLatDeg(modelRoot->getFloatValue("/position/longitude-deg",0),
00061 modelRoot->getFloatValue("/position/latitude-deg",0));
00062 osg::Matrix om(toOsg(q));
00063 osg::Vec3 v(0,0,9.81);
00064 gravity = om.preMult(v);
00065 const osg::Vec3& zUpWind = Particles::getWindVector();
00066 osg::Vec3 w(zUpWind.y(), zUpWind.x(), - zUpWind.z());
00067 wind = om.preMult(w);
00068
00069
00070 }
00071
00072
00073
00074 osg::Vec3 GlobalParticleCallback::gravity;
00075 osg::Vec3 GlobalParticleCallback::wind;
00076 bool GlobalParticleCallback::enabled = true;
00077 SGConstPropertyNode_ptr GlobalParticleCallback::enabledNode = 0;
00078
00079 osg::ref_ptr<osg::Group> Particles::commonRoot;
00080 osg::ref_ptr<osgParticle::ParticleSystemUpdater> Particles::psu = new osgParticle::ParticleSystemUpdater;
00081 osg::ref_ptr<osg::Geode> Particles::commonGeode = new osg::Geode;;
00082 osg::Vec3 Particles::_wind;
00083
00084 Particles::Particles() :
00085 useGravity(false),
00086 useWind(false)
00087 {
00088 }
00089
00090 template <typename Object>
00091 class PointerGuard{
00092 public:
00093 PointerGuard() : _ptr(0) {}
00094 Object* get() { return _ptr; }
00095 Object* operator () ()
00096 {
00097 if (!_ptr)
00098 _ptr = new Object;
00099 return _ptr;
00100 }
00101 private:
00102 Object* _ptr;
00103 };
00104
00105 osg::Group* Particles::getCommonRoot()
00106 {
00107 if(!commonRoot.valid())
00108 {
00109 SG_LOG(SG_GENERAL, SG_DEBUG, "Particle common root called!\n");
00110 commonRoot = new osg::Group;
00111 commonRoot.get()->setName("common particle system root");
00112 commonGeode.get()->setName("common particle system geode");
00113 commonRoot.get()->addChild(commonGeode.get());
00114 commonRoot.get()->addChild(psu.get());
00115 }
00116 return commonRoot.get();
00117 }
00118
00119 void transformParticles(osgParticle::ParticleSystem* particleSys,
00120 const osg::Matrix& mat)
00121 {
00122 const int numParticles = particleSys->numParticles();
00123 if (particleSys->areAllParticlesDead())
00124 return;
00125 for (int i = 0; i < numParticles; ++i) {
00126 osgParticle::Particle* P = particleSys->getParticle(i);
00127 if (!P->isAlive())
00128 continue;
00129 P->transformPositionVelocity(mat);
00130 }
00131 }
00132
00133 osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
00134 SGPropertyNode* modelRoot,
00135 const osgDB::ReaderWriter::Options*
00136 options)
00137 {
00138 SG_LOG(SG_GENERAL, SG_DEBUG, "Setting up a particle system!\n");
00139
00140 osgParticle::ParticleSystem *particleSys;
00141
00142
00143 std::string type = configNode->getStringValue("type", "normal");
00144 if (type == "normal")
00145 particleSys = new osgParticle::ParticleSystem;
00146 else
00147 particleSys = new osgParticle::ConnectedParticleSystem;
00148
00149 PointerGuard<Particles> callback;
00150
00151 getPSU()->addParticleSystem(particleSys);
00152 getPSU()->setUpdateCallback(new GlobalParticleCallback(modelRoot));
00153
00154 osgParticle::ModularEmitter* emitter = new osgParticle::ModularEmitter;
00155
00156 emitter->setParticleSystem(particleSys);
00157
00158
00159
00160 osg::MatrixTransform *align = new osg::MatrixTransform;
00161 osg::Matrix res_matrix;
00162 res_matrix.makeRotate(
00163 configNode->getFloatValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
00164 osg::Vec3(0, 1, 0),
00165 configNode->getFloatValue("offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
00166 osg::Vec3(1, 0, 0),
00167 configNode->getFloatValue("offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
00168 osg::Vec3(0, 0, 1));
00169
00170 osg::Matrix tmat;
00171 tmat.makeTranslate(configNode->getFloatValue("offsets/x-m", 0.0),
00172 configNode->getFloatValue("offsets/y-m", 0.0),
00173 configNode->getFloatValue("offsets/z-m", 0.0));
00174 align->setMatrix(res_matrix * tmat);
00175
00176 align->setName("particle align");
00177
00178
00179
00180
00181 align->addChild(emitter);
00182
00183
00184 std::string name = configNode->getStringValue("name", "");
00185 if (!name.empty())
00186 align->setName(name);
00187 std::string attach = configNode->getStringValue("attach", "world");
00188 if (attach == "local") {
00189 osg::Geode* g = new osg::Geode;
00190 align->addChild(g);
00191 g->addDrawable(particleSys);
00192 #ifndef OSG_PARTICLE_FIX
00193 emitter->setReferenceFrame(osgParticle::Emitter::ABSOLUTE_RF);
00194 #endif
00195 } else {
00196 #ifdef OSG_PARTICLE_FIX
00197 callback()->particleFrame = new osg::MatrixTransform();
00198 osg::Geode* g = new osg::Geode;
00199 g->addDrawable(particleSys);
00200 callback()->particleFrame->addChild(g);
00201 getCommonRoot()->addChild(callback()->particleFrame.get());
00202 #else
00203 getCommonGeode()->addDrawable(particleSys);
00204 #endif
00205 }
00206 std::string textureFile;
00207 if (configNode->hasValue("texture")) {
00208
00209
00210 textureFile= osgDB::findFileInPath(configNode->getStringValue("texture",
00211 ""),
00212 options->getDatabasePathList());
00213
00214
00215
00216
00217
00218 }
00219
00220 particleSys->setDefaultAttributes(textureFile,
00221 configNode->getBoolValue("emissive",
00222 true),
00223 configNode->getBoolValue("lighting",
00224 false));
00225
00226 std::string alignstr = configNode->getStringValue("align", "billboard");
00227
00228 if (alignstr == "fixed")
00229 particleSys->setParticleAlignment(osgParticle::ParticleSystem::FIXED);
00230
00231 const SGPropertyNode* placernode = configNode->getChild("placer");
00232
00233 if (placernode) {
00234 std::string emitterType = placernode->getStringValue("type", "point");
00235
00236 if (emitterType == "sector") {
00237 osgParticle::SectorPlacer *splacer = new osgParticle::SectorPlacer;
00238 float minRadius, maxRadius, minPhi, maxPhi;
00239
00240 minRadius = placernode->getFloatValue("radius-min-m",0);
00241 maxRadius = placernode->getFloatValue("radius-max-m",1);
00242 minPhi = (placernode->getFloatValue("phi-min-deg",0)
00243 * SG_DEGREES_TO_RADIANS);
00244 maxPhi = (placernode->getFloatValue("phi-max-deg",360.0f)
00245 * SG_DEGREES_TO_RADIANS);
00246
00247 splacer->setRadiusRange(minRadius, maxRadius);
00248 splacer->setPhiRange(minPhi, maxPhi);
00249 emitter->setPlacer(splacer);
00250 } else if (emitterType == "segments") {
00251 std::vector<SGPropertyNode_ptr> segments
00252 = placernode->getChildren("vertex");
00253 if (segments.size()>1) {
00254 osgParticle::MultiSegmentPlacer *msplacer
00255 = new osgParticle::MultiSegmentPlacer();
00256 float x,y,z;
00257
00258 for (unsigned i = 0; i < segments.size(); ++i) {
00259 x = segments[i]->getFloatValue("x-m",0);
00260 y = segments[i]->getFloatValue("y-m",0);
00261 z = segments[i]->getFloatValue("z-m",0);
00262 msplacer->addVertex(x, y, z);
00263 }
00264 emitter->setPlacer(msplacer);
00265 } else {
00266 SG_LOG(SG_GENERAL, SG_ALERT,
00267 "Detected particle system using segment(s) with less than 2 vertices\n");
00268 }
00269 }
00270 }
00271
00272 const SGPropertyNode* shnode = configNode->getChild("shooter");
00273
00274 if (shnode) {
00275 float minTheta, maxTheta, minPhi, maxPhi, speed, spread;
00276
00277 minTheta = (shnode->getFloatValue("theta-min-deg",0)
00278 * SG_DEGREES_TO_RADIANS);
00279 maxTheta = (shnode->getFloatValue("theta-max-deg",360.0f)
00280 * SG_DEGREES_TO_RADIANS);
00281 minPhi = shnode->getFloatValue("phi-min-deg",0)* SG_DEGREES_TO_RADIANS;
00282 maxPhi = (shnode->getFloatValue("phi-max-deg",360.0f)
00283 * SG_DEGREES_TO_RADIANS);
00284
00285 osgParticle::RadialShooter *shooter = new osgParticle::RadialShooter;
00286 emitter->setShooter(shooter);
00287
00288 shooter->setThetaRange(minTheta, maxTheta);
00289 shooter->setPhiRange(minPhi, maxPhi);
00290
00291 const SGPropertyNode* speednode = shnode->getChild("speed-mps");
00292
00293 if (speednode) {
00294 if (speednode->hasValue("value")) {
00295 speed = speednode->getFloatValue("value",0);
00296 spread = speednode->getFloatValue("spread",0);
00297 shooter->setInitialSpeedRange(speed-spread, speed+spread);
00298 } else {
00299 callback()->setupShooterSpeedData(speednode, modelRoot);
00300 }
00301 }
00302
00303 const SGPropertyNode* rotspeednode = shnode->getChild("rotation-speed");
00304
00305 if (rotspeednode) {
00306 float x1,y1,z1,x2,y2,z2;
00307 x1 = rotspeednode->getFloatValue("x-min-deg-sec",0) * SG_DEGREES_TO_RADIANS;
00308 y1 = rotspeednode->getFloatValue("y-min-deg-sec",0) * SG_DEGREES_TO_RADIANS;
00309 z1 = rotspeednode->getFloatValue("z-min-deg-sec",0) * SG_DEGREES_TO_RADIANS;
00310 x2 = rotspeednode->getFloatValue("x-max-deg-sec",0) * SG_DEGREES_TO_RADIANS;
00311 y2 = rotspeednode->getFloatValue("y-max-deg-sec",0) * SG_DEGREES_TO_RADIANS;
00312 z2 = rotspeednode->getFloatValue("z-max-deg-sec",0) * SG_DEGREES_TO_RADIANS;
00313 shooter->setInitialRotationalSpeedRange(osg::Vec3f(x1,y1,z1), osg::Vec3f(x2,y2,z2));
00314 }
00315 }
00316
00317
00318 const SGPropertyNode* conditionNode = configNode->getChild("condition");
00319 const SGPropertyNode* counternode = configNode->getChild("counter");
00320
00321 if (conditionNode || counternode) {
00322 osgParticle::RandomRateCounter* counter
00323 = new osgParticle::RandomRateCounter;
00324 emitter->setCounter(counter);
00325 float pps = 0.0f, spread = 0.0f;
00326
00327 if (counternode) {
00328 const SGPropertyNode* ppsnode = counternode->getChild("particles-per-sec");
00329 if (ppsnode) {
00330 if (ppsnode->hasValue("value")) {
00331 pps = ppsnode->getFloatValue("value",0);
00332 spread = ppsnode->getFloatValue("spread",0);
00333 counter->setRateRange(pps-spread, pps+spread);
00334 } else {
00335 callback()->setupCounterData(ppsnode, modelRoot);
00336 }
00337 }
00338 }
00339
00340 if (conditionNode) {
00341 callback()->setupCounterCondition(conditionNode, modelRoot);
00342 callback()->setupCounterCondition(pps, spread);
00343 }
00344 }
00345
00346 const SGPropertyNode* particlenode = configNode->getChild("particle");
00347 if (particlenode) {
00348 osgParticle::Particle &particle
00349 = particleSys->getDefaultParticleTemplate();
00350 float r1=0, g1=0, b1=0, a1=1, r2=0, g2=0, b2=0, a2=1;
00351 const SGPropertyNode* startcolornode
00352 = particlenode->getNode("start/color");
00353 if (startcolornode) {
00354 const SGPropertyNode* componentnode
00355 = startcolornode->getChild("red");
00356 if (componentnode) {
00357 if (componentnode->hasValue("value"))
00358 r1 = componentnode->getFloatValue("value",0);
00359 else
00360 callback()->setupColorComponent(componentnode, modelRoot,
00361 0, 0);
00362 }
00363 componentnode = startcolornode->getChild("green");
00364 if (componentnode) {
00365 if (componentnode->hasValue("value"))
00366 g1 = componentnode->getFloatValue("value", 0);
00367 else
00368 callback()->setupColorComponent(componentnode, modelRoot,
00369 0, 1);
00370 }
00371 componentnode = startcolornode->getChild("blue");
00372 if (componentnode) {
00373 if (componentnode->hasValue("value"))
00374 b1 = componentnode->getFloatValue("value",0);
00375 else
00376 callback()->setupColorComponent(componentnode, modelRoot,
00377 0, 2);
00378 }
00379 componentnode = startcolornode->getChild("alpha");
00380 if (componentnode) {
00381 if (componentnode->hasValue("value"))
00382 a1 = componentnode->getFloatValue("value",0);
00383 else
00384 callback()->setupColorComponent(componentnode, modelRoot,
00385 0, 3);
00386 }
00387 }
00388 const SGPropertyNode* endcolornode = particlenode->getNode("end/color");
00389 if (endcolornode) {
00390 const SGPropertyNode* componentnode = endcolornode->getChild("red");
00391
00392 if (componentnode) {
00393 if (componentnode->hasValue("value"))
00394 r2 = componentnode->getFloatValue("value",0);
00395 else
00396 callback()->setupColorComponent(componentnode, modelRoot,
00397 1, 0);
00398 }
00399 componentnode = endcolornode->getChild("green");
00400 if (componentnode) {
00401 if (componentnode->hasValue("value"))
00402 g2 = componentnode->getFloatValue("value",0);
00403 else
00404 callback()->setupColorComponent(componentnode, modelRoot,
00405 1, 1);
00406 }
00407 componentnode = endcolornode->getChild("blue");
00408 if (componentnode) {
00409 if (componentnode->hasValue("value"))
00410 b2 = componentnode->getFloatValue("value",0);
00411 else
00412 callback()->setupColorComponent(componentnode, modelRoot,
00413 1, 2);
00414 }
00415 componentnode = endcolornode->getChild("alpha");
00416 if (componentnode) {
00417 if (componentnode->hasValue("value"))
00418 a2 = componentnode->getFloatValue("value",0);
00419 else
00420 callback()->setupColorComponent(componentnode, modelRoot,
00421 1, 3);
00422 }
00423 }
00424 particle.setColorRange(osgParticle::rangev4(osg::Vec4(r1,g1,b1,a1),
00425 osg::Vec4(r2,g2,b2,a2)));
00426
00427 float startsize=1, endsize=0.1f;
00428 const SGPropertyNode* startsizenode = particlenode->getNode("start/size");
00429 if (startsizenode) {
00430 if (startsizenode->hasValue("value"))
00431 startsize = startsizenode->getFloatValue("value",0);
00432 else
00433 callback()->setupStartSizeData(startsizenode, modelRoot);
00434 }
00435 const SGPropertyNode* endsizenode = particlenode->getNode("end/size");
00436 if (endsizenode) {
00437 if (endsizenode->hasValue("value"))
00438 endsize = endsizenode->getFloatValue("value",0);
00439 else
00440 callback()->setupEndSizeData(endsizenode, modelRoot);
00441 }
00442 particle.setSizeRange(osgParticle::rangef(startsize, endsize));
00443 float life=5;
00444 const SGPropertyNode* lifenode = particlenode->getChild("life-sec");
00445 if (lifenode) {
00446 if (lifenode->hasValue("value"))
00447 life = lifenode->getFloatValue("value",0);
00448 else
00449 callback()->setupLifeData(lifenode, modelRoot);
00450 }
00451
00452 particle.setLifeTime(life);
00453 if (particlenode->hasValue("radius-m"))
00454 particle.setRadius(particlenode->getFloatValue("radius-m",0));
00455 if (particlenode->hasValue("mass-kg"))
00456 particle.setMass(particlenode->getFloatValue("mass-kg",0));
00457 if (callback.get()) {
00458 callback.get()->setupStaticColorComponent(r1, g1, b1, a1,
00459 r2, g2, b2, a2);
00460 callback.get()->setupStaticSizeData(startsize, endsize);
00461 }
00462
00463 }
00464
00465 const SGPropertyNode* programnode = configNode->getChild("program");
00466 osgParticle::FluidProgram *program = new osgParticle::FluidProgram();
00467
00468 if (programnode) {
00469 std::string fluid = programnode->getStringValue("fluid", "air");
00470
00471 if (fluid=="air")
00472 program->setFluidToAir();
00473 else
00474 program->setFluidToWater();
00475
00476 if (programnode->getBoolValue("gravity", true)) {
00477 #ifdef OSG_PARTICLE_FIX
00478 program->setToGravity();
00479 #else
00480 if (attach == "world")
00481 callback()->setupProgramGravity(true);
00482 else
00483 program->setToGravity();
00484 #endif
00485 } else
00486 program->setAcceleration(osg::Vec3(0,0,0));
00487
00488 if (programnode->getBoolValue("wind", true))
00489 callback()->setupProgramWind(true);
00490 else
00491 program->setWind(osg::Vec3(0,0,0));
00492
00493 align->addChild(program);
00494
00495 program->setParticleSystem(particleSys);
00496 }
00497
00498 if (callback.get()) {
00499 SG_LOG(SG_GENERAL, SG_DEBUG, "setting up particle system user data and callback\n");
00500
00501 callback.get()->setGeneralData(dynamic_cast<osgParticle::RadialShooter*>(emitter->getShooter()),
00502 dynamic_cast<osgParticle::RandomRateCounter*>(emitter->getCounter()),
00503 particleSys, program);
00504 emitter->setUpdateCallback(callback.get());
00505 }
00506
00507 return align;
00508 }
00509
00510 void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv)
00511 {
00512
00513 using namespace osg;
00514 if (shooterValue)
00515 shooter->setInitialSpeedRange(shooterValue->getValue(),
00516 (shooterValue->getValue()
00517 + shooterExtraRange));
00518 if (counterValue)
00519 counter->setRateRange(counterValue->getValue(),
00520 counterValue->getValue() + counterExtraRange);
00521 else if (counterCond)
00522 counter->setRateRange(counterStaticValue,
00523 counterStaticValue + counterStaticExtraRange);
00524 if (!GlobalParticleCallback::getEnabled() || (counterCond && !counterCond->test()))
00525 counter->setRateRange(0, 0);
00526 bool colorchange=false;
00527 for (int i = 0; i < 8; ++i) {
00528 if (colorComponents[i]) {
00529 staticColorComponents[i] = colorComponents[i]->getValue();
00530 colorchange=true;
00531 }
00532 }
00533 if (colorchange)
00534 particleSys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4( Vec4(staticColorComponents[0], staticColorComponents[1], staticColorComponents[2], staticColorComponents[3]), Vec4(staticColorComponents[4], staticColorComponents[5], staticColorComponents[6], staticColorComponents[7])));
00535 if (startSizeValue)
00536 startSize = startSizeValue->getValue();
00537 if (endSizeValue)
00538 endSize = endSizeValue->getValue();
00539 if (startSizeValue || endSizeValue)
00540 particleSys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(startSize, endSize));
00541 if (lifeValue)
00542 particleSys->getDefaultParticleTemplate().setLifeTime(lifeValue->getValue());
00543 #ifdef OSG_PARTICLE_FIX
00544 if (particleFrame.valid()) {
00545 MatrixList mlist = node->getWorldMatrices();
00546 if (!mlist.empty()) {
00547 const Matrix& particleMat = particleFrame->getMatrix();
00548 Vec3d emitOrigin(mlist[0](3, 0), mlist[0](3, 1), mlist[0](3, 2));
00549 Vec3d displace
00550 = emitOrigin - Vec3d(particleMat(3, 0), particleMat(3, 1),
00551 particleMat(3, 2));
00552 if (displace * displace > 10000.0 * 10000.0) {
00553
00554
00555 SGGeod geod = SGGeod::fromCart(toSG(emitOrigin));
00556 Matrix newParticleMat = geod.makeZUpFrame();
00557 Matrix changeParticleFrame
00558 = particleMat * Matrix::inverse(newParticleMat);
00559 particleFrame->setMatrix(newParticleMat);
00560 transformParticles(particleSys.get(), changeParticleFrame);
00561 }
00562 }
00563 }
00564 if (program.valid() && useWind)
00565 program->setWind(_wind);
00566 #else
00567 if (program.valid()) {
00568 if (useGravity)
00569 program->setAcceleration(GlobalParticleCallback::getGravityVector());
00570 if (useWind)
00571 program->setWind(GlobalParticleCallback::getWindVector());
00572 }
00573 #endif
00574 }
00575 }