mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-30 09:15:38 +00:00
BulletNifLoader updates
Refactor root node, visual collision type and filename handling Only handle BSXFlags for the root, handle BSXFlags collision flag and absence Properly distinguish collision node and autogenerated flag
This commit is contained in:
parent
bb6fdc1e21
commit
40313019ef
2 changed files with 77 additions and 51 deletions
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <components/nif/data.hpp>
|
||||
#include <components/nif/extra.hpp>
|
||||
#include <components/nif/nifstream.hpp>
|
||||
#include <components/nif/node.hpp>
|
||||
#include <components/nif/parent.hpp>
|
||||
|
||||
|
@ -162,18 +163,17 @@ namespace NifBullet
|
|||
if (node)
|
||||
roots.emplace_back(node);
|
||||
}
|
||||
const std::string filename = Files::pathToUnicodeString(nif.getFilename());
|
||||
mShape->mFileName = filename;
|
||||
mShape->mFileName = Files::pathToUnicodeString(nif.getFilename());
|
||||
if (roots.empty())
|
||||
{
|
||||
warn("Found no root nodes in NIF file " + filename);
|
||||
warn("Found no root nodes in NIF file " + mShape->mFileName);
|
||||
return mShape;
|
||||
}
|
||||
|
||||
// Try to find a valid bounding box first. If one's found for any root node, use that.
|
||||
for (const Nif::NiAVObject* node : roots)
|
||||
{
|
||||
if (findBoundingBox(*node, filename))
|
||||
// Try to find a valid bounding box first. If one's found for any root node, use that.
|
||||
if (findBoundingBox(*node))
|
||||
{
|
||||
const btVector3 extents = Misc::Convert::toBullet(mShape->mCollisionBox.mExtents);
|
||||
const btVector3 center = Misc::Convert::toBullet(mShape->mCollisionBox.mCenter);
|
||||
|
@ -188,29 +188,18 @@ namespace NifBullet
|
|||
return mShape;
|
||||
}
|
||||
}
|
||||
|
||||
HandleNodeArgs args;
|
||||
|
||||
// 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
|
||||
// TODO: investigate whether this should and could be optimized.
|
||||
const bool isAnimated = pathFileNameStartsWithX(filename);
|
||||
args.mAnimated = pathFileNameStartsWithX(mShape->mFileName);
|
||||
|
||||
// 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::NiAVObject* node : roots)
|
||||
{
|
||||
const Nif::NiNode* colNode = findRootCollisionNode(*node);
|
||||
bool hasCollisionShape = false;
|
||||
if (colNode != nullptr)
|
||||
{
|
||||
if (colNode->mBounds.mType == Nif::BoundingVolume::Type::BASE_BV && !colNode->mChildren.empty())
|
||||
hasCollisionShape = true;
|
||||
else
|
||||
mShape->mVisualCollisionType = Resource::VisualCollisionType::Camera;
|
||||
}
|
||||
HandleNodeArgs args;
|
||||
args.mAutogenerated = args.mIsCollisionNode = !hasCollisionShape;
|
||||
args.mAnimated = isAnimated;
|
||||
handleNode(filename, *node, nullptr, args, mShape->mVisualCollisionType);
|
||||
}
|
||||
handleRoot(nif, *node, args);
|
||||
|
||||
if (mCompoundShape)
|
||||
mShape->mCollisionShape = std::move(mCompoundShape);
|
||||
|
@ -223,7 +212,7 @@ namespace NifBullet
|
|||
|
||||
// Find a boundingBox in the node hierarchy.
|
||||
// Return: use bounding box for collision?
|
||||
bool BulletNifLoader::findBoundingBox(const Nif::NiAVObject& node, const std::string& filename)
|
||||
bool BulletNifLoader::findBoundingBox(const Nif::NiAVObject& node)
|
||||
{
|
||||
unsigned int type = node.mBounds.mType;
|
||||
switch (type)
|
||||
|
@ -238,7 +227,7 @@ namespace NifBullet
|
|||
{
|
||||
std::stringstream warning;
|
||||
warning << "Unsupported BoundingVolume type " << type << " in node " << node.recIndex;
|
||||
warning << " in file " << filename;
|
||||
warning << " in file " << mShape->mFileName;
|
||||
warn(warning.str());
|
||||
}
|
||||
}
|
||||
|
@ -249,27 +238,70 @@ namespace NifBullet
|
|||
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&node))
|
||||
{
|
||||
for (const auto& child : ninode->mChildren)
|
||||
if (!child.empty() && findBoundingBox(child.get(), filename))
|
||||
if (!child.empty() && findBoundingBox(child.get()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const Nif::NiNode* BulletNifLoader::findRootCollisionNode(const Nif::NiAVObject& rootNode) const
|
||||
void BulletNifLoader::handleRoot(Nif::FileView nif, const Nif::NiAVObject& node, HandleNodeArgs args)
|
||||
{
|
||||
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&rootNode))
|
||||
// Gamebryo/Bethbryo meshes
|
||||
if (nif.getVersion() >= Nif::NIFStream::generateVersion(10, 0, 1, 0))
|
||||
{
|
||||
for (const auto& child : ninode->mChildren)
|
||||
// Handle BSXFlags
|
||||
const Nif::NiIntegerExtraData* bsxFlags = nullptr;
|
||||
for (const auto& e : node.getExtraList())
|
||||
{
|
||||
if (!child.empty() && child.getPtr()->recType == Nif::RC_RootCollisionNode)
|
||||
return static_cast<const Nif::NiNode*>(child.getPtr());
|
||||
if (e->recType == Nif::RC_BSXFlags)
|
||||
{
|
||||
bsxFlags = static_cast<const Nif::NiIntegerExtraData*>(e.getPtr());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Collision flag
|
||||
if (!bsxFlags || !(bsxFlags->mData & 2))
|
||||
return;
|
||||
|
||||
// Editor marker flag
|
||||
if (bsxFlags->mData & 32)
|
||||
args.mHasMarkers = true;
|
||||
|
||||
// FIXME: hack, using rendered geometry instead of Bethesda Havok data
|
||||
args.mAutogenerated = true;
|
||||
}
|
||||
// Pre-Gamebryo meshes
|
||||
else
|
||||
{
|
||||
// Handle RootCollisionNode
|
||||
const Nif::NiNode* colNode = nullptr;
|
||||
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&node))
|
||||
{
|
||||
for (const auto& child : ninode->mChildren)
|
||||
{
|
||||
if (!child.empty() && child.getPtr()->recType == Nif::RC_RootCollisionNode)
|
||||
{
|
||||
colNode = static_cast<const Nif::NiNode*>(child.getPtr());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
args.mAutogenerated = colNode == nullptr;
|
||||
|
||||
// FIXME: BulletNifLoader should never have to provide rendered geometry for camera collision
|
||||
if (colNode && colNode->mChildren.empty())
|
||||
{
|
||||
args.mAutogenerated = true;
|
||||
mShape->mVisualCollisionType = Resource::VisualCollisionType::Camera;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
handleNode(node, nullptr, args);
|
||||
}
|
||||
|
||||
void BulletNifLoader::handleNode(const std::string& fileName, const Nif::NiAVObject& node,
|
||||
const Nif::Parent* parent, HandleNodeArgs args, Resource::VisualCollisionType& visualCollisionType)
|
||||
void BulletNifLoader::handleNode(const Nif::NiAVObject& node, const Nif::Parent* parent, HandleNodeArgs args)
|
||||
{
|
||||
// TODO: allow on-the fly collision switching via toggling this flag
|
||||
if (node.recType == Nif::RC_NiCollisionSwitch && !node.collisionActive())
|
||||
|
@ -295,20 +327,23 @@ namespace NifBullet
|
|||
|
||||
if (node.recType == Nif::RC_RootCollisionNode)
|
||||
{
|
||||
args.mIsCollisionNode = true;
|
||||
if (args.mAutogenerated)
|
||||
{
|
||||
// Encountered a RootCollisionNode inside an autogenerated mesh.
|
||||
|
||||
// We treat empty RootCollisionNodes as NCC flag (set collisionType to `Camera`)
|
||||
// and generate the camera collision shape based on rendered geometry.
|
||||
if (visualCollisionType == Resource::VisualCollisionType::Camera)
|
||||
if (mShape->mVisualCollisionType == Resource::VisualCollisionType::Camera)
|
||||
return;
|
||||
|
||||
// Otherwise we'll want to notify the user.
|
||||
Log(Debug::Info) << "RootCollisionNode is not attached to the root node in " << fileName
|
||||
Log(Debug::Info) << "RootCollisionNode is not attached to the root node in " << mShape->mFileName
|
||||
<< ". Treating it as a common NiTriShape.";
|
||||
}
|
||||
else
|
||||
{
|
||||
args.mIsCollisionNode = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't collide with AvoidNode shapes
|
||||
|
@ -330,10 +365,10 @@ namespace NifBullet
|
|||
// uppercase
|
||||
if (sd->mData.length() > 2 && sd->mData[2] == 'C')
|
||||
// Collide only with camera.
|
||||
visualCollisionType = Resource::VisualCollisionType::Camera;
|
||||
mShape->mVisualCollisionType = Resource::VisualCollisionType::Camera;
|
||||
else
|
||||
// No collision.
|
||||
visualCollisionType = Resource::VisualCollisionType::Default;
|
||||
mShape->mVisualCollisionType = Resource::VisualCollisionType::Default;
|
||||
}
|
||||
// Don't autogenerate collision if MRK is set.
|
||||
// FIXME: verify if this covers the entire subtree
|
||||
|
@ -342,15 +377,9 @@ namespace NifBullet
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if (e->recType == Nif::RC_BSXFlags)
|
||||
{
|
||||
auto bsxFlags = static_cast<const Nif::NiIntegerExtraData*>(e.getPtr());
|
||||
if (bsxFlags->mData & 32) // Editor marker flag
|
||||
args.mHasMarkers = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (args.mIsCollisionNode)
|
||||
if (args.mAutogenerated || args.mIsCollisionNode)
|
||||
{
|
||||
// NOTE: a trishape with bounds, but no BBoxCollision flag should NOT go through handleNiTriShape!
|
||||
// It must be ignored completely.
|
||||
|
@ -369,7 +398,7 @@ namespace NifBullet
|
|||
continue;
|
||||
|
||||
assert(std::find(child->mParents.begin(), child->mParents.end(), ninode) != child->mParents.end());
|
||||
handleNode(fileName, child.get(), ¤tParent, args, visualCollisionType);
|
||||
handleNode(child.get(), ¤tParent, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace NifBullet
|
|||
osg::ref_ptr<Resource::BulletShape> load(Nif::FileView file);
|
||||
|
||||
private:
|
||||
bool findBoundingBox(const Nif::NiAVObject& node, const std::string& filename);
|
||||
bool findBoundingBox(const Nif::NiAVObject& node);
|
||||
|
||||
struct HandleNodeArgs
|
||||
{
|
||||
|
@ -59,11 +59,8 @@ namespace NifBullet
|
|||
bool mAvoid{ false };
|
||||
};
|
||||
|
||||
void handleNode(const std::string& fileName, const Nif::NiAVObject& node, const Nif::Parent* parent,
|
||||
HandleNodeArgs args, Resource::VisualCollisionType& visualCollisionType);
|
||||
|
||||
const Nif::NiNode* findRootCollisionNode(const Nif::NiAVObject& rootNode) const;
|
||||
|
||||
void handleRoot(Nif::FileView nif, const Nif::NiAVObject& node, HandleNodeArgs args);
|
||||
void handleNode(const Nif::NiAVObject& node, const Nif::Parent* parent, HandleNodeArgs args);
|
||||
void handleNiTriShape(const Nif::NiGeometry& nifNode, const Nif::Parent* parent, HandleNodeArgs args);
|
||||
|
||||
std::unique_ptr<btCompoundShape, Resource::DeleteCollisionShape> mCompoundShape;
|
||||
|
|
Loading…
Reference in a new issue