Support multiple parents for NIF nodes

Choose a parent base on which node is used to iterate over children nodes.
This leads to duplicate handing of child nodes. A node will be handled so many
times how many parents it has.

For example:
p1 p2
 \ /
  c

Will be handled as:
p1 p2
|  |
c  c

If c has children they will be handled X times c is handled.
ptmikheev-master-patch-38354
elsid 3 years ago
parent 063af50dee
commit 3f14011087
No known key found for this signature in database
GPG Key ID: B845CB9FEE18AB40

@ -277,7 +277,7 @@ namespace
value.flags = 0;
init(value.trafo);
value.hasBounds = false;
value.parent = nullptr;
value.parents.push_back(nullptr);
value.isBone = false;
}
@ -512,7 +512,7 @@ namespace
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
mNode.parent = &mNiNode;
mNode.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode)}));
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
@ -538,7 +538,7 @@ namespace
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
mNode.parent = &mNiNode;
mNode.parents.push_back(&mNiNode);
mNiNode.hasBounds = true;
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
@ -569,13 +569,13 @@ namespace
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
mNode.parent = &mNiNode;
mNode.parents.push_back(&mNiNode);
mNode2.hasBounds = true;
mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);
mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);
mNode2.parent = &mNiNode;
mNode2.parents.push_back(&mNiNode);
mNiNode.hasBounds = true;
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
@ -605,14 +605,14 @@ namespace
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
mNode.parent = &mNiNode;
mNode.parents.push_back(&mNiNode);
mNode2.hasBounds = true;
mNode2.flags |= Nif::NiNode::Flag_BBoxCollision;
mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);
mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);
mNode2.parent = &mNiNode;
mNode2.parents.push_back(&mNiNode);
mNiNode.hasBounds = true;
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
@ -691,7 +691,7 @@ namespace
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_should_return_shape_with_triangle_mesh_shape)
{
mNiTriShape.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
@ -710,9 +710,9 @@ namespace
TEST_F(TestBulletNifLoader, for_nested_tri_shape_child_should_return_shape_with_triangle_mesh_shape)
{
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiNode2)}));
mNiNode2.parent = &mNiNode;
mNiNode2.parents.push_back(&mNiNode);
mNiNode2.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
mNiTriShape.parent = &mNiNode2;
mNiTriShape.parents.push_back(&mNiNode2);
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
@ -729,8 +729,8 @@ namespace
TEST_F(TestBulletNifLoader, for_two_tri_shape_children_should_return_shape_with_triangle_mesh_shape_with_all_meshes)
{
mNiTriShape.parent = &mNiNode;
mNiTriShape2.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
mNiTriShape2.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({
Nif::NodePtr(&mNiTriShape),
Nif::NodePtr(&mNiTriShape2)
@ -753,7 +753,7 @@ namespace
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_and_filename_starting_with_x_and_not_empty_skin_should_return_shape_with_triangle_mesh_shape)
{
mNiTriShape.skin = Nif::NiSkinInstancePtr(&mNiSkinInstance);
mNiTriShape.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
@ -796,7 +796,7 @@ namespace
{
copy(mTransform, mNiTriShape.trafo);
mNiTriShape.trafo.scale = 3;
mNiTriShape.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
mNiNode.trafo.scale = 4;
@ -822,11 +822,11 @@ namespace
{
copy(mTransform, mNiTriShape.trafo);
mNiTriShape.trafo.scale = 3;
mNiTriShape.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
copy(mTransform, mNiTriShape2.trafo);
mNiTriShape2.trafo.scale = 3;
mNiTriShape2.parent = &mNiNode;
mNiTriShape2.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({
Nif::NodePtr(&mNiTriShape),
@ -864,7 +864,7 @@ namespace
mController.flags |= Nif::NiNode::ControllerFlag_Active;
copy(mTransform, mNiTriShape.trafo);
mNiTriShape.trafo.scale = 3;
mNiTriShape.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
mNiTriShape.controller = Nif::ControllerPtr(&mController);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
mNiNode.trafo.scale = 4;
@ -893,10 +893,10 @@ namespace
mController.flags |= Nif::NiNode::ControllerFlag_Active;
copy(mTransform, mNiTriShape.trafo);
mNiTriShape.trafo.scale = 3;
mNiTriShape.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
copy(mTransform, mNiTriShape2.trafo);
mNiTriShape2.trafo.scale = 3;
mNiTriShape2.parent = &mNiNode;
mNiTriShape2.parents.push_back(&mNiNode);
mNiTriShape2.controller = Nif::ControllerPtr(&mController);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({
Nif::NodePtr(&mNiTriShape),
@ -931,7 +931,7 @@ namespace
TEST_F(TestBulletNifLoader, should_add_static_mesh_to_existing_compound_mesh)
{
mNiTriShape.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(2));
@ -959,7 +959,7 @@ namespace
TEST_F(TestBulletNifLoader, for_root_avoid_node_and_tri_shape_child_node_should_return_shape_with_null_collision_shape)
{
mNiTriShape.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
mNiNode.recType = Nif::RC_AvoidNode;
@ -979,7 +979,7 @@ namespace
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_should_return_shape_with_null_collision_shape)
{
mNiTriShape.data = Nif::NiGeometryDataPtr(nullptr);
mNiTriShape.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
@ -996,7 +996,7 @@ namespace
{
auto data = static_cast<Nif::NiTriShapeData*>(mNiTriShape.data.getPtr());
data->triangles.clear();
mNiTriShape.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
@ -1014,7 +1014,7 @@ namespace
mNiStringExtraData.string = "NC___";
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
@ -1033,7 +1033,7 @@ namespace
mNiStringExtraData2.string = "NC___";
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
@ -1051,7 +1051,7 @@ namespace
mNiStringExtraData.string = "MRK";
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
@ -1069,10 +1069,10 @@ namespace
mNiStringExtraData.string = "MRK";
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parent = &mNiNode2;
mNiTriShape.parents.push_back(&mNiNode2);
mNiNode2.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
mNiNode2.recType = Nif::RC_RootCollisionNode;
mNiNode2.parent = &mNiNode;
mNiNode2.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiNode2)}));
mNiNode.recType = Nif::RC_NiNode;
@ -1163,7 +1163,7 @@ namespace
TEST_F(TestBulletNifLoader, for_avoid_collision_mesh_should_ignore_tri_strips_data_with_less_than_3_strips)
{
mNiTriShape.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
mNiNode.recType = Nif::RC_AvoidNode;
mNiTriStripsData.strips.front() = {0, 1};
@ -1181,7 +1181,7 @@ namespace
TEST_F(TestBulletNifLoader, for_animated_mesh_should_ignore_tri_strips_data_with_less_than_3_strips)
{
mNiTriStripsData.strips.front() = {0, 1};
mNiTriStrips.parent = &mNiNode;
mNiTriStrips.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriStrips)}));
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
@ -1197,7 +1197,7 @@ namespace
TEST_F(TestBulletNifLoader, should_not_add_static_mesh_with_no_triangles_to_compound_shape)
{
mNiTriStripsData.strips.front() = {0, 1};
mNiTriShape.parent = &mNiNode;
mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(2));
@ -1218,4 +1218,54 @@ namespace
EXPECT_EQ(*result, expected);
}
TEST_F(TestBulletNifLoader, should_handle_node_with_multiple_parents)
{
copy(mTransform, mNiTriShape.trafo);
mNiTriShape.trafo.scale = 4;
mNiTriShape.parents = {&mNiNode, &mNiNode2};
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
mNiNode.trafo.scale = 2;
mNiNode2.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
mNiNode2.trafo.scale = 3;
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(2));
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
EXPECT_CALL(mNifFile, getRoot(1)).WillOnce(Return(&mNiNode2));
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif"));
const auto result = mLoader.load(mNifFile);
std::unique_ptr<btTriangleMesh> triangles1(new btTriangleMesh(false));
triangles1->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
std::unique_ptr<Resource::TriangleMeshShape> mesh1(new Resource::TriangleMeshShape(triangles1.release(), true));
mesh1->setLocalScaling(btVector3(8, 8, 8));
std::unique_ptr<btTriangleMesh> triangles2(new btTriangleMesh(false));
triangles2->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
std::unique_ptr<Resource::TriangleMeshShape> mesh2(new Resource::TriangleMeshShape(triangles2.release(), true));
mesh2->setLocalScaling(btVector3(12, 12, 12));
std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
const btTransform transform1 {
btMatrix3x3(
1, 0, 0,
0, 0.8004512795493964327775415767973754555, 0.59939782204119995689950428641168400645,
0, -0.59939782204119995689950428641168400645, 0.8004512795493964327775415767973754555
),
btVector3(2, 4, 6)
};
const btTransform transform2 {
btMatrix3x3(
1, 0, 0,
0, 0.79515431915808965079861536651151254773, 0.60640713116208888600056070572463795543,
0, -0.60640713116208888600056070572463795543, 0.79515431915808965079861536651151254773
),
btVector3(3, 6, 9)
};
shape->addChildShape(transform1, mesh1.release());
shape->addChildShape(transform2, mesh2.release());
Resource::BulletShape expected;
expected.mCollisionShape.reset(shape.release());
expected.mAnimatedShapes = {{-1, 0}};
EXPECT_EQ(*result, expected);
}
}

@ -166,7 +166,7 @@ struct Node : public Named
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
collision.read(nif);
parent = nullptr;
parents.clear();
isBone = false;
}
@ -180,7 +180,7 @@ struct Node : public Named
// Parent node, or nullptr for the root node. As far as I'm aware, only
// NiNodes (or types derived from NiNodes) can be parents.
NiNode *parent;
std::vector<NiNode*> parents;
bool isBone{false};
@ -238,7 +238,7 @@ struct NiNode : Node
{
// Why would a unique list of children contain empty refs?
if(!children[i].empty())
children[i]->parent = this;
children[i]->parents.push_back(this);
}
}
};

