Handle multiple root nodes (bug #5604)

pull/3029/head
Alexei Dobrohotov 4 years ago
parent 96e22bd44e
commit 5b6377b061

@ -58,6 +58,7 @@
Bug #5557: Diagonal movement is noticeably slower with analogue stick Bug #5557: Diagonal movement is noticeably slower with analogue stick
Bug #5588: Randomly clicking on the journal's right-side page when it's empty shows random topics Bug #5588: Randomly clicking on the journal's right-side page when it's empty shows random topics
Bug #5603: Setting constant effect cast style doesn't correct effects view Bug #5603: Setting constant effect cast style doesn't correct effects view
Bug #5604: Only one valid NIF root node is loaded from a single file
Bug #5611: Usable items with "0 Uses" should be used only once Bug #5611: Usable items with "0 Uses" should be used only once
Bug #5622: Can't properly interact with the console when in pause menu Bug #5622: Can't properly interact with the console when in pause menu
Bug #5633: Damage Spells in effect before god mode is enabled continue to hurt the player character and can kill them Bug #5633: Damage Spells in effect before god mode is enabled continue to hurt the player character and can kill them

@ -123,73 +123,79 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
mStaticMesh.reset(); mStaticMesh.reset();
mAvoidStaticMesh.reset(); mAvoidStaticMesh.reset();
Nif::Node* node = nullptr;
const size_t numRoots = nif.numRoots(); const size_t numRoots = nif.numRoots();
std::vector<Nif::Node*> roots;
for (size_t i = 0; i < numRoots; ++i) for (size_t i = 0; i < numRoots; ++i)
{ {
Nif::Record* r = nif.getRoot(i); Nif::Record* r = nif.getRoot(i);
assert(r != nullptr); assert(r != nullptr);
Nif::Node* node = nullptr;
if ((node = dynamic_cast<Nif::Node*>(r))) if ((node = dynamic_cast<Nif::Node*>(r)))
break; roots.emplace_back(node);
} }
const std::string filename = nif.getFilename(); const std::string filename = nif.getFilename();
if (!node) if (roots.empty())
{ {
warn("Found no root nodes in NIF file " + filename); warn("Found no root nodes in NIF file " + filename);
return mShape; return mShape;
} }
if (findBoundingBox(node, filename)) // Try to find a valid bounding box first. If one's found for any root node, use that.
for (const Nif::Node* node : roots)
{ {
const btVector3 halfExtents = Misc::Convert::toBullet(mShape->mCollisionBoxHalfExtents); if (findBoundingBox(node, filename))
const btVector3 origin = Misc::Convert::toBullet(mShape->mCollisionBoxTranslate); {
std::unique_ptr<btCompoundShape> compound (new btCompoundShape); const btVector3 halfExtents = Misc::Convert::toBullet(mShape->mCollisionBoxHalfExtents);
std::unique_ptr<btBoxShape> boxShape(new btBoxShape(halfExtents)); const btVector3 origin = Misc::Convert::toBullet(mShape->mCollisionBoxTranslate);
btTransform transform = btTransform::getIdentity(); std::unique_ptr<btCompoundShape> compound (new btCompoundShape);
transform.setOrigin(origin); std::unique_ptr<btBoxShape> boxShape(new btBoxShape(halfExtents));
compound->addChildShape(transform, boxShape.get()); btTransform transform = btTransform::getIdentity();
boxShape.release(); transform.setOrigin(origin);
compound->addChildShape(transform, boxShape.get());
mShape->mCollisionShape = compound.release(); boxShape.release();
return mShape;
mShape->mCollisionShape = compound.release();
return mShape;
}
} }
else // files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource).
// assume all nodes in the file will be animated
const bool isAnimated = pathFileNameStartsWithX(filename);
// If there's no bounding box, we'll have to generate a Bullet collision shape
// from the collision data present in every root node.
for (const Nif::Node* node : roots)
{ {
bool autogenerated = hasAutoGeneratedCollision(node); bool autogenerated = hasAutoGeneratedCollision(node);
// files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource).
// assume all nodes in the file will be animated
const bool isAnimated = pathFileNameStartsWithX(filename);
handleNode(filename, node, 0, autogenerated, isAnimated, autogenerated); handleNode(filename, node, 0, autogenerated, isAnimated, autogenerated);
}
if (mCompoundShape) if (mCompoundShape)
{ {
if (mStaticMesh) if (mStaticMesh)
{
btTransform trans;
trans.setIdentity();
std::unique_ptr<btCollisionShape> child(new Resource::TriangleMeshShape(mStaticMesh.get(), true));
mCompoundShape->addChildShape(trans, child.get());
child.release();
mStaticMesh.release();
}
mShape->mCollisionShape = mCompoundShape.release();
}
else if (mStaticMesh)
{ {
mShape->mCollisionShape = new Resource::TriangleMeshShape(mStaticMesh.get(), true); btTransform trans;
trans.setIdentity();
std::unique_ptr<btCollisionShape> child(new Resource::TriangleMeshShape(mStaticMesh.get(), true));
mCompoundShape->addChildShape(trans, child.get());
child.release();
mStaticMesh.release(); mStaticMesh.release();
} }
mShape->mCollisionShape = mCompoundShape.release();
}
else if (mStaticMesh)
{
mShape->mCollisionShape = new Resource::TriangleMeshShape(mStaticMesh.get(), true);
mStaticMesh.release();
}
if (mAvoidStaticMesh) if (mAvoidStaticMesh)
{ {
mShape->mAvoidCollisionShape = new Resource::TriangleMeshShape(mAvoidStaticMesh.get(), false); mShape->mAvoidCollisionShape = new Resource::TriangleMeshShape(mAvoidStaticMesh.get(), false);
mAvoidStaticMesh.release(); mAvoidStaticMesh.release();
}
return mShape;
} }
return mShape;
} }
// Find a boundingBox in the node hierarchy. // Find a boundingBox in the node hierarchy.
@ -228,8 +234,7 @@ bool BulletNifLoader::findBoundingBox(const Nif::Node* node, const std::string&
{ {
if(!list[i].empty()) if(!list[i].empty())
{ {
bool found = findBoundingBox (list[i].getPtr(), filename); if (findBoundingBox(list[i].getPtr(), filename))
if (found)
return true; return true;
} }
} }

@ -231,6 +231,9 @@ namespace NifOsg
size_t mFirstRootTextureIndex = -1; size_t mFirstRootTextureIndex = -1;
bool mFoundFirstRootTexturingProperty = false; bool mFoundFirstRootTexturingProperty = false;
bool mHasNightDayLabel = false;
bool mHasHerbalismLabel = false;
// This is used to queue emitters that weren't attached to their node yet. // 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; std::vector<std::pair<size_t, osg::ref_ptr<Emitter>>> mEmitterQueue;
@ -294,20 +297,31 @@ namespace NifOsg
osg::ref_ptr<osg::Node> load(Nif::NIFFilePtr nif, Resource::ImageManager* imageManager) osg::ref_ptr<osg::Node> load(Nif::NIFFilePtr nif, Resource::ImageManager* imageManager)
{ {
const Nif::Node* nifNode = nullptr;
const size_t numRoots = nif->numRoots(); const size_t numRoots = nif->numRoots();
std::vector<const Nif::Node*> roots;
for (size_t i = 0; i < numRoots; ++i) for (size_t i = 0; i < numRoots; ++i)
{ {
const Nif::Record* r = nif->getRoot(i); const Nif::Record* r = nif->getRoot(i);
const Nif::Node* nifNode = nullptr;
if ((nifNode = dynamic_cast<const Nif::Node*>(r))) if ((nifNode = dynamic_cast<const Nif::Node*>(r)))
break; roots.emplace_back(nifNode);
} }
if (!nifNode) if (roots.empty())
nif->fail("Found no root nodes"); nif->fail("Found no root nodes");
osg::ref_ptr<TextKeyMapHolder> textkeys (new TextKeyMapHolder); osg::ref_ptr<TextKeyMapHolder> textkeys (new TextKeyMapHolder);
osg::ref_ptr<osg::Node> created = handleNode(nifNode, nullptr, imageManager, std::vector<unsigned int>(), 0, false, false, false, &textkeys->mTextKeys); osg::ref_ptr<osg::Group> created(new osg::Group);
created->setDataVariance(osg::Object::STATIC);
for (const Nif::Node* root : roots)
{
auto node = handleNode(root, nullptr, imageManager, std::vector<unsigned int>(), 0, false, false, false, &textkeys->mTextKeys);
created->addChild(node);
}
if (mHasNightDayLabel)
created->getOrCreateUserDataContainer()->addDescription(Constants::NightDayLabel);
if (mHasHerbalismLabel)
created->getOrCreateUserDataContainer()->addDescription(Constants::HerbalismLabel);
// Attach particle emitters to their nodes which should all be loaded by now. // Attach particle emitters to their nodes which should all be loaded by now.
handleQueuedParticleEmitters(created, nif); handleQueuedParticleEmitters(created, nif);
@ -315,18 +329,11 @@ namespace NifOsg
if (nif->getUseSkinning()) if (nif->getUseSkinning())
{ {
osg::ref_ptr<SceneUtil::Skeleton> skel = new SceneUtil::Skeleton; osg::ref_ptr<SceneUtil::Skeleton> skel = new SceneUtil::Skeleton;
skel->setStateSet(created->getStateSet());
osg::Group* root = created->asGroup(); skel->setName(created->getName());
if (root && root->getDataVariance() == osg::Object::STATIC && !root->asTransform()) for (unsigned int i=0; i < created->getNumChildren(); ++i)
{ skel->addChild(created->getChild(i));
skel->setStateSet(root->getStateSet()); created->removeChildren(0, created->getNumChildren());
skel->setName(root->getName());
for (unsigned int i=0; i<root->getNumChildren(); ++i)
skel->addChild(root->getChild(i));
root->removeChildren(0, root->getNumChildren());
}
else
skel->addChild(created);
created = skel; created = skel;
} }
@ -632,7 +639,7 @@ namespace NifOsg
} }
if(nifNode->recType == Nif::RC_NiAutoNormalParticles || nifNode->recType == Nif::RC_NiRotatingParticles) if(nifNode->recType == Nif::RC_NiAutoNormalParticles || nifNode->recType == Nif::RC_NiRotatingParticles)
handleParticleSystem(nifNode, node, composite, animflags, rootNode); handleParticleSystem(nifNode, node, composite, animflags);
if (composite->getNumControllers() > 0) if (composite->getNumControllers() > 0)
{ {
@ -662,10 +669,10 @@ namespace NifOsg
const Nif::NiSwitchNode* niSwitchNode = static_cast<const Nif::NiSwitchNode*>(nifNode); const Nif::NiSwitchNode* niSwitchNode = static_cast<const Nif::NiSwitchNode*>(nifNode);
osg::ref_ptr<osg::Switch> switchNode = handleSwitchNode(niSwitchNode); osg::ref_ptr<osg::Switch> switchNode = handleSwitchNode(niSwitchNode);
node->addChild(switchNode); node->addChild(switchNode);
if (niSwitchNode->name == Constants::NightDayLabel && !SceneUtil::hasUserDescription(rootNode, Constants::NightDayLabel)) if (niSwitchNode->name == Constants::NightDayLabel)
rootNode->getOrCreateUserDataContainer()->addDescription(Constants::NightDayLabel); mHasNightDayLabel = true;
else if (niSwitchNode->name == Constants::HerbalismLabel && !SceneUtil::hasUserDescription(rootNode, Constants::HerbalismLabel)) else if (niSwitchNode->name == Constants::HerbalismLabel)
rootNode->getOrCreateUserDataContainer()->addDescription(Constants::HerbalismLabel); mHasHerbalismLabel = true;
currentNode = switchNode; currentNode = switchNode;
} }
@ -1023,7 +1030,7 @@ namespace NifOsg
return emitter; return emitter;
} }
void handleQueuedParticleEmitters(osg::Node* rootNode, Nif::NIFFilePtr nif) void handleQueuedParticleEmitters(osg::Group* rootNode, Nif::NIFFilePtr nif)
{ {
for (const auto& emitterPair : mEmitterQueue) for (const auto& emitterPair : mEmitterQueue)
{ {
@ -1044,7 +1051,7 @@ namespace NifOsg
mEmitterQueue.clear(); 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::ref_ptr<ParticleSystem> partsys (new ParticleSystem); osg::ref_ptr<ParticleSystem> partsys (new ParticleSystem);
partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT); partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT);

Loading…
Cancel
Save