|
|
|
@ -219,6 +219,9 @@ namespace NifOsg
|
|
|
|
|
size_t mFirstRootTextureIndex = -1;
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
if(nif->numRoots() < 1)
|
|
|
|
@ -290,6 +293,9 @@ namespace NifOsg
|
|
|
|
|
|
|
|
|
|
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())
|
|
|
|
|
{
|
|
|
|
|
osg::ref_ptr<SceneUtil::Skeleton> skel = new SceneUtil::Skeleton;
|
|
|
|
@ -979,6 +985,27 @@ namespace NifOsg
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
osg::ref_ptr<ParticleSystem> partsys (new ParticleSystem);
|
|
|
|
@ -1028,22 +1055,8 @@ namespace NifOsg
|
|
|
|
|
emitter->setParticleSystem(partsys);
|
|
|
|
|
emitter->setReferenceFrame(osgParticle::ParticleProcessor::RELATIVE_RF);
|
|
|
|
|
|
|
|
|
|
// Note: we assume that the Emitter node is placed *before* the Particle node in the scene graph.
|
|
|
|
|
// 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 something ever violates this assumption, the worst that could happen is the culling being one frame late, which wouldn't be a disaster.
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
// The emitter node may not actually be handled yet, so let's delay attaching the emitter to a later moment.
|
|
|
|
|
mEmitterQueue.emplace_back(partctrl->emitter->recIndex, emitter);
|
|
|
|
|
|
|
|
|
|
osg::ref_ptr<ParticleSystemController> callback(new ParticleSystemController(partctrl));
|
|
|
|
|
setupController(partctrl, callback, animflags);
|
|
|
|
|