Merge branch 'alsonif' into 'master'

Modernize NIF loader, part V: Skyrim

See merge request OpenMW/openmw!3428
macos_ci_fix
psi29a 1 year ago
commit 9333840bb1

@ -33,8 +33,8 @@ namespace Nif::Testing
inline void init(NiGeometry& value) inline void init(NiGeometry& value)
{ {
init(static_cast<NiAVObject&>(value)); init(static_cast<NiAVObject&>(value));
value.data = NiGeometryDataPtr(nullptr); value.mData = NiGeometryDataPtr(nullptr);
value.skin = NiSkinInstancePtr(nullptr); value.mSkin = NiSkinInstancePtr(nullptr);
} }
inline void init(NiTriShape& value) inline void init(NiTriShape& value)

@ -326,20 +326,20 @@ namespace
mNiTriShapeData.mVertices = { osg::Vec3f(0, 0, 0), osg::Vec3f(1, 0, 0), osg::Vec3f(1, 1, 0) }; mNiTriShapeData.mVertices = { osg::Vec3f(0, 0, 0), osg::Vec3f(1, 0, 0), osg::Vec3f(1, 1, 0) };
mNiTriShapeData.mNumTriangles = 1; mNiTriShapeData.mNumTriangles = 1;
mNiTriShapeData.mTriangles = { 0, 1, 2 }; mNiTriShapeData.mTriangles = { 0, 1, 2 };
mNiTriShape.data = Nif::NiGeometryDataPtr(&mNiTriShapeData); mNiTriShape.mData = Nif::NiGeometryDataPtr(&mNiTriShapeData);
mNiTriShapeData2.recType = Nif::RC_NiTriShapeData; mNiTriShapeData2.recType = Nif::RC_NiTriShapeData;
mNiTriShapeData2.mVertices = { osg::Vec3f(0, 0, 1), osg::Vec3f(1, 0, 1), osg::Vec3f(1, 1, 1) }; mNiTriShapeData2.mVertices = { osg::Vec3f(0, 0, 1), osg::Vec3f(1, 0, 1), osg::Vec3f(1, 1, 1) };
mNiTriShapeData2.mNumTriangles = 1; mNiTriShapeData2.mNumTriangles = 1;
mNiTriShapeData2.mTriangles = { 0, 1, 2 }; mNiTriShapeData2.mTriangles = { 0, 1, 2 };
mNiTriShape2.data = Nif::NiGeometryDataPtr(&mNiTriShapeData2); mNiTriShape2.mData = Nif::NiGeometryDataPtr(&mNiTriShapeData2);
mNiTriStripsData.recType = Nif::RC_NiTriStripsData; mNiTriStripsData.recType = Nif::RC_NiTriStripsData;
mNiTriStripsData.mVertices mNiTriStripsData.mVertices
= { osg::Vec3f(0, 0, 0), osg::Vec3f(1, 0, 0), osg::Vec3f(1, 1, 0), osg::Vec3f(0, 1, 0) }; = { osg::Vec3f(0, 0, 0), osg::Vec3f(1, 0, 0), osg::Vec3f(1, 1, 0), osg::Vec3f(0, 1, 0) };
mNiTriStripsData.mNumTriangles = 2; mNiTriStripsData.mNumTriangles = 2;
mNiTriStripsData.mStrips = { { 0, 1, 2, 3 } }; mNiTriStripsData.mStrips = { { 0, 1, 2, 3 } };
mNiTriStrips.data = Nif::NiGeometryDataPtr(&mNiTriStripsData); mNiTriStrips.mData = Nif::NiGeometryDataPtr(&mNiTriStripsData);
} }
}; };
@ -415,9 +415,9 @@ namespace
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.mFlags |= Nif::NiAVObject::Flag_BBoxCollision; mNode.mFlags |= Nif::NiAVObject::Flag_BBoxCollision;
mNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
mNode.mBounds.box.extents = osg::Vec3f(1, 2, 3); mNode.mBounds.mBox.mExtents = osg::Vec3f(1, 2, 3);
mNode.mBounds.box.center = osg::Vec3f(-1, -2, -3); mNode.mBounds.mBox.mCenter = 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,9 +439,9 @@ namespace
TEST_F(TestBulletNifLoader, for_child_nif_node_with_bounding_box) TEST_F(TestBulletNifLoader, for_child_nif_node_with_bounding_box)
{ {
mNode.mFlags |= Nif::NiAVObject::Flag_BBoxCollision; mNode.mFlags |= Nif::NiAVObject::Flag_BBoxCollision;
mNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
mNode.mBounds.box.extents = osg::Vec3f(1, 2, 3); mNode.mBounds.mBox.mExtents = osg::Vec3f(1, 2, 3);
mNode.mBounds.box.center = osg::Vec3f(-1, -2, -3); mNode.mBounds.mBox.mCenter = osg::Vec3f(-1, -2, -3);
mNode.mParents.push_back(&mNiNode); mNode.mParents.push_back(&mNiNode);
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode) }; mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode) };
@ -466,14 +466,14 @@ namespace
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.mFlags |= Nif::NiAVObject::Flag_BBoxCollision; mNode.mFlags |= Nif::NiAVObject::Flag_BBoxCollision;
mNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
mNode.mBounds.box.extents = osg::Vec3f(1, 2, 3); mNode.mBounds.mBox.mExtents = osg::Vec3f(1, 2, 3);
mNode.mBounds.box.center = osg::Vec3f(-1, -2, -3); mNode.mBounds.mBox.mCenter = osg::Vec3f(-1, -2, -3);
mNode.mParents.push_back(&mNiNode); mNode.mParents.push_back(&mNiNode);
mNiNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNiNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
mNiNode.mBounds.box.extents = osg::Vec3f(4, 5, 6); mNiNode.mBounds.mBox.mExtents = osg::Vec3f(4, 5, 6);
mNiNode.mBounds.box.center = osg::Vec3f(-4, -5, -6); mNiNode.mBounds.mBox.mCenter = osg::Vec3f(-4, -5, -6);
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode) }; mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode) };
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
@ -497,19 +497,19 @@ namespace
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.mFlags |= Nif::NiAVObject::Flag_BBoxCollision; mNode.mFlags |= Nif::NiAVObject::Flag_BBoxCollision;
mNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
mNode.mBounds.box.extents = osg::Vec3f(1, 2, 3); mNode.mBounds.mBox.mExtents = osg::Vec3f(1, 2, 3);
mNode.mBounds.box.center = osg::Vec3f(-1, -2, -3); mNode.mBounds.mBox.mCenter = osg::Vec3f(-1, -2, -3);
mNode.mParents.push_back(&mNiNode); mNode.mParents.push_back(&mNiNode);
mNode2.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode2.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
mNode2.mBounds.box.extents = osg::Vec3f(4, 5, 6); mNode2.mBounds.mBox.mExtents = osg::Vec3f(4, 5, 6);
mNode2.mBounds.box.center = osg::Vec3f(-4, -5, -6); mNode2.mBounds.mBox.mCenter = osg::Vec3f(-4, -5, -6);
mNode2.mParents.push_back(&mNiNode); mNode2.mParents.push_back(&mNiNode);
mNiNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNiNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
mNiNode.mBounds.box.extents = osg::Vec3f(7, 8, 9); mNiNode.mBounds.mBox.mExtents = osg::Vec3f(7, 8, 9);
mNiNode.mBounds.box.center = osg::Vec3f(-7, -8, -9); mNiNode.mBounds.mBox.mCenter = osg::Vec3f(-7, -8, -9);
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode), Nif::NiAVObjectPtr(&mNode2) }; mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode), Nif::NiAVObjectPtr(&mNode2) };
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
@ -532,20 +532,20 @@ 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.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
mNode.mBounds.box.extents = osg::Vec3f(1, 2, 3); mNode.mBounds.mBox.mExtents = osg::Vec3f(1, 2, 3);
mNode.mBounds.box.center = osg::Vec3f(-1, -2, -3); mNode.mBounds.mBox.mCenter = osg::Vec3f(-1, -2, -3);
mNode.mParents.push_back(&mNiNode); mNode.mParents.push_back(&mNiNode);
mNode2.mFlags |= Nif::NiAVObject::Flag_BBoxCollision; mNode2.mFlags |= Nif::NiAVObject::Flag_BBoxCollision;
mNode2.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode2.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
mNode2.mBounds.box.extents = osg::Vec3f(4, 5, 6); mNode2.mBounds.mBox.mExtents = osg::Vec3f(4, 5, 6);
mNode2.mBounds.box.center = osg::Vec3f(-4, -5, -6); mNode2.mBounds.mBox.mCenter = osg::Vec3f(-4, -5, -6);
mNode2.mParents.push_back(&mNiNode); mNode2.mParents.push_back(&mNiNode);
mNiNode.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNiNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
mNiNode.mBounds.box.extents = osg::Vec3f(7, 8, 9); mNiNode.mBounds.mBox.mExtents = osg::Vec3f(7, 8, 9);
mNiNode.mBounds.box.center = osg::Vec3f(-7, -8, -9); mNiNode.mBounds.mBox.mCenter = osg::Vec3f(-7, -8, -9);
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode), Nif::NiAVObjectPtr(&mNode2) }; mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode), Nif::NiAVObjectPtr(&mNode2) };
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
@ -568,9 +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.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
mNode.mBounds.box.extents = osg::Vec3f(1, 2, 3); mNode.mBounds.mBox.mExtents = osg::Vec3f(1, 2, 3);
mNode.mBounds.box.center = osg::Vec3f(-1, -2, -3); mNode.mBounds.mBox.mCenter = 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);
@ -608,9 +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.mBounds.type = Nif::NiBoundingVolume::Type::BOX_BV; mNiTriShape.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
mNiTriShape.mBounds.box.extents = osg::Vec3f(1, 2, 3); mNiTriShape.mBounds.mBox.mExtents = osg::Vec3f(1, 2, 3);
mNiTriShape.mBounds.box.center = osg::Vec3f(-1, -2, -3); mNiTriShape.mBounds.mBox.mCenter = 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);
@ -703,7 +703,7 @@ namespace
TEST_F(TestBulletNifLoader, TEST_F(TestBulletNifLoader,
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.mSkin = Nif::NiSkinInstancePtr(&mNiSkinInstance);
mNiTriShape.mParents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) }; mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
@ -943,7 +943,7 @@ 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.mData = Nif::NiGeometryDataPtr(nullptr);
mNiTriShape.mParents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) }; mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
@ -961,7 +961,7 @@ namespace
TEST_F(TestBulletNifLoader, TEST_F(TestBulletNifLoader,
for_tri_shape_child_node_with_empty_data_triangles_should_return_shape_with_null_collision_shape) for_tri_shape_child_node_with_empty_data_triangles_should_return_shape_with_null_collision_shape)
{ {
auto data = static_cast<Nif::NiTriShapeData*>(mNiTriShape.data.getPtr()); auto data = static_cast<Nif::NiTriShapeData*>(mNiTriShape.mData.getPtr());
data->mTriangles.clear(); data->mTriangles.clear();
mNiTriShape.mParents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) }; mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
@ -1095,7 +1095,7 @@ namespace
init(niTriShape); init(niTriShape);
init(emptyCollisionNode); init(emptyCollisionNode);
niTriShape.data = Nif::NiGeometryDataPtr(&mNiTriShapeData); niTriShape.mData = Nif::NiGeometryDataPtr(&mNiTriShapeData);
niTriShape.mParents.push_back(&mNiNode); niTriShape.mParents.push_back(&mNiNode);
emptyCollisionNode.recType = Nif::RC_RootCollisionNode; emptyCollisionNode.recType = Nif::RC_RootCollisionNode;
@ -1192,21 +1192,6 @@ namespace
EXPECT_EQ(*result, expected); EXPECT_EQ(*result, expected);
} }
TEST_F(TestBulletNifLoader, should_ignore_tri_shape_data_with_mismatching_data_rec_type)
{
mNiTriShape.data = Nif::NiGeometryDataPtr(&mNiTriStripsData);
Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiTriShape);
file.mHash = mHash;
const auto result = mLoader.load(file);
const Resource::BulletShape expected;
EXPECT_EQ(*result, expected);
}
TEST_F(TestBulletNifLoader, for_tri_strips_root_node_should_return_static_shape) TEST_F(TestBulletNifLoader, for_tri_strips_root_node_should_return_static_shape)
{ {
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
@ -1227,21 +1212,6 @@ namespace
EXPECT_EQ(*result, expected); EXPECT_EQ(*result, expected);
} }
TEST_F(TestBulletNifLoader, should_ignore_tri_strips_data_with_mismatching_data_rec_type)
{
mNiTriStrips.data = Nif::NiGeometryDataPtr(&mNiTriShapeData);
Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiTriStrips);
file.mHash = mHash;
const auto result = mLoader.load(file);
const Resource::BulletShape expected;
EXPECT_EQ(*result, expected);
}
TEST_F(TestBulletNifLoader, should_ignore_tri_strips_data_with_empty_strips) TEST_F(TestBulletNifLoader, should_ignore_tri_strips_data_with_empty_strips)
{ {
mNiTriStripsData.mStrips.clear(); mNiTriStripsData.mStrips.clear();

@ -1,8 +1,9 @@
#include "node.hpp" #include "node.hpp"
#include <components/misc/strings/algorithm.hpp>
#include <cstdint> #include <cstdint>
#include <components/misc/strings/algorithm.hpp>
#include "data.hpp" #include "data.hpp"
#include "exception.hpp" #include "exception.hpp"
#include "physics.hpp" #include "physics.hpp"
@ -10,67 +11,65 @@
namespace Nif namespace Nif
{ {
void NiBoundingVolume::read(NIFStream* nif)
void BoundingVolume::read(NIFStream* nif)
{ {
nif->read(type); nif->read(mType);
switch (type) switch (mType)
{ {
case BASE_BV: case BASE_BV:
break; break;
case SPHERE_BV: case SPHERE_BV:
{ {
nif->read(sphere); nif->read(mSphere);
break; break;
} }
case BOX_BV: case BOX_BV:
{ {
box.center = nif->getVector3(); nif->read(mBox.mCenter);
nif->read(box.axes); nif->read(mBox.mAxes);
box.extents = nif->getVector3(); nif->read(mBox.mExtents);
break; break;
} }
case CAPSULE_BV: case CAPSULE_BV:
{ {
capsule.center = nif->getVector3(); nif->read(mCapsule.mCenter);
capsule.axis = nif->getVector3(); nif->read(mCapsule.mAxis);
capsule.extent = nif->getFloat(); nif->read(mCapsule.mExtent);
capsule.radius = nif->getFloat(); nif->read(mCapsule.mRadius);
break; break;
} }
case LOZENGE_BV: case LOZENGE_BV:
{ {
lozenge.radius = nif->getFloat(); nif->read(mLozenge.mRadius);
if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 1, 0)) if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 1, 0))
{ {
lozenge.extent0 = nif->getFloat(); nif->read(mLozenge.mExtent0);
lozenge.extent1 = nif->getFloat(); nif->read(mLozenge.mExtent1);
} }
lozenge.center = nif->getVector3(); nif->read(mLozenge.mCenter);
lozenge.axis0 = nif->getVector3(); nif->read(mLozenge.mAxis0);
lozenge.axis1 = nif->getVector3(); nif->read(mLozenge.mAxis1);
break; break;
} }
case UNION_BV: case UNION_BV:
{ {
unsigned int numChildren = nif->getUInt(); mChildren.resize(nif->get<uint32_t>());
if (numChildren == 0) for (BoundingVolume& child : mChildren)
break;
children.resize(numChildren);
for (NiBoundingVolume& child : children)
child.read(nif); child.read(nif);
break; break;
} }
case HALFSPACE_BV: case HALFSPACE_BV:
{ {
halfSpace.plane = osg::Plane(nif->getVector4()); mHalfSpace.mPlane = osg::Plane(nif->get<osg::Vec4f>());
if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 1, 0)) if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 1, 0))
halfSpace.origin = nif->getVector3(); nif->read(mHalfSpace.mOrigin);
break; break;
} }
default: default:
{ {
throw Nif::Exception( throw Nif::Exception(
"Unhandled NiBoundingVolume type: " + std::to_string(type), nif->getFile().getFilename()); "Unhandled BoundingVolume type: " + std::to_string(mType), nif->getFile().getFilename());
} }
} }
} }
@ -152,125 +151,153 @@ namespace Nif
{ {
if (nif->getVersion() < NIFStream::generateVersion(10, 0, 1, 0)) if (nif->getVersion() < NIFStream::generateVersion(10, 0, 1, 0))
return; return;
unsigned int num = 0;
if (nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 3) && nif->get<bool>())
num = 1;
else if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5))
num = nif->getUInt();
nif->readVector(names, num);
nif->readVector(extra, num);
if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5)) if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5))
active = nif->getUInt(); mNames.resize(nif->get<uint32_t>());
else if (nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 3))
mNames.resize(nif->get<bool>());
nif->readVector(mNames, mNames.size());
nif->readVector(mExtra, mNames.size());
if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5))
nif->read(mActive);
if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS) if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS)
nif->read(needsUpdate); nif->read(mNeedsUpdate);
} }
void NiGeometry::read(NIFStream* nif) void NiGeometry::read(NIFStream* nif)
{ {
NiAVObject::read(nif); NiAVObject::read(nif);
data.read(nif);
skin.read(nif); mData.read(nif);
material.read(nif); if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
mSkin.read(nif);
mMaterial.read(nif);
if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS
&& nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3) && nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
{ {
shaderprop.read(nif); mShaderProperty.read(nif);
alphaprop.read(nif); mAlphaProperty.read(nif);
} }
} }
void NiGeometry::post(Reader& nif) void NiGeometry::post(Reader& nif)
{ {
NiAVObject::post(nif); NiAVObject::post(nif);
data.post(nif);
skin.post(nif); mData.post(nif);
shaderprop.post(nif); mSkin.post(nif);
alphaprop.post(nif); mShaderProperty.post(nif);
if (recType != RC_NiParticles && !skin.empty()) mAlphaProperty.post(nif);
if (recType != RC_NiParticles && !mSkin.empty())
nif.setUseSkinning(true); nif.setUseSkinning(true);
if (!mData.empty())
{
switch (recType)
{
case RC_NiTriShape:
case RC_BSLODTriShape:
if (mData->recType != RC_NiTriShapeData)
mData = NiGeometryDataPtr(nullptr);
break;
case RC_NiTriStrips:
if (mData->recType != RC_NiTriStripsData)
mData = NiGeometryDataPtr(nullptr);
break;
case RC_NiParticles:
if (mData->recType != RC_NiParticlesData)
mData = NiGeometryDataPtr(nullptr);
break;
case RC_NiLines:
if (mData->recType != RC_NiLinesData)
mData = NiGeometryDataPtr(nullptr);
break;
default:
break;
}
}
} }
void BSLODTriShape::read(NIFStream* nif) void BSLODTriShape::read(NIFStream* nif)
{ {
NiTriShape::read(nif); NiTriBasedGeom::read(nif);
lod0 = nif->getUInt();
lod1 = nif->getUInt(); nif->readArray(mLOD);
lod2 = nif->getUInt();
} }
void NiCamera::Camera::read(NIFStream* nif) void NiCamera::read(NIFStream* nif)
{ {
NiAVObject::read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
cameraFlags = nif->getUShort(); nif->read(mCameraFlags);
left = nif->getFloat(); nif->read(mLeft);
right = nif->getFloat(); nif->read(mRight);
top = nif->getFloat(); nif->read(mTop);
bottom = nif->getFloat(); nif->read(mBottom);
nearDist = nif->getFloat(); nif->read(mNearDist);
farDist = nif->getFloat(); nif->read(mFarDist);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
nif->read(orthographic); nif->read(mOrthographic);
vleft = nif->getFloat(); nif->read(mVLeft);
vright = nif->getFloat(); nif->read(mVRight);
vtop = nif->getFloat(); nif->read(mVTop);
vbottom = nif->getFloat(); nif->read(mVBottom);
nif->read(mLODAdjust);
LOD = nif->getFloat(); mScene.read(nif);
nif->skip(4); // Unused
if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 1, 0))
nif->skip(4); // Unused
} }
void NiCamera::read(NIFStream* nif) void NiCamera::post(Reader& nif)
{ {
NiAVObject::read(nif); NiAVObject::post(nif);
cam.read(nif);
nif->getInt(); // -1 mScene.post(nif);
nif->getInt(); // 0
if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 1, 0))
nif->getInt(); // 0
} }
void NiSwitchNode::read(NIFStream* nif) void NiSwitchNode::read(NIFStream* nif)
{ {
NiNode::read(nif); NiNode::read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
switchFlags = nif->getUShort(); nif->read(mSwitchFlags);
initialIndex = nif->getUInt(); nif->read(mInitialIndex);
} }
void NiLODNode::read(NIFStream* nif) void NiLODNode::read(NIFStream* nif)
{ {
NiSwitchNode::read(nif); NiSwitchNode::read(nif);
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW
&& nif->getVersion() <= NIFStream::generateVersion(10, 0, 1, 0)) if (nif->getVersion() > NIFStream::generateVersion(10, 0, 1, 0))
lodCenter = nif->getVector3();
else if (nif->getVersion() > NIFStream::generateVersion(10, 0, 1, 0))
{ {
nif->skip(4); // NiLODData, unsupported at the moment nif->skip(4); // NiLODData, unsupported at the moment
return; return;
} }
unsigned int numLodLevels = nif->getUInt(); if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW)
for (unsigned int i = 0; i < numLodLevels; ++i) nif->read(mLODCenter);
mLODLevels.resize(nif->get<uint32_t>());
for (LODRange& level : mLODLevels)
{ {
LODRange r; nif->read(level.mMinRange);
r.minRange = nif->getFloat(); nif->read(level.mMaxRange);
r.maxRange = nif->getFloat();
lodLevels.push_back(r);
} }
} }
void NiFltAnimationNode::read(NIFStream* nif) void NiFltAnimationNode::read(NIFStream* nif)
{ {
NiSwitchNode::read(nif); NiSwitchNode::read(nif);
mDuration = nif->getFloat();
nif->read(mDuration);
} }
void NiSortAdjustNode::read(NIFStream* nif) void NiSortAdjustNode::read(NIFStream* nif)
{ {
NiNode::read(nif); NiNode::read(nif);
mMode = nif->getInt();
mMode = static_cast<SortingMode>(nif->get<uint32_t>());
if (nif->getVersion() <= NIFStream::generateVersion(20, 0, 0, 3)) if (nif->getVersion() <= NIFStream::generateVersion(20, 0, 0, 3))
mSubSorter.read(nif); mSubSorter.read(nif);
} }
@ -278,14 +305,16 @@ namespace Nif
void NiSortAdjustNode::post(Reader& nif) void NiSortAdjustNode::post(Reader& nif)
{ {
NiNode::post(nif); NiNode::post(nif);
mSubSorter.post(nif); mSubSorter.post(nif);
} }
void NiBillboardNode::read(NIFStream* nif) void NiBillboardNode::read(NIFStream* nif)
{ {
NiNode::read(nif); NiNode::read(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->get<uint16_t>() & 0x7;
else else
mMode = (mFlags >> 5) & 0x3; mMode = (mFlags >> 5) & 0x3;
} }
@ -293,8 +322,9 @@ namespace Nif
void NiDefaultAVObjectPalette::read(NIFStream* nif) void NiDefaultAVObjectPalette::read(NIFStream* nif)
{ {
mScene.read(nif); mScene.read(nif);
size_t numObjects = nif->getUInt(); uint32_t numObjects;
for (size_t i = 0; i < numObjects; i++) nif->read(numObjects);
for (uint32_t i = 0; i < numObjects; i++)
mObjects[nif->getSizedString()].read(nif); mObjects[nif->getSizedString()].read(nif);
} }
@ -308,6 +338,7 @@ namespace Nif
void BSTreeNode::read(NIFStream* nif) void BSTreeNode::read(NIFStream* nif)
{ {
NiNode::read(nif); NiNode::read(nif);
readRecordList(nif, mBones1); readRecordList(nif, mBones1);
readRecordList(nif, mBones2); readRecordList(nif, mBones2);
} }
@ -315,6 +346,7 @@ namespace Nif
void BSTreeNode::post(Reader& nif) void BSTreeNode::post(Reader& nif)
{ {
NiNode::post(nif); NiNode::post(nif);
postRecordList(nif, mBones1); postRecordList(nif, mBones1);
postRecordList(nif, mBones2); postRecordList(nif, mBones2);
} }
@ -322,67 +354,53 @@ namespace Nif
void BSMultiBoundNode::read(NIFStream* nif) void BSMultiBoundNode::read(NIFStream* nif)
{ {
NiNode::read(nif); NiNode::read(nif);
mMultiBound.read(nif); mMultiBound.read(nif);
if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_SKY) if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_SKY)
mType = nif->getUInt(); mCullingType = static_cast<BSCPCullingType>(nif->get<uint32_t>());
} }
void BSMultiBoundNode::post(Reader& nif) void BSMultiBoundNode::post(Reader& nif)
{ {
NiNode::post(nif); NiNode::post(nif);
mMultiBound.post(nif); mMultiBound.post(nif);
} }
void BSTriShape::read(NIFStream* nif) void BSTriShape::read(NIFStream* nif)
{ {
NiAVObject::read(nif); NiAVObject::read(nif);
nif->read(mBoundingSphere);
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_F76) nif->read(mBoundingSphere);
{ if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76)
nif->readArray(mBoundMinMax); nif->readArray(mBoundMinMax);
}
mSkin.read(nif); mSkin.read(nif);
mShaderProperty.read(nif); mShaderProperty.read(nif);
mAlphaProperty.read(nif); mAlphaProperty.read(nif);
mVertDesc.read(nif); mVertDesc.read(nif);
unsigned int triNum; if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4)
if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4) mTriangles.resize(nif->get<uint32_t>() * 3);
{
triNum = nif->get<unsigned short>();
}
else else
{ mTriangles.resize(nif->get<uint16_t>() * 3);
nif->read(triNum); mVertData.resize(nif->get<uint16_t>());
}
unsigned short vertNum;
nif->read(vertNum);
nif->read(mDataSize); nif->read(mDataSize);
if (mDataSize > 0)
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE)
{ {
mVertData.resize(vertNum);
for (auto& vertex : mVertData) for (auto& vertex : mVertData)
vertex.read(nif, mVertDesc.mFlags); vertex.read(nif, mVertDesc.mFlags);
} nif->readVector(mTriangles, mTriangles.size());
else if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4)
{
throw Nif::Exception("FO4 BSTriShape is not supported yet: ", nif->getFile().getFilename());
} }
if (mDataSize > 0)
nif->readVector(mTriangles, triNum * 3);
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE) if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE)
{ {
nif->read(mParticleDataSize); nif->read(mParticleDataSize);
if (mParticleDataSize > 0) if (mParticleDataSize > 0)
{ {
throw Nif::Exception("Unhandled Particle Data in BSTriShape: ", nif->getFile().getFilename()); nif->readVector(mParticleVerts, mVertData.size() * 3);
nif->readVector(mParticleNormals, mVertData.size() * 3);
nif->readVector(mParticleTriangles, mTriangles.size());
} }
} }
} }
@ -390,6 +408,7 @@ namespace Nif
void BSTriShape::post(Reader& nif) void BSTriShape::post(Reader& nif)
{ {
NiAVObject::post(nif); NiAVObject::post(nif);
mSkin.post(nif); mSkin.post(nif);
mShaderProperty.post(nif); mShaderProperty.post(nif);
mAlphaProperty.post(nif); mAlphaProperty.post(nif);
@ -443,7 +462,6 @@ namespace Nif
if (normalsFlag) if (normalsFlag)
{ {
nif->readArray(mNormal); nif->readArray(mNormal);
nif->read(mBitangentY); nif->read(mBitangentY);
} }
@ -451,7 +469,6 @@ namespace Nif
== (BSVertexDesc::VertexAttribute::Normals | BSVertexDesc::VertexAttribute::Tangents)) == (BSVertexDesc::VertexAttribute::Normals | BSVertexDesc::VertexAttribute::Tangents))
{ {
nif->readArray(mTangent); nif->readArray(mTangent);
nif->read(mBitangentZ); nif->read(mBitangentZ);
} }
@ -468,14 +485,14 @@ namespace Nif
if (flags & BSVertexDesc::VertexAttribute::Eye_Data) if (flags & BSVertexDesc::VertexAttribute::Eye_Data)
{ {
throw Nif::Exception("Unhandled Eye Data in BSTriShape: ", nif->getFile().getFilename()); nif->read(mEyeData);
// nif->read(mEyeData);
} }
} }
void BSValueNode::read(NIFStream* nif) void BSValueNode::read(NIFStream* nif)
{ {
NiNode::read(nif); NiNode::read(nif);
nif->read(mValue); nif->read(mValue);
nif->read(mValueFlags); nif->read(mValueFlags);
} }
@ -483,6 +500,7 @@ namespace Nif
void BSOrderedNode::read(NIFStream* nif) void BSOrderedNode::read(NIFStream* nif)
{ {
NiNode::read(nif); NiNode::read(nif);
nif->read(mAlphaSortBound); nif->read(mAlphaSortBound);
nif->read(mStaticBound); nif->read(mStaticBound);
} }
@ -490,8 +508,10 @@ namespace Nif
void BSRangeNode::read(NIFStream* nif) void BSRangeNode::read(NIFStream* nif)
{ {
NiNode::read(nif); NiNode::read(nif);
nif->read(mMin); nif->read(mMin);
nif->read(mMax); nif->read(mMax);
nif->read(mCurrent); nif->read(mCurrent);
} }
} }

@ -13,7 +13,7 @@ namespace Nif
struct NiNode; struct NiNode;
struct NiBoundingVolume struct BoundingVolume
{ {
enum Type : uint32_t enum Type : uint32_t
{ {
@ -28,36 +28,36 @@ namespace Nif
struct NiBoxBV struct NiBoxBV
{ {
osg::Vec3f center; osg::Vec3f mCenter;
Matrix3 axes; Matrix3 mAxes;
osg::Vec3f extents; osg::Vec3f mExtents;
}; };
struct NiCapsuleBV struct NiCapsuleBV
{ {
osg::Vec3f center, axis; osg::Vec3f mCenter, mAxis;
float extent{ 0.f }, radius{ 0.f }; float mExtent{ 0.f }, mRadius{ 0.f };
}; };
struct NiLozengeBV struct NiLozengeBV
{ {
float radius{ 0.f }, extent0{ 0.f }, extent1{ 0.f }; float mRadius{ 0.f }, mExtent0{ 0.f }, mExtent1{ 0.f };
osg::Vec3f center, axis0, axis1; osg::Vec3f mCenter, mAxis0, mAxis1;
}; };
struct NiHalfSpaceBV struct NiHalfSpaceBV
{ {
osg::Plane plane; osg::Plane mPlane;
osg::Vec3f origin; osg::Vec3f mOrigin;
}; };
uint32_t type{ BASE_BV }; uint32_t mType{ BASE_BV };
osg::BoundingSpheref sphere; osg::BoundingSpheref mSphere;
NiBoxBV box; NiBoxBV mBox;
NiCapsuleBV capsule; NiCapsuleBV mCapsule;
NiLozengeBV lozenge; NiLozengeBV mLozenge;
std::vector<NiBoundingVolume> children; std::vector<BoundingVolume> mChildren;
NiHalfSpaceBV halfSpace; NiHalfSpaceBV mHalfSpace;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
@ -68,7 +68,7 @@ namespace Nif
// NiAVObject is an object that is a part of the main NIF tree. It has // 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. // a parent node (unless it's the root) and transformation relative to its parent.
struct NiAVObject : public NiObjectNET struct NiAVObject : NiObjectNET
{ {
enum Flags enum Flags
{ {
@ -83,7 +83,7 @@ namespace Nif
NiTransform mTransform; NiTransform mTransform;
osg::Vec3f mVelocity; osg::Vec3f mVelocity;
PropertyList mProperties; PropertyList mProperties;
NiBoundingVolume mBounds; BoundingVolume mBounds;
NiCollisionObjectPtr mCollision; NiCollisionObjectPtr mCollision;
// Parent nodes for the node. Only types derived from NiNode can be parents. // Parent nodes for the node. Only types derived from NiNode can be parents.
std::vector<NiNode*> mParents; std::vector<NiNode*> mParents;
@ -119,7 +119,14 @@ namespace Nif
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
struct NiGeometry : NiAVObject struct GeometryInterface
{
NiSkinInstancePtr mSkin;
BSShaderPropertyPtr mShaderProperty;
NiAlphaPropertyPtr mAlphaProperty;
};
struct NiGeometry : NiAVObject, GeometryInterface
{ {
/* Possible flags: /* Possible flags:
0x40 - mesh has no vertex normals ? 0x40 - mesh has no vertex normals ?
@ -130,97 +137,95 @@ namespace Nif
struct MaterialData struct MaterialData
{ {
std::vector<std::string> names; std::vector<std::string> mNames;
std::vector<int> extra; std::vector<int> mExtra;
unsigned int active{ 0 }; int32_t mActive{ -1 };
bool needsUpdate{ false }; bool mNeedsUpdate{ false };
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
NiGeometryDataPtr data; NiGeometryDataPtr mData;
NiSkinInstancePtr skin; MaterialData mMaterial;
MaterialData material;
BSShaderPropertyPtr shaderprop;
NiAlphaPropertyPtr alphaprop;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
struct NiTriShape : NiGeometry // Abstract triangle-based geometry
struct NiTriBasedGeom : NiGeometry
{ {
}; };
struct BSLODTriShape : NiTriShape
struct NiTriShape : NiTriBasedGeom
{ {
unsigned int lod0, lod1, lod2;
void read(NIFStream* nif) override;
}; };
struct NiTriStrips : NiGeometry
struct NiTriStrips : NiTriBasedGeom
{ {
}; };
struct NiLines : NiGeometry
struct NiLines : NiTriBasedGeom
{ {
}; };
struct NiParticles : NiGeometry struct NiParticles : NiGeometry
{ {
}; };
struct NiCamera : NiAVObject struct BSLODTriShape : NiTriBasedGeom
{ {
struct Camera std::array<uint32_t, 3> mLOD;
{ void read(NIFStream* nif) override;
unsigned short cameraFlags{ 0 }; };
// Camera frustrum
float left, right, top, bottom, nearDist, farDist;
// Viewport
float vleft, vright, vtop, vbottom;
// Level of detail modifier
float LOD;
// Orthographic projection usage flag
bool orthographic{ false };
void read(NIFStream* nif); struct NiCamera : NiAVObject
}; {
Camera cam; uint16_t mCameraFlags{ 0 };
// Camera frustum
float mLeft, mRight, mTop, mBottom, mNearDist, mFarDist;
bool mOrthographic{ false };
// Viewport
float mVLeft, mVRight, mVTop, mVBottom;
float mLODAdjust;
NiAVObjectPtr mScene;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override;
}; };
// A node used as the base to switch between child nodes, such as for LOD levels. // A node used as the base to switch between child nodes, such as for LOD levels.
struct NiSwitchNode : public NiNode struct NiSwitchNode : NiNode
{ {
unsigned int switchFlags{ 0 }; uint16_t mSwitchFlags;
unsigned int initialIndex{ 0 }; uint32_t mInitialIndex;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct NiLODNode : public NiSwitchNode struct NiLODNode : NiSwitchNode
{ {
osg::Vec3f lodCenter;
struct LODRange struct LODRange
{ {
float minRange; float mMinRange;
float maxRange; float mMaxRange;
}; };
std::vector<LODRange> lodLevels;
osg::Vec3f mLODCenter;
std::vector<LODRange> mLODLevels;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct NiFltAnimationNode : public NiSwitchNode struct NiFltAnimationNode : NiSwitchNode
{ {
float mDuration;
enum Flags enum Flags
{ {
Flag_Swing = 0x40 Flag_Swing = 0x40
}; };
float mDuration;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
bool swing() const { return mFlags & Flag_Swing; } bool swing() const { return mFlags & Flag_Swing; }
@ -236,20 +241,21 @@ namespace Nif
struct NiClusterAccumulator : NiAccumulator struct NiClusterAccumulator : NiAccumulator
{ {
}; };
struct NiAlphaAccumulator : NiClusterAccumulator struct NiAlphaAccumulator : NiClusterAccumulator
{ {
}; };
struct NiSortAdjustNode : NiNode struct NiSortAdjustNode : NiNode
{ {
enum SortingMode enum class SortingMode : uint32_t
{ {
SortingMode_Inherit, Inherit,
SortingMode_Off, Off,
SortingMode_Subsort Subsort,
}; };
int mMode; SortingMode mMode;
NiAccumulatorPtr mSubSorter; NiAccumulatorPtr mSubSorter;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
@ -258,7 +264,7 @@ namespace Nif
struct NiBillboardNode : NiNode struct NiBillboardNode : NiNode
{ {
int mMode{ 0 }; int mMode;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -275,14 +281,24 @@ namespace Nif
struct BSTreeNode : NiNode struct BSTreeNode : NiNode
{ {
NiAVObjectList 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;
}; };
struct BSMultiBoundNode : NiNode struct BSMultiBoundNode : NiNode
{ {
enum class BSCPCullingType : uint32_t
{
Normal,
AllPass,
AllFail,
IgnoreMultiBounds,
ForceMultiBoundsNoUpdate,
};
BSMultiBoundPtr mMultiBound; BSMultiBoundPtr mMultiBound;
unsigned int mType{ 0 }; BSCPCullingType mCullingType;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
@ -290,17 +306,17 @@ namespace Nif
struct BSVertexDesc struct BSVertexDesc
{ {
unsigned char mVertexDataSize; uint8_t mVertexDataSize;
unsigned char mDynamicVertexSize; uint8_t mDynamicVertexSize;
unsigned char mUV1Offset; uint8_t mUV1Offset;
unsigned char mUV2Offset; uint8_t mUV2Offset;
unsigned char mNormalOffset; uint8_t mNormalOffset;
unsigned char mTangentOffset; uint8_t mTangentOffset;
unsigned char mColorOffset; uint8_t mColorOffset;
unsigned char mSkinningDataOffset; uint8_t mSkinningDataOffset;
unsigned char mLandscapeDataOffset; uint8_t mLandscapeDataOffset;
unsigned char mEyeDataOffset; uint8_t mEyeDataOffset;
unsigned short mFlags; uint16_t mFlags;
enum VertexAttribute enum VertexAttribute
{ {
@ -324,9 +340,8 @@ namespace Nif
{ {
osg::Vec3f mVertex; osg::Vec3f mVertex;
float mBitangentX; float mBitangentX;
unsigned int mUnusedW; uint32_t mUnusedW;
std::array<Misc::float16_t, 2> mUV; std::array<Misc::float16_t, 2> mUV;
std::array<char, 3> mNormal; std::array<char, 3> mNormal;
char mBitangentY; char mBitangentY;
std::array<char, 3> mTangent; std::array<char, 3> mTangent;
@ -339,25 +354,18 @@ namespace Nif
void read(NIFStream* nif, uint16_t flags); void read(NIFStream* nif, uint16_t flags);
}; };
struct BSTriShape : NiAVObject struct BSTriShape : NiAVObject, GeometryInterface
{ {
osg::BoundingSpheref mBoundingSphere; osg::BoundingSpheref mBoundingSphere;
std::array<float, 6> mBoundMinMax; std::array<float, 6> mBoundMinMax;
NiSkinInstancePtr mSkin;
BSShaderPropertyPtr mShaderProperty;
NiAlphaPropertyPtr mAlphaProperty;
BSVertexDesc mVertDesc; BSVertexDesc mVertDesc;
uint32_t mDataSize;
unsigned int mDataSize;
unsigned int mParticleDataSize;
std::vector<BSVertexData> mVertData; std::vector<BSVertexData> mVertData;
std::vector<unsigned short> mTriangles; std::vector<unsigned short> mTriangles;
uint32_t mParticleDataSize;
std::vector<Misc::float16_t> mParticleVerts;
std::vector<Misc::float16_t> mParticleNormals;
std::vector<unsigned short> mParticleTriangles; std::vector<unsigned short> mParticleTriangles;
std::vector<osg::Vec3f> mParticleVerts;
std::vector<osg::Vec3f> mParticleNormals;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
@ -365,8 +373,14 @@ namespace Nif
struct BSValueNode : NiNode struct BSValueNode : NiNode
{ {
unsigned int mValue; enum Flags
char mValueFlags; {
Flag_BillboardWorldZ = 0x1,
Flag_UsePlayerAdjust = 0x2,
};
uint32_t mValue;
uint8_t mValueFlags;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -374,7 +388,7 @@ namespace Nif
struct BSOrderedNode : NiNode struct BSOrderedNode : NiNode
{ {
osg::Vec4f mAlphaSortBound; osg::Vec4f mAlphaSortBound;
char mStaticBound; bool mStaticBound;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };

@ -79,14 +79,11 @@ namespace
template <class Function> template <class Function>
auto handleNiGeometry(const Nif::NiGeometry& geometry, Function&& function) auto handleNiGeometry(const Nif::NiGeometry& geometry, Function&& function)
-> decltype(function(static_cast<const Nif::NiTriShapeData&>(geometry.data.get()))) -> decltype(function(static_cast<const Nif::NiTriShapeData&>(geometry.mData.get())))
{ {
if (geometry.recType == Nif::RC_NiTriShape || geometry.recType == Nif::RC_BSLODTriShape) if (geometry.recType == Nif::RC_NiTriShape || geometry.recType == Nif::RC_BSLODTriShape)
{ {
if (geometry.data->recType != Nif::RC_NiTriShapeData) auto data = static_cast<const Nif::NiTriShapeData*>(geometry.mData.getPtr());
return {};
auto data = static_cast<const Nif::NiTriShapeData*>(geometry.data.getPtr());
if (data->mTriangles.empty()) if (data->mTriangles.empty())
return {}; return {};
@ -95,10 +92,7 @@ namespace
if (geometry.recType == Nif::RC_NiTriStrips) if (geometry.recType == Nif::RC_NiTriStrips)
{ {
if (geometry.data->recType != Nif::RC_NiTriStripsData) auto data = static_cast<const Nif::NiTriStripsData*>(geometry.mData.getPtr());
return {};
auto data = static_cast<const Nif::NiTriStripsData*>(geometry.data.getPtr());
if (data->mStrips.empty()) if (data->mStrips.empty())
return {}; return {};
@ -181,7 +175,7 @@ namespace NifBullet
bool hasCollisionShape = false; bool hasCollisionShape = false;
if (colNode != nullptr) if (colNode != nullptr)
{ {
if (colNode->mBounds.type == Nif::NiBoundingVolume::Type::BASE_BV && !colNode->mChildren.empty()) if (colNode->mBounds.mType == Nif::BoundingVolume::Type::BASE_BV && !colNode->mChildren.empty())
hasCollisionShape = true; hasCollisionShape = true;
else else
mShape->mVisualCollisionType = Resource::VisualCollisionType::Camera; mShape->mVisualCollisionType = Resource::VisualCollisionType::Camera;
@ -205,25 +199,25 @@ namespace NifBullet
// Return: use bounding box for collision? // Return: use bounding box for collision?
bool BulletNifLoader::findBoundingBox(const Nif::NiAVObject& node, const std::string& filename) bool BulletNifLoader::findBoundingBox(const Nif::NiAVObject& node, const std::string& filename)
{ {
unsigned int type = node.mBounds.type; unsigned int type = node.mBounds.mType;
switch (type) switch (type)
{ {
case Nif::NiBoundingVolume::Type::BASE_BV: case Nif::BoundingVolume::Type::BASE_BV:
break; break;
case Nif::NiBoundingVolume::Type::BOX_BV: case Nif::BoundingVolume::Type::BOX_BV:
mShape->mCollisionBox.mExtents = node.mBounds.box.extents; mShape->mCollisionBox.mExtents = node.mBounds.mBox.mExtents;
mShape->mCollisionBox.mCenter = node.mBounds.box.center; mShape->mCollisionBox.mCenter = node.mBounds.mBox.mCenter;
break; break;
default: default:
{ {
std::stringstream warning; std::stringstream warning;
warning << "Unsupported NiBoundingVolume type " << type << " in node " << node.recIndex; warning << "Unsupported BoundingVolume type " << type << " in node " << node.recIndex;
warning << " in file " << filename; warning << " in file " << filename;
warn(warning.str()); warn(warning.str());
} }
} }
if (type != Nif::NiBoundingVolume::Type::BASE_BV && node.hasBBoxCollision()) if (type != Nif::BoundingVolume::Type::BASE_BV && node.hasBBoxCollision())
return true; return true;
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&node)) if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&node))
@ -335,7 +329,7 @@ namespace NifBullet
// NOTE: a trishape with bounds, 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.mBounds.type == Nif::NiBoundingVolume::Type::BASE_BV if (node.mBounds.mType == Nif::BoundingVolume::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))
{ {
@ -366,10 +360,10 @@ namespace NifBullet
if (args.mHasMarkers && Misc::StringUtils::ciStartsWith(niGeometry.mName, "EditorMarker")) if (args.mHasMarkers && Misc::StringUtils::ciStartsWith(niGeometry.mName, "EditorMarker"))
return; return;
if (niGeometry.data.empty() || niGeometry.data->mVertices.empty()) if (niGeometry.mData.empty() || niGeometry.mData->mVertices.empty())
return; return;
if (!niGeometry.skin.empty()) if (!niGeometry.mSkin.empty())
args.mAnimated = false; args.mAnimated = false;
// TODO: handle NiSkinPartition // TODO: handle NiSkinPartition

@ -128,10 +128,10 @@ namespace
auto geometry = dynamic_cast<const Nif::NiGeometry*>(nifNode); auto geometry = dynamic_cast<const Nif::NiGeometry*>(nifNode);
if (geometry) if (geometry)
{ {
if (!geometry->shaderprop.empty()) if (!geometry->mShaderProperty.empty())
out.emplace_back(geometry->shaderprop.getPtr()); out.emplace_back(geometry->mShaderProperty.getPtr());
if (!geometry->alphaprop.empty()) if (!geometry->mAlphaProperty.empty())
out.emplace_back(geometry->alphaprop.getPtr()); out.emplace_back(geometry->mAlphaProperty.getPtr());
} }
} }
@ -444,8 +444,8 @@ namespace NifOsg
auto geometry = dynamic_cast<const Nif::NiGeometry*>(nifNode); auto geometry = dynamic_cast<const Nif::NiGeometry*>(nifNode);
// NiGeometry's NiAlphaProperty doesn't get handled here because it's a drawable property // NiGeometry's NiAlphaProperty doesn't get handled here because it's a drawable property
if (geometry && !geometry->shaderprop.empty()) if (geometry && !geometry->mShaderProperty.empty())
handleProperty(geometry->shaderprop.getPtr(), applyTo, composite, imageManager, boundTextures, handleProperty(geometry->mShaderProperty.getPtr(), applyTo, composite, imageManager, boundTextures,
animflags, hasStencilProperty); animflags, hasStencilProperty);
} }
@ -463,11 +463,11 @@ namespace NifOsg
osg::ref_ptr<osg::LOD> lod(new osg::LOD); osg::ref_ptr<osg::LOD> lod(new osg::LOD);
lod->setName(niLodNode->mName); 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->mLODCenter);
for (unsigned int i = 0; i < niLodNode->lodLevels.size(); ++i) for (unsigned int i = 0; i < niLodNode->mLODLevels.size(); ++i)
{ {
const Nif::NiLODNode::LODRange& range = niLodNode->lodLevels[i]; const Nif::NiLODNode::LODRange& range = niLodNode->mLODLevels[i];
lod->setRange(i, range.minRange, range.maxRange); lod->setRange(i, range.mMinRange, range.mMaxRange);
} }
lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT); lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
return lod; return lod;
@ -478,7 +478,7 @@ namespace NifOsg
osg::ref_ptr<osg::Switch> switchNode(new osg::Switch); osg::ref_ptr<osg::Switch> switchNode(new osg::Switch);
switchNode->setName(niSwitchNode->mName); switchNode->setName(niSwitchNode->mName);
switchNode->setNewChildDefaultValue(false); switchNode->setNewChildDefaultValue(false);
switchNode->setSingleChildOn(niSwitchNode->initialIndex); switchNode->setSingleChildOn(niSwitchNode->mInitialIndex);
return switchNode; return switchNode;
} }
@ -706,7 +706,7 @@ namespace NifOsg
else else
{ {
if (mPushedSorter && !mPushedSorter->mSubSorter.empty() if (mPushedSorter && !mPushedSorter->mSubSorter.empty()
&& mPushedSorter->mMode != Nif::NiSortAdjustNode::SortingMode_Inherit) && mPushedSorter->mMode != Nif::NiSortAdjustNode::SortingMode::Inherit)
mLastAppliedNoInheritSorter = mPushedSorter; mLastAppliedNoInheritSorter = mPushedSorter;
mPushedSorter = sortNode; mPushedSorter = sortNode;
} }
@ -761,7 +761,7 @@ namespace NifOsg
skip = args.mHasMarkers && Misc::StringUtils::ciStartsWith(nifNode->mName, "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)->mSkin;
if (skin.empty()) if (skin.empty())
handleGeometry(nifNode, parent, node, composite, args.mBoundTextures, args.mAnimFlags); handleGeometry(nifNode, parent, node, composite, args.mBoundTextures, args.mAnimFlags);
@ -1121,13 +1121,13 @@ namespace NifOsg
const Nif::NiAVObject* 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->mData.empty())
{ {
partsys->setQuota(partctrl->mParticles.size()); partsys->setQuota(partctrl->mParticles.size());
return; return;
} }
auto particledata = static_cast<const Nif::NiParticlesData*>(particleNode->data.getPtr()); auto particledata = static_cast<const Nif::NiParticlesData*>(particleNode->mData.getPtr());
partsys->setQuota(particledata->mNumParticles); partsys->setQuota(particledata->mNumParticles);
osg::BoundingBox box; osg::BoundingBox box;
@ -1379,13 +1379,13 @@ namespace NifOsg
const std::vector<unsigned int>& boundTextures, int animflags) const std::vector<unsigned int>& boundTextures, int animflags)
{ {
const Nif::NiGeometry* niGeometry = static_cast<const Nif::NiGeometry*>(nifNode); const Nif::NiGeometry* niGeometry = static_cast<const Nif::NiGeometry*>(nifNode);
if (niGeometry->data.empty()) if (niGeometry->mData.empty())
return; return;
bool hasPartitions = false; bool hasPartitions = false;
if (!niGeometry->skin.empty()) if (!niGeometry->mSkin.empty())
{ {
const Nif::NiSkinInstance* skin = niGeometry->skin.getPtr(); const Nif::NiSkinInstance* skin = niGeometry->mSkin.getPtr();
const Nif::NiSkinData* data = nullptr; const Nif::NiSkinData* data = nullptr;
const Nif::NiSkinPartition* partitions = nullptr; const Nif::NiSkinPartition* partitions = nullptr;
if (!skin->mData.empty()) if (!skin->mData.empty())
@ -1419,13 +1419,11 @@ namespace NifOsg
} }
} }
const Nif::NiGeometryData* niGeometryData = niGeometry->data.getPtr(); const Nif::NiGeometryData* niGeometryData = niGeometry->mData.getPtr();
if (!hasPartitions) if (!hasPartitions)
{ {
if (niGeometry->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_BSLODTriShape) if (niGeometry->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_BSLODTriShape)
{ {
if (niGeometryData->recType != Nif::RC_NiTriShapeData)
return;
auto data = static_cast<const Nif::NiTriShapeData*>(niGeometryData); auto data = static_cast<const Nif::NiTriShapeData*>(niGeometryData);
const std::vector<unsigned short>& triangles = data->mTriangles; const std::vector<unsigned short>& triangles = data->mTriangles;
if (triangles.empty()) if (triangles.empty())
@ -1435,8 +1433,6 @@ namespace NifOsg
} }
else if (niGeometry->recType == Nif::RC_NiTriStrips) else if (niGeometry->recType == Nif::RC_NiTriStrips)
{ {
if (niGeometryData->recType != Nif::RC_NiTriStripsData)
return;
auto data = static_cast<const Nif::NiTriStripsData*>(niGeometryData); auto data = static_cast<const Nif::NiTriStripsData*>(niGeometryData);
bool hasGeometry = false; bool hasGeometry = false;
for (const std::vector<unsigned short>& strip : data->mStrips) for (const std::vector<unsigned short>& strip : data->mStrips)
@ -1452,8 +1448,6 @@ namespace NifOsg
} }
else if (niGeometry->recType == Nif::RC_NiLines) else if (niGeometry->recType == Nif::RC_NiLines)
{ {
if (niGeometryData->recType != Nif::RC_NiLinesData)
return;
auto data = static_cast<const Nif::NiLinesData*>(niGeometryData); auto data = static_cast<const Nif::NiLinesData*>(niGeometryData);
const auto& line = data->mLines; const auto& line = data->mLines;
if (line.empty()) if (line.empty())
@ -1545,7 +1539,7 @@ namespace NifOsg
// 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)->mSkin.getPtr();
const Nif::NiSkinData* data = skin->mData.getPtr(); const Nif::NiSkinData* data = skin->mData.getPtr();
const Nif::NiAVObjectList& 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)
@ -2627,8 +2621,8 @@ namespace NifOsg
if (!mPushedSorter) if (!mPushedSorter)
return; return;
auto assignBin = [&](int mode, int type) { auto assignBin = [&](Nif::NiSortAdjustNode::SortingMode mode, int type) {
if (mode == Nif::NiSortAdjustNode::SortingMode_Off) if (mode == Nif::NiSortAdjustNode::SortingMode::Off)
{ {
setBin_Traversal(stateset); setBin_Traversal(stateset);
return; return;
@ -2649,7 +2643,7 @@ namespace NifOsg
switch (mPushedSorter->mMode) switch (mPushedSorter->mMode)
{ {
case Nif::NiSortAdjustNode::SortingMode_Inherit: case Nif::NiSortAdjustNode::SortingMode::Inherit:
{ {
if (mLastAppliedNoInheritSorter) if (mLastAppliedNoInheritSorter)
assignBin(mLastAppliedNoInheritSorter->mMode, mLastAppliedNoInheritSorter->mSubSorter->recType); assignBin(mLastAppliedNoInheritSorter->mMode, mLastAppliedNoInheritSorter->mSubSorter->recType);
@ -2657,12 +2651,12 @@ namespace NifOsg
assignBin(mPushedSorter->mMode, Nif::RC_NiAlphaAccumulator); assignBin(mPushedSorter->mMode, Nif::RC_NiAlphaAccumulator);
break; break;
} }
case Nif::NiSortAdjustNode::SortingMode_Off: case Nif::NiSortAdjustNode::SortingMode::Off:
{ {
setBin_Traversal(stateset); setBin_Traversal(stateset);
break; break;
} }
case Nif::NiSortAdjustNode::SortingMode_Subsort: case Nif::NiSortAdjustNode::SortingMode::Subsort:
{ {
assignBin(mPushedSorter->mMode, mPushedSorter->mSubSorter->recType); assignBin(mPushedSorter->mMode, mPushedSorter->mSubSorter->recType);
break; break;

Loading…
Cancel
Save