@ -0,0 +1,15 @@
#ifndef OPENMW_COMPONENTS_NIF_PARENT_HPP
#define OPENMW_COMPONENTS_NIF_PARENT_HPP
namespace Nif
{
struct NiNode;
struct Parent
{
const NiNode& mNiNode;
const Parent* mParent;
};
}
#endif

@ -16,15 +16,17 @@
#include <components/nif/node.hpp>
#include <components/nif/data.hpp>
#include <components/nif/extra.hpp>
#include <components/nif/parent.hpp>
namespace
{
osg::Matrixf getWorldTransform(const Nif::Node& node)
osg::Matrixf getWorldTransform(const Nif::Node& node, const Nif::Parent* nodeParent)
{
if(node.parent != nullptr)
return node.trafo.toMatrix() * getWorldTransform(*node.parent);
return node.trafo.toMatrix();
osg::Matrixf result = node.trafo.toMatrix();
for (const Nif::Parent* parent = nodeParent; parent != nullptr; parent = parent->mParent)
result *= parent->mNiNode.trafo.toMatrix();
return result;
}
bool pathFileNameStartsWithX(const std::string& path)
@ -214,7 +216,7 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
for (const Nif::Node* node : roots)
{
bool autogenerated = hasAutoGeneratedCollision(*node);
handleNode(filename, *node, 0, autogenerated, isAnimated, autogenerated);
handleNode(filename, *node, nullptr, 0, autogenerated, isAnimated, autogenerated);
}
if (mCompoundShape)
@ -305,8 +307,8 @@ bool BulletNifLoader::hasAutoGeneratedCollision(const Nif::Node& rootNode)
return true;
}
void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node& node, int flags,
bool isCollisionNode, bool isAnimated, bool autogenerated, bool avoid)
void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node& node, const Nif::Parent* parent,
int flags, bool isCollisionNode, bool isAnimated, bool autogenerated, bool avoid)
{
// TODO: allow on-the fly collision switching via toggling this flag
if (node.recType == Nif::RC_NiCollisionSwitch && !(node.flags & Nif::NiNode::Flag_ActiveCollision))
@ -361,7 +363,7 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node& n
|| node.recType == Nif::RC_NiTriStrips
|| node.recType == Nif::RC_BSLODTriShape))
{
handleNiTriShape(node, flags, getWorldTransform(node), isAnimated, avoid);
handleNiTriShape(node, parent, flags, getWorldTransform(node, parent), isAnimated, avoid);
}
}
@ -369,30 +371,31 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node& n
if (const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(&node))
{
const Nif::NodeList &list = ninode->children;
const Nif::Parent currentParent {*ninode, parent};
for(size_t i = 0;i < list.length();i++)
{
if (list[i].empty())
continue;
assert(list[i].get().parent == &node);
handleNode(fileName, list[i].get(), flags, isCollisionNode, isAnimated, autogenerated, avoid);
assert(std::find(list[i]->parents.begin(), list[i]->parents.end(), ninode) != list[i]->parents.end());
handleNode(fileName, list[i].get(), &currentParent, flags, isCollisionNode, isAnimated, autogenerated, avoid);
}
}
}
void BulletNifLoader::handleNiTriShape(const Nif::Node& nifNode, int flags, const osg::Matrixf &transform,
bool isAnimated, bool avoid)
void BulletNifLoader::handleNiTriShape(const Nif::Node& nifNode, const Nif::Parent* parent, int flags,
const osg::Matrixf &transform, bool isAnimated, bool avoid)
{
// If the object was marked "NCO" earlier, it shouldn't collide with
// anything. So don't do anything.
if ((flags & 0x800))
return;
handleNiTriShape(static_cast<const Nif::NiGeometry&>(nifNode), transform, isAnimated, avoid);
handleNiTriShape(static_cast<const Nif::NiGeometry&>(nifNode), parent, transform, isAnimated, avoid);
}
void BulletNifLoader::handleNiTriShape(const Nif::NiGeometry& niGeometry, const osg::Matrixf &transform,
bool isAnimated, bool avoid)
void BulletNifLoader::handleNiTriShape(const Nif::NiGeometry& niGeometry, const Nif::Parent* nodeParent,
const osg::Matrixf &transform, bool isAnimated, bool avoid)
{
if (niGeometry.data.empty() || niGeometry.data->vertices.empty())
return;
@ -413,8 +416,8 @@ void BulletNifLoader::handleNiTriShape(const Nif::NiGeometry& niGeometry, const
childMesh.release();
float scale = niGeometry.trafo.scale;
for (const Nif::Node* parent = niGeometry.parent; parent != nullptr; parent = parent->parent)
scale *= parent->trafo.scale;
for (const Nif::Parent* parent = nodeParent; parent != nullptr; parent = parent->mParent)
scale *= parent->mNiNode.trafo.scale;
osg::Quat q = transform.getRotate();
osg::Vec3f v = transform.getTrans();
childShape->setLocalScaling(btVector3(scale, scale, scale));

@ -28,6 +28,7 @@ namespace Nif
struct NiTriShape;
struct NiTriStrips;
struct NiGeometry;
struct Parent;
}
namespace NifBullet
@ -55,14 +56,16 @@ public:
private:
bool findBoundingBox(const Nif::Node& node, const std::string& filename);
void handleNode(const std::string& fileName, const Nif::Node& node, int flags, bool isCollisionNode,
bool isAnimated=false, bool autogenerated=false, bool avoid=false);
void handleNode(const std::string& fileName, const Nif::Node& node, const Nif::Parent* parent, int flags,
bool isCollisionNode, bool isAnimated=false, bool autogenerated=false, bool avoid=false);
bool hasAutoGeneratedCollision(const Nif::Node& rootNode);
void handleNiTriShape(const Nif::Node& nifNode, int flags, const osg::Matrixf& transform, bool isAnimated, bool avoid);
void handleNiTriShape(const Nif::Node& nifNode, const Nif::Parent* parent, int flags, const osg::Matrixf& transform,
bool isAnimated, bool avoid);
void handleNiTriShape(const Nif::NiGeometry& nifNode, const osg::Matrixf& transform, bool isAnimated, bool avoid);
void handleNiTriShape(const Nif::NiGeometry& nifNode, const Nif::Parent* parent, const osg::Matrixf& transform,
bool isAnimated, bool avoid);
std::unique_ptr<btCompoundShape, Resource::DeleteCollisionShape> mCompoundShape;

@ -17,6 +17,7 @@
#include <components/misc/resourcehelpers.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/misc/osguservalues.hpp>
#include <components/nif/parent.hpp>
// particle
#include <osgParticle/ParticleSystem>
@ -74,10 +75,10 @@ namespace
}
// Collect all properties affecting the given drawable that should be handled on drawable basis rather than on the node hierarchy above it.
void collectDrawableProperties(const Nif::Node* nifNode, std::vector<const Nif::Property*>& out)
void collectDrawableProperties(const Nif::Node* nifNode, const Nif::Parent* parent, std::vector<const Nif::Property*>& out)
{
if (nifNode->parent)
collectDrawableProperties(nifNode->parent, out);
if (parent != nullptr)
collectDrawableProperties(&parent->mNiNode, parent->mParent, out);
const Nif::PropertyList& props = nifNode->props;
for (size_t i = 0; i <props.length();++i)
{
@ -300,7 +301,7 @@ namespace NifOsg
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);
auto node = handleNode(root, nullptr, nullptr, imageManager, std::vector<unsigned int>(), 0, false, false, false, &textkeys->mTextKeys);
created->addChild(node);
}
if (mHasNightDayLabel)
@ -339,7 +340,7 @@ namespace NifOsg
{
// Get the lowest numbered recIndex of the NiTexturingProperty root node.
// This is what is overridden when a spell effect "particle texture" is used.
if (nifNode->parent == nullptr && !mFoundFirstRootTexturingProperty && props[i].getPtr()->recType == Nif::RC_NiTexturingProperty)
if (nifNode->parents.empty() && !mFoundFirstRootTexturingProperty && props[i].getPtr()->recType == Nif::RC_NiTexturingProperty)
{
mFirstRootTextureIndex = props[i].getPtr()->recIndex;
mFoundFirstRootTexturingProperty = true;
@ -483,7 +484,7 @@ namespace NifOsg
// The Root node can be created as a Group if no transformation is required.
// This takes advantage of the fact root nodes can't have additional controllers
// loaded from an external .kf file (original engine just throws "can't find node" errors if you try).
if (!nifNode->parent && nifNode->controller.empty() && nifNode->trafo.isIdentity())
if (nifNode->parents.empty() && nifNode->controller.empty() && nifNode->trafo.isIdentity())
node = new osg::Group;
dataVariance = nifNode->isBone ? osg::Object::DYNAMIC : osg::Object::STATIC;
@ -505,8 +506,10 @@ namespace NifOsg
return node;
}
osg::ref_ptr<osg::Node> handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* imageManager,
std::vector<unsigned int> boundTextures, int animflags, bool skipMeshes, bool hasMarkers, bool hasAnimatedParents, SceneUtil::TextKeyMap* textKeys, osg::Node* rootNode=nullptr)
osg::ref_ptr<osg::Node> handleNode(const Nif::Node* nifNode, const Nif::Parent* parent, osg::Group* parentNode,
Resource::ImageManager* imageManager, std::vector<unsigned int> boundTextures, int animflags,
bool skipMeshes, bool hasMarkers, bool hasAnimatedParents, SceneUtil::TextKeyMap* textKeys,
osg::Node* rootNode=nullptr)
{
if (rootNode != nullptr && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box"))
return nullptr;
@ -615,9 +618,9 @@ namespace NifOsg
Nif::NiSkinInstancePtr skin = static_cast<const Nif::NiGeometry*>(nifNode)->skin;
if (skin.empty())
handleGeometry(nifNode, node, composite, boundTextures, animflags);
handleGeometry(nifNode, parent, node, composite, boundTextures, animflags);
else
handleSkinnedGeometry(nifNode, node, composite, boundTextures, animflags);
handleSkinnedGeometry(nifNode, parent, node, composite, boundTextures, animflags);
if (!nifNode->controller.empty())
handleMeshControllers(nifNode, node, composite, boundTextures, animflags);
@ -625,7 +628,7 @@ namespace NifOsg
}
if (nifNode->recType == Nif::RC_NiParticles)
handleParticleSystem(nifNode, node, composite, animflags);
handleParticleSystem(nifNode, parent, node, composite, animflags);
if (composite->getNumControllers() > 0)
{
@ -681,10 +684,11 @@ namespace NifOsg
}
const Nif::NodeList &children = ninode->children;
const Nif::Parent currentParent {*ninode, parent};
for(size_t i = 0;i < children.length();++i)
{
if(!children[i].empty())
handleNode(children[i].getPtr(), currentNode, imageManager, boundTextures, animflags, skipMeshes, hasMarkers, hasAnimatedParents, textKeys, rootNode);
handleNode(children[i].getPtr(), &currentParent, currentNode, imageManager, boundTextures, animflags, skipMeshes, hasMarkers, hasAnimatedParents, textKeys, rootNode);
}
}
@ -1026,7 +1030,8 @@ namespace NifOsg
mEmitterQueue.clear();
}
void handleParticleSystem(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, int animflags)
void handleParticleSystem(const Nif::Node *nifNode, const Nif::Parent* parent, osg::Group *parentNode,
SceneUtil::CompositeStateSetUpdater* composite, int animflags)
{
osg::ref_ptr<ParticleSystem> partsys (new ParticleSystem);
partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT);
@ -1095,7 +1100,7 @@ namespace NifOsg
handleParticlePrograms(partctrl->affectors, partctrl->colliders, parentNode, partsys.get(), rf);
std::vector<const Nif::Property*> drawableProps;
collectDrawableProperties(nifNode, drawableProps);
collectDrawableProperties(nifNode, parent, drawableProps);
applyDrawableProperties(parentNode, drawableProps, composite, true, animflags);
// particle system updater (after the emitters and affectors in the scene graph)
@ -1146,7 +1151,9 @@ namespace NifOsg
}
}
void handleNiGeometry(const Nif::Node *nifNode, osg::Geometry *geometry, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures, int animflags)
void handleNiGeometry(const Nif::Node *nifNode, const Nif::Parent* parent, osg::Geometry *geometry,
osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite,
const std::vector<unsigned int>& boundTextures, int animflags)
{
const Nif::NiGeometry* niGeometry = static_cast<const Nif::NiGeometry*>(nifNode);
if (niGeometry->data.empty())
@ -1198,15 +1205,17 @@ namespace NifOsg
// - there are 3 "overlapping" nif properties that all affect the osg::Material, handling them
// above the actual renderable would be tedious.
std::vector<const Nif::Property*> drawableProps;
collectDrawableProperties(nifNode, drawableProps);
collectDrawableProperties(nifNode, parent, drawableProps);
applyDrawableProperties(parentNode, drawableProps, composite, !niGeometryData->colors.empty(), animflags);
}
void handleGeometry(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures, int animflags)
void handleGeometry(const Nif::Node* nifNode, const Nif::Parent* parent, osg::Group* parentNode,
SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures,
int animflags)
{
assert(isTypeGeometry(nifNode->recType));
osg::ref_ptr<osg::Geometry> geom (new osg::Geometry);
handleNiGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags);
handleNiGeometry(nifNode, parent, geom, parentNode, composite, boundTextures, animflags);
// If the record had no valid geometry data in it, early-out
if (geom->empty())
return;
@ -1250,12 +1259,12 @@ namespace NifOsg
return morphGeom;
}
void handleSkinnedGeometry(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite,
const std::vector<unsigned int>& boundTextures, int animflags)
void handleSkinnedGeometry(const Nif::Node *nifNode, const Nif::Parent* parent, osg::Group *parentNode,
SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures, int animflags)
{
assert(isTypeGeometry(nifNode->recType));
osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);
handleNiGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags);
handleNiGeometry(nifNode, parent, geometry, parentNode, composite, boundTextures, animflags);
if (geometry->empty())
return;
osg::ref_ptr<SceneUtil::RigGeometry> rig(new SceneUtil::RigGeometry);

Loading…
Cancel
Save