mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-03 16:19:41 +00:00
Merge pull request #2811 from Capostrophic/emitter
Allow particle emitters to be attached to an arbitrary node
This commit is contained in:
commit
bf6daa7269
3 changed files with 56 additions and 28 deletions
components
|
@ -231,6 +231,9 @@ namespace NifOsg
|
||||||
size_t mFirstRootTextureIndex = -1;
|
size_t mFirstRootTextureIndex = -1;
|
||||||
bool mFoundFirstRootTexturingProperty = false;
|
bool mFoundFirstRootTexturingProperty = false;
|
||||||
|
|
||||||
|
// This is used to queue emitters that weren't attached to their node yet.
|
||||||
|
std::vector<std::pair<size_t, osg::ref_ptr<Emitter>>> mEmitterQueue;
|
||||||
|
|
||||||
static void loadKf(Nif::NIFFilePtr nif, KeyframeHolder& target)
|
static void loadKf(Nif::NIFFilePtr nif, KeyframeHolder& target)
|
||||||
{
|
{
|
||||||
if(nif->numRoots() < 1)
|
if(nif->numRoots() < 1)
|
||||||
|
@ -302,6 +305,9 @@ namespace NifOsg
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> created = handleNode(nifNode, nullptr, imageManager, std::vector<unsigned int>(), 0, false, false, false, &textkeys->mTextKeys);
|
osg::ref_ptr<osg::Node> created = handleNode(nifNode, nullptr, imageManager, std::vector<unsigned int>(), 0, false, false, false, &textkeys->mTextKeys);
|
||||||
|
|
||||||
|
// Attach particle emitters to their nodes which should all be loaded by now.
|
||||||
|
handleQueuedParticleEmitters(created, nif);
|
||||||
|
|
||||||
if (nif->getUseSkinning())
|
if (nif->getUseSkinning())
|
||||||
{
|
{
|
||||||
osg::ref_ptr<SceneUtil::Skeleton> skel = new SceneUtil::Skeleton;
|
osg::ref_ptr<SceneUtil::Skeleton> skel = new SceneUtil::Skeleton;
|
||||||
|
@ -994,6 +1000,27 @@ namespace NifOsg
|
||||||
return emitter;
|
return emitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleQueuedParticleEmitters(osg::Node* rootNode, Nif::NIFFilePtr nif)
|
||||||
|
{
|
||||||
|
for (const auto& emitterPair : mEmitterQueue)
|
||||||
|
{
|
||||||
|
size_t recIndex = emitterPair.first;
|
||||||
|
FindGroupByRecIndex findEmitterNode(recIndex);
|
||||||
|
rootNode->accept(findEmitterNode);
|
||||||
|
osg::Group* emitterNode = findEmitterNode.mFound;
|
||||||
|
if (!emitterNode)
|
||||||
|
{
|
||||||
|
nif->warn("Failed to find particle emitter emitter node (node record index " + std::to_string(recIndex) + ")");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emitter attached to the emitter node. Note one side effect of the emitter using the CullVisitor is that hiding its node
|
||||||
|
// actually causes the emitter to stop firing. Convenient, because MW behaves this way too!
|
||||||
|
emitterNode->addChild(emitterPair.second);
|
||||||
|
}
|
||||||
|
mEmitterQueue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void handleParticleSystem(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, int animflags, osg::Node* rootNode)
|
void handleParticleSystem(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, int animflags, osg::Node* rootNode)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<ParticleSystem> partsys (new ParticleSystem);
|
osg::ref_ptr<ParticleSystem> partsys (new ParticleSystem);
|
||||||
|
@ -1043,22 +1070,10 @@ namespace NifOsg
|
||||||
emitter->setParticleSystem(partsys);
|
emitter->setParticleSystem(partsys);
|
||||||
emitter->setReferenceFrame(osgParticle::ParticleProcessor::RELATIVE_RF);
|
emitter->setReferenceFrame(osgParticle::ParticleProcessor::RELATIVE_RF);
|
||||||
|
|
||||||
// Note: we assume that the Emitter node is placed *before* the Particle node in the scene graph.
|
// The emitter node may not actually be handled yet, so let's delay attaching the emitter to a later moment.
|
||||||
// This seems to be true for all NIF files in the game that I've checked, suggesting that NIFs work similar to OSG with regards to update order.
|
// If the emitter node is placed later than the particle node, it'll have a single frame delay in particle processing.
|
||||||
// If something ever violates this assumption, the worst that could happen is the culling being one frame late, which wouldn't be a disaster.
|
// But that shouldn't be a game-breaking issue.
|
||||||
|
mEmitterQueue.emplace_back(partctrl->emitter->recIndex, emitter);
|
||||||
FindGroupByRecIndex find (partctrl->emitter->recIndex);
|
|
||||||
rootNode->accept(find);
|
|
||||||
if (!find.mFound)
|
|
||||||
{
|
|
||||||
Log(Debug::Info) << "can't find emitter node, wrong node order? in " << mFilename;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
osg::Group* emitterNode = find.mFound;
|
|
||||||
|
|
||||||
// Emitter attached to the emitter node. Note one side effect of the emitter using the CullVisitor is that hiding its node
|
|
||||||
// actually causes the emitter to stop firing. Convenient, because MW behaves this way too!
|
|
||||||
emitterNode->addChild(emitter);
|
|
||||||
|
|
||||||
osg::ref_ptr<ParticleSystemController> callback(new ParticleSystemController(partctrl));
|
osg::ref_ptr<ParticleSystemController> callback(new ParticleSystemController(partctrl));
|
||||||
setupController(partctrl, callback, animflags);
|
setupController(partctrl, callback, animflags);
|
||||||
|
@ -1075,7 +1090,7 @@ namespace NifOsg
|
||||||
partsys->update(0.0, nv);
|
partsys->update(0.0, nv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// affectors must be attached *after* the emitter in the scene graph for correct update order
|
// affectors should be attached *after* the emitter in the scene graph for correct update order
|
||||||
// attach to same node as the ParticleSystem, we need osgParticle Operators to get the correct
|
// attach to same node as the ParticleSystem, we need osgParticle Operators to get the correct
|
||||||
// localToWorldMatrix for transforming to particle space
|
// localToWorldMatrix for transforming to particle space
|
||||||
handleParticlePrograms(partctrl->affectors, partctrl->colliders, parentNode, partsys.get(), rf);
|
handleParticlePrograms(partctrl->affectors, partctrl->colliders, parentNode, partsys.get(), rf);
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace SceneUtil
|
||||||
if (const osgParticle::ParticleSystemUpdater* updater = dynamic_cast<const osgParticle::ParticleSystemUpdater*>(node))
|
if (const osgParticle::ParticleSystemUpdater* updater = dynamic_cast<const osgParticle::ParticleSystemUpdater*>(node))
|
||||||
{
|
{
|
||||||
osgParticle::ParticleSystemUpdater* cloned = new osgParticle::ParticleSystemUpdater(*updater, osg::CopyOp::SHALLOW_COPY);
|
osgParticle::ParticleSystemUpdater* cloned = new osgParticle::ParticleSystemUpdater(*updater, osg::CopyOp::SHALLOW_COPY);
|
||||||
mMap2[cloned] = updater->getParticleSystem(0);
|
mUpdaterToOldPs[cloned] = updater->getParticleSystem(0);
|
||||||
return cloned;
|
return cloned;
|
||||||
}
|
}
|
||||||
return osg::CopyOp::operator()(node);
|
return osg::CopyOp::operator()(node);
|
||||||
|
@ -69,7 +69,16 @@ namespace SceneUtil
|
||||||
osgParticle::ParticleProcessor* CopyOp::operator() (const osgParticle::ParticleProcessor* processor) const
|
osgParticle::ParticleProcessor* CopyOp::operator() (const osgParticle::ParticleProcessor* processor) const
|
||||||
{
|
{
|
||||||
osgParticle::ParticleProcessor* cloned = osg::clone(processor, osg::CopyOp::DEEP_COPY_CALLBACKS);
|
osgParticle::ParticleProcessor* cloned = osg::clone(processor, osg::CopyOp::DEEP_COPY_CALLBACKS);
|
||||||
mMap[cloned] = processor->getParticleSystem();
|
for (const auto& oldPsNewPsPair : mOldPsToNewPs)
|
||||||
|
{
|
||||||
|
if (processor->getParticleSystem() == oldPsNewPsPair.first)
|
||||||
|
{
|
||||||
|
cloned->setParticleSystem(oldPsNewPsPair.second);
|
||||||
|
return cloned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mProcessorToOldPs[cloned] = processor->getParticleSystem();
|
||||||
return cloned;
|
return cloned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,22 +86,25 @@ namespace SceneUtil
|
||||||
{
|
{
|
||||||
osgParticle::ParticleSystem* cloned = osg::clone(partsys, *this);
|
osgParticle::ParticleSystem* cloned = osg::clone(partsys, *this);
|
||||||
|
|
||||||
for (std::map<osgParticle::ParticleProcessor*, const osgParticle::ParticleSystem*>::const_iterator it = mMap.begin(); it != mMap.end(); ++it)
|
for (const auto& processorPsPair : mProcessorToOldPs)
|
||||||
{
|
{
|
||||||
if (it->second == partsys)
|
if (processorPsPair.second == partsys)
|
||||||
{
|
{
|
||||||
it->first->setParticleSystem(cloned);
|
processorPsPair.first->setParticleSystem(cloned);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (std::map<osgParticle::ParticleSystemUpdater*, const osgParticle::ParticleSystem*>::const_iterator it = mMap2.begin(); it != mMap2.end(); ++it)
|
for (const auto& updaterPsPair : mUpdaterToOldPs)
|
||||||
{
|
{
|
||||||
if (it->second == partsys)
|
if (updaterPsPair.second == partsys)
|
||||||
{
|
{
|
||||||
osgParticle::ParticleSystemUpdater* updater = it->first;
|
osgParticle::ParticleSystemUpdater* updater = updaterPsPair.first;
|
||||||
updater->removeParticleSystem(updater->getParticleSystem(0));
|
updater->removeParticleSystem(updater->getParticleSystem(0));
|
||||||
updater->addParticleSystem(cloned);
|
updater->addParticleSystem(cloned);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// In rare situations a particle processor may be placed after the particle system in the scene graph.
|
||||||
|
mOldPsToNewPs[partsys] = cloned;
|
||||||
|
|
||||||
return cloned;
|
return cloned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,11 @@ namespace SceneUtil
|
||||||
virtual osg::Object* operator ()(const osg::Object* node) const;
|
virtual osg::Object* operator ()(const osg::Object* node) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// maps new ParticleProcessor to their old ParticleSystem pointer
|
// maps new pointers to their old pointers
|
||||||
// a little messy, but I think this should be the most efficient way
|
// a little messy, but I think this should be the most efficient way
|
||||||
mutable std::map<osgParticle::ParticleProcessor*, const osgParticle::ParticleSystem*> mMap;
|
mutable std::map<osgParticle::ParticleProcessor*, const osgParticle::ParticleSystem*> mProcessorToOldPs;
|
||||||
mutable std::map<osgParticle::ParticleSystemUpdater*, const osgParticle::ParticleSystem*> mMap2;
|
mutable std::map<osgParticle::ParticleSystemUpdater*, const osgParticle::ParticleSystem*> mUpdaterToOldPs;
|
||||||
|
mutable std::map<const osgParticle::ParticleSystem*, osgParticle::ParticleSystem*> mOldPsToNewPs;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue