1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-06-19 21:41:33 +00:00

Merge branch 'nif' into 'master'

Modernize NIF loader, part 3

See merge request OpenMW/openmw!3418
This commit is contained in:
psi29a 2023-09-12 08:34:46 +00:00
commit 42b77342c5
27 changed files with 606 additions and 644 deletions

View file

@ -6,9 +6,9 @@
namespace Nif::Testing namespace Nif::Testing
{ {
inline void init(Transformation& value) inline void init(NiTransform& value)
{ {
value = Transformation::getIdentity(); value = NiTransform::getIdentity();
} }
inline void init(Extra& value) inline void init(Extra& value)
@ -16,25 +16,23 @@ namespace Nif::Testing
value.mNext = ExtraPtr(nullptr); value.mNext = ExtraPtr(nullptr);
} }
inline void init(Named& value) inline void init(NiObjectNET& value)
{ {
value.extra = ExtraPtr(nullptr); value.mExtra = ExtraPtr(nullptr);
value.extralist = ExtraList(); value.mExtraList = ExtraList();
value.controller = ControllerPtr(nullptr); value.mController = ControllerPtr(nullptr);
} }
inline void init(Node& value) inline void init(NiAVObject& value)
{ {
init(static_cast<Named&>(value)); init(static_cast<NiObjectNET&>(value));
value.flags = 0; value.mFlags = 0;
init(value.trafo); init(value.mTransform);
value.hasBounds = false;
value.isBone = false;
} }
inline void init(NiGeometry& value) inline void init(NiGeometry& value)
{ {
init(static_cast<Node&>(value)); init(static_cast<NiAVObject&>(value));
value.data = NiGeometryDataPtr(nullptr); value.data = NiGeometryDataPtr(nullptr);
value.skin = NiSkinInstancePtr(nullptr); value.skin = NiSkinInstancePtr(nullptr);
} }
@ -54,7 +52,7 @@ namespace Nif::Testing
inline void init(NiSkinInstance& value) inline void init(NiSkinInstance& value)
{ {
value.mData = NiSkinDataPtr(nullptr); value.mData = NiSkinDataPtr(nullptr);
value.mRoot = NodePtr(nullptr); value.mRoot = NiAVObjectPtr(nullptr);
} }
inline void init(Controller& value) inline void init(Controller& value)
@ -65,7 +63,7 @@ namespace Nif::Testing
value.phase = 0; value.phase = 0;
value.timeStart = 0; value.timeStart = 0;
value.timeStop = 0; value.timeStop = 0;
value.target = NamedPtr(nullptr); value.target = NiObjectNETPtr(nullptr);
} }
} }

View file

@ -274,19 +274,19 @@ namespace
using namespace Nif::Testing; using namespace Nif::Testing;
using NifBullet::BulletNifLoader; using NifBullet::BulletNifLoader;
void copy(const btTransform& src, Nif::Transformation& dst) void copy(const btTransform& src, Nif::NiTransform& dst)
{ {
dst.pos = osg::Vec3f(src.getOrigin().x(), src.getOrigin().y(), src.getOrigin().z()); dst.mTranslation = osg::Vec3f(src.getOrigin().x(), src.getOrigin().y(), src.getOrigin().z());
for (int row = 0; row < 3; ++row) for (int row = 0; row < 3; ++row)
for (int column = 0; column < 3; ++column) for (int column = 0; column < 3; ++column)
dst.rotation.mValues[row][column] = src.getBasis().getRow(row)[column]; dst.mRotation.mValues[row][column] = src.getBasis().getRow(row)[column];
} }
struct TestBulletNifLoader : Test struct TestBulletNifLoader : Test
{ {
BulletNifLoader mLoader; BulletNifLoader mLoader;
Nif::Node mNode; Nif::NiAVObject mNode;
Nif::Node mNode2; Nif::NiAVObject mNode2;
Nif::NiNode mNiNode; Nif::NiNode mNiNode;
Nif::NiNode mNiNode2; Nif::NiNode mNiNode2;
Nif::NiNode mNiNode3; Nif::NiNode mNiNode3;
@ -414,11 +414,10 @@ namespace
TEST_F( TEST_F(
TestBulletNifLoader, for_root_nif_node_with_bounding_box_should_return_shape_with_compound_shape_and_box_inside) TestBulletNifLoader, for_root_nif_node_with_bounding_box_should_return_shape_with_compound_shape_and_box_inside)
{ {
mNode.hasBounds = true; mNode.mFlags |= Nif::NiAVObject::Flag_BBoxCollision;
mNode.flags |= Nif::Node::Flag_BBoxCollision; mNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode.mBounds.box.extents = osg::Vec3f(1, 2, 3);
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); mNode.mBounds.box.center = osg::Vec3f(-1, -2, -3);
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNode); file.mRoots.push_back(&mNode);
@ -439,13 +438,12 @@ namespace
TEST_F(TestBulletNifLoader, for_child_nif_node_with_bounding_box) TEST_F(TestBulletNifLoader, for_child_nif_node_with_bounding_box)
{ {
mNode.hasBounds = true; mNode.mFlags |= Nif::NiAVObject::Flag_BBoxCollision;
mNode.flags |= Nif::Node::Flag_BBoxCollision; mNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode.mBounds.box.extents = osg::Vec3f(1, 2, 3);
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); mNode.mBounds.box.center = osg::Vec3f(-1, -2, -3);
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); mNode.mParents.push_back(&mNiNode);
mNode.parents.push_back(&mNiNode); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode) };
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNode) }));
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -467,18 +465,16 @@ namespace
TEST_F(TestBulletNifLoader, TEST_F(TestBulletNifLoader,
for_root_and_child_nif_node_with_bounding_box_but_root_without_flag_should_use_child_bounds) for_root_and_child_nif_node_with_bounding_box_but_root_without_flag_should_use_child_bounds)
{ {
mNode.hasBounds = true; mNode.mFlags |= Nif::NiAVObject::Flag_BBoxCollision;
mNode.flags |= Nif::Node::Flag_BBoxCollision; mNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode.mBounds.box.extents = osg::Vec3f(1, 2, 3);
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); mNode.mBounds.box.center = osg::Vec3f(-1, -2, -3);
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); mNode.mParents.push_back(&mNiNode);
mNode.parents.push_back(&mNiNode);
mNiNode.hasBounds = true; mNiNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNiNode.mBounds.box.extents = osg::Vec3f(4, 5, 6);
mNiNode.bounds.box.extents = osg::Vec3f(4, 5, 6); mNiNode.mBounds.box.center = osg::Vec3f(-4, -5, -6);
mNiNode.bounds.box.center = osg::Vec3f(-4, -5, -6); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode) };
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNode) }));
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -500,24 +496,21 @@ namespace
TEST_F(TestBulletNifLoader, TEST_F(TestBulletNifLoader,
for_root_and_two_children_where_both_with_bounds_but_only_first_with_flag_should_use_first_bounds) for_root_and_two_children_where_both_with_bounds_but_only_first_with_flag_should_use_first_bounds)
{ {
mNode.hasBounds = true; mNode.mFlags |= Nif::NiAVObject::Flag_BBoxCollision;
mNode.flags |= Nif::Node::Flag_BBoxCollision; mNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode.mBounds.box.extents = osg::Vec3f(1, 2, 3);
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); mNode.mBounds.box.center = osg::Vec3f(-1, -2, -3);
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); mNode.mParents.push_back(&mNiNode);
mNode.parents.push_back(&mNiNode);
mNode2.hasBounds = true; mNode2.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode2.mBounds.box.extents = osg::Vec3f(4, 5, 6);
mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6); mNode2.mBounds.box.center = osg::Vec3f(-4, -5, -6);
mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6); mNode2.mParents.push_back(&mNiNode);
mNode2.parents.push_back(&mNiNode);
mNiNode.hasBounds = true; mNiNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNiNode.mBounds.box.extents = osg::Vec3f(7, 8, 9);
mNiNode.bounds.box.extents = osg::Vec3f(7, 8, 9); mNiNode.mBounds.box.center = osg::Vec3f(-7, -8, -9);
mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode), Nif::NiAVObjectPtr(&mNode2) };
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2) }));
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -539,24 +532,21 @@ namespace
TEST_F(TestBulletNifLoader, TEST_F(TestBulletNifLoader,
for_root_and_two_children_where_both_with_bounds_but_only_second_with_flag_should_use_second_bounds) for_root_and_two_children_where_both_with_bounds_but_only_second_with_flag_should_use_second_bounds)
{ {
mNode.hasBounds = true; mNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode.mBounds.box.extents = osg::Vec3f(1, 2, 3);
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); mNode.mBounds.box.center = osg::Vec3f(-1, -2, -3);
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); mNode.mParents.push_back(&mNiNode);
mNode.parents.push_back(&mNiNode);
mNode2.hasBounds = true; mNode2.mFlags |= Nif::NiAVObject::Flag_BBoxCollision;
mNode2.flags |= Nif::Node::Flag_BBoxCollision; mNode2.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode2.mBounds.box.extents = osg::Vec3f(4, 5, 6);
mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6); mNode2.mBounds.box.center = osg::Vec3f(-4, -5, -6);
mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6); mNode2.mParents.push_back(&mNiNode);
mNode2.parents.push_back(&mNiNode);
mNiNode.hasBounds = true; mNiNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNiNode.mBounds.box.extents = osg::Vec3f(7, 8, 9);
mNiNode.bounds.box.extents = osg::Vec3f(7, 8, 9); mNiNode.mBounds.box.center = osg::Vec3f(-7, -8, -9);
mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode), Nif::NiAVObjectPtr(&mNode2) };
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2) }));
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -578,10 +568,9 @@ namespace
TEST_F(TestBulletNifLoader, TEST_F(TestBulletNifLoader,
for_root_nif_node_with_bounds_but_without_flag_should_return_shape_with_bounds_but_with_null_collision_shape) for_root_nif_node_with_bounds_but_without_flag_should_return_shape_with_bounds_but_with_null_collision_shape)
{ {
mNode.hasBounds = true; mNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode.mBounds.box.extents = osg::Vec3f(1, 2, 3);
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); mNode.mBounds.box.center = osg::Vec3f(-1, -2, -3);
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNode); file.mRoots.push_back(&mNode);
@ -619,10 +608,9 @@ namespace
TEST_F(TestBulletNifLoader, TEST_F(TestBulletNifLoader,
for_tri_shape_root_node_with_bounds_should_return_static_shape_with_bounds_but_with_null_collision_shape) for_tri_shape_root_node_with_bounds_should_return_static_shape_with_bounds_but_with_null_collision_shape)
{ {
mNiTriShape.hasBounds = true; mNiTriShape.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNiTriShape.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNiTriShape.mBounds.box.extents = osg::Vec3f(1, 2, 3);
mNiTriShape.bounds.box.extents = osg::Vec3f(1, 2, 3); mNiTriShape.mBounds.box.center = osg::Vec3f(-1, -2, -3);
mNiTriShape.bounds.box.center = osg::Vec3f(-1, -2, -3);
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiTriShape); file.mRoots.push_back(&mNiTriShape);
@ -639,8 +627,8 @@ namespace
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_should_return_static_shape) TEST_F(TestBulletNifLoader, for_tri_shape_child_node_should_return_static_shape)
{ {
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -662,10 +650,10 @@ namespace
TEST_F(TestBulletNifLoader, for_nested_tri_shape_child_should_return_static_shape) TEST_F(TestBulletNifLoader, for_nested_tri_shape_child_should_return_static_shape)
{ {
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiNode2) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiNode2) };
mNiNode2.parents.push_back(&mNiNode); mNiNode2.mParents.push_back(&mNiNode);
mNiNode2.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode2.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
mNiTriShape.parents.push_back(&mNiNode2); mNiTriShape.mParents.push_back(&mNiNode2);
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -687,10 +675,9 @@ namespace
TEST_F(TestBulletNifLoader, for_two_tri_shape_children_should_return_static_shape_with_all_meshes) TEST_F(TestBulletNifLoader, for_two_tri_shape_children_should_return_static_shape_with_all_meshes)
{ {
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiTriShape2.parents.push_back(&mNiNode); mNiTriShape2.mParents.push_back(&mNiNode);
mNiNode.children mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape), Nif::NiAVObjectPtr(&mNiTriShape2) };
= Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape), Nif::NodePtr(&mNiTriShape2) }));
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -717,8 +704,8 @@ namespace
for_tri_shape_child_node_and_filename_starting_with_x_and_not_empty_skin_should_return_static_shape) for_tri_shape_child_node_and_filename_starting_with_x_and_not_empty_skin_should_return_static_shape)
{ {
mNiTriShape.skin = Nif::NiSkinInstancePtr(&mNiSkinInstance); mNiTriShape.skin = Nif::NiSkinInstancePtr(&mNiSkinInstance);
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file("xtest.nif"); Nif::NIFFile file("xtest.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -739,8 +726,8 @@ namespace
TEST_F(TestBulletNifLoader, for_tri_shape_root_node_and_filename_starting_with_x_should_return_animated_shape) TEST_F(TestBulletNifLoader, for_tri_shape_root_node_and_filename_starting_with_x_should_return_animated_shape)
{ {
copy(mTransform, mNiTriShape.trafo); copy(mTransform, mNiTriShape.mTransform);
mNiTriShape.trafo.scale = 3; mNiTriShape.mTransform.mScale = 3;
Nif::NIFFile file("xtest.nif"); Nif::NIFFile file("xtest.nif");
file.mRoots.push_back(&mNiTriShape); file.mRoots.push_back(&mNiTriShape);
@ -763,11 +750,11 @@ namespace
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_and_filename_starting_with_x_should_return_animated_shape) TEST_F(TestBulletNifLoader, for_tri_shape_child_node_and_filename_starting_with_x_should_return_animated_shape)
{ {
copy(mTransform, mNiTriShape.trafo); copy(mTransform, mNiTriShape.mTransform);
mNiTriShape.trafo.scale = 3; mNiTriShape.mTransform.mScale = 3;
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
mNiNode.trafo.scale = 4; mNiNode.mTransform.mScale = 4;
Nif::NIFFile file("xtest.nif"); Nif::NIFFile file("xtest.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -791,18 +778,15 @@ namespace
TEST_F( TEST_F(
TestBulletNifLoader, for_two_tri_shape_children_nodes_and_filename_starting_with_x_should_return_animated_shape) TestBulletNifLoader, for_two_tri_shape_children_nodes_and_filename_starting_with_x_should_return_animated_shape)
{ {
copy(mTransform, mNiTriShape.trafo); copy(mTransform, mNiTriShape.mTransform);
mNiTriShape.trafo.scale = 3; mNiTriShape.mTransform.mScale = 3;
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
copy(mTransform, mNiTriShape2.trafo); copy(mTransform, mNiTriShape2.mTransform);
mNiTriShape2.trafo.scale = 3; mNiTriShape2.mTransform.mScale = 3;
mNiTriShape2.parents.push_back(&mNiNode); mNiTriShape2.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape), Nif::NiAVObjectPtr(&mNiTriShape2) };
Nif::NodePtr(&mNiTriShape),
Nif::NodePtr(&mNiTriShape2),
}));
Nif::NIFFile file("xtest.nif"); Nif::NIFFile file("xtest.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -834,12 +818,12 @@ namespace
{ {
mController.recType = Nif::RC_NiKeyframeController; mController.recType = Nif::RC_NiKeyframeController;
mController.flags |= Nif::Controller::Flag_Active; mController.flags |= Nif::Controller::Flag_Active;
copy(mTransform, mNiTriShape.trafo); copy(mTransform, mNiTriShape.mTransform);
mNiTriShape.trafo.scale = 3; mNiTriShape.mTransform.mScale = 3;
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiTriShape.controller = Nif::ControllerPtr(&mController); mNiTriShape.mController = Nif::ControllerPtr(&mController);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
mNiNode.trafo.scale = 4; mNiNode.mTransform.mScale = 4;
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -864,18 +848,18 @@ namespace
{ {
mController.recType = Nif::RC_NiKeyframeController; mController.recType = Nif::RC_NiKeyframeController;
mController.flags |= Nif::Controller::Flag_Active; mController.flags |= Nif::Controller::Flag_Active;
copy(mTransform, mNiTriShape.trafo); copy(mTransform, mNiTriShape.mTransform);
mNiTriShape.trafo.scale = 3; mNiTriShape.mTransform.mScale = 3;
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
copy(mTransform, mNiTriShape2.trafo); copy(mTransform, mNiTriShape2.mTransform);
mNiTriShape2.trafo.scale = 3; mNiTriShape2.mTransform.mScale = 3;
mNiTriShape2.parents.push_back(&mNiNode); mNiTriShape2.mParents.push_back(&mNiNode);
mNiTriShape2.controller = Nif::ControllerPtr(&mController); mNiTriShape2.mController = Nif::ControllerPtr(&mController);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ mNiNode.mChildren = Nif::NiAVObjectList{
Nif::NodePtr(&mNiTriShape), Nif::NiAVObjectPtr(&mNiTriShape),
Nif::NodePtr(&mNiTriShape2), Nif::NiAVObjectPtr(&mNiTriShape2),
})); };
mNiNode.trafo.scale = 4; mNiNode.mTransform.mScale = 4;
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -905,8 +889,8 @@ namespace
TEST_F(TestBulletNifLoader, should_add_static_mesh_to_existing_compound_mesh) TEST_F(TestBulletNifLoader, should_add_static_mesh_to_existing_compound_mesh)
{ {
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file("xtest.nif"); Nif::NIFFile file("xtest.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -936,8 +920,8 @@ namespace
TEST_F( TEST_F(
TestBulletNifLoader, for_root_avoid_node_and_tri_shape_child_node_should_return_shape_with_null_collision_shape) TestBulletNifLoader, for_root_avoid_node_and_tri_shape_child_node_should_return_shape_with_null_collision_shape)
{ {
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
mNiNode.recType = Nif::RC_AvoidNode; mNiNode.recType = Nif::RC_AvoidNode;
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
@ -960,8 +944,8 @@ namespace
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_should_return_shape_with_null_collision_shape) TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_should_return_shape_with_null_collision_shape)
{ {
mNiTriShape.data = Nif::NiGeometryDataPtr(nullptr); mNiTriShape.data = Nif::NiGeometryDataPtr(nullptr);
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -979,8 +963,8 @@ namespace
{ {
auto data = static_cast<Nif::NiTriShapeData*>(mNiTriShape.data.getPtr()); auto data = static_cast<Nif::NiTriShapeData*>(mNiTriShape.data.getPtr());
data->mTriangles.clear(); data->mTriangles.clear();
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -998,9 +982,9 @@ namespace
{ {
mNiStringExtraData.mData = "NCC__"; mNiStringExtraData.mData = "NCC__";
mNiStringExtraData.recType = Nif::RC_NiStringExtraData; mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); mNiTriShape.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -1027,9 +1011,9 @@ namespace
mNiStringExtraData.mNext = Nif::ExtraPtr(&mNiStringExtraData2); mNiStringExtraData.mNext = Nif::ExtraPtr(&mNiStringExtraData2);
mNiStringExtraData2.mData = "NCC__"; mNiStringExtraData2.mData = "NCC__";
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData; mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); mNiTriShape.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -1054,9 +1038,9 @@ namespace
{ {
mNiStringExtraData.mData = "NC___"; mNiStringExtraData.mData = "NC___";
mNiStringExtraData.recType = Nif::RC_NiStringExtraData; mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); mNiTriShape.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -1082,9 +1066,9 @@ namespace
mNiStringExtraData.mNext = Nif::ExtraPtr(&mNiStringExtraData2); mNiStringExtraData.mNext = Nif::ExtraPtr(&mNiStringExtraData2);
mNiStringExtraData2.mData = "NC___"; mNiStringExtraData2.mData = "NC___";
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData; mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); mNiTriShape.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -1112,13 +1096,13 @@ namespace
init(emptyCollisionNode); init(emptyCollisionNode);
niTriShape.data = Nif::NiGeometryDataPtr(&mNiTriShapeData); niTriShape.data = Nif::NiGeometryDataPtr(&mNiTriShapeData);
niTriShape.parents.push_back(&mNiNode); niTriShape.mParents.push_back(&mNiNode);
emptyCollisionNode.recType = Nif::RC_RootCollisionNode; emptyCollisionNode.recType = Nif::RC_RootCollisionNode;
emptyCollisionNode.parents.push_back(&mNiNode); emptyCollisionNode.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList( mNiNode.mChildren
std::vector<Nif::NodePtr>({ Nif::NodePtr(&niTriShape), Nif::NodePtr(&emptyCollisionNode) })); = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&niTriShape), Nif::NiAVObjectPtr(&emptyCollisionNode) };
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -1143,9 +1127,9 @@ namespace
{ {
mNiStringExtraData.mData = "MRK"; mNiStringExtraData.mData = "MRK";
mNiStringExtraData.recType = Nif::RC_NiStringExtraData; mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); mNiTriShape.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -1162,10 +1146,10 @@ namespace
{ {
mNiIntegerExtraData.mData = 32; // BSX flag "editor marker" mNiIntegerExtraData.mData = 32; // BSX flag "editor marker"
mNiIntegerExtraData.recType = Nif::RC_BSXFlags; mNiIntegerExtraData.recType = Nif::RC_BSXFlags;
mNiTriShape.extralist.push_back(Nif::ExtraPtr(&mNiIntegerExtraData)); mNiTriShape.mExtraList.push_back(Nif::ExtraPtr(&mNiIntegerExtraData));
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiTriShape.name = "EditorMarker"; mNiTriShape.mName = "EditorMarker";
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -1183,12 +1167,12 @@ namespace
{ {
mNiStringExtraData.mData = "MRK"; mNiStringExtraData.mData = "MRK";
mNiStringExtraData.recType = Nif::RC_NiStringExtraData; mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); mNiTriShape.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parents.push_back(&mNiNode2); mNiTriShape.mParents.push_back(&mNiNode2);
mNiNode2.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode2.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
mNiNode2.recType = Nif::RC_RootCollisionNode; mNiNode2.recType = Nif::RC_RootCollisionNode;
mNiNode2.parents.push_back(&mNiNode); mNiNode2.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiNode2) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiNode2) };
mNiNode.recType = Nif::RC_NiNode; mNiNode.recType = Nif::RC_NiNode;
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
@ -1290,8 +1274,8 @@ namespace
TEST_F(TestBulletNifLoader, for_avoid_collision_mesh_should_ignore_tri_strips_data_with_less_than_3_strips) TEST_F(TestBulletNifLoader, for_avoid_collision_mesh_should_ignore_tri_strips_data_with_less_than_3_strips)
{ {
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
mNiNode.recType = Nif::RC_AvoidNode; mNiNode.recType = Nif::RC_AvoidNode;
mNiTriStripsData.mStrips.front() = { 0, 1 }; mNiTriStripsData.mStrips.front() = { 0, 1 };
@ -1309,8 +1293,8 @@ namespace
TEST_F(TestBulletNifLoader, for_animated_mesh_should_ignore_tri_strips_data_with_less_than_3_strips) TEST_F(TestBulletNifLoader, for_animated_mesh_should_ignore_tri_strips_data_with_less_than_3_strips)
{ {
mNiTriStripsData.mStrips.front() = { 0, 1 }; mNiTriStripsData.mStrips.front() = { 0, 1 };
mNiTriStrips.parents.push_back(&mNiNode); mNiTriStrips.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriStrips) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriStrips) };
Nif::NIFFile file("xtest.nif"); Nif::NIFFile file("xtest.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -1326,8 +1310,8 @@ namespace
TEST_F(TestBulletNifLoader, should_not_add_static_mesh_with_no_triangles_to_compound_shape) TEST_F(TestBulletNifLoader, should_not_add_static_mesh_with_no_triangles_to_compound_shape)
{ {
mNiTriStripsData.mStrips.front() = { 0, 1 }; mNiTriStripsData.mStrips.front() = { 0, 1 };
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file("xtest.nif"); Nif::NIFFile file("xtest.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);
@ -1351,13 +1335,13 @@ namespace
TEST_F(TestBulletNifLoader, should_handle_node_with_multiple_parents) TEST_F(TestBulletNifLoader, should_handle_node_with_multiple_parents)
{ {
copy(mTransform, mNiTriShape.trafo); copy(mTransform, mNiTriShape.mTransform);
mNiTriShape.trafo.scale = 4; mNiTriShape.mTransform.mScale = 4;
mNiTriShape.parents = { &mNiNode, &mNiNode2 }; mNiTriShape.mParents = { &mNiNode, &mNiNode2 };
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
mNiNode.trafo.scale = 2; mNiNode.mTransform.mScale = 2;
mNiNode2.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode2.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
mNiNode2.trafo.scale = 3; mNiNode2.mTransform.mScale = 3;
Nif::NIFFile file("xtest.nif"); Nif::NIFFile file("xtest.nif");
file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode);

View file

@ -66,7 +66,7 @@ namespace
TEST_F(NifOsgLoaderTest, shouldLoadFileWithDefaultNode) TEST_F(NifOsgLoaderTest, shouldLoadFileWithDefaultNode)
{ {
Nif::Node node; Nif::NiAVObject node;
init(node); init(node);
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&node); file.mRoots.push_back(&node);
@ -183,14 +183,14 @@ osg::Group {
TEST_P(NifOsgLoaderBSShaderPrefixTest, shouldAddShaderPrefix) TEST_P(NifOsgLoaderBSShaderPrefixTest, shouldAddShaderPrefix)
{ {
Nif::Node node; Nif::NiAVObject node;
init(node); init(node);
Nif::BSShaderPPLightingProperty property; Nif::BSShaderPPLightingProperty property;
property.recType = Nif::RC_BSShaderPPLightingProperty; property.recType = Nif::RC_BSShaderPPLightingProperty;
property.textureSet = nullptr; property.textureSet = nullptr;
property.controller = nullptr; property.mController = nullptr;
property.type = GetParam().mShaderType; property.type = GetParam().mShaderType;
node.props.push_back(Nif::RecordPtrT<Nif::Property>(&property)); node.mProperties.push_back(Nif::RecordPtrT<Nif::Property>(&property));
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&node); file.mRoots.push_back(&node);
auto result = Loader::load(file, &mImageManager); auto result = Loader::load(file, &mImageManager);
@ -211,14 +211,14 @@ osg::Group {
TEST_P(NifOsgLoaderBSLightingShaderPrefixTest, shouldAddShaderPrefix) TEST_P(NifOsgLoaderBSLightingShaderPrefixTest, shouldAddShaderPrefix)
{ {
Nif::Node node; Nif::NiAVObject node;
init(node); init(node);
Nif::BSLightingShaderProperty property; Nif::BSLightingShaderProperty property;
property.recType = Nif::RC_BSLightingShaderProperty; property.recType = Nif::RC_BSLightingShaderProperty;
property.mTextureSet = nullptr; property.mTextureSet = nullptr;
property.controller = nullptr; property.mController = nullptr;
property.type = GetParam().mShaderType; property.type = GetParam().mShaderType;
node.props.push_back(Nif::RecordPtrT<Nif::Property>(&property)); node.mProperties.push_back(Nif::RecordPtrT<Nif::Property>(&property));
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&node); file.mRoots.push_back(&node);
auto result = Loader::load(file, &mImageManager); auto result = Loader::load(file, &mImageManager);

View file

@ -13,20 +13,28 @@ namespace Nif
} }
} }
void Named::read(NIFStream* nif) void NiObjectNET::read(NIFStream* nif)
{ {
name = nif->getString(); nif->read(mName);
if (nif->getVersion() < NIFStream::generateVersion(10, 0, 1, 0)) if (nif->getVersion() < NIFStream::generateVersion(10, 0, 1, 0))
extra.read(nif); mExtra.read(nif);
else else
readRecordList(nif, extralist); readRecordList(nif, mExtraList);
controller.read(nif); mController.read(nif);
} }
void Named::post(Reader& nif) void NiObjectNET::post(Reader& nif)
{ {
extra.post(nif); mExtra.post(nif);
postRecordList(nif, extralist); postRecordList(nif, mExtraList);
controller.post(nif); mController.post(nif);
}
ExtraList NiObjectNET::getExtraList() const
{
ExtraList list = mExtraList;
for (ExtraPtr extra = mExtra; !extra.empty(); extra = extra->mNext)
list.emplace_back(extra);
return list;
} }
} }

View file

@ -40,7 +40,7 @@ namespace Nif
int flags; int flags;
float frequency, phase; float frequency, phase;
float timeStart, timeStop; float timeStart, timeStop;
NamedPtr target; NiObjectNETPtr target;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
@ -49,18 +49,20 @@ namespace Nif
ExtrapolationMode extrapolationMode() const { return static_cast<ExtrapolationMode>(flags & Mask); } ExtrapolationMode extrapolationMode() const { return static_cast<ExtrapolationMode>(flags & Mask); }
}; };
/// Has name, extra-data and controller /// Abstract object that has a name, extra data and controllers
struct Named : public Record struct NiObjectNET : public Record
{ {
std::string name; std::string mName;
ExtraPtr extra; ExtraPtr mExtra;
ExtraList extralist; ExtraList mExtraList;
ControllerPtr controller; ControllerPtr mController;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
};
using NiSequenceStreamHelper = Named;
} // Namespace // Collect extra records attached to the object
ExtraList getExtraList() const;
};
}
#endif #endif

View file

@ -308,7 +308,7 @@ namespace Nif
{ {
NiInterpController::read(nif); NiInterpController::read(nif);
size_t numTargets = nif->getUShort(); size_t numTargets = nif->getUShort();
std::vector<NodePtr> targets; std::vector<NiAVObjectPtr> targets;
targets.resize(numTargets); targets.resize(numTargets);
for (size_t i = 0; i < targets.size(); i++) for (size_t i = 0; i < targets.size(); i++)
targets[i].read(nif); targets[i].read(nif);

View file

@ -160,7 +160,7 @@ namespace Nif
osg::Vec3f offsetRandom; osg::Vec3f offsetRandom;
NodePtr emitter; NiAVObjectPtr emitter;
int numParticles; int numParticles;
int activeCount; int activeCount;
@ -211,7 +211,7 @@ namespace Nif
struct NiLookAtController : public Controller struct NiLookAtController : public Controller
{ {
NodePtr target; NiAVObjectPtr target;
unsigned short lookAtFlags{ 0 }; unsigned short lookAtFlags{ 0 };
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
@ -237,7 +237,7 @@ namespace Nif
struct NiMultiTargetTransformController : public NiInterpController struct NiMultiTargetTransformController : public NiInterpController
{ {
NodeList mExtraTargets; NiAVObjectList mExtraTargets;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;

View file

@ -347,9 +347,7 @@ namespace Nif
void NiSkinData::read(NIFStream* nif) void NiSkinData::read(NIFStream* nif)
{ {
nif->read(mTransform.rotation); nif->read(mTransform);
nif->read(mTransform.pos);
nif->read(mTransform.scale);
uint32_t numBones; uint32_t numBones;
nif->read(numBones); nif->read(numBones);
@ -366,9 +364,7 @@ namespace Nif
mBones.resize(numBones); mBones.resize(numBones);
for (BoneInfo& bi : mBones) for (BoneInfo& bi : mBones)
{ {
nif->read(bi.mTransform.rotation); nif->read(bi.mTransform);
nif->read(bi.mTransform.pos);
nif->read(bi.mTransform.scale);
nif->read(bi.mBoundSphere); nif->read(bi.mBoundSphere);
uint16_t numVertices; uint16_t numVertices;

View file

@ -1,33 +1,10 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: https://openmw.org/
This file (data.h) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
https://www.gnu.org/licenses/ .
*/
#ifndef OPENMW_COMPONENTS_NIF_DATA_HPP #ifndef OPENMW_COMPONENTS_NIF_DATA_HPP
#define OPENMW_COMPONENTS_NIF_DATA_HPP #define OPENMW_COMPONENTS_NIF_DATA_HPP
#include "nifkey.hpp" #include "nifkey.hpp"
#include "niftypes.hpp" // Transformation #include "niftypes.hpp" // NiTransform
#include "node.hpp"
#include "recordptr.hpp" #include "recordptr.hpp"
#include <components/nif/node.hpp>
namespace Nif namespace Nif
{ {
@ -252,8 +229,8 @@ namespace Nif
{ {
NiSkinDataPtr mData; NiSkinDataPtr mData;
NiSkinPartitionPtr mPartitions; NiSkinPartitionPtr mPartitions;
NodePtr mRoot; NiAVObjectPtr mRoot;
NodeList mBones; NiAVObjectList mBones;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
@ -278,12 +255,12 @@ namespace Nif
struct BoneInfo struct BoneInfo
{ {
Transformation mTransform; NiTransform mTransform;
osg::BoundingSpheref mBoundSphere; osg::BoundingSpheref mBoundSphere;
std::vector<VertWeight> mWeights; std::vector<VertWeight> mWeights;
}; };
Transformation mTransform; NiTransform mTransform;
std::vector<BoneInfo> mBones; std::vector<BoneInfo> mBones;
NiSkinPartitionPtr mPartitions; NiSkinPartitionPtr mPartitions;
@ -413,5 +390,5 @@ namespace Nif
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
} // Namespace }
#endif #endif

View file

@ -8,7 +8,7 @@ namespace Nif
void NiDynamicEffect::read(NIFStream* nif) void NiDynamicEffect::read(NIFStream* nif)
{ {
Node::read(nif); NiAVObject::read(nif);
if (nif->getVersion() > NIFFile::VER_MW && nif->getVersion() < nif->generateVersion(10, 1, 0, 0)) if (nif->getVersion() > NIFFile::VER_MW && nif->getVersion() < nif->generateVersion(10, 1, 0, 0))
return; return;

View file

@ -30,7 +30,7 @@ namespace Nif
{ {
// Abstract // Abstract
struct NiDynamicEffect : public Node struct NiDynamicEffect : public NiAVObject
{ {
bool mSwitchState{ true }; bool mSwitchState{ true };
void read(NIFStream* nif) override; void read(NIFStream* nif) override;

View file

@ -25,13 +25,13 @@ namespace Nif
{ {
Reader::Reader(NIFFile& file) Reader::Reader(NIFFile& file)
: ver(file.mVersion) : mVersion(file.mVersion)
, userVer(file.mUserVersion) , mUserVersion(file.mUserVersion)
, bethVer(file.mBethVersion) , mBethVersion(file.mBethVersion)
, filename(file.mPath) , mFilename(file.mPath)
, hash(file.mHash) , mHash(file.mHash)
, records(file.mRecords) , mRecords(file.mRecords)
, roots(file.mRoots) , mRoots(file.mRoots)
, mUseSkinning(file.mUseSkinning) , mUseSkinning(file.mUseSkinning)
{ {
} }
@ -315,7 +315,7 @@ namespace Nif
/// Make the factory map used for parsing the file /// Make the factory map used for parsing the file
static const std::map<std::string, CreateRecord> factories = makeFactory(); static const std::map<std::string, CreateRecord> factories = makeFactory();
std::string Reader::printVersion(unsigned int version) std::string Reader::versionToString(std::uint32_t version)
{ {
int major = (version >> 24) & 0xFF; int major = (version >> 24) & 0xFF;
int minor = (version >> 16) & 0xFF; int minor = (version >> 16) & 0xFF;
@ -329,8 +329,8 @@ namespace Nif
void Reader::parse(Files::IStreamPtr&& stream) void Reader::parse(Files::IStreamPtr&& stream)
{ {
const std::array<std::uint64_t, 2> fileHash = Files::getHash(filename, *stream); const std::array<std::uint64_t, 2> fileHash = Files::getHash(mFilename, *stream);
hash.append(reinterpret_cast<const char*>(fileHash.data()), fileHash.size() * sizeof(std::uint64_t)); mHash.append(reinterpret_cast<const char*>(fileHash.data()), fileHash.size() * sizeof(std::uint64_t));
NIFStream nif(*this, std::move(stream)); NIFStream nif(*this, std::move(stream));
@ -343,151 +343,172 @@ namespace Nif
const bool supportedHeader = std::any_of(verStrings.begin(), verStrings.end(), const bool supportedHeader = std::any_of(verStrings.begin(), verStrings.end(),
[&](const std::string& verString) { return head.starts_with(verString); }); [&](const std::string& verString) { return head.starts_with(verString); });
if (!supportedHeader) if (!supportedHeader)
throw Nif::Exception("Invalid NIF header: " + head, filename); throw Nif::Exception("Invalid NIF header: " + head, mFilename);
// Get BCD version // Get BCD version
ver = nif.getUInt(); nif.read(mVersion);
// 4.0.0.0 is an older, practically identical version of the format. // 4.0.0.0 is an older, practically identical version of the format.
// It's not used by Morrowind assets but Morrowind supports it. // It's not used by Morrowind assets but Morrowind supports it.
static const std::array<uint32_t, 2> supportedVers = { static const std::array<uint32_t, 2> supportedVers = {
NIFStream::generateVersion(4, 0, 0, 0), NIFStream::generateVersion(4, 0, 0, 0),
NIFFile::VER_MW, NIFFile::VER_MW,
}; };
const bool supportedVersion = std::find(supportedVers.begin(), supportedVers.end(), ver) != supportedVers.end(); const bool supportedVersion
= std::find(supportedVers.begin(), supportedVers.end(), mVersion) != supportedVers.end();
const bool writeDebugLog = sWriteNifDebugLog; const bool writeDebugLog = sWriteNifDebugLog;
if (!supportedVersion) if (!supportedVersion)
{ {
if (!sLoadUnsupportedFiles) if (!sLoadUnsupportedFiles)
throw Nif::Exception("Unsupported NIF version: " + printVersion(ver), filename); throw Nif::Exception("Unsupported NIF version: " + versionToString(mVersion), mFilename);
if (writeDebugLog) if (writeDebugLog)
Log(Debug::Warning) << " NIFFile Warning: Unsupported NIF version: " << printVersion(ver) Log(Debug::Warning) << " NIFFile Warning: Unsupported NIF version: " << versionToString(mVersion)
<< ". Proceed with caution! File: " << filename; << ". Proceed with caution! File: " << mFilename;
} }
// NIF data endianness const bool hasEndianness = mVersion >= NIFStream::generateVersion(20, 0, 0, 4);
if (ver >= NIFStream::generateVersion(20, 0, 0, 4)) const bool hasUserVersion = mVersion >= NIFStream::generateVersion(10, 0, 1, 8);
const bool hasRecTypeListings = mVersion >= NIFStream::generateVersion(5, 0, 0, 1);
const bool hasRecTypeHashes = mVersion == NIFStream::generateVersion(20, 3, 1, 2);
const bool hasRecordSizes = mVersion >= NIFStream::generateVersion(20, 2, 0, 5);
const bool hasGroups = mVersion >= NIFStream::generateVersion(5, 0, 0, 6);
const bool hasStringTable = mVersion >= NIFStream::generateVersion(20, 1, 0, 1);
const bool hasRecordSeparators
= mVersion >= NIFStream::generateVersion(10, 0, 0, 0) && mVersion < NIFStream::generateVersion(10, 2, 0, 0);
// Record type list
std::vector<std::string> recTypes;
// Record type mapping for each record
std::vector<std::uint16_t> recTypeIndices;
{ {
unsigned char endianness = nif.getChar(); std::uint8_t endianness = 1;
if (hasEndianness)
nif.read(endianness);
// TODO: find some big-endian files and investigate the difference
if (endianness == 0) if (endianness == 0)
throw Nif::Exception("Big endian NIF files are unsupported", filename); throw Nif::Exception("Big endian NIF files are unsupported", mFilename);
} }
// User version if (hasUserVersion)
if (ver > NIFStream::generateVersion(10, 0, 1, 8)) nif.read(mUserVersion);
userVer = nif.getUInt();
// Number of records mRecords.resize(nif.get<std::uint32_t>());
const std::size_t recNum = nif.getUInt();
records.resize(recNum);
// Bethesda stream header // Bethesda stream header
// It contains Bethesda format version and (useless) export information
if (ver == NIFFile::VER_OB_OLD
|| (userVer >= 3
&& ((ver == NIFFile::VER_OB || ver == NIFFile::VER_BGS)
|| (ver >= NIFStream::generateVersion(10, 1, 0, 0) && ver <= NIFStream::generateVersion(20, 0, 0, 4)
&& userVer <= 11))))
{ {
bethVer = nif.getUInt(); bool hasBSStreamHeader = false;
nif.getExportString(); // Author if (mVersion == NIFFile::VER_OB_OLD)
if (bethVer > NIFFile::BETHVER_FO4) hasBSStreamHeader = true;
nif.getUInt(); // Unknown else if (mUserVersion >= 3 && mVersion >= NIFStream::generateVersion(10, 1, 0, 0))
nif.getExportString(); // Process script
nif.getExportString(); // Export script
if (bethVer == NIFFile::BETHVER_FO4)
nif.getExportString(); // Max file path
}
std::vector<std::string> recTypes;
std::vector<unsigned short> recTypeIndices;
const bool hasRecTypeListings = ver >= NIFStream::generateVersion(5, 0, 0, 1);
if (hasRecTypeListings)
{
unsigned short recTypeNum = nif.getUShort();
// Record type list
nif.getSizedStrings(recTypes, recTypeNum);
// Record type mapping for each record
nif.readVector(recTypeIndices, recNum);
if (ver >= NIFStream::generateVersion(5, 0, 0, 6)) // Groups
{ {
if (ver >= NIFStream::generateVersion(20, 1, 0, 1)) // String table if (mVersion <= NIFFile::VER_OB || mVersion == NIFFile::VER_BGS)
{ hasBSStreamHeader = mUserVersion <= 11 || mVersion >= NIFFile::VER_OB;
if (ver >= NIFStream::generateVersion(20, 2, 0, 5)) // Record sizes }
{
std::vector<unsigned int> recSizes; // Currently unused if (hasBSStreamHeader)
nif.readVector(recSizes, recNum); {
} nif.read(mBethVersion);
const std::size_t stringNum = nif.getUInt(); nif.getExportString(); // Author
nif.getUInt(); // Max string length if (mBethVersion >= 131)
nif.getSizedStrings(strings, stringNum); nif.get<std::uint32_t>(); // Unknown
} else
std::vector<unsigned int> groups; // Currently unused nif.getExportString(); // Process script
unsigned int groupNum = nif.getUInt(); nif.getExportString(); // Export script
nif.readVector(groups, groupNum); if (mBethVersion >= 103)
nif.getExportString(); // Max file path
} }
} }
const bool hasRecordSeparators if (hasRecTypeListings)
= ver >= NIFStream::generateVersion(10, 0, 0, 0) && ver < NIFStream::generateVersion(10, 2, 0, 0); {
for (std::size_t i = 0; i < recNum; i++) // TODO: 20.3.1.2 uses DJB hashes instead of strings
if (hasRecTypeHashes)
throw Nif::Exception("Hashed record types are unsupported", mFilename);
else
{
nif.getSizedStrings(recTypes, nif.get<std::uint16_t>());
nif.readVector(recTypeIndices, mRecords.size());
}
}
if (hasRecordSizes) // Record sizes
{
std::vector<std::uint32_t> recSizes; // Currently unused
nif.readVector(recSizes, mRecords.size());
}
if (hasStringTable)
{
std::uint32_t stringNum, maxStringLength;
nif.read(stringNum);
nif.read(maxStringLength);
nif.getSizedStrings(mStrings, stringNum);
}
if (hasGroups)
{
std::vector<std::uint32_t> groups; // Currently unused
nif.readVector(groups, nif.get<std::uint32_t>());
}
for (std::size_t i = 0; i < mRecords.size(); i++)
{ {
std::unique_ptr<Record> r; std::unique_ptr<Record> r;
std::string rec = hasRecTypeListings ? recTypes[recTypeIndices[i]] : nif.getString(); std::string rec = hasRecTypeListings ? recTypes[recTypeIndices[i]] : nif.get<std::string>();
if (rec.empty()) if (rec.empty())
{ {
std::stringstream error; std::stringstream error;
error << "Record type is blank (index " << i << ")"; error << "Record type is blank (index " << i << ")";
throw Nif::Exception(error.str(), filename); throw Nif::Exception(error.str(), mFilename);
} }
// Record separator. Some Havok records in Oblivion do not have it. // Record separator. Some Havok records in Oblivion do not have it.
if (hasRecordSeparators && !rec.starts_with("bhk")) if (hasRecordSeparators && !rec.starts_with("bhk"))
if (nif.getInt()) if (nif.get<int32_t>())
Log(Debug::Warning) << "NIFFile Warning: Record of type " << rec << ", index " << i Log(Debug::Warning) << "NIFFile Warning: Record of type " << rec << ", index " << i
<< " is preceded by a non-zero separator. File: " << filename; << " is preceded by a non-zero separator. File: " << mFilename;
const auto entry = factories.find(rec); const auto entry = factories.find(rec);
if (entry == factories.end()) if (entry == factories.end())
throw Nif::Exception("Unknown record type " + rec, filename); throw Nif::Exception("Unknown record type " + rec, mFilename);
r = entry->second(); r = entry->second();
if (!supportedVersion && writeDebugLog) if (!supportedVersion && writeDebugLog)
Log(Debug::Verbose) << "NIF Debug: Reading record of type " << rec << ", index " << i << " (" Log(Debug::Verbose) << "NIF Debug: Reading record of type " << rec << ", index " << i << " ("
<< filename << ")"; << mFilename << ")";
assert(r != nullptr); assert(r != nullptr);
assert(r->recType != RC_MISSING); assert(r->recType != RC_MISSING);
r->recName = rec; r->recName = rec;
r->recIndex = i; r->recIndex = i;
r->read(&nif); r->read(&nif);
records[i] = std::move(r); mRecords[i] = std::move(r);
} }
const std::size_t rootNum = nif.getUInt();
roots.resize(rootNum);
// Determine which records are roots // Determine which records are roots
for (std::size_t i = 0; i < rootNum; i++) mRoots.resize(nif.get<uint32_t>());
for (std::size_t i = 0; i < mRoots.size(); i++)
{ {
int idx = nif.getInt(); std::int32_t idx;
if (idx >= 0 && static_cast<std::size_t>(idx) < records.size()) nif.read(idx);
if (idx >= 0 && static_cast<std::size_t>(idx) < mRecords.size())
{ {
roots[i] = records[idx].get(); mRoots[i] = mRecords[idx].get();
} }
else else
{ {
roots[i] = nullptr; mRoots[i] = nullptr;
Log(Debug::Warning) << "NIFFile Warning: Root " << i + 1 << " does not point to a record: index " << idx Log(Debug::Warning) << "NIFFile Warning: Root " << i + 1 << " does not point to a record: index " << idx
<< ". File: " << filename; << ". File: " << mFilename;
} }
} }
// Once parsing is done, do post-processing. // Once parsing is done, do post-processing.
for (const auto& record : records) for (const auto& record : mRecords)
record->post(*this); record->post(*this);
} }
@ -513,7 +534,7 @@ namespace Nif
{ {
if (index == std::numeric_limits<std::uint32_t>::max()) if (index == std::numeric_limits<std::uint32_t>::max())
return std::string(); return std::string();
return strings.at(index); return mStrings.at(index);
} }
} }

View file

@ -30,13 +30,14 @@ namespace Nif
BETHVER_SKY = 83, // Skyrim BETHVER_SKY = 83, // Skyrim
BETHVER_SSE = 100, // Skyrim SE BETHVER_SSE = 100, // Skyrim SE
BETHVER_FO4 = 130, // Fallout 4 BETHVER_FO4 = 130, // Fallout 4
BETHVER_F76 = 155 // Fallout 76 BETHVER_F76 = 155, // Fallout 76
BETHVER_STF = 172, // Starfield
}; };
/// File version, user version, Bethesda version /// File version, user version, Bethesda version
unsigned int mVersion = 0; std::uint32_t mVersion = 0;
unsigned int mUserVersion = 0; std::uint32_t mUserVersion = 0;
unsigned int mBethVersion = 0; std::uint32_t mBethVersion = 0;
/// File name, used for error messages and opening the file /// File name, used for error messages and opening the file
std::filesystem::path mPath; std::filesystem::path mPath;
@ -76,13 +77,13 @@ namespace Nif
const std::string& getHash() const { return mFile->mHash; } const std::string& getHash() const { return mFile->mHash; }
/// Get the version of the NIF format used /// Get the version of the NIF format used
unsigned int getVersion() const { return mFile->mVersion; } std::uint32_t getVersion() const { return mFile->mVersion; }
/// Get the user version of the NIF format used /// Get the user version of the NIF format used
unsigned int getUserVersion() const { return mFile->mUserVersion; } std::uint32_t getUserVersion() const { return mFile->mUserVersion; }
/// Get the Bethesda version of the NIF format used /// Get the Bethesda version of the NIF format used
unsigned int getBethVersion() const { return mFile->mBethVersion; } std::uint32_t getBethVersion() const { return mFile->mBethVersion; }
bool getUseSkinning() const { return mFile->mUseSkinning; } bool getUseSkinning() const { return mFile->mUseSkinning; }
@ -93,22 +94,22 @@ namespace Nif
class Reader class Reader
{ {
/// File version, user version, Bethesda version /// File version, user version, Bethesda version
unsigned int& ver; std::uint32_t& mVersion;
unsigned int& userVer; std::uint32_t& mUserVersion;
unsigned int& bethVer; std::uint32_t& mBethVersion;
/// File name, used for error messages and opening the file /// File name, used for error messages and opening the file
std::filesystem::path& filename; std::filesystem::path& mFilename;
std::string& hash; std::string& mHash;
/// Record list /// Record list
std::vector<std::unique_ptr<Record>>& records; std::vector<std::unique_ptr<Record>>& mRecords;
/// Root list. This is a select portion of the pointers from records /// Root list. This is a select portion of the pointers from records
std::vector<Record*>& roots; std::vector<Record*>& mRoots;
/// String table /// String table
std::vector<std::string> strings; std::vector<std::string> mStrings;
bool& mUseSkinning; bool& mUseSkinning;
@ -117,7 +118,7 @@ namespace Nif
/// Get the file's version in a human readable form /// Get the file's version in a human readable form
///\returns A string containing a human readable NIF version number ///\returns A string containing a human readable NIF version number
std::string printVersion(unsigned int version); std::string versionToString(std::uint32_t version);
public: public:
/// Open a NIF stream. The name is used for error messages. /// Open a NIF stream. The name is used for error messages.
@ -127,26 +128,26 @@ namespace Nif
void parse(Files::IStreamPtr&& stream); void parse(Files::IStreamPtr&& stream);
/// Get a given record /// Get a given record
Record* getRecord(size_t index) const { return records.at(index).get(); } Record* getRecord(size_t index) const { return mRecords.at(index).get(); }
/// Get a given string from the file's string table /// Get a given string from the file's string table
std::string getString(uint32_t index) const; std::string getString(std::uint32_t index) const;
/// Set whether there is skinning contained in this NIF file. /// Set whether there is skinning contained in this NIF file.
/// @note This is just a hint for users of the NIF file and has no effect on the loading procedure. /// @note This is just a hint for users of the NIF file and has no effect on the loading procedure.
void setUseSkinning(bool skinning); void setUseSkinning(bool skinning);
/// Get the name of the file /// Get the name of the file
std::filesystem::path getFilename() const { return filename; } std::filesystem::path getFilename() const { return mFilename; }
/// Get the version of the NIF format used /// Get the version of the NIF format used
unsigned int getVersion() const { return ver; } std::uint32_t getVersion() const { return mVersion; }
/// Get the user version of the NIF format used /// Get the user version of the NIF format used
unsigned int getUserVersion() const { return userVer; } std::uint32_t getUserVersion() const { return mUserVersion; }
/// Get the Bethesda version of the NIF format used /// Get the Bethesda version of the NIF format used
unsigned int getBethVersion() const { return bethVer; } std::uint32_t getBethVersion() const { return mBethVersion; }
static void setLoadUnsupportedFiles(bool load); static void setLoadUnsupportedFiles(bool load);

View file

@ -130,11 +130,11 @@ namespace Nif
} }
template <> template <>
void NIFStream::read<Transformation>(Transformation& t) void NIFStream::read<NiTransform>(NiTransform& transform)
{ {
read(t.pos); read(transform.mRotation);
read(t.rotation); read(transform.mTranslation);
read(t.scale); read(transform.mScale);
} }
template <> template <>
@ -192,7 +192,7 @@ namespace Nif
} }
template <> template <>
void NIFStream::read<Transformation>(Transformation* dest, size_t size) void NIFStream::read<NiTransform>(NiTransform* dest, size_t size)
{ {
readRange(*this, dest, size); readRange(*this, dest, size);
} }

View file

@ -159,7 +159,6 @@ namespace Nif
osg::Vec4f getVector4() { return get<osg::Vec4f>(); } osg::Vec4f getVector4() { return get<osg::Vec4f>(); }
Matrix3 getMatrix3() { return get<Matrix3>(); } Matrix3 getMatrix3() { return get<Matrix3>(); }
osg::Quat getQuaternion() { return get<osg::Quat>(); } osg::Quat getQuaternion() { return get<osg::Quat>(); }
Transformation getTrafo() { return get<Transformation>(); }
bool getBoolean() { return get<bool>(); } bool getBoolean() { return get<bool>(); }
std::string getString() { return get<std::string>(); } std::string getString() { return get<std::string>(); }
}; };
@ -177,7 +176,7 @@ namespace Nif
template <> template <>
void NIFStream::read<osg::BoundingSpheref>(osg::BoundingSpheref& sphere); void NIFStream::read<osg::BoundingSpheref>(osg::BoundingSpheref& sphere);
template <> template <>
void NIFStream::read<Transformation>(Transformation& t); void NIFStream::read<NiTransform>(NiTransform& transform);
template <> template <>
void NIFStream::read<bool>(bool& data); void NIFStream::read<bool>(bool& data);
template <> template <>
@ -196,7 +195,7 @@ namespace Nif
template <> template <>
void NIFStream::read<osg::BoundingSpheref>(osg::BoundingSpheref* dest, size_t size); void NIFStream::read<osg::BoundingSpheref>(osg::BoundingSpheref* dest, size_t size);
template <> template <>
void NIFStream::read<Transformation>(Transformation* dest, size_t size); void NIFStream::read<NiTransform>(NiTransform* dest, size_t size);
template <> template <>
void NIFStream::read<bool>(bool* dest, size_t size); void NIFStream::read<bool>(bool* dest, size_t size);
template <> template <>

View file

@ -53,29 +53,29 @@ namespace Nif
} }
}; };
struct Transformation struct NiTransform
{ {
osg::Vec3f pos; Matrix3 mRotation; // this can contain scale components too, including negative and nonuniform scales
Matrix3 rotation; // this can contain scale components too, including negative and nonuniform scales osg::Vec3f mTranslation;
float scale; float mScale;
osg::Matrixf toMatrix() const osg::Matrixf toMatrix() const
{ {
osg::Matrixf transform; osg::Matrixf transform;
transform.setTrans(pos); transform.setTrans(mTranslation);
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j) for (int j = 0; j < 3; ++j)
transform(j, i) = rotation.mValues[i][j] * scale; // NB column/row major difference transform(j, i) = mRotation.mValues[i][j] * mScale; // NB column/row major difference
return transform; return transform;
} }
bool isIdentity() const { return pos == osg::Vec3f(0, 0, 0) && rotation.isIdentity() && scale == 1.f; } bool isIdentity() const { return mRotation.isIdentity() && mTranslation == osg::Vec3f() && mScale == 1.f; }
static const Transformation& getIdentity() static const NiTransform& getIdentity()
{ {
static const Transformation identity = { osg::Vec3f(), Matrix3(), 1.0f }; static const NiTransform identity = { Matrix3(), osg::Vec3f(), 1.0f };
return identity; return identity;
} }
}; };

View file

@ -12,7 +12,7 @@ namespace Nif
{ {
void NiBoundingVolume::read(NIFStream* nif) void NiBoundingVolume::read(NIFStream* nif)
{ {
type = nif->getUInt(); nif->read(type);
switch (type) switch (type)
{ {
case BASE_BV: case BASE_BV:
@ -75,48 +75,47 @@ namespace Nif
} }
} }
void Node::read(NIFStream* nif) void NiAVObject::read(NIFStream* nif)
{ {
Named::read(nif); NiObjectNET::read(nif);
flags = nif->getBethVersion() <= 26 ? nif->getUShort() : nif->getUInt(); if (nif->getBethVersion() <= 26)
trafo = nif->getTrafo(); mFlags = nif->get<uint16_t>();
else
nif->read(mFlags);
nif->read(mTransform.mTranslation);
nif->read(mTransform.mRotation);
nif->read(mTransform.mScale);
if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0)) if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0))
velocity = nif->getVector3(); nif->read(mVelocity);
if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3) if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
readRecordList(nif, props); readRecordList(nif, mProperties);
if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0) && nif->get<bool>())
if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0)) mBounds.read(nif);
hasBounds = nif->getBoolean();
if (hasBounds)
bounds.read(nif);
// Reference to the collision object in Gamebryo files.
if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0)) if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0))
collision.read(nif); mCollision.read(nif);
parents.clear();
isBone = false;
} }
void Node::post(Reader& nif) void NiAVObject::post(Reader& nif)
{ {
Named::post(nif); NiObjectNET::post(nif);
postRecordList(nif, props);
collision.post(nif); postRecordList(nif, mProperties);
mCollision.post(nif);
} }
void Node::setBone() void NiAVObject::setBone()
{ {
isBone = true; mIsBone = true;
} }
void NiNode::read(NIFStream* nif) void NiNode::read(NIFStream* nif)
{ {
Node::read(nif); NiAVObject::read(nif);
readRecordList(nif, children);
readRecordList(nif, mChildren);
if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4) if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4)
readRecordList(nif, effects); readRecordList(nif, mEffects);
// FIXME: stopgap solution until we figure out what Oblivion does if it does anything // FIXME: stopgap solution until we figure out what Oblivion does if it does anything
if (nif->getVersion() > NIFFile::NIFVersion::VER_MW && nif->getVersion() < NIFFile::NIFVersion::VER_BGS) if (nif->getVersion() > NIFFile::NIFVersion::VER_MW && nif->getVersion() < NIFFile::NIFVersion::VER_BGS)
@ -128,23 +127,24 @@ namespace Nif
// FIXME: if node 0 is *not* the only root node, this must not happen. // FIXME: if node 0 is *not* the only root node, this must not happen.
// FIXME: doing this here is awful. // FIXME: doing this here is awful.
// We want to do this on world scene graph level rather than local scene graph level. // We want to do this on world scene graph level rather than local scene graph level.
if (0 == recIndex && !Misc::StringUtils::ciEqual(name, "bip01")) if (recIndex == 0 && !Misc::StringUtils::ciEqual(mName, "bip01"))
{ {
trafo = Nif::Transformation::getIdentity(); mTransform = Nif::NiTransform::getIdentity();
} }
} }
void NiNode::post(Reader& nif) void NiNode::post(Reader& nif)
{ {
Node::post(nif); NiAVObject::post(nif);
postRecordList(nif, children);
postRecordList(nif, effects);
for (auto& child : children) postRecordList(nif, mChildren);
postRecordList(nif, mEffects);
for (auto& child : mChildren)
{ {
// Why would a unique list of children contain empty refs? // Why would a unique list of children contain empty refs?
if (!child.empty()) if (!child.empty())
child->parents.push_back(this); child->mParents.push_back(this);
} }
} }
@ -168,7 +168,7 @@ namespace Nif
void NiGeometry::read(NIFStream* nif) void NiGeometry::read(NIFStream* nif)
{ {
Node::read(nif); NiAVObject::read(nif);
data.read(nif); data.read(nif);
skin.read(nif); skin.read(nif);
material.read(nif); material.read(nif);
@ -182,7 +182,7 @@ namespace Nif
void NiGeometry::post(Reader& nif) void NiGeometry::post(Reader& nif)
{ {
Node::post(nif); NiAVObject::post(nif);
data.post(nif); data.post(nif);
skin.post(nif); skin.post(nif);
shaderprop.post(nif); shaderprop.post(nif);
@ -221,7 +221,7 @@ namespace Nif
void NiCamera::read(NIFStream* nif) void NiCamera::read(NIFStream* nif)
{ {
Node::read(nif); NiAVObject::read(nif);
cam.read(nif); cam.read(nif);
@ -287,7 +287,7 @@ namespace Nif
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
mMode = nif->getUShort() & 0x7; mMode = nif->getUShort() & 0x7;
else else
mMode = (flags >> 5) & 0x3; mMode = (mFlags >> 5) & 0x3;
} }
void NiDefaultAVObjectPalette::read(NIFStream* nif) void NiDefaultAVObjectPalette::read(NIFStream* nif)
@ -335,7 +335,7 @@ namespace Nif
void BSTriShape::read(NIFStream* nif) void BSTriShape::read(NIFStream* nif)
{ {
Node::read(nif); NiAVObject::read(nif);
nif->read(mBoundingSphere); nif->read(mBoundingSphere);
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_F76) if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_F76)
@ -389,7 +389,7 @@ namespace Nif
void BSTriShape::post(Reader& nif) void BSTriShape::post(Reader& nif)
{ {
Node::post(nif); NiAVObject::post(nif);
mSkin.post(nif); mSkin.post(nif);
mShaderProperty.post(nif); mShaderProperty.post(nif);
mAlphaProperty.post(nif); mAlphaProperty.post(nif);

View file

@ -15,7 +15,7 @@ namespace Nif
struct NiBoundingVolume struct NiBoundingVolume
{ {
enum Type enum Type : uint32_t
{ {
BASE_BV = 0xFFFFFFFF, BASE_BV = 0xFFFFFFFF,
SPHERE_BV = 0, SPHERE_BV = 0,
@ -51,7 +51,7 @@ namespace Nif
osg::Vec3f origin; osg::Vec3f origin;
}; };
unsigned int type; uint32_t type{ BASE_BV };
osg::BoundingSpheref sphere; osg::BoundingSpheref sphere;
NiBoxBV box; NiBoxBV box;
NiCapsuleBV capsule; NiCapsuleBV capsule;
@ -62,11 +62,13 @@ namespace Nif
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
/** A Node is an object that's part of the main NIF tree. It has struct NiSequenceStreamHelper : NiObjectNET
parent node (unless it's the root), and transformation (location {
and rotation) relative to it's parent. };
*/
struct Node : public Named // NiAVObject is an object that is a part of the main NIF tree. It has
// a parent node (unless it's the root) and transformation relative to its parent.
struct NiAVObject : public NiObjectNET
{ {
enum Flags enum Flags
{ {
@ -76,57 +78,48 @@ namespace Nif
Flag_ActiveCollision = 0x0020 Flag_ActiveCollision = 0x0020
}; };
// Node flags. Interpretation depends somewhat on the type of node. // Node flags. Interpretation depends on the record type.
unsigned int flags; uint32_t mFlags;
NiTransform mTransform;
Transformation trafo; osg::Vec3f mVelocity;
osg::Vec3f velocity; // Unused? Might be a run-time game state PropertyList mProperties;
PropertyList props; NiBoundingVolume mBounds;
NiCollisionObjectPtr mCollision;
// Bounding box info // Parent nodes for the node. Only types derived from NiNode can be parents.
bool hasBounds{ false }; std::vector<NiNode*> mParents;
NiBoundingVolume bounds; bool mIsBone{ false };
// Collision object info
NiCollisionObjectPtr collision;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
// Parent node, or nullptr for the root node. As far as I'm aware, only
// NiNodes (or types derived from NiNodes) can be parents.
std::vector<NiNode*> parents;
bool isBone{ false };
void setBone(); void setBone();
bool isHidden() const { return mFlags & Flag_Hidden; }
bool isHidden() const { return flags & Flag_Hidden; } bool hasMeshCollision() const { return mFlags & Flag_MeshCollision; }
bool hasMeshCollision() const { return flags & Flag_MeshCollision; } bool hasBBoxCollision() const { return mFlags & Flag_BBoxCollision; }
bool hasBBoxCollision() const { return flags & Flag_BBoxCollision; } bool collisionActive() const { return mFlags & Flag_ActiveCollision; }
bool collisionActive() const { return flags & Flag_ActiveCollision; }
}; };
struct NiNode : Node struct NiNode : NiAVObject
{ {
NodeList children;
NodeList effects;
enum BSAnimFlags enum BSAnimFlags
{ {
AnimFlag_AutoPlay = 0x0020 AnimFlag_AutoPlay = 0x0020
}; };
enum BSParticleFlags enum BSParticleFlags
{ {
ParticleFlag_AutoPlay = 0x0020, ParticleFlag_AutoPlay = 0x0020,
ParticleFlag_LocalSpace = 0x0080 ParticleFlag_LocalSpace = 0x0080
}; };
NiAVObjectList mChildren;
NiAVObjectList mEffects;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
struct NiGeometry : Node struct NiGeometry : NiAVObject
{ {
/* Possible flags: /* Possible flags:
0x40 - mesh has no vertex normals ? 0x40 - mesh has no vertex normals ?
@ -172,7 +165,7 @@ namespace Nif
{ {
}; };
struct NiCamera : Node struct NiCamera : NiAVObject
{ {
struct Camera struct Camera
{ {
@ -230,7 +223,7 @@ namespace Nif
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
bool swing() const { return flags & Flag_Swing; } bool swing() const { return mFlags & Flag_Swing; }
}; };
// Abstract // Abstract
@ -272,8 +265,8 @@ namespace Nif
struct NiDefaultAVObjectPalette : Record struct NiDefaultAVObjectPalette : Record
{ {
NodePtr mScene; NiAVObjectPtr mScene;
std::unordered_map<std::string, NodePtr> mObjects; std::unordered_map<std::string, NiAVObjectPtr> mObjects;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
@ -281,7 +274,7 @@ namespace Nif
struct BSTreeNode : NiNode struct BSTreeNode : NiNode
{ {
NodeList mBones1, mBones2; NiAVObjectList mBones1, mBones2;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
@ -346,7 +339,7 @@ namespace Nif
void read(NIFStream* nif, uint16_t flags); void read(NIFStream* nif, uint16_t flags);
}; };
struct BSTriShape : Node struct BSTriShape : NiAVObject
{ {
osg::BoundingSpheref mBoundingSphere; osg::BoundingSpheref mBoundingSphere;
std::array<float, 6> mBoundMinMax; std::array<float, 6> mBoundMinMax;
@ -393,5 +386,6 @@ namespace Nif
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
} // Namespace
}
#endif #endif

View file

@ -374,7 +374,7 @@ namespace Nif
struct NiCollisionObject : public Record struct NiCollisionObject : public Record
{ {
// The node that references this object // The node that references this object
NodePtr mTarget; NiAVObjectPtr mTarget;
void read(NIFStream* nif) override { mTarget.read(nif); } void read(NIFStream* nif) override { mTarget.read(nif); }
void post(Reader& nif) override { mTarget.post(nif); } void post(Reader& nif) override { mTarget.post(nif); }
@ -541,7 +541,7 @@ namespace Nif
struct bhkCompressedMeshShape : public bhkShape struct bhkCompressedMeshShape : public bhkShape
{ {
NodePtr mTarget; NiAVObjectPtr mTarget;
uint32_t mUserData; uint32_t mUserData;
float mRadius; float mRadius;
osg::Vec4f mScale; osg::Vec4f mScale;

View file

@ -29,7 +29,7 @@
namespace Nif namespace Nif
{ {
struct Property : public Named struct Property : public NiObjectNET
{ {
}; };

View file

@ -1,9 +1,10 @@
#ifndef OPENMW_COMPONENTS_NIF_RECORDPTR_HPP #ifndef OPENMW_COMPONENTS_NIF_RECORDPTR_HPP
#define OPENMW_COMPONENTS_NIF_RECORDPTR_HPP #define OPENMW_COMPONENTS_NIF_RECORDPTR_HPP
#include <vector>
#include "niffile.hpp" #include "niffile.hpp"
#include "nifstream.hpp" #include "nifstream.hpp"
#include <vector>
namespace Nif namespace Nif
{ {
@ -39,7 +40,7 @@ namespace Nif
assert(index == -2); assert(index == -2);
// Store the index for later // Store the index for later
index = nif->getInt(); index = nif->get<int32_t>();
assert(index >= -1); assert(index >= -1);
} }
@ -90,12 +91,13 @@ namespace Nif
template <class T> template <class T>
void readRecordList(NIFStream* nif, RecordListT<T>& list) void readRecordList(NIFStream* nif, RecordListT<T>& list)
{ {
const int length = nif->getInt(); const std::uint32_t length = nif->get<std::uint32_t>();
if (length < 0) // No reasonable list can hit this generous limit
throw std::runtime_error("Negative NIF record list length: " + std::to_string(length)); if (length >= (1 << 24))
throw std::runtime_error("Record list too long: " + std::to_string(length));
list.resize(static_cast<std::size_t>(length)); list.resize(length);
for (auto& value : list) for (auto& value : list)
value.read(nif); value.read(nif);
@ -108,14 +110,14 @@ namespace Nif
value.post(nif); value.post(nif);
} }
struct Node; struct NiAVObject;
struct Extra; struct Extra;
struct Property; struct Property;
struct NiUVData; struct NiUVData;
struct NiPosData; struct NiPosData;
struct NiVisData; struct NiVisData;
struct Controller; struct Controller;
struct Named; struct NiObjectNET;
struct NiSkinData; struct NiSkinData;
struct NiFloatData; struct NiFloatData;
struct NiMorphData; struct NiMorphData;
@ -150,13 +152,13 @@ namespace Nif
struct BSMultiBound; struct BSMultiBound;
struct BSMultiBoundData; struct BSMultiBoundData;
using NodePtr = RecordPtrT<Node>; using NiAVObjectPtr = RecordPtrT<NiAVObject>;
using ExtraPtr = RecordPtrT<Extra>; using ExtraPtr = RecordPtrT<Extra>;
using NiUVDataPtr = RecordPtrT<NiUVData>; using NiUVDataPtr = RecordPtrT<NiUVData>;
using NiPosDataPtr = RecordPtrT<NiPosData>; using NiPosDataPtr = RecordPtrT<NiPosData>;
using NiVisDataPtr = RecordPtrT<NiVisData>; using NiVisDataPtr = RecordPtrT<NiVisData>;
using ControllerPtr = RecordPtrT<Controller>; using ControllerPtr = RecordPtrT<Controller>;
using NamedPtr = RecordPtrT<Named>; using NiObjectNETPtr = RecordPtrT<NiObjectNET>;
using NiSkinDataPtr = RecordPtrT<NiSkinData>; using NiSkinDataPtr = RecordPtrT<NiSkinData>;
using NiMorphDataPtr = RecordPtrT<NiMorphData>; using NiMorphDataPtr = RecordPtrT<NiMorphData>;
using NiPixelDataPtr = RecordPtrT<NiPixelData>; using NiPixelDataPtr = RecordPtrT<NiPixelData>;
@ -188,7 +190,7 @@ namespace Nif
using BSMultiBoundPtr = RecordPtrT<BSMultiBound>; using BSMultiBoundPtr = RecordPtrT<BSMultiBound>;
using BSMultiBoundDataPtr = RecordPtrT<BSMultiBoundData>; using BSMultiBoundDataPtr = RecordPtrT<BSMultiBoundData>;
using NodeList = RecordListT<Node>; using NiAVObjectList = RecordListT<NiAVObject>;
using PropertyList = RecordListT<Property>; using PropertyList = RecordListT<Property>;
using ExtraList = RecordListT<Extra>; using ExtraList = RecordListT<Extra>;
using NiSourceTextureList = RecordListT<NiSourceTexture>; using NiSourceTextureList = RecordListT<NiSourceTexture>;

View file

@ -6,7 +6,7 @@
namespace Nif namespace Nif
{ {
struct NiTexture : public Named struct NiTexture : public NiObjectNET
{ {
}; };

View file

@ -132,13 +132,13 @@ namespace NifBullet
mShape->mFileHash = nif.getHash(); mShape->mFileHash = nif.getHash();
const size_t numRoots = nif.numRoots(); const size_t numRoots = nif.numRoots();
std::vector<const Nif::Node*> roots; std::vector<const Nif::NiAVObject*> 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);
if (!r) if (!r)
continue; continue;
const Nif::Node* node = dynamic_cast<const Nif::Node*>(r); const Nif::NiAVObject* node = dynamic_cast<const Nif::NiAVObject*>(r);
if (node) if (node)
roots.emplace_back(node); roots.emplace_back(node);
} }
@ -151,7 +151,7 @@ namespace NifBullet
} }
// Try to find a valid bounding box first. If one's found for any root node, use that. // Try to find a valid bounding box first. If one's found for any root node, use that.
for (const Nif::Node* node : roots) for (const Nif::NiAVObject* node : roots)
{ {
if (findBoundingBox(*node, filename)) if (findBoundingBox(*node, filename))
{ {
@ -175,15 +175,19 @@ namespace NifBullet
// If there's no bounding box, we'll have to generate a Bullet collision shape // If there's no bounding box, we'll have to generate a Bullet collision shape
// from the collision data present in every root node. // from the collision data present in every root node.
for (const Nif::Node* node : roots) for (const Nif::NiAVObject* node : roots)
{ {
bool hasCollisionNode = hasRootCollisionNode(*node); const Nif::NiNode* colNode = findRootCollisionNode(*node);
bool hasCollisionShape = hasCollisionNode && !collisionShapeIsEmpty(*node); bool hasCollisionShape = false;
if (hasCollisionNode && !hasCollisionShape) if (colNode != nullptr)
mShape->mVisualCollisionType = Resource::VisualCollisionType::Camera; {
bool generateCollisionShape = !hasCollisionShape; if (colNode->mBounds.type == Nif::NiBoundingVolume::Type::BASE_BV && !colNode->mChildren.empty())
hasCollisionShape = true;
else
mShape->mVisualCollisionType = Resource::VisualCollisionType::Camera;
}
HandleNodeArgs args; HandleNodeArgs args;
args.mAutogenerated = args.mIsCollisionNode = generateCollisionShape; args.mAutogenerated = args.mIsCollisionNode = !hasCollisionShape;
args.mAnimated = isAnimated; args.mAnimated = isAnimated;
handleNode(filename, *node, nullptr, args, mShape->mVisualCollisionType); handleNode(filename, *node, nullptr, args, mShape->mVisualCollisionType);
} }
@ -199,87 +203,75 @@ namespace NifBullet
// Find a boundingBox in the node hierarchy. // Find a boundingBox in the node hierarchy.
// Return: use bounding box for collision? // Return: use bounding box for collision?
bool BulletNifLoader::findBoundingBox(const Nif::Node& node, const std::string& filename) bool BulletNifLoader::findBoundingBox(const Nif::NiAVObject& node, const std::string& filename)
{ {
if (node.hasBounds) unsigned int type = node.mBounds.type;
switch (type)
{ {
unsigned int type = node.bounds.type; case Nif::NiBoundingVolume::Type::BASE_BV:
switch (type) break;
case Nif::NiBoundingVolume::Type::BOX_BV:
mShape->mCollisionBox.mExtents = node.mBounds.box.extents;
mShape->mCollisionBox.mCenter = node.mBounds.box.center;
break;
default:
{ {
case Nif::NiBoundingVolume::Type::BOX_BV: std::stringstream warning;
mShape->mCollisionBox.mExtents = node.bounds.box.extents; warning << "Unsupported NiBoundingVolume type " << type << " in node " << node.recIndex;
mShape->mCollisionBox.mCenter = node.bounds.box.center; warning << " in file " << filename;
break; warn(warning.str());
default:
{
std::stringstream warning;
warning << "Unsupported NiBoundingVolume type " << type << " in node " << node.recIndex;
warning << " in file " << filename;
warn(warning.str());
}
}
if (node.hasBBoxCollision())
{
return true;
} }
} }
if (type != Nif::NiBoundingVolume::Type::BASE_BV && node.hasBBoxCollision())
return true;
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&node)) if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&node))
{ {
const Nif::NodeList& list = ninode->children; for (const auto& child : ninode->mChildren)
for (const auto& child : list)
if (!child.empty() && findBoundingBox(child.get(), filename)) if (!child.empty() && findBoundingBox(child.get(), filename))
return true; return true;
} }
return false; return false;
} }
bool BulletNifLoader::hasRootCollisionNode(const Nif::Node& rootNode) const const Nif::NiNode* BulletNifLoader::findRootCollisionNode(const Nif::NiAVObject& rootNode) const
{ {
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&rootNode)) if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&rootNode))
{ {
for (const auto& child : ninode->children) for (const auto& child : ninode->mChildren)
{ {
if (child.empty()) if (!child.empty() && child.getPtr()->recType == Nif::RC_RootCollisionNode)
continue; return static_cast<const Nif::NiNode*>(child.getPtr());
if (child.getPtr()->recType == Nif::RC_RootCollisionNode)
return true;
} }
} }
return false; return nullptr;
} }
bool BulletNifLoader::collisionShapeIsEmpty(const Nif::Node& rootNode) const void BulletNifLoader::handleNode(const std::string& fileName, const Nif::NiAVObject& node,
{ const Nif::Parent* parent, HandleNodeArgs args, Resource::VisualCollisionType& visualCollisionType)
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&rootNode))
{
for (const auto& child : ninode->children)
{
if (child.empty())
continue;
const Nif::Node* childNode = child.getPtr();
if (childNode->recType != Nif::RC_RootCollisionNode)
continue;
const Nif::NiNode* niChildnode
= static_cast<const Nif::NiNode*>(childNode); // RootCollisionNode is always a NiNode
if (childNode->hasBounds || niChildnode->children.size() > 0)
return false;
}
}
return true;
}
void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node& node, const Nif::Parent* parent,
HandleNodeArgs args, Resource::VisualCollisionType& visualCollisionType)
{ {
// TODO: allow on-the fly collision switching via toggling this flag // TODO: allow on-the fly collision switching via toggling this flag
if (node.recType == Nif::RC_NiCollisionSwitch && !node.collisionActive()) if (node.recType == Nif::RC_NiCollisionSwitch && !node.collisionActive())
return; return;
if (!node.controller.empty() && node.controller->recType == Nif::RC_NiKeyframeController for (Nif::ControllerPtr ctrl = node.mController; !ctrl.empty(); ctrl = ctrl->next)
&& node.controller->isActive()) {
args.mAnimated = true; if (args.mAnimated)
break;
if (!ctrl->isActive())
continue;
switch (ctrl->recType)
{
case Nif::RC_NiKeyframeController:
case Nif::RC_NiPathController:
case Nif::RC_NiRollController:
args.mAnimated = true;
break;
default:
continue;
}
}
if (node.recType == Nif::RC_RootCollisionNode) if (node.recType == Nif::RC_RootCollisionNode)
{ {
@ -304,13 +296,7 @@ namespace NifBullet
args.mAvoid = true; args.mAvoid = true;
// Check for extra data // Check for extra data
std::vector<Nif::ExtraPtr> extraCollection; for (const auto& e : node.getExtraList())
for (Nif::ExtraPtr e = node.extra; !e.empty(); e = e->mNext)
extraCollection.emplace_back(e);
for (const auto& extraNode : node.extralist)
if (!extraNode.empty())
extraCollection.emplace_back(extraNode);
for (const auto& e : extraCollection)
{ {
if (e->recType == Nif::RC_NiStringExtraData) if (e->recType == Nif::RC_NiStringExtraData)
{ {
@ -346,10 +332,10 @@ namespace NifBullet
if (args.mIsCollisionNode) if (args.mIsCollisionNode)
{ {
// NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape! // NOTE: a trishape with bounds, but no BBoxCollision flag should NOT go through handleNiTriShape!
// It must be ignored completely. // It must be ignored completely.
// (occurs in tr_ex_imp_wall_arch_04.nif) // (occurs in tr_ex_imp_wall_arch_04.nif)
if (!node.hasBounds if (node.mBounds.type == Nif::NiBoundingVolume::Type::BASE_BV
&& (node.recType == Nif::RC_NiTriShape || node.recType == Nif::RC_NiTriStrips && (node.recType == Nif::RC_NiTriShape || node.recType == Nif::RC_NiTriStrips
|| node.recType == Nif::RC_BSLODTriShape)) || node.recType == Nif::RC_BSLODTriShape))
{ {
@ -360,14 +346,13 @@ namespace NifBullet
// For NiNodes, loop through children // For NiNodes, loop through children
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&node)) if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&node))
{ {
const Nif::NodeList& list = ninode->children;
const Nif::Parent currentParent{ *ninode, parent }; const Nif::Parent currentParent{ *ninode, parent };
for (const auto& child : list) for (const auto& child : ninode->mChildren)
{ {
if (child.empty()) if (child.empty())
continue; continue;
assert(std::find(child->parents.begin(), child->parents.end(), ninode) != child->parents.end()); assert(std::find(child->mParents.begin(), child->mParents.end(), ninode) != child->mParents.end());
handleNode(fileName, child.get(), &currentParent, args, visualCollisionType); handleNode(fileName, child.get(), &currentParent, args, visualCollisionType);
} }
} }
@ -378,7 +363,7 @@ namespace NifBullet
{ {
// mHasMarkers is specifically BSXFlags editor marker flag. // mHasMarkers is specifically BSXFlags editor marker flag.
// If this changes, the check must be corrected. // If this changes, the check must be corrected.
if (args.mHasMarkers && Misc::StringUtils::ciStartsWith(niGeometry.name, "EditorMarker")) if (args.mHasMarkers && Misc::StringUtils::ciStartsWith(niGeometry.mName, "EditorMarker"))
return; return;
if (niGeometry.data.empty() || niGeometry.data->mVertices.empty()) if (niGeometry.data.empty() || niGeometry.data->mVertices.empty())
@ -395,9 +380,9 @@ namespace NifBullet
auto childShape = std::make_unique<Resource::TriangleMeshShape>(childMesh.get(), true); auto childShape = std::make_unique<Resource::TriangleMeshShape>(childMesh.get(), true);
std::ignore = childMesh.release(); std::ignore = childMesh.release();
osg::Matrixf transform = niGeometry.trafo.toMatrix(); osg::Matrixf transform = niGeometry.mTransform.toMatrix();
for (const Nif::Parent* parent = nodeParent; parent != nullptr; parent = parent->mParent) for (const Nif::Parent* parent = nodeParent; parent != nullptr; parent = parent->mParent)
transform *= parent->mNiNode.trafo.toMatrix(); transform *= parent->mNiNode.mTransform.toMatrix();
childShape->setLocalScaling(Misc::Convert::toBullet(transform.getScale())); childShape->setLocalScaling(Misc::Convert::toBullet(transform.getScale()));
transform.orthoNormalize(transform); transform.orthoNormalize(transform);

View file

@ -22,10 +22,8 @@ class btCollisionShape;
namespace Nif namespace Nif
{ {
struct Node; struct NiAVObject;
struct Transformation; struct NiNode;
struct NiTriShape;
struct NiTriStrips;
struct NiGeometry; struct NiGeometry;
struct Parent; struct Parent;
} }
@ -50,7 +48,7 @@ namespace NifBullet
osg::ref_ptr<Resource::BulletShape> load(Nif::FileView file); osg::ref_ptr<Resource::BulletShape> load(Nif::FileView file);
private: private:
bool findBoundingBox(const Nif::Node& node, const std::string& filename); bool findBoundingBox(const Nif::NiAVObject& node, const std::string& filename);
struct HandleNodeArgs struct HandleNodeArgs
{ {
@ -61,11 +59,10 @@ namespace NifBullet
bool mAvoid{ false }; bool mAvoid{ false };
}; };
void handleNode(const std::string& fileName, const Nif::Node& node, const Nif::Parent* parent, void handleNode(const std::string& fileName, const Nif::NiAVObject& node, const Nif::Parent* parent,
HandleNodeArgs args, Resource::VisualCollisionType& visualCollisionType); HandleNodeArgs args, Resource::VisualCollisionType& visualCollisionType);
bool hasRootCollisionNode(const Nif::Node& rootNode) const; const Nif::NiNode* findRootCollisionNode(const Nif::NiAVObject& rootNode) const;
bool collisionShapeIsEmpty(const Nif::Node& rootNode) const;
void handleNiTriShape(const Nif::NiGeometry& nifNode, const Nif::Parent* parent, HandleNodeArgs args); void handleNiTriShape(const Nif::NiGeometry& nifNode, const Nif::Parent* parent, HandleNodeArgs args);

View file

@ -2,10 +2,10 @@
namespace NifOsg namespace NifOsg
{ {
MatrixTransform::MatrixTransform(const Nif::Transformation& trafo) MatrixTransform::MatrixTransform(const Nif::NiTransform& transform)
: osg::MatrixTransform(trafo.toMatrix()) : osg::MatrixTransform(transform.toMatrix())
, mScale(trafo.scale) , mScale(transform.mScale)
, mRotationScale(trafo.rotation) , mRotationScale(transform.mRotation)
{ {
} }

View file

@ -12,7 +12,7 @@ namespace NifOsg
{ {
public: public:
MatrixTransform() = default; MatrixTransform() = default;
MatrixTransform(const Nif::Transformation& trafo); MatrixTransform(const Nif::NiTransform& transform);
MatrixTransform(const MatrixTransform& copy, const osg::CopyOp& copyop); MatrixTransform(const MatrixTransform& copy, const osg::CopyOp& copyop);
META_Node(NifOsg, MatrixTransform) META_Node(NifOsg, MatrixTransform)

View file

@ -76,12 +76,12 @@ namespace
void apply(osg::Drawable& node) override { traverse(node); } void apply(osg::Drawable& node) override { traverse(node); }
}; };
void getAllNiNodes(const Nif::Node* node, std::vector<int>& outIndices) void getAllNiNodes(const Nif::NiAVObject* node, std::vector<int>& outIndices)
{ {
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(node)) if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(node))
{ {
outIndices.push_back(ninode->recIndex); outIndices.push_back(ninode->recIndex);
for (const auto& child : ninode->children) for (const auto& child : ninode->mChildren)
if (!child.empty()) if (!child.empty())
getAllNiNodes(child.getPtr(), outIndices); getAllNiNodes(child.getPtr(), outIndices);
} }
@ -103,11 +103,11 @@ namespace
// Collect all properties affecting the given drawable that should be handled on drawable basis rather than on the // Collect all properties affecting the given drawable that should be handled on drawable basis rather than on the
// node hierarchy above it. // node hierarchy above it.
void collectDrawableProperties( void collectDrawableProperties(
const Nif::Node* nifNode, const Nif::Parent* parent, std::vector<const Nif::Property*>& out) const Nif::NiAVObject* nifNode, const Nif::Parent* parent, std::vector<const Nif::Property*>& out)
{ {
if (parent != nullptr) if (parent != nullptr)
collectDrawableProperties(&parent->mNiNode, parent->mParent, out); collectDrawableProperties(&parent->mNiNode, parent->mParent, out);
for (const auto& property : nifNode->props) for (const auto& property : nifNode->mProperties)
{ {
if (!property.empty()) if (!property.empty())
{ {
@ -275,21 +275,28 @@ namespace NifOsg
return; return;
} }
Nif::ExtraPtr extra = seq->extra; Nif::ExtraList extraList = seq->getExtraList();
if (extra.empty() || extra->recType != Nif::RC_NiTextKeyExtraData) if (extraList.empty())
{ {
Log(Debug::Warning) << "NIFFile Warning: First extra data was not a NiTextKeyExtraData, but a " Log(Debug::Warning) << "NIFFile Warning: NiSequenceStreamHelper has no text keys. File: "
<< (extra.empty() ? std::string_view("nil") : std::string_view(extra->recName)) << nif.getFilename();
<< ". File: " << nif.getFilename();
return; return;
} }
extractTextKeys(static_cast<const Nif::NiTextKeyExtraData*>(extra.getPtr()), target.mTextKeys); if (extraList[0]->recType != Nif::RC_NiTextKeyExtraData)
extra = extra->mNext;
Nif::ControllerPtr ctrl = seq->controller;
for (; !extra.empty() && !ctrl.empty(); (extra = extra->mNext), (ctrl = ctrl->next))
{ {
Log(Debug::Warning) << "NIFFile Warning: First extra data was not a NiTextKeyExtraData, but a "
<< std::string_view(extraList[0]->recName) << ". File: " << nif.getFilename();
return;
}
auto textKeyExtraData = static_cast<const Nif::NiTextKeyExtraData*>(extraList[0].getPtr());
extractTextKeys(textKeyExtraData, target.mTextKeys);
Nif::ControllerPtr ctrl = seq->mController;
for (size_t i = 1; i < extraList.size() && !ctrl.empty(); i++, (ctrl = ctrl->next))
{
Nif::ExtraPtr extra = extraList[i];
if (extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController) if (extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController)
{ {
Log(Debug::Warning) << "NIFFile Warning: Unexpected extra data " << extra->recName Log(Debug::Warning) << "NIFFile Warning: Unexpected extra data " << extra->recName
@ -338,13 +345,13 @@ namespace NifOsg
osg::ref_ptr<osg::Node> load(Nif::FileView nif, Resource::ImageManager* imageManager) osg::ref_ptr<osg::Node> load(Nif::FileView nif, Resource::ImageManager* imageManager)
{ {
const size_t numRoots = nif.numRoots(); const size_t numRoots = nif.numRoots();
std::vector<const Nif::Node*> roots; std::vector<const Nif::NiAVObject*> 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);
if (!r) if (!r)
continue; continue;
const Nif::Node* nifNode = dynamic_cast<const Nif::Node*>(r); const Nif::NiAVObject* nifNode = dynamic_cast<const Nif::NiAVObject*>(r);
if (nifNode) if (nifNode)
roots.emplace_back(nifNode); roots.emplace_back(nifNode);
} }
@ -355,7 +362,7 @@ namespace NifOsg
osg::ref_ptr<osg::Group> created(new osg::Group); osg::ref_ptr<osg::Group> created(new osg::Group);
created->setDataVariance(osg::Object::STATIC); created->setDataVariance(osg::Object::STATIC);
for (const Nif::Node* root : roots) for (const Nif::NiAVObject* root : roots)
{ {
auto node = handleNode(root, nullptr, nullptr, auto node = handleNode(root, nullptr, nullptr,
{ .mNifVersion = nif.getVersion(), { .mNifVersion = nif.getVersion(),
@ -390,13 +397,13 @@ namespace NifOsg
return created; return created;
} }
void applyNodeProperties(const Nif::Node* nifNode, osg::Node* applyTo, void applyNodeProperties(const Nif::NiAVObject* nifNode, osg::Node* applyTo,
SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager,
std::vector<unsigned int>& boundTextures, int animflags) std::vector<unsigned int>& boundTextures, int animflags)
{ {
bool hasStencilProperty = false; bool hasStencilProperty = false;
for (const auto& property : nifNode->props) for (const auto& property : nifNode->mProperties)
{ {
if (property.empty()) if (property.empty())
continue; continue;
@ -413,13 +420,13 @@ namespace NifOsg
} }
} }
for (const auto& property : nifNode->props) for (const auto& property : nifNode->mProperties)
{ {
if (!property.empty()) if (!property.empty())
{ {
// Get the lowest numbered recIndex of the NiTexturingProperty root node. // Get the lowest numbered recIndex of the NiTexturingProperty root node.
// This is what is overridden when a spell effect "particle texture" is used. // This is what is overridden when a spell effect "particle texture" is used.
if (nifNode->parents.empty() && !mFoundFirstRootTexturingProperty if (nifNode->mParents.empty() && !mFoundFirstRootTexturingProperty
&& property.getPtr()->recType == Nif::RC_NiTexturingProperty) && property.getPtr()->recType == Nif::RC_NiTexturingProperty)
{ {
mFirstRootTextureIndex = property.getPtr()->recIndex; mFirstRootTextureIndex = property.getPtr()->recIndex;
@ -454,7 +461,7 @@ namespace NifOsg
static osg::ref_ptr<osg::LOD> handleLodNode(const Nif::NiLODNode* niLodNode) static osg::ref_ptr<osg::LOD> handleLodNode(const Nif::NiLODNode* niLodNode)
{ {
osg::ref_ptr<osg::LOD> lod(new osg::LOD); osg::ref_ptr<osg::LOD> lod(new osg::LOD);
lod->setName(niLodNode->name); lod->setName(niLodNode->mName);
lod->setCenterMode(osg::LOD::USER_DEFINED_CENTER); lod->setCenterMode(osg::LOD::USER_DEFINED_CENTER);
lod->setCenter(niLodNode->lodCenter); lod->setCenter(niLodNode->lodCenter);
for (unsigned int i = 0; i < niLodNode->lodLevels.size(); ++i) for (unsigned int i = 0; i < niLodNode->lodLevels.size(); ++i)
@ -469,29 +476,29 @@ namespace NifOsg
static osg::ref_ptr<osg::Switch> handleSwitchNode(const Nif::NiSwitchNode* niSwitchNode) static osg::ref_ptr<osg::Switch> handleSwitchNode(const Nif::NiSwitchNode* niSwitchNode)
{ {
osg::ref_ptr<osg::Switch> switchNode(new osg::Switch); osg::ref_ptr<osg::Switch> switchNode(new osg::Switch);
switchNode->setName(niSwitchNode->name); switchNode->setName(niSwitchNode->mName);
switchNode->setNewChildDefaultValue(false); switchNode->setNewChildDefaultValue(false);
switchNode->setSingleChildOn(niSwitchNode->initialIndex); switchNode->setSingleChildOn(niSwitchNode->initialIndex);
return switchNode; return switchNode;
} }
static osg::ref_ptr<osg::Sequence> prepareSequenceNode(const Nif::Node* nifNode) static osg::ref_ptr<osg::Sequence> prepareSequenceNode(const Nif::NiAVObject* nifNode)
{ {
const Nif::NiFltAnimationNode* niFltAnimationNode = static_cast<const Nif::NiFltAnimationNode*>(nifNode); const Nif::NiFltAnimationNode* niFltAnimationNode = static_cast<const Nif::NiFltAnimationNode*>(nifNode);
osg::ref_ptr<osg::Sequence> sequenceNode(new osg::Sequence); osg::ref_ptr<osg::Sequence> sequenceNode(new osg::Sequence);
sequenceNode->setName(niFltAnimationNode->name); sequenceNode->setName(niFltAnimationNode->mName);
if (!niFltAnimationNode->children.empty()) if (!niFltAnimationNode->mChildren.empty())
{ {
if (niFltAnimationNode->swing()) if (niFltAnimationNode->swing())
sequenceNode->setDefaultTime( sequenceNode->setDefaultTime(
niFltAnimationNode->mDuration / (niFltAnimationNode->children.size() * 2)); niFltAnimationNode->mDuration / (niFltAnimationNode->mChildren.size() * 2));
else else
sequenceNode->setDefaultTime(niFltAnimationNode->mDuration / niFltAnimationNode->children.size()); sequenceNode->setDefaultTime(niFltAnimationNode->mDuration / niFltAnimationNode->mChildren.size());
} }
return sequenceNode; return sequenceNode;
} }
static void activateSequenceNode(osg::Group* osgNode, const Nif::Node* nifNode) static void activateSequenceNode(osg::Group* osgNode, const Nif::NiAVObject* nifNode)
{ {
const Nif::NiFltAnimationNode* niFltAnimationNode = static_cast<const Nif::NiFltAnimationNode*>(nifNode); const Nif::NiFltAnimationNode* niFltAnimationNode = static_cast<const Nif::NiFltAnimationNode*>(nifNode);
osg::Sequence* sequenceNode = static_cast<osg::Sequence*>(osgNode); osg::Sequence* sequenceNode = static_cast<osg::Sequence*>(osgNode);
@ -528,7 +535,7 @@ namespace NifOsg
texture->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);
} }
bool handleEffect(const Nif::Node* nifNode, osg::StateSet* stateset, Resource::ImageManager* imageManager) bool handleEffect(const Nif::NiAVObject* nifNode, osg::StateSet* stateset, Resource::ImageManager* imageManager)
{ {
if (nifNode->recType != Nif::RC_NiTextureEffect) if (nifNode->recType != Nif::RC_NiTextureEffect)
{ {
@ -589,7 +596,7 @@ namespace NifOsg
} }
// Get a default dataVariance for this node to be used as a hint by optimization (post)routines // Get a default dataVariance for this node to be used as a hint by optimization (post)routines
osg::ref_ptr<osg::Group> createNode(const Nif::Node* nifNode) osg::ref_ptr<osg::Group> createNode(const Nif::NiAVObject* nifNode)
{ {
osg::ref_ptr<osg::Group> node; osg::ref_ptr<osg::Group> node;
osg::Object::DataVariance dataVariance = osg::Object::UNSPECIFIED; osg::Object::DataVariance dataVariance = osg::Object::UNSPECIFIED;
@ -604,15 +611,15 @@ namespace NifOsg
// This takes advantage of the fact root nodes can't have additional controllers // 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 // loaded from an external .kf file (original engine just throws "can't find node" errors if you
// try). // try).
if (nifNode->parents.empty() && nifNode->controller.empty() && nifNode->trafo.isIdentity()) if (nifNode->mParents.empty() && nifNode->mController.empty() && nifNode->mTransform.isIdentity())
node = new osg::Group; node = new osg::Group;
dataVariance = nifNode->isBone ? osg::Object::DYNAMIC : osg::Object::STATIC; dataVariance = nifNode->mIsBone ? osg::Object::DYNAMIC : osg::Object::STATIC;
break; break;
} }
if (!node) if (!node)
node = new NifOsg::MatrixTransform(nifNode->trafo); node = new NifOsg::MatrixTransform(nifNode->mTransform);
node->setDataVariance(dataVariance); node->setDataVariance(dataVariance);
@ -620,9 +627,9 @@ namespace NifOsg
} }
osg::ref_ptr<osg::Node> handleNode( osg::ref_ptr<osg::Node> handleNode(
const Nif::Node* nifNode, const Nif::Parent* parent, osg::Group* parentNode, HandleNodeArgs args) const Nif::NiAVObject* nifNode, const Nif::Parent* parent, osg::Group* parentNode, HandleNodeArgs args)
{ {
if (args.mRootNode && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box")) if (args.mRootNode && Misc::StringUtils::ciEqual(nifNode->mName, "Bounding Box"))
return nullptr; return nullptr;
osg::ref_ptr<osg::Group> node = createNode(nifNode); osg::ref_ptr<osg::Group> node = createNode(nifNode);
@ -632,7 +639,7 @@ namespace NifOsg
node->addCullCallback(new BillboardCallback); node->addCullCallback(new BillboardCallback);
} }
node->setName(nifNode->name); node->setName(nifNode->mName);
if (parentNode) if (parentNode)
parentNode->addChild(node); parentNode->addChild(node);
@ -646,16 +653,7 @@ namespace NifOsg
// - finding a random child NiNode in NiBspArrayController // - finding a random child NiNode in NiBspArrayController
node->setUserValue("recIndex", nifNode->recIndex); node->setUserValue("recIndex", nifNode->recIndex);
std::vector<Nif::ExtraPtr> extraCollection; for (const auto& e : nifNode->getExtraList())
for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->mNext)
extraCollection.emplace_back(e);
for (const auto& extraNode : nifNode->extralist)
if (!extraNode.empty())
extraCollection.emplace_back(extraNode);
for (const auto& e : extraCollection)
{ {
if (e->recType == Nif::RC_NiTextKeyExtraData && args.mTextKeys) if (e->recType == Nif::RC_NiTextKeyExtraData && args.mTextKeys)
{ {
@ -694,7 +692,7 @@ namespace NifOsg
} }
if (nifNode->recType == Nif::RC_NiBSAnimationNode || nifNode->recType == Nif::RC_NiBSParticleNode) if (nifNode->recType == Nif::RC_NiBSAnimationNode || nifNode->recType == Nif::RC_NiBSParticleNode)
args.mAnimFlags = nifNode->flags; args.mAnimFlags = nifNode->mFlags;
if (nifNode->recType == Nif::RC_NiSortAdjustNode) if (nifNode->recType == Nif::RC_NiSortAdjustNode)
{ {
@ -727,7 +725,7 @@ namespace NifOsg
if (nifNode->isHidden()) if (nifNode->isHidden())
{ {
bool hasVisController = false; bool hasVisController = false;
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) for (Nif::ControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->next)
{ {
hasVisController |= (ctrl->recType == Nif::RC_NiVisController); hasVisController |= (ctrl->recType == Nif::RC_NiVisController);
if (hasVisController) if (hasVisController)
@ -755,12 +753,12 @@ namespace NifOsg
bool skip; bool skip;
if (args.mNifVersion <= Nif::NIFFile::NIFVersion::VER_MW) if (args.mNifVersion <= Nif::NIFFile::NIFVersion::VER_MW)
{ {
skip = (args.mHasMarkers && Misc::StringUtils::ciStartsWith(nifNode->name, "tri editormarker")) skip = (args.mHasMarkers && Misc::StringUtils::ciStartsWith(nifNode->mName, "tri editormarker"))
|| Misc::StringUtils::ciStartsWith(nifNode->name, "shadow") || Misc::StringUtils::ciStartsWith(nifNode->mName, "shadow")
|| Misc::StringUtils::ciStartsWith(nifNode->name, "tri shadow"); || Misc::StringUtils::ciStartsWith(nifNode->mName, "tri shadow");
} }
else else
skip = args.mHasMarkers && Misc::StringUtils::ciStartsWith(nifNode->name, "EditorMarker"); skip = args.mHasMarkers && Misc::StringUtils::ciStartsWith(nifNode->mName, "EditorMarker");
if (!skip) if (!skip)
{ {
Nif::NiSkinInstancePtr skin = static_cast<const Nif::NiGeometry*>(nifNode)->skin; Nif::NiSkinInstancePtr skin = static_cast<const Nif::NiGeometry*>(nifNode)->skin;
@ -770,7 +768,7 @@ namespace NifOsg
else else
handleSkinnedGeometry(nifNode, parent, node, composite, args.mBoundTextures, args.mAnimFlags); handleSkinnedGeometry(nifNode, parent, node, composite, args.mBoundTextures, args.mAnimFlags);
if (!nifNode->controller.empty()) if (!nifNode->mController.empty())
handleMeshControllers(nifNode, node, composite, args.mBoundTextures, args.mAnimFlags); handleMeshControllers(nifNode, node, composite, args.mBoundTextures, args.mAnimFlags);
} }
} }
@ -807,9 +805,9 @@ 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) if (niSwitchNode->mName == Constants::NightDayLabel)
mHasNightDayLabel = true; mHasNightDayLabel = true;
else if (niSwitchNode->name == Constants::HerbalismLabel) else if (niSwitchNode->mName == Constants::HerbalismLabel)
mHasHerbalismLabel = true; mHasHerbalismLabel = true;
currentNode = switchNode; currentNode = switchNode;
@ -831,7 +829,7 @@ namespace NifOsg
const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(nifNode); const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(nifNode);
if (ninode) if (ninode)
{ {
const Nif::NodeList& children = ninode->children; const Nif::NiAVObjectList& children = ninode->mChildren;
const Nif::Parent currentParent{ *ninode, parent }; const Nif::Parent currentParent{ *ninode, parent };
for (const auto& child : children) for (const auto& child : children)
if (!child.empty()) if (!child.empty())
@ -840,7 +838,7 @@ namespace NifOsg
// Propagate effects to the the direct subgraph instead of the node itself // Propagate effects to the the direct subgraph instead of the node itself
// This simulates their "affected node list" which Morrowind appears to replace with the subgraph (?) // This simulates their "affected node list" which Morrowind appears to replace with the subgraph (?)
// Note that the serialized affected node list is actually unused // Note that the serialized affected node list is actually unused
for (const auto& effect : ninode->effects) for (const auto& effect : ninode->mEffects)
if (!effect.empty()) if (!effect.empty())
{ {
osg::ref_ptr<osg::StateSet> effectStateSet = new osg::StateSet; osg::ref_ptr<osg::StateSet> effectStateSet = new osg::StateSet;
@ -856,11 +854,11 @@ namespace NifOsg
return node; return node;
} }
void handleMeshControllers(const Nif::Node* nifNode, osg::Node* node, void handleMeshControllers(const Nif::NiAVObject* nifNode, osg::Node* node,
SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures,
int animflags) int animflags)
{ {
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) for (Nif::ControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->next)
{ {
if (!ctrl->isActive()) if (!ctrl->isActive())
continue; continue;
@ -885,9 +883,9 @@ namespace NifOsg
} }
} }
void handleNodeControllers(const Nif::Node* nifNode, osg::Node* node, int animflags, bool& isAnimated) void handleNodeControllers(const Nif::NiAVObject* nifNode, osg::Node* node, int animflags, bool& isAnimated)
{ {
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) for (Nif::ControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->next)
{ {
if (!ctrl->isActive()) if (!ctrl->isActive())
continue; continue;
@ -965,7 +963,7 @@ namespace NifOsg
void handleMaterialControllers(const Nif::Property* materialProperty, void handleMaterialControllers(const Nif::Property* materialProperty,
SceneUtil::CompositeStateSetUpdater* composite, int animflags, const osg::Material* baseMaterial) SceneUtil::CompositeStateSetUpdater* composite, int animflags, const osg::Material* baseMaterial)
{ {
for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next) for (Nif::ControllerPtr ctrl = materialProperty->mController; !ctrl.empty(); ctrl = ctrl->next)
{ {
if (!ctrl->isActive()) if (!ctrl->isActive())
continue; continue;
@ -1016,7 +1014,7 @@ namespace NifOsg
void handleTextureControllers(const Nif::Property* texProperty, SceneUtil::CompositeStateSetUpdater* composite, void handleTextureControllers(const Nif::Property* texProperty, SceneUtil::CompositeStateSetUpdater* composite,
Resource::ImageManager* imageManager, osg::StateSet* stateset, int animflags) Resource::ImageManager* imageManager, osg::StateSet* stateset, int animflags)
{ {
for (Nif::ControllerPtr ctrl = texProperty->controller; !ctrl.empty(); ctrl = ctrl->next) for (Nif::ControllerPtr ctrl = texProperty->mController; !ctrl.empty(); ctrl = ctrl->next)
{ {
if (!ctrl->isActive()) if (!ctrl->isActive())
continue; continue;
@ -1123,7 +1121,7 @@ namespace NifOsg
// Load the initial state of the particle system, i.e. the initial particles and their positions, velocity and // Load the initial state of the particle system, i.e. the initial particles and their positions, velocity and
// colors. // colors.
void handleParticleInitialState( void handleParticleInitialState(
const Nif::Node* nifNode, ParticleSystem* partsys, const Nif::NiParticleSystemController* partctrl) const Nif::NiAVObject* nifNode, ParticleSystem* partsys, const Nif::NiParticleSystemController* partctrl)
{ {
auto particleNode = static_cast<const Nif::NiParticles*>(nifNode); auto particleNode = static_cast<const Nif::NiParticles*>(nifNode);
if (particleNode->data.empty() || particleNode->data->recType != Nif::RC_NiParticlesData) if (particleNode->data.empty() || particleNode->data->recType != Nif::RC_NiParticlesData)
@ -1249,14 +1247,14 @@ namespace NifOsg
mEmitterQueue.clear(); mEmitterQueue.clear();
} }
void handleParticleSystem(const Nif::Node* nifNode, const Nif::Parent* parent, osg::Group* parentNode, void handleParticleSystem(const Nif::NiAVObject* nifNode, const Nif::Parent* parent, osg::Group* parentNode,
SceneUtil::CompositeStateSetUpdater* composite, int animflags) 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);
const Nif::NiParticleSystemController* partctrl = nullptr; const Nif::NiParticleSystemController* partctrl = nullptr;
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) for (Nif::ControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->next)
{ {
if (!ctrl->isActive()) if (!ctrl->isActive())
continue; continue;
@ -1377,7 +1375,7 @@ namespace NifOsg
} }
} }
void handleNiGeometry(const Nif::Node* nifNode, const Nif::Parent* parent, osg::Geometry* geometry, void handleNiGeometry(const Nif::NiAVObject* nifNode, const Nif::Parent* parent, osg::Geometry* geometry,
osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite,
const std::vector<unsigned int>& boundTextures, int animflags) const std::vector<unsigned int>& boundTextures, int animflags)
{ {
@ -1465,7 +1463,7 @@ namespace NifOsg
new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, line.size(), line.data())); new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, line.size(), line.data()));
} }
} }
handleNiGeometryData(geometry, niGeometryData, boundTextures, nifNode->name); handleNiGeometryData(geometry, niGeometryData, boundTextures, nifNode->mName);
// osg::Material properties are handled here for two reasons: // osg::Material properties are handled here for two reasons:
// - if there are no vertex colors, we need to disable colorMode. // - if there are no vertex colors, we need to disable colorMode.
@ -1476,7 +1474,7 @@ namespace NifOsg
applyDrawableProperties(parentNode, drawableProps, composite, !niGeometryData->mColors.empty(), animflags); applyDrawableProperties(parentNode, drawableProps, composite, !niGeometryData->mColors.empty(), animflags);
} }
void handleGeometry(const Nif::Node* nifNode, const Nif::Parent* parent, osg::Group* parentNode, void handleGeometry(const Nif::NiAVObject* nifNode, const Nif::Parent* parent, osg::Group* parentNode,
SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures,
int animflags) int animflags)
{ {
@ -1487,7 +1485,7 @@ namespace NifOsg
if (geom->empty()) if (geom->empty())
return; return;
osg::ref_ptr<osg::Drawable> drawable; osg::ref_ptr<osg::Drawable> drawable;
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) for (Nif::ControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->next)
{ {
if (!ctrl->isActive()) if (!ctrl->isActive())
continue; continue;
@ -1507,7 +1505,7 @@ namespace NifOsg
} }
if (!drawable.get()) if (!drawable.get())
drawable = geom; drawable = geom;
drawable->setName(nifNode->name); drawable->setName(nifNode->mName);
parentNode->addChild(drawable); parentNode->addChild(drawable);
} }
@ -1532,7 +1530,7 @@ namespace NifOsg
return morphGeom; return morphGeom;
} }
void handleSkinnedGeometry(const Nif::Node* nifNode, const Nif::Parent* parent, osg::Group* parentNode, void handleSkinnedGeometry(const Nif::NiAVObject* nifNode, const Nif::Parent* parent, osg::Group* parentNode,
SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures,
int animflags) int animflags)
{ {
@ -1543,17 +1541,17 @@ namespace NifOsg
return; return;
osg::ref_ptr<SceneUtil::RigGeometry> rig(new SceneUtil::RigGeometry); osg::ref_ptr<SceneUtil::RigGeometry> rig(new SceneUtil::RigGeometry);
rig->setSourceGeometry(geometry); rig->setSourceGeometry(geometry);
rig->setName(nifNode->name); rig->setName(nifNode->mName);
// Assign bone weights // Assign bone weights
osg::ref_ptr<SceneUtil::RigGeometry::InfluenceMap> map(new SceneUtil::RigGeometry::InfluenceMap); osg::ref_ptr<SceneUtil::RigGeometry::InfluenceMap> map(new SceneUtil::RigGeometry::InfluenceMap);
const Nif::NiSkinInstance* skin = static_cast<const Nif::NiGeometry*>(nifNode)->skin.getPtr(); const Nif::NiSkinInstance* skin = static_cast<const Nif::NiGeometry*>(nifNode)->skin.getPtr();
const Nif::NiSkinData* data = skin->mData.getPtr(); const Nif::NiSkinData* data = skin->mData.getPtr();
const Nif::NodeList& bones = skin->mBones; const Nif::NiAVObjectList& bones = skin->mBones;
for (std::size_t i = 0; i < bones.size(); ++i) for (std::size_t i = 0; i < bones.size(); ++i)
{ {
std::string boneName = Misc::StringUtils::lowerCase(bones[i].getPtr()->name); std::string boneName = Misc::StringUtils::lowerCase(bones[i].getPtr()->mName);
SceneUtil::RigGeometry::BoneInfluence influence; SceneUtil::RigGeometry::BoneInfluence influence;
influence.mWeights = data->mBones[i].mWeights; influence.mWeights = data->mBones[i].mWeights;
@ -1839,7 +1837,7 @@ namespace NifOsg
for (size_t i = 0; i < texprop->textures.size(); ++i) for (size_t i = 0; i < texprop->textures.size(); ++i)
{ {
if (texprop->textures[i].inUse if (texprop->textures[i].inUse
|| (i == Nif::NiTexturingProperty::BaseTexture && !texprop->controller.empty())) || (i == Nif::NiTexturingProperty::BaseTexture && !texprop->mController.empty()))
{ {
switch (i) switch (i)
{ {
@ -1866,7 +1864,7 @@ namespace NifOsg
if (texprop->textures[i].inUse) if (texprop->textures[i].inUse)
{ {
const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i]; const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i];
if (tex.texture.empty() && texprop->controller.empty()) if (tex.texture.empty() && texprop->mController.empty())
{ {
if (i == 0) if (i == 0)
Log(Debug::Warning) << "Base texture is in use but empty on shape \"" << nodeName Log(Debug::Warning) << "Base texture is in use but empty on shape \"" << nodeName
@ -2424,7 +2422,7 @@ namespace NifOsg
mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.specular, 1.f)); mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.specular, 1.f));
mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->data.glossiness); mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->data.glossiness);
if (!matprop->controller.empty()) if (!matprop->mController.empty())
{ {
hasMatCtrl = true; hasMatCtrl = true;
handleMaterialControllers(matprop, composite, animflags, mat); handleMaterialControllers(matprop, composite, animflags, mat);