Particles: rewrite global manager for thread-safety
Avoid re-registering the global node callback each time a new particle animation is loaded, and use mutexes to protected all shared data.
This commit is contained in:
parent
fd191b51ce
commit
5a1ed52d7c
@ -490,7 +490,8 @@ sgLoad3DModel_internal(const SGPath& path,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GlobalParticleCallback::getEnabled()){//dbOptions->getPluginStringData("SimGear::PARTICLESYSTEM") != "OFF") {
|
auto particlesManager = ParticlesGlobalManager::instance();
|
||||||
|
if (particlesManager->isEnabled()) { //dbOptions->getPluginStringData("SimGear::PARTICLESYSTEM") != "OFF") {
|
||||||
std::vector<SGPropertyNode_ptr> particle_nodes;
|
std::vector<SGPropertyNode_ptr> particle_nodes;
|
||||||
particle_nodes = props->getChildren("particlesystem");
|
particle_nodes = props->getChildren("particlesystem");
|
||||||
for (unsigned i = 0; i < particle_nodes.size(); ++i) {
|
for (unsigned i = 0; i < particle_nodes.size(); ++i) {
|
||||||
@ -503,7 +504,7 @@ sgLoad3DModel_internal(const SGPath& path,
|
|||||||
|
|
||||||
options2->setDatabasePath(texturepath.utf8Str());
|
options2->setDatabasePath(texturepath.utf8Str());
|
||||||
}
|
}
|
||||||
group->addChild(Particles::appendParticles(particle_nodes[i],
|
group->addChild(particlesManager->appendParticles(particle_nodes[i],
|
||||||
prop_root,
|
prop_root,
|
||||||
options2.get()));
|
options2.get()));
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,12 @@
|
|||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include <simgear_config.h>
|
#include <simgear_config.h>
|
||||||
#endif
|
|
||||||
|
#include "particles.hxx"
|
||||||
|
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <simgear/misc/sg_path.hxx>
|
#include <simgear/misc/sg_path.hxx>
|
||||||
#include <simgear/props/props.hxx>
|
#include <simgear/props/props.hxx>
|
||||||
@ -42,53 +45,122 @@
|
|||||||
#include <osg/MatrixTransform>
|
#include <osg/MatrixTransform>
|
||||||
#include <osg/Node>
|
#include <osg/Node>
|
||||||
|
|
||||||
#include "particles.hxx"
|
|
||||||
|
#include <simgear/scene/model/animation.hxx>
|
||||||
|
|
||||||
namespace simgear
|
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(toOsg(q));
|
|
||||||
osg::Vec3 v(0,0,9.81);
|
|
||||||
gravity = om.preMult(v);
|
|
||||||
// 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_PARTICLES, SG_ALERT,
|
class ParticlesGlobalManager::ParticlesGlobalManagerPrivate : public osg::NodeCallback
|
||||||
// "wind vector:" << w[0] << "," <<w[1] << "," << w[2]);
|
{
|
||||||
|
public:
|
||||||
|
ParticlesGlobalManagerPrivate() : _updater(new osgParticle::ParticleSystemUpdater),
|
||||||
|
_commonGeode(new osg::Geode)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void operator()(osg::Node* node, osg::NodeVisitor* nv) override
|
||||||
//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)
|
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> g(_lock);
|
||||||
|
_enabled = !_enabledNode || _enabledNode->getBoolValue();
|
||||||
|
|
||||||
|
if (!_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto q = SGQuatd::fromLonLatDeg(_longitudeNode->getFloatValue(), _latitudeNode->getFloatValue());
|
||||||
|
osg::Matrix om(toOsg(q));
|
||||||
|
osg::Vec3 v(0, 0, 9.81);
|
||||||
|
|
||||||
|
_gravity = om.preMult(v);
|
||||||
|
|
||||||
|
// NOTE: THIS WIND COMPUTATION DOESN'T SEEM TO AFFECT PARTICLES
|
||||||
|
// const osg::Vec3& zUpWind = _wind;
|
||||||
|
// osg::Vec3 w(zUpWind.y(), zUpWind.x(), -zUpWind.z());
|
||||||
|
// _localWind = om.preMult(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
// only call this with the lock held!
|
||||||
|
osg::Group* internalGetCommonRoot()
|
||||||
|
{
|
||||||
|
if (!_commonRoot.valid()) {
|
||||||
|
SG_LOG(SG_PARTICLES, SG_DEBUG, "Particle common root called.");
|
||||||
|
_commonRoot = new osg::Group;
|
||||||
|
_commonRoot->setName("common particle system root");
|
||||||
|
_commonGeode->setName("common particle system geode");
|
||||||
|
_commonRoot->addChild(_commonGeode);
|
||||||
|
_commonRoot->addChild(_updater);
|
||||||
|
_commonRoot->setNodeMask(~simgear::MODELLIGHT_BIT);
|
||||||
|
}
|
||||||
|
return _commonRoot.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::mutex _lock;
|
||||||
|
bool _frozen = false;
|
||||||
|
osg::ref_ptr<osgParticle::ParticleSystemUpdater> _updater;
|
||||||
|
osg::ref_ptr<osg::Group> _commonRoot;
|
||||||
|
osg::ref_ptr<osg::Geode> _commonGeode;
|
||||||
|
osg::Vec3 _wind;
|
||||||
|
bool _globalCallbackRegistered = false;
|
||||||
|
bool _enabled = true;
|
||||||
|
osg::Vec3 _gravity;
|
||||||
|
// osg::Vec3 _localWind;
|
||||||
|
|
||||||
|
SGConstPropertyNode_ptr _enabledNode;
|
||||||
|
SGConstPropertyNode_ptr _longitudeNode, _latitudeNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::mutex static_managerLock;
|
||||||
|
static std::unique_ptr<ParticlesGlobalManager> static_instance;
|
||||||
|
|
||||||
|
ParticlesGlobalManager* ParticlesGlobalManager::instance()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> g(static_managerLock);
|
||||||
|
if (!static_instance) {
|
||||||
|
static_instance.reset(new ParticlesGlobalManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_instance.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticlesGlobalManager::clear()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> g(static_managerLock);
|
||||||
|
static_instance.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticlesGlobalManager::ParticlesGlobalManager() : d(new ParticlesGlobalManagerPrivate)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticlesGlobalManager::~ParticlesGlobalManager()
|
||||||
|
{
|
||||||
|
if (d->_globalCallbackRegistered) {
|
||||||
|
// is this actually necessary? possibly not
|
||||||
|
d->_updater->setUpdateCallback(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParticlesGlobalManager::isEnabled() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> g(d->_lock);
|
||||||
|
return d->_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParticlesGlobalManager::isFrozen() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> g(d->_lock);
|
||||||
|
return d->_frozen;
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::Vec3 ParticlesGlobalManager::getWindVector() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> g(d->_lock);
|
||||||
|
return d->_wind;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Object>
|
template <typename Object>
|
||||||
class PointerGuard{
|
class PointerGuard{
|
||||||
public:
|
public:
|
||||||
PointerGuard() : _ptr(0) {}
|
|
||||||
Object* get() { return _ptr; }
|
Object* get() { return _ptr; }
|
||||||
Object* operator () ()
|
Object* operator () ()
|
||||||
{
|
{
|
||||||
@ -97,24 +169,9 @@ public:
|
|||||||
return _ptr;
|
return _ptr;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
Object* _ptr;
|
Object* _ptr = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
osg::Group* Particles::getCommonRoot()
|
|
||||||
{
|
|
||||||
if(!commonRoot.valid())
|
|
||||||
{
|
|
||||||
SG_LOG(SG_PARTICLES, SG_DEBUG, "Particle common root called.");
|
|
||||||
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,
|
void transformParticles(osgParticle::ParticleSystem* particleSys,
|
||||||
const osg::Matrix& mat)
|
const osg::Matrix& mat)
|
||||||
{
|
{
|
||||||
@ -129,10 +186,182 @@ void transformParticles(osgParticle::ParticleSystem* particleSys,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
|
void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||||
SGPropertyNode* modelRoot,
|
{
|
||||||
const osgDB::Options*
|
auto globalManager = ParticlesGlobalManager::instance();
|
||||||
options)
|
|
||||||
|
//SG_LOG(SG_PARTICLES, SG_ALERT, "callback!\n");
|
||||||
|
particleSys->setFrozen(globalManager->isFrozen());
|
||||||
|
|
||||||
|
using namespace osg;
|
||||||
|
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 (!globalManager->isEnabled() || (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(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)
|
||||||
|
endSize = endSizeValue->getValue();
|
||||||
|
if (startSizeValue || endSizeValue)
|
||||||
|
particleSys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(startSize, endSize));
|
||||||
|
if (lifeValue)
|
||||||
|
particleSys->getDefaultParticleTemplate().setLifeTime(lifeValue->getValue());
|
||||||
|
|
||||||
|
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(globalManager->getWindVector());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Particles::setupShooterSpeedData(const SGPropertyNode* configNode,
|
||||||
|
SGPropertyNode* modelRoot)
|
||||||
|
{
|
||||||
|
shooterValue = read_value(configNode, modelRoot, "-m",
|
||||||
|
-SGLimitsd::max(), SGLimitsd::max());
|
||||||
|
if (!shooterValue) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: shooter property error!\n");
|
||||||
|
}
|
||||||
|
shooterExtraRange = configNode->getFloatValue("extrarange", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Particles::setupCounterData(const SGPropertyNode* configNode,
|
||||||
|
SGPropertyNode* modelRoot)
|
||||||
|
{
|
||||||
|
counterValue = read_value(configNode, modelRoot, "-m",
|
||||||
|
-SGLimitsd::max(), SGLimitsd::max());
|
||||||
|
if (!counterValue) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_DEV_WARN, "counter property error!\n");
|
||||||
|
}
|
||||||
|
counterExtraRange = configNode->getFloatValue("extrarange", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Particles::Particles::setupCounterCondition(const SGPropertyNode* configNode,
|
||||||
|
SGPropertyNode* modelRoot)
|
||||||
|
{
|
||||||
|
counterCond = sgReadCondition(modelRoot, configNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Particles::setupCounterCondition(float aCounterStaticValue,
|
||||||
|
float aCounterStaticExtraRange)
|
||||||
|
{
|
||||||
|
counterStaticValue = aCounterStaticValue;
|
||||||
|
counterStaticExtraRange = aCounterStaticExtraRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Particles::setupStartSizeData(const SGPropertyNode* configNode,
|
||||||
|
SGPropertyNode* modelRoot)
|
||||||
|
{
|
||||||
|
startSizeValue = read_value(configNode, modelRoot, "-m",
|
||||||
|
-SGLimitsd::max(), SGLimitsd::max());
|
||||||
|
if (!startSizeValue) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: startSizeValue error!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Particles::setupEndSizeData(const SGPropertyNode* configNode,
|
||||||
|
SGPropertyNode* modelRoot)
|
||||||
|
{
|
||||||
|
endSizeValue = read_value(configNode, modelRoot, "-m",
|
||||||
|
-SGLimitsd::max(), SGLimitsd::max());
|
||||||
|
if (!endSizeValue) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: startSizeValue error!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Particles::setupLifeData(const SGPropertyNode* configNode,
|
||||||
|
SGPropertyNode* modelRoot)
|
||||||
|
{
|
||||||
|
lifeValue = read_value(configNode, modelRoot, "-m",
|
||||||
|
-SGLimitsd::max(), SGLimitsd::max());
|
||||||
|
if (!lifeValue) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: lifeValue error!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Particles::setupColorComponent(const SGPropertyNode* configNode,
|
||||||
|
SGPropertyNode* modelRoot, int color,
|
||||||
|
int component)
|
||||||
|
{
|
||||||
|
SGSharedPtr<SGExpressiond> colorValue = read_value(configNode, modelRoot, "-m",
|
||||||
|
-SGLimitsd::max(),
|
||||||
|
SGLimitsd::max());
|
||||||
|
if (!colorValue) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: color property error!\n");
|
||||||
|
}
|
||||||
|
colorComponents[(color * 4) + component] = colorValue;
|
||||||
|
//number of color components = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
void Particles::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticlesGlobalManager::setWindVector(const osg::Vec3& wind)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> g(d->_lock);
|
||||||
|
d->_wind = wind;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticlesGlobalManager::setWindFrom(const double from_deg, const double speed_kt)
|
||||||
|
{
|
||||||
|
double map_rad = -from_deg * SG_DEGREES_TO_RADIANS;
|
||||||
|
double speed_mps = speed_kt * SG_KT_TO_MPS;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> g(d->_lock);
|
||||||
|
d->_wind[0] = cos(map_rad) * speed_mps;
|
||||||
|
d->_wind[1] = sin(map_rad) * speed_mps;
|
||||||
|
d->_wind[2] = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
osg::Group* ParticlesGlobalManager::getCommonRoot()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> g(d->_lock);
|
||||||
|
return d->internalGetCommonRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Group> ParticlesGlobalManager::appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options)
|
||||||
{
|
{
|
||||||
SG_LOG(SG_PARTICLES, SG_DEBUG,
|
SG_LOG(SG_PARTICLES, SG_DEBUG,
|
||||||
"Setting up a particle system." << std::boolalpha
|
"Setting up a particle system." << std::boolalpha
|
||||||
@ -152,7 +381,7 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
|
|||||||
<< "\n Wind: " << configNode->getChild("program")->getBoolValue("wind", true)
|
<< "\n Wind: " << configNode->getChild("program")->getBoolValue("wind", true)
|
||||||
<< std::noboolalpha);
|
<< std::noboolalpha);
|
||||||
|
|
||||||
osgParticle::ParticleSystem *particleSys;
|
osg::ref_ptr<osgParticle::ParticleSystem> particleSys;
|
||||||
|
|
||||||
//create a generic particle system
|
//create a generic particle system
|
||||||
std::string type = configNode->getStringValue("type", "normal");
|
std::string type = configNode->getStringValue("type", "normal");
|
||||||
@ -160,11 +389,10 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
|
|||||||
particleSys = new osgParticle::ParticleSystem;
|
particleSys = new osgParticle::ParticleSystem;
|
||||||
else
|
else
|
||||||
particleSys = new osgParticle::ConnectedParticleSystem;
|
particleSys = new osgParticle::ConnectedParticleSystem;
|
||||||
|
|
||||||
//may not be used depending on the configuration
|
//may not be used depending on the configuration
|
||||||
PointerGuard<Particles> callback;
|
PointerGuard<Particles> callback;
|
||||||
|
|
||||||
getPSU()->addParticleSystem(particleSys);
|
|
||||||
getPSU()->setUpdateCallback(new GlobalParticleCallback(modelRoot));
|
|
||||||
//contains counter, placer and shooter by default
|
//contains counter, placer and shooter by default
|
||||||
osgParticle::ModularEmitter* emitter = new osgParticle::ModularEmitter;
|
osgParticle::ModularEmitter* emitter = new osgParticle::ModularEmitter;
|
||||||
|
|
||||||
@ -172,7 +400,7 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
|
|||||||
|
|
||||||
// Set up the alignment node ("stolen" from animation.cxx)
|
// Set up the alignment node ("stolen" from animation.cxx)
|
||||||
// XXX Order of rotations is probably not correct.
|
// XXX Order of rotations is probably not correct.
|
||||||
osg::MatrixTransform *align = new osg::MatrixTransform;
|
osg::ref_ptr<osg::MatrixTransform> align = new osg::MatrixTransform;
|
||||||
osg::Matrix res_matrix;
|
osg::Matrix res_matrix;
|
||||||
res_matrix.makeRotate(
|
res_matrix.makeRotate(
|
||||||
configNode->getFloatValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
|
configNode->getFloatValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
|
||||||
@ -187,12 +415,8 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
|
|||||||
configNode->getFloatValue("offsets/y-m", 0.0),
|
configNode->getFloatValue("offsets/y-m", 0.0),
|
||||||
configNode->getFloatValue("offsets/z-m", 0.0));
|
configNode->getFloatValue("offsets/z-m", 0.0));
|
||||||
align->setMatrix(res_matrix * tmat);
|
align->setMatrix(res_matrix * tmat);
|
||||||
|
|
||||||
align->setName("particle align");
|
align->setName("particle align");
|
||||||
|
|
||||||
//if (dynamic_cast<CustomModularEmitter*>(emitter)==0) SG_LOG(SG_PARTICLES, SG_ALERT, "observer error\n");
|
|
||||||
//align->addObserver(dynamic_cast<CustomModularEmitter*>(emitter));
|
|
||||||
|
|
||||||
align->addChild(emitter);
|
align->addChild(emitter);
|
||||||
|
|
||||||
//this name can be used in the XML animation as if it was a submodel
|
//this name can be used in the XML animation as if it was a submodel
|
||||||
@ -209,7 +433,6 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
|
|||||||
osg::Geode* g = new osg::Geode;
|
osg::Geode* g = new osg::Geode;
|
||||||
g->addDrawable(particleSys);
|
g->addDrawable(particleSys);
|
||||||
callback()->particleFrame->addChild(g);
|
callback()->particleFrame->addChild(g);
|
||||||
getCommonRoot()->addChild(callback()->particleFrame.get());
|
|
||||||
}
|
}
|
||||||
std::string textureFile;
|
std::string textureFile;
|
||||||
if (configNode->hasValue("texture")) {
|
if (configNode->hasValue("texture")) {
|
||||||
@ -505,66 +728,38 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
|
|||||||
emitter->setUpdateCallback(callback.get());
|
emitter->setUpdateCallback(callback.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// touch shared data now (and not before)
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> g(d->_lock);
|
||||||
|
d->_updater->addParticleSystem(particleSys);
|
||||||
|
|
||||||
|
if (attach != "local") {
|
||||||
|
d->internalGetCommonRoot()->addChild(callback()->particleFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!d->_globalCallbackRegistered) {
|
||||||
|
SG_LOG(SG_PARTICLES, SG_INFO, "Registering global particles callback");
|
||||||
|
d->_globalCallbackRegistered = true;
|
||||||
|
d->_longitudeNode = modelRoot->getNode("/position/longitude-deg", true);
|
||||||
|
d->_latitudeNode = modelRoot->getNode("/position/latitude-deg", true);
|
||||||
|
d->_updater->setUpdateCallback(d.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return align;
|
return align;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
void ParticlesGlobalManager::setSwitchNode(const SGPropertyNode* n)
|
||||||
{
|
{
|
||||||
//SG_LOG(SG_PARTICLES, SG_ALERT, "callback!\n");
|
std::lock_guard<std::mutex> g(d->_lock);
|
||||||
this->particleSys->setFrozen(_frozen);
|
d->_enabledNode = n;
|
||||||
|
}
|
||||||
|
|
||||||
using namespace osg;
|
void ParticlesGlobalManager::setFrozen(bool b)
|
||||||
if (shooterValue)
|
{
|
||||||
shooter->setInitialSpeedRange(shooterValue->getValue(),
|
std::lock_guard<std::mutex> g(d->_lock);
|
||||||
(shooterValue->getValue()
|
d->_frozen = b;
|
||||||
+ shooterExtraRange));
|
|
||||||
if (counterValue)
|
|
||||||
counter->setRateRange(counterValue->getValue(),
|
|
||||||
counterValue->getValue() + counterExtraRange);
|
|
||||||
else if (counterCond)
|
|
||||||
counter->setRateRange(counterStaticValue,
|
|
||||||
counterStaticValue + counterStaticExtraRange);
|
|
||||||
if (!GlobalParticleCallback::getEnabled() || (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( 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)
|
|
||||||
endSize = endSizeValue->getValue();
|
|
||||||
if (startSizeValue || endSizeValue)
|
|
||||||
particleSys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(startSize, endSize));
|
|
||||||
if (lifeValue)
|
|
||||||
particleSys->getDefaultParticleTemplate().setLifeTime(lifeValue->getValue());
|
|
||||||
|
|
||||||
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
|
} // namespace simgear
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// particles.hxx - classes to manage particles
|
// particles.hxx - classes to manage particles
|
||||||
// Copyright (C) 2008 Tiago Gusmão
|
// Copyright (C) 2008 Tiago Gusm<EFBFBD>o
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License as
|
// modify it under the terms of the GNU General Public License as
|
||||||
@ -53,133 +53,37 @@ class ParticleSystemUpdater;
|
|||||||
#include <simgear/math/SGMatrix.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"
|
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
|
|
||||||
class GlobalParticleCallback : public osg::NodeCallback
|
class ParticlesGlobalManager;
|
||||||
{
|
|
||||||
public:
|
|
||||||
GlobalParticleCallback(const SGPropertyNode* modelRoot)
|
|
||||||
{
|
|
||||||
this->modelRoot=modelRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
|
|
||||||
|
|
||||||
static const osg::Vec3 &getGravityVector()
|
|
||||||
{
|
|
||||||
return gravity;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const osg::Vec3 &getWindVector()
|
|
||||||
{
|
|
||||||
return wind;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setSwitch(const SGPropertyNode* n)
|
|
||||||
{
|
|
||||||
enabledNode = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool getEnabled()
|
|
||||||
{
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static osg::Vec3 gravity;
|
|
||||||
static osg::Vec3 wind;
|
|
||||||
SGConstPropertyNode_ptr modelRoot;
|
|
||||||
static SGConstPropertyNode_ptr enabledNode;
|
|
||||||
static bool enabled;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Particles : public osg::NodeCallback
|
class Particles : public osg::NodeCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Particles();
|
Particles() = default;
|
||||||
|
|
||||||
static osg::Group * appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options);
|
void operator()(osg::Node* node, osg::NodeVisitor* nv) override;
|
||||||
|
|
||||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
|
|
||||||
|
|
||||||
void setupShooterSpeedData(const SGPropertyNode* configNode,
|
void setupShooterSpeedData(const SGPropertyNode* configNode,
|
||||||
SGPropertyNode* modelRoot)
|
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,
|
void setupCounterData(const SGPropertyNode* configNode,
|
||||||
SGPropertyNode* modelRoot)
|
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,
|
void setupCounterCondition(const SGPropertyNode* configNode,
|
||||||
SGPropertyNode* modelRoot)
|
SGPropertyNode* modelRoot);
|
||||||
{
|
|
||||||
counterCond = sgReadCondition(modelRoot, configNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupCounterCondition(float counterStaticValue,
|
void setupCounterCondition(float counterStaticValue,
|
||||||
float counterStaticExtraRange)
|
float counterStaticExtraRange);
|
||||||
{
|
|
||||||
this->counterStaticValue = counterStaticValue;
|
|
||||||
this->counterStaticExtraRange = counterStaticExtraRange;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupStartSizeData(const SGPropertyNode* configNode,
|
void setupStartSizeData(const SGPropertyNode* configNode,
|
||||||
SGPropertyNode* modelRoot)
|
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,
|
void setupEndSizeData(const SGPropertyNode* configNode,
|
||||||
SGPropertyNode* modelRoot)
|
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,
|
void setupLifeData(const SGPropertyNode* configNode,
|
||||||
SGPropertyNode* modelRoot)
|
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)
|
void setupStaticSizeData(float startSize, float endSize)
|
||||||
{
|
{
|
||||||
@ -212,60 +116,14 @@ public:
|
|||||||
//component: r=0, g=1, ... ; color: 0=start color, 1=end color
|
//component: r=0, g=1, ... ; color: 0=start color, 1=end color
|
||||||
void setupColorComponent(const SGPropertyNode* configNode,
|
void setupColorComponent(const SGPropertyNode* configNode,
|
||||||
SGPropertyNode* modelRoot, int color,
|
SGPropertyNode* modelRoot, int color,
|
||||||
int component)
|
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,
|
void setupStaticColorComponent(float r1, float g1, float b1, float a1,
|
||||||
float r2, float g2, float b2, float a2)
|
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();
|
|
||||||
|
|
||||||
static osg::Geode * getCommonGeode()
|
|
||||||
{
|
|
||||||
return commonGeode.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
static osgParticle::ParticleSystemUpdater * getPSU()
|
|
||||||
{
|
|
||||||
return psu.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setFrozen(bool e) { _frozen = e; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set and get the wind vector for particles in the
|
|
||||||
* atmosphere. This vector is in the Z-up Y-north frame, and the
|
|
||||||
* magnitude is the velocity in meters per second.
|
|
||||||
*/
|
|
||||||
static void setWindVector(const osg::Vec3& wind) { _wind = wind; }
|
|
||||||
static void setWindFrom(const double from_deg, const double speed_kt) {
|
|
||||||
double map_rad = -from_deg * SG_DEGREES_TO_RADIANS;
|
|
||||||
double speed_mps = speed_kt * SG_KT_TO_MPS;
|
|
||||||
_wind[0] = cos(map_rad) * speed_mps;
|
|
||||||
_wind[1] = sin(map_rad) * speed_mps;
|
|
||||||
_wind[2] = 0.0;
|
|
||||||
}
|
|
||||||
static const osg::Vec3& getWindVector() { return _wind; }
|
|
||||||
protected:
|
protected:
|
||||||
|
friend class ParticlesGlobalManager;
|
||||||
|
|
||||||
float shooterExtraRange;
|
float shooterExtraRange;
|
||||||
float counterExtraRange;
|
float counterExtraRange;
|
||||||
SGSharedPtr<SGExpressiond> shooterValue;
|
SGSharedPtr<SGExpressiond> shooterValue;
|
||||||
@ -286,38 +144,53 @@ protected:
|
|||||||
osg::ref_ptr<osgParticle::FluidProgram> program;
|
osg::ref_ptr<osgParticle::FluidProgram> program;
|
||||||
osg::ref_ptr<osg::MatrixTransform> particleFrame;
|
osg::ref_ptr<osg::MatrixTransform> particleFrame;
|
||||||
|
|
||||||
bool useGravity;
|
bool useGravity = false;
|
||||||
bool useWind;
|
bool useWind = false;
|
||||||
static bool _frozen;
|
|
||||||
static osg::ref_ptr<osgParticle::ParticleSystemUpdater> psu;
|
|
||||||
static osg::ref_ptr<osg::Group> commonRoot;
|
|
||||||
static osg::ref_ptr<osg::Geode> commonGeode;
|
|
||||||
static osg::Vec3 _wind;
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
class ParticlesGlobalManager
|
||||||
/*
|
|
||||||
class CustomModularEmitter : public osgParticle::ModularEmitter, public osg::Observer
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
~ParticlesGlobalManager();
|
||||||
|
|
||||||
virtual void objectDeleted (void *)
|
static ParticlesGlobalManager* instance();
|
||||||
{
|
static void clear();
|
||||||
SG_LOG(SG_GENERAL, SG_ALERT, "object deleted!\n");
|
|
||||||
|
|
||||||
SGParticles::getCommonRoot()->removeChild(this->getParticleSystem()->getParent(0));
|
bool isEnabled() const;
|
||||||
SGParticles::getPSU()->removeParticleSystem(this->getParticleSystem());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Group> appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options);
|
||||||
|
|
||||||
// ~CustomModularEmitter()
|
osg::Group* getCommonRoot();
|
||||||
// {
|
|
||||||
// SG_LOG(SG_GENERAL, SG_ALERT, "object deleted!\n");
|
osg::Geode* getCommonGeode();
|
||||||
//
|
|
||||||
// SGParticles::getCommonRoot()->removeChild(this->getParticleSystem()->getParent(0));
|
osgParticle::ParticleSystemUpdater* getPSU();
|
||||||
// SGParticles::getPSU()->removeParticleSystem(this->getParticleSystem());
|
|
||||||
// }
|
void setFrozen(bool e);
|
||||||
};
|
bool isFrozen() const;
|
||||||
|
|
||||||
|
void setSwitchNode(const SGPropertyNode* n);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set and get the wind vector for particles in the
|
||||||
|
* atmosphere. This vector is in the Z-up Y-north frame, and the
|
||||||
|
* magnitude is the velocity in meters per second.
|
||||||
*/
|
*/
|
||||||
|
void setWindVector(const osg::Vec3& wind);
|
||||||
|
void setWindFrom(const double from_deg, const double speed_kt);
|
||||||
|
|
||||||
|
osg::Vec3 getWindVector() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ParticlesGlobalManager();
|
||||||
|
|
||||||
|
class ParticlesGlobalManagerPrivate;
|
||||||
|
|
||||||
|
// because Private inherits NodeCallback, we need to own it
|
||||||
|
// via an osg::ref_ptr
|
||||||
|
osg::ref_ptr<ParticlesGlobalManagerPrivate> d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace simgear
|
||||||
|
|
||||||
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user