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 #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 #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 #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

@ -123,73 +123,79 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
mStaticMesh.reset();
mAvoidStaticMesh.reset();
Nif::Node* node = nullptr;
const size_t numRoots = nif.numRoots();
std::vector<Nif::Node*> roots;
for (size_t i = 0; i < numRoots; ++i)
{
Nif::Record* r = nif.getRoot(i);
assert(r != nullptr);
Nif::Node* node = nullptr;
if ((node = dynamic_cast<Nif::Node*>(r)))
break;
roots.emplace_back(node);
}
const std::string filename = nif.getFilename();
if (!node)
if (roots.empty())
{
warn("Found no root nodes in NIF file " + filename);
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);
const btVector3 origin = Misc::Convert::toBullet(mShape->mCollisionBoxTranslate);
std::unique_ptr<btCompoundShape> compound (new btCompoundShape);
std::unique_ptr<btBoxShape> boxShape(new btBoxShape(halfExtents));
btTransform transform = btTransform::getIdentity();
transform.setOrigin(origin);
compound->addChildShape(transform, boxShape.get());
boxShape.release();
mShape->mCollisionShape = compound.release();
return mShape;
if (findBoundingBox(node, filename))
{
const btVector3 halfExtents = Misc::Convert::toBullet(mShape->mCollisionBoxHalfExtents);
const btVector3 origin = Misc::Convert::toBullet(mShape->mCollisionBoxTranslate);
std::unique_ptr<btCompoundShape> compound (new btCompoundShape);
std::unique_ptr<btBoxShape> boxShape(new btBoxShape(halfExtents));
btTransform transform = btTransform::getIdentity();
transform.setOrigin(origin);
compound->addChildShape(transform, boxShape.get());
boxShape.release();
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);
// 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);
}
if (mCompoundShape)
{
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)
if (mCompoundShape)
{
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();
}
mShape->mCollisionShape = mCompoundShape.release();
}
else if (mStaticMesh)
{
mShape->mCollisionShape = new Resource::TriangleMeshShape(mStaticMesh.get(), true);
mStaticMesh.release();
}
if (mAvoidStaticMesh)
{
mShape->mAvoidCollisionShape = new Resource::TriangleMeshShape(mAvoidStaticMesh.get(), false);
mAvoidStaticMesh.release();
}
return mShape;
if (mAvoidStaticMesh)
{
mShape->mAvoidCollisionShape = new Resource::TriangleMeshShape(mAvoidStaticMesh.get(), false);
mAvoidStaticMesh.release();
}
return mShape;
}
// 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())
{
bool found = findBoundingBox (list[i].getPtr(), filename);
if (found)
if (findBoundingBox(list[i].getPtr(), filename))
return true;
}
}

@ -231,6 +231,9 @@ namespace NifOsg
size_t mFirstRootTextureIndex = -1;
bool mFoundFirstRootTexturingProperty = false;
bool mHasNightDayLabel = false;
bool mHasHerbalismLabel = 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;
@ -294,20 +297,31 @@ namespace NifOsg
osg::ref_ptr<osg::Node> load(Nif::NIFFilePtr nif, Resource::ImageManager* imageManager)
{
const Nif::Node* nifNode = nullptr;
const size_t numRoots = nif->numRoots();
std::vector<const Nif::Node*> roots;
for (size_t i = 0; i < numRoots; ++i)
{
const Nif::Record* r = nif->getRoot(i);
const Nif::Node* nifNode = nullptr;
if ((nifNode = dynamic_cast<const Nif::Node*>(r)))
break;
roots.emplace_back(nifNode);
}
if (!nifNode)
if (roots.empty())
nif->fail("Found no root nodes");
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.
handleQueuedParticleEmitters(created, nif);
@ -315,18 +329,11 @@ namespace NifOsg
if (nif->getUseSkinning())
{
osg::ref_ptr<SceneUtil::Skeleton> skel = new SceneUtil::Skeleton;
osg::Group* root = created->asGroup();
if (root && root->getDataVariance() == osg::Object::STATIC && !root->asTransform())
{
skel->setStateSet(root->getStateSet());
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);
skel->setStateSet(created->getStateSet());
skel->setName(created->getName());
for (unsigned int i=0; i < created->getNumChildren(); ++i)
skel->addChild(created->getChild(i));
created->removeChildren(0, created->getNumChildren());
created = skel;
}
@ -632,7 +639,7 @@ namespace NifOsg
}
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)
{
@ -662,10 +669,10 @@ namespace NifOsg
const Nif::NiSwitchNode* niSwitchNode = static_cast<const Nif::NiSwitchNode*>(nifNode);
osg::ref_ptr<osg::Switch> switchNode = handleSwitchNode(niSwitchNode);
node->addChild(switchNode);
if (niSwitchNode->name == Constants::NightDayLabel && !SceneUtil::hasUserDescription(rootNode, Constants::NightDayLabel))
rootNode->getOrCreateUserDataContainer()->addDescription(Constants::NightDayLabel);
else if (niSwitchNode->name == Constants::HerbalismLabel && !SceneUtil::hasUserDescription(rootNode, Constants::HerbalismLabel))
rootNode->getOrCreateUserDataContainer()->addDescription(Constants::HerbalismLabel);
if (niSwitchNode->name == Constants::NightDayLabel)
mHasNightDayLabel = true;
else if (niSwitchNode->name == Constants::HerbalismLabel)
mHasHerbalismLabel = true;
currentNode = switchNode;
}
@ -1023,7 +1030,7 @@ namespace NifOsg
return emitter;
}
void handleQueuedParticleEmitters(osg::Node* rootNode, Nif::NIFFilePtr nif)
void handleQueuedParticleEmitters(osg::Group* rootNode, Nif::NIFFilePtr nif)
{
for (const auto& emitterPair : mEmitterQueue)
{
@ -1044,7 +1051,7 @@ namespace NifOsg
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);
partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT);

Loading…
Cancel
Save