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

Merge branch 'nif' into 'master'

Modernize NIF loader, part 2

See merge request OpenMW/openmw!3404
This commit is contained in:
psi29a 2023-09-09 08:06:32 +00:00
commit 0a47e927d5
No known key found for this signature in database
14 changed files with 645 additions and 551 deletions

View file

@ -53,8 +53,8 @@ namespace Nif::Testing
inline void init(NiSkinInstance& value) inline void init(NiSkinInstance& value)
{ {
value.data = NiSkinDataPtr(nullptr); value.mData = NiSkinDataPtr(nullptr);
value.root = NodePtr(nullptr); value.mRoot = NodePtr(nullptr);
} }
inline void init(Controller& value) inline void init(Controller& value)

View file

@ -323,22 +323,22 @@ namespace
init(mController); init(mController);
mNiTriShapeData.recType = Nif::RC_NiTriShapeData; mNiTriShapeData.recType = Nif::RC_NiTriShapeData;
mNiTriShapeData.vertices = { 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.triangles = { 0, 1, 2 }; mNiTriShapeData.mTriangles = { 0, 1, 2 };
mNiTriShape.data = Nif::NiGeometryDataPtr(&mNiTriShapeData); mNiTriShape.data = Nif::NiGeometryDataPtr(&mNiTriShapeData);
mNiTriShapeData2.recType = Nif::RC_NiTriShapeData; mNiTriShapeData2.recType = Nif::RC_NiTriShapeData;
mNiTriShapeData2.vertices = { 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.triangles = { 0, 1, 2 }; mNiTriShapeData2.mTriangles = { 0, 1, 2 };
mNiTriShape2.data = Nif::NiGeometryDataPtr(&mNiTriShapeData2); mNiTriShape2.data = Nif::NiGeometryDataPtr(&mNiTriShapeData2);
mNiTriStripsData.recType = Nif::RC_NiTriStripsData; mNiTriStripsData.recType = Nif::RC_NiTriStripsData;
mNiTriStripsData.vertices 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.strips = { { 0, 1, 2, 3 } }; mNiTriStripsData.mStrips = { { 0, 1, 2, 3 } };
mNiTriStrips.data = Nif::NiGeometryDataPtr(&mNiTriStripsData); mNiTriStrips.data = Nif::NiGeometryDataPtr(&mNiTriStripsData);
} }
}; };
@ -978,7 +978,7 @@ namespace
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.data.getPtr());
data->triangles.clear(); data->mTriangles.clear();
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) }));
@ -1260,7 +1260,7 @@ namespace
TEST_F(TestBulletNifLoader, should_ignore_tri_strips_data_with_empty_strips) TEST_F(TestBulletNifLoader, should_ignore_tri_strips_data_with_empty_strips)
{ {
mNiTriStripsData.strips.clear(); mNiTriStripsData.mStrips.clear();
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiTriStrips); file.mRoots.push_back(&mNiTriStrips);
@ -1275,7 +1275,7 @@ namespace
TEST_F(TestBulletNifLoader, for_static_mesh_should_ignore_tri_strips_data_with_less_than_3_strips) TEST_F(TestBulletNifLoader, for_static_mesh_should_ignore_tri_strips_data_with_less_than_3_strips)
{ {
mNiTriStripsData.strips.front() = { 0, 1 }; mNiTriStripsData.mStrips.front() = { 0, 1 };
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiTriStrips); file.mRoots.push_back(&mNiTriStrips);
@ -1293,7 +1293,7 @@ namespace
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) }));
mNiNode.recType = Nif::RC_AvoidNode; mNiNode.recType = Nif::RC_AvoidNode;
mNiTriStripsData.strips.front() = { 0, 1 }; mNiTriStripsData.mStrips.front() = { 0, 1 };
Nif::NIFFile file("test.nif"); Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiTriStrips); file.mRoots.push_back(&mNiTriStrips);
@ -1308,7 +1308,7 @@ 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.strips.front() = { 0, 1 }; mNiTriStripsData.mStrips.front() = { 0, 1 };
mNiTriStrips.parents.push_back(&mNiNode); mNiTriStrips.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriStrips) })); mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriStrips) }));
@ -1325,7 +1325,7 @@ 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.strips.front() = { 0, 1 }; mNiTriStripsData.mStrips.front() = { 0, 1 };
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) })); mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) }));

View file

@ -1,200 +1,149 @@
#include "data.hpp" #include "data.hpp"
#include <components/debug/debuglog.hpp>
#include "exception.hpp" #include "exception.hpp"
#include "nifkey.hpp" #include "nifkey.hpp"
#include "node.hpp" #include "node.hpp"
#include <components/debug/debuglog.hpp>
namespace Nif namespace Nif
{ {
void NiSkinInstance::read(NIFStream* nif)
{
data.read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 101))
partitions.read(nif);
root.read(nif);
readRecordList(nif, bones);
}
void NiSkinInstance::post(Reader& nif)
{
data.post(nif);
partitions.post(nif);
root.post(nif);
postRecordList(nif, bones);
if (data.empty() || root.empty())
throw Nif::Exception("NiSkinInstance missing root or data", nif.getFilename());
if (bones.size() != data->bones.size())
throw Nif::Exception("Mismatch in NiSkinData bone count", nif.getFilename());
for (auto& bone : bones)
{
if (bone.empty())
throw Nif::Exception("Oops: Missing bone! Don't know how to handle this.", nif.getFilename());
bone->setBone();
}
}
void BSDismemberSkinInstance::read(NIFStream* nif)
{
NiSkinInstance::read(nif);
unsigned int numPartitions = nif->getUInt();
nif->skip(4 * numPartitions); // Body part information
}
void NiGeometryData::read(NIFStream* nif) void NiGeometryData::read(NIFStream* nif)
{ {
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 114)) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 114))
nif->getInt(); // Group ID. (Almost?) always 0. nif->read(mGroupId);
int verts = nif->getUShort(); // Note: has special meaning for NiPSysData (unimplemented)
nif->read(mNumVertices);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
nif->skip(2); // Keep flags and compress flags
if (nif->getBoolean())
nif->readVector(vertices, verts);
unsigned int dataFlags = 0;
if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0))
dataFlags = nif->getUShort();
if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS
&& nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
nif->getUInt(); // Material CRC
if (nif->getBoolean())
{ {
nif->readVector(normals, verts); nif->read(mKeepFlags);
if (dataFlags & 0x1000) nif->read(mCompressFlags);
}
if (nif->get<bool>())
nif->readVector(mVertices, mNumVertices);
if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0))
{
nif->read(mDataFlags);
if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS
&& nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
nif->read(mMaterialHash);
}
if (nif->get<bool>())
{
nif->readVector(mNormals, mNumVertices);
if (mDataFlags & DataFlag_HasTangents)
{ {
nif->readVector(tangents, verts); nif->readVector(mTangents, mNumVertices);
nif->readVector(bitangents, verts); nif->readVector(mBitangents, mNumVertices);
} }
} }
center = nif->getVector3(); nif->read(mBoundingSphere);
radius = nif->getFloat();
if (nif->getBoolean()) if (nif->get<bool>())
nif->readVector(colors, verts); nif->readVector(mColors, mNumVertices);
unsigned int numUVs = dataFlags;
if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0)) if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0))
numUVs = nif->getUShort(); nif->read(mDataFlags);
// In Morrowind this field only corresponds to the number of UV sets. // In 4.0.0.2 the flags field corresponds to the number of UV sets.
// In later games only the first 6 bits are used as a count and the rest are flags. // In later revisions the part that corresponds to the number is narrower.
uint16_t numUVs = mDataFlags;
if (nif->getVersion() > NIFFile::NIFVersion::VER_MW) if (nif->getVersion() > NIFFile::NIFVersion::VER_MW)
{ {
numUVs &= 0x3f; numUVs &= DataFlag_NumUVsMask;
if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > 0) if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > 0)
numUVs &= 0x1; numUVs &= DataFlag_HasUV;
} }
else if (!nif->get<bool>())
numUVs = 0;
bool hasUVs = true; mUVList.resize(numUVs);
if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW) for (std::vector<osg::Vec2f>& list : mUVList)
hasUVs = nif->getBoolean();
if (hasUVs)
{ {
uvlist.resize(numUVs); nif->readVector(list, mNumVertices);
for (unsigned int i = 0; i < numUVs; i++) // flip the texture coordinates to convert them to the OpenGL convention of bottom-left image origin
{ for (osg::Vec2f& uv : list)
nif->readVector(uvlist[i], verts); uv.y() = 1.f - uv.y();
// flip the texture coordinates to convert them to the OpenGL convention of bottom-left image origin
for (unsigned int uv = 0; uv < uvlist[i].size(); ++uv)
{
uvlist[i][uv] = osg::Vec2f(uvlist[i][uv].x(), 1.f - uvlist[i][uv].y());
}
}
} }
if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0)) if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0))
nif->getUShort(); // Consistency flags {
nif->read(mConsistencyType);
if (nif->getVersion() >= NIFStream::generateVersion(20, 0, 0, 4)) if (nif->getVersion() >= NIFStream::generateVersion(20, 0, 0, 4))
nif->skip(4); // Additional data nif->skip(4); // Additional data
}
} }
void NiTriBasedGeomData::read(NIFStream* nif) void NiTriBasedGeomData::read(NIFStream* nif)
{ {
NiGeometryData::read(nif); NiGeometryData::read(nif);
mNumTriangles = nif->getUShort();
nif->read(mNumTriangles);
} }
void NiTriShapeData::read(NIFStream* nif) void NiTriShapeData::read(NIFStream* nif)
{ {
NiTriBasedGeomData::read(nif); NiTriBasedGeomData::read(nif);
// We have three times as many vertices as triangles, so this uint32_t numIndices;
// is always equal to mNumTriangles * 3. nif->read(numIndices);
int cnt = nif->getInt(); if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD && !nif->get<bool>())
bool hasTriangles = true; numIndices = 0;
if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD) nif->readVector(mTriangles, numIndices);
hasTriangles = nif->getBoolean(); mMatchGroups.resize(nif->get<uint16_t>());
if (hasTriangles) for (auto& group : mMatchGroups)
nif->readVector(triangles, cnt); nif->readVector(group, nif->get<uint16_t>());
// Read the match list, which lists the vertices that are equal to
// vertices. We don't actually need need this for anything, so
// just skip it.
unsigned short verts = nif->getUShort();
for (unsigned short i = 0; i < verts; i++)
{
// Number of vertices matching vertex 'i'
int num = nif->getUShort();
nif->skip(num * sizeof(short));
}
} }
void NiTriStripsData::read(NIFStream* nif) void NiTriStripsData::read(NIFStream* nif)
{ {
NiTriBasedGeomData::read(nif); NiTriBasedGeomData::read(nif);
// Number of triangle strips uint16_t numStrips;
int numStrips = nif->getUShort(); nif->read(numStrips);
std::vector<uint16_t> lengths;
std::vector<unsigned short> lengths;
nif->readVector(lengths, numStrips); nif->readVector(lengths, numStrips);
if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD && !nif->get<bool>())
// "Has Strips" flag. Exceptionally useful. numStrips = 0;
bool hasStrips = true; mStrips.resize(numStrips);
if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD)
hasStrips = nif->getBoolean();
if (!hasStrips || !numStrips)
return;
strips.resize(numStrips);
for (int i = 0; i < numStrips; i++) for (int i = 0; i < numStrips; i++)
nif->readVector(strips[i], lengths[i]); nif->readVector(mStrips[i], lengths[i]);
} }
void NiLinesData::read(NIFStream* nif) void NiLinesData::read(NIFStream* nif)
{ {
NiGeometryData::read(nif); NiGeometryData::read(nif);
size_t num = vertices.size();
std::vector<uint8_t> flags; std::vector<uint8_t> flags;
nif->readVector(flags, num); nif->readVector(flags, mNumVertices);
// Can't construct a line from a single vertex. // Can't construct a line from a single vertex.
if (num < 2) if (mNumVertices < 2)
return; return;
// There can't be more than 2 indices for each vertex
mLines.reserve(mNumVertices * 2);
// Convert connectivity flags into usable geometry. The last element needs special handling. // Convert connectivity flags into usable geometry. The last element needs special handling.
for (size_t i = 0; i < num - 1; ++i) for (uint16_t i = 0; i < mNumVertices - 1; ++i)
{ {
if (flags[i] & 1) if (flags[i] & 1)
{ {
lines.emplace_back(i); mLines.emplace_back(i);
lines.emplace_back(i + 1); mLines.emplace_back(i + 1);
} }
} }
// If there are just two vertices, they can be connected twice. Probably isn't critical. // If there are just two vertices, they can be connected twice. Probably isn't critical.
if (flags[num - 1] & 1) if (flags[mNumVertices - 1] & 1)
{ {
lines.emplace_back(num - 1); mLines.emplace_back(mNumVertices - 1);
lines.emplace_back(0); mLines.emplace_back(0);
} }
mLines.shrink_to_fit();
} }
void NiParticlesData::read(NIFStream* nif) void NiParticlesData::read(NIFStream* nif)
@ -203,26 +152,47 @@ namespace Nif
// Should always match the number of vertices // Should always match the number of vertices
if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW) if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW)
numParticles = nif->getUShort(); nif->read(mNumParticles);
else if (nif->getVersion() != NIFFile::NIFVersion::VER_BGS || nif->getBethVersion() == 0)
mNumParticles = mNumVertices;
if (nif->getVersion() <= NIFStream::generateVersion(10, 0, 1, 0)) bool numRadii = 1;
std::fill(particleRadii.begin(), particleRadii.end(), nif->getFloat()); if (nif->getVersion() > NIFStream::generateVersion(10, 0, 1, 0))
else if (nif->getBoolean()) numRadii = nif->get<bool>() ? mNumParticles : 0;
nif->readVector(particleRadii, vertices.size()); nif->readVector(mRadii, numRadii);
activeCount = nif->getUShort(); nif->read(mActiveCount);
if (nif->get<bool>())
nif->readVector(mSizes, mNumParticles);
// Particle sizes if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0))
if (nif->getBoolean())
nif->readVector(sizes, vertices.size());
if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0) && nif->getBoolean())
nif->readVector(rotations, vertices.size());
if (nif->getVersion() >= NIFStream::generateVersion(20, 0, 0, 4))
{ {
if (nif->getBoolean()) if (nif->get<bool>())
nif->readVector(rotationAngles, vertices.size()); nif->readVector(mRotations, mNumParticles);
if (nif->getBoolean()) if (nif->getVersion() >= NIFStream::generateVersion(20, 0, 0, 4))
nif->readVector(rotationAxes, vertices.size()); {
if (nif->get<bool>())
nif->readVector(mRotationAngles, mNumParticles);
if (nif->get<bool>())
nif->readVector(mRotationAxes, mNumParticles);
if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > 0)
{
nif->read(mHasTextureIndices);
uint32_t numSubtextureOffsets;
if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
numSubtextureOffsets = nif->get<uint8_t>();
else
nif->read(numSubtextureOffsets);
nif->readVector(mSubtextureOffsets, numSubtextureOffsets);
if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
{
nif->read(mAspectRatio);
nif->read(mAspectFlags);
nif->read(mAspectRatio2);
nif->read(mAspectSpeed);
nif->read(mAspectSpeed2);
}
}
}
} }
} }
@ -230,8 +200,8 @@ namespace Nif
{ {
NiParticlesData::read(nif); NiParticlesData::read(nif);
if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0) && nif->getBoolean()) if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0) && nif->get<bool>())
nif->readVector(rotations, vertices.size()); nif->readVector(mRotations, mNumParticles);
} }
void NiPosData::read(NIFStream* nif) void NiPosData::read(NIFStream* nif)
@ -242,10 +212,10 @@ namespace Nif
void NiUVData::read(NIFStream* nif) void NiUVData::read(NIFStream* nif)
{ {
for (int i = 0; i < 4; i++) for (FloatKeyMapPtr& keys : mKeyList)
{ {
mKeyList[i] = std::make_shared<FloatKeyMap>(); keys = std::make_shared<FloatKeyMap>();
mKeyList[i]->read(nif); keys->read(nif);
} }
} }
@ -255,58 +225,61 @@ namespace Nif
mKeyList->read(nif); mKeyList->read(nif);
} }
void NiPixelFormat::read(NIFStream* nif)
{
mFormat = static_cast<Format>(nif->get<uint32_t>());
if (nif->getVersion() <= NIFStream::generateVersion(10, 4, 0, 1))
{
nif->readArray(mColorMasks);
nif->read(mBitsPerPixel);
nif->readArray(mCompareBits);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
nif->read(mPixelTiling);
}
else
{
mBitsPerPixel = nif->get<uint8_t>();
nif->read(mRendererHint);
nif->read(mExtraData);
nif->read(mFlags);
nif->read(mPixelTiling);
if (nif->getVersion() >= NIFStream::generateVersion(20, 3, 0, 4))
nif->read(mUseSrgb);
for (int i = 0; i < 4; i++)
mChannels[i].read(nif);
}
}
void NiPixelFormat::ChannelData::read(NIFStream* nif)
{
mType = static_cast<Type>(nif->get<uint32_t>());
mConvention = static_cast<Convention>(nif->get<uint32_t>());
nif->read(mBitsPerChannel);
nif->read(mSigned);
}
void NiPixelData::read(NIFStream* nif) void NiPixelData::read(NIFStream* nif)
{ {
fmt = (Format)nif->getUInt(); mPixelFormat.read(nif);
mPalette.read(nif);
if (nif->getVersion() < NIFStream::generateVersion(10, 4, 0, 2)) mMipmaps.resize(nif->get<uint32_t>());
nif->read(mBytesPerPixel);
for (Mipmap& mip : mMipmaps)
{ {
for (unsigned int i = 0; i < 4; ++i) nif->read(mip.mWidth);
colorMask[i] = nif->getUInt(); nif->read(mip.mHeight);
bpp = nif->getUInt(); nif->read(mip.mOffset);
nif->skip(8); // "Old Fast Compare". Whatever that means.
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
pixelTiling = nif->getUInt();
} }
else // TODO: see if anything from here needs to be implemented uint32_t numPixels;
{ nif->read(numPixels);
bpp = nif->getChar(); if (nif->getVersion() >= NIFStream::generateVersion(10, 4, 0, 2))
nif->skip(4); // Renderer hint nif->read(mNumFaces);
nif->skip(4); // Extra data nif->readVector(mData, numPixels * mNumFaces);
nif->skip(4); // Flags
pixelTiling = nif->getUInt();
if (nif->getVersion() >= NIFStream::generateVersion(20, 3, 0, 4))
sRGB = nif->getBoolean();
nif->skip(4 * 10); // Channel data
}
palette.read(nif);
numberOfMipmaps = nif->getUInt();
// Bytes per pixel, should be bpp / 8
/* int bytes = */ nif->getUInt();
for (unsigned int i = 0; i < numberOfMipmaps; i++)
{
// Image size and offset in the following data field
Mipmap m;
m.width = nif->getUInt();
m.height = nif->getUInt();
m.dataOffset = nif->getUInt();
mipmaps.push_back(m);
}
// Read the data
unsigned int numPixels = nif->getUInt();
bool hasFaces = nif->getVersion() >= NIFStream::generateVersion(10, 4, 0, 2);
unsigned int numFaces = hasFaces ? nif->getUInt() : 1;
nif->readVector(data, numPixels * numFaces);
} }
void NiPixelData::post(Reader& nif) void NiPixelData::post(Reader& nif)
{ {
palette.post(nif); mPalette.post(nif);
} }
void NiColorData::read(NIFStream* nif) void NiColorData::read(NIFStream* nif)
@ -317,62 +290,110 @@ namespace Nif
void NiVisData::read(NIFStream* nif) void NiVisData::read(NIFStream* nif)
{ {
int count = nif->getInt(); mKeys = std::make_shared<std::map<float, bool>>();
mVis.resize(count); uint32_t numKeys;
for (size_t i = 0; i < mVis.size(); i++) nif->read(numKeys);
for (size_t i = 0; i < numKeys; i++)
{ {
mVis[i].time = nif->getFloat(); float time;
mVis[i].isSet = (nif->getChar() != 0); char value;
nif->read(time);
nif->read(value);
(*mKeys)[time] = (value != 0);
}
}
void NiSkinInstance::read(NIFStream* nif)
{
mData.read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 101))
mPartitions.read(nif);
mRoot.read(nif);
readRecordList(nif, mBones);
}
void NiSkinInstance::post(Reader& nif)
{
mData.post(nif);
mPartitions.post(nif);
mRoot.post(nif);
postRecordList(nif, mBones);
if (mData.empty() || mRoot.empty())
throw Nif::Exception("NiSkinInstance missing root or data", nif.getFilename());
if (mBones.size() != mData->mBones.size())
throw Nif::Exception("Mismatch in NiSkinData bone count", nif.getFilename());
for (auto& bone : mBones)
{
if (bone.empty())
throw Nif::Exception("Oops: Missing bone! Don't know how to handle this.", nif.getFilename());
bone->setBone();
}
}
void BSDismemberSkinInstance::read(NIFStream* nif)
{
NiSkinInstance::read(nif);
mParts.resize(nif->get<uint32_t>());
for (BodyPart& part : mParts)
{
nif->read(part.mFlags);
nif->read(part.mType);
} }
} }
void NiSkinData::read(NIFStream* nif) void NiSkinData::read(NIFStream* nif)
{ {
trafo.rotation = nif->getMatrix3(); nif->read(mTransform.rotation);
trafo.pos = nif->getVector3(); nif->read(mTransform.pos);
trafo.scale = nif->getFloat(); nif->read(mTransform.scale);
int boneNum = nif->getInt();
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW
&& nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 0))
partitions.read(nif);
uint32_t numBones;
nif->read(numBones);
bool hasVertexWeights = true; bool hasVertexWeights = true;
if (nif->getVersion() > NIFStream::generateVersion(4, 2, 1, 0)) if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW)
hasVertexWeights = nif->getBoolean();
bones.resize(boneNum);
for (BoneInfo& bi : bones)
{ {
bi.trafo.rotation = nif->getMatrix3(); if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 0))
bi.trafo.pos = nif->getVector3(); mPartitions.read(nif);
bi.trafo.scale = nif->getFloat();
bi.boundSphereCenter = nif->getVector3();
bi.boundSphereRadius = nif->getFloat();
size_t numVertices = nif->getUShort(); if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 1, 0))
nif->read(hasVertexWeights);
}
mBones.resize(numBones);
for (BoneInfo& bi : mBones)
{
nif->read(bi.mTransform.rotation);
nif->read(bi.mTransform.pos);
nif->read(bi.mTransform.scale);
nif->read(bi.mBoundSphere);
uint16_t numVertices;
nif->read(numVertices);
if (!hasVertexWeights) if (!hasVertexWeights)
continue; continue;
bi.weights.resize(numVertices); bi.mWeights.resize(numVertices);
for (size_t j = 0; j < bi.weights.size(); j++) for (auto& [vertex, weight] : bi.mWeights)
{ {
bi.weights[j].vertex = nif->getUShort(); nif->read(vertex);
bi.weights[j].weight = nif->getFloat(); nif->read(weight);
} }
} }
} }
void NiSkinData::post(Reader& nif) void NiSkinData::post(Reader& nif)
{ {
partitions.post(nif); mPartitions.post(nif);
} }
void NiSkinPartition::read(NIFStream* nif) void NiSkinPartition::read(NIFStream* nif)
{ {
nif->read(mPartitionNum); mPartitions.resize(nif->get<uint32_t>());
mPartitions.resize(mPartitionNum);
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE) if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE)
{ {
@ -391,103 +412,76 @@ namespace Nif
void NiSkinPartition::Partition::read(NIFStream* nif) void NiSkinPartition::Partition::read(NIFStream* nif)
{ {
size_t numVertices = nif->getUShort(); uint16_t numVertices, numTriangles, numBones, numStrips, bonesPerVertex;
size_t numTriangles = nif->getUShort(); nif->read(numVertices);
size_t numBones = nif->getUShort(); nif->read(numTriangles);
size_t numStrips = nif->getUShort(); nif->read(numBones);
size_t bonesPerVertex = nif->getUShort(); nif->read(numStrips);
nif->readVector(bones, numBones); nif->read(bonesPerVertex);
nif->readVector(mBones, numBones);
bool hasVertexMap = true; bool hasPresenceFlags = nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) if (!hasPresenceFlags || nif->get<bool>())
hasVertexMap = nif->getBoolean(); nif->readVector(mVertexMap, numVertices);
if (hasVertexMap) if (!hasPresenceFlags || nif->get<bool>())
nif->readVector(vertexMap, numVertices); nif->readVector(mWeights, numVertices * bonesPerVertex);
bool hasVertexWeights = true;
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
hasVertexWeights = nif->getBoolean();
if (hasVertexWeights)
nif->readVector(weights, numVertices * bonesPerVertex);
std::vector<unsigned short> stripLengths; std::vector<unsigned short> stripLengths;
nif->readVector(stripLengths, numStrips); nif->readVector(stripLengths, numStrips);
if (!hasPresenceFlags || nif->get<bool>())
bool hasFaces = true;
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
hasFaces = nif->getBoolean();
if (hasFaces)
{ {
if (numStrips) if (numStrips)
{ {
strips.resize(numStrips); mStrips.resize(numStrips);
for (size_t i = 0; i < numStrips; i++) for (size_t i = 0; i < numStrips; i++)
nif->readVector(strips[i], stripLengths[i]); nif->readVector(mStrips[i], stripLengths[i]);
} }
else else
nif->readVector(triangles, numTriangles * 3); nif->readVector(mTriangles, numTriangles * 3);
} }
bool hasBoneIndices = nif->getChar() != 0; if (nif->get<uint8_t>() != 0)
if (hasBoneIndices) nif->readVector(mBoneIndices, numVertices * bonesPerVertex);
nif->readVector(boneIndices, numVertices * bonesPerVertex);
if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3) if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
{ {
nif->getChar(); // LOD level nif->read(mLODLevel);
nif->getBoolean(); // Global VB nif->read(mGlobalVB);
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE)
{
mVertexDesc.read(nif);
nif->readVector(mTrueTriangles, numTriangles * 3);
}
} }
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE) // Not technically a part of the loading process
if (mTrueTriangles.empty() && !mVertexMap.empty())
{ {
mVertexDesc.read(nif); if (!mStrips.empty())
nif->readVector(trueTriangles, numTriangles * 3); {
mTrueStrips = mStrips;
for (auto& strip : mTrueStrips)
for (auto& index : strip)
index = mVertexMap[index];
}
else if (!mTriangles.empty())
{
mTrueTriangles = mTriangles;
for (unsigned short& index : mTrueTriangles)
index = mVertexMap[index];
}
} }
} }
std::vector<unsigned short> NiSkinPartition::Partition::getTrueTriangles() const
{
if (!trueTriangles.empty())
return trueTriangles;
std::vector<unsigned short> remappedTriangles;
if (vertexMap.empty() || triangles.empty())
return remappedTriangles;
remappedTriangles = triangles;
for (unsigned short& index : remappedTriangles)
index = vertexMap[index];
return remappedTriangles;
}
std::vector<std::vector<unsigned short>> NiSkinPartition::Partition::getTrueStrips() const
{
if (!trueTriangles.empty())
return {};
std::vector<std::vector<unsigned short>> remappedStrips;
if (vertexMap.empty() || strips.empty())
return remappedStrips;
remappedStrips = strips;
for (auto& strip : remappedStrips)
for (auto& index : strip)
index = vertexMap[index];
return remappedStrips;
}
void NiMorphData::read(NIFStream* nif) void NiMorphData::read(NIFStream* nif)
{ {
int morphCount = nif->getInt(); uint32_t numMorphs, numVerts;
int vertCount = nif->getInt(); nif->read(numMorphs);
nif->getChar(); // Relative targets, always 1 nif->read(numVerts);
nif->read(mRelativeTargets);
mMorphs.resize(morphCount); mMorphs.resize(numMorphs);
for (int i = 0; i < morphCount; i++) for (MorphData& morph : mMorphs)
{ {
mMorphs[i].mKeyFrames = std::make_shared<FloatKeyMap>(); morph.mKeyFrames = std::make_shared<FloatKeyMap>();
mMorphs[i].mKeyFrames->read(nif, /*morph*/ true); morph.mKeyFrames->read(nif, /*morph*/ true);
nif->readVector(mMorphs[i].mVertices, vertCount); nif->readVector(morph.mVertices, numVerts);
} }
} }
@ -498,7 +492,7 @@ namespace Nif
if (mRotations->mInterpolationType == InterpolationType_XYZ) if (mRotations->mInterpolationType == InterpolationType_XYZ)
{ {
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 0)) if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 0))
mAxisOrder = static_cast<AxisOrder>(nif->getInt()); mAxisOrder = static_cast<AxisOrder>(nif->get<uint32_t>());
mXRotations = std::make_shared<FloatKeyMap>(); mXRotations = std::make_shared<FloatKeyMap>();
mYRotations = std::make_shared<FloatKeyMap>(); mYRotations = std::make_shared<FloatKeyMap>();
mZRotations = std::make_shared<FloatKeyMap>(); mZRotations = std::make_shared<FloatKeyMap>();
@ -514,18 +508,24 @@ namespace Nif
void NiPalette::read(NIFStream* nif) void NiPalette::read(NIFStream* nif)
{ {
unsigned int alphaMask = !nif->getChar() ? 0xFF000000 : 0; bool useAlpha = nif->get<uint8_t>() != 0;
uint32_t alphaMask = useAlpha ? 0 : 0xFF000000;
uint32_t numEntries;
nif->read(numEntries);
// Fill the entire palette with black even if there isn't enough entries. // Fill the entire palette with black even if there isn't enough entries.
colors.resize(256); mColors.resize(numEntries > 256 ? numEntries : 256);
unsigned int numEntries = nif->getUInt(); for (uint32_t i = 0; i < numEntries; i++)
for (unsigned int i = 0; i < numEntries; i++) {
colors[i] = nif->getUInt() | alphaMask; nif->read(mColors[i]);
mColors[i] |= alphaMask;
}
} }
void NiStringPalette::read(NIFStream* nif) void NiStringPalette::read(NIFStream* nif)
{ {
palette = nif->getStringPalette(); mPalette = nif->getStringPalette();
if (nif->getUInt() != palette.size()) if (nif->get<uint32_t>() != mPalette.size())
Log(Debug::Warning) << "NIFFile Warning: Failed size check in NiStringPalette. File: " Log(Debug::Warning) << "NIFFile Warning: Failed size check in NiStringPalette. File: "
<< nif->getFile().getFilename(); << nif->getFile().getFilename();
} }
@ -548,15 +548,14 @@ namespace Nif
void BSMultiBoundOBB::read(NIFStream* nif) void BSMultiBoundOBB::read(NIFStream* nif)
{ {
mCenter = nif->getVector3(); nif->read(mCenter);
mSize = nif->getVector3(); nif->read(mSize);
mRotation = nif->getMatrix3(); nif->read(mRotation);
} }
void BSMultiBoundSphere::read(NIFStream* nif) void BSMultiBoundSphere::read(NIFStream* nif)
{ {
mCenter = nif->getVector3(); nif->read(mSphere);
mRadius = nif->getFloat();
} }
} // Namespace } // Namespace

View file

@ -35,11 +35,26 @@ namespace Nif
// Common ancestor for several data classes // Common ancestor for several data classes
struct NiGeometryData : public Record struct NiGeometryData : public Record
{ {
std::vector<osg::Vec3f> vertices, normals, tangents, bitangents; // Interpretation of Flags field differs depending on the version
std::vector<osg::Vec4f> colors; enum DataFlags
std::vector<std::vector<osg::Vec2f>> uvlist; {
osg::Vec3f center; DataFlag_HasUV = 0x0001,
float radius; DataFlag_NumUVsMask = 0x003F,
DataFlag_HasTangents = 0x1000,
};
int32_t mGroupId{ 0 };
uint16_t mNumVertices;
uint8_t mKeepFlags{ 0 };
uint8_t mCompressFlags{ 0 };
std::vector<osg::Vec3f> mVertices;
uint16_t mDataFlags{ 0 };
uint32_t mMaterialHash;
std::vector<osg::Vec3f> mNormals, mTangents, mBitangents;
osg::BoundingSpheref mBoundingSphere;
std::vector<osg::Vec4f> mColors;
std::vector<std::vector<osg::Vec2f>> mUVList;
uint16_t mConsistencyType;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -47,7 +62,7 @@ namespace Nif
// Abstract // Abstract
struct NiTriBasedGeomData : public NiGeometryData struct NiTriBasedGeomData : public NiGeometryData
{ {
size_t mNumTriangles; uint16_t mNumTriangles;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -55,7 +70,8 @@ namespace Nif
struct NiTriShapeData : public NiTriBasedGeomData struct NiTriShapeData : public NiTriBasedGeomData
{ {
// Triangles, three vertex indices per triangle // Triangles, three vertex indices per triangle
std::vector<unsigned short> triangles; std::vector<unsigned short> mTriangles;
std::vector<std::vector<unsigned short>> mMatchGroups;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -63,7 +79,7 @@ namespace Nif
struct NiTriStripsData : public NiTriBasedGeomData struct NiTriStripsData : public NiTriBasedGeomData
{ {
// Triangle strips, series of vertex indices. // Triangle strips, series of vertex indices.
std::vector<std::vector<unsigned short>> strips; std::vector<std::vector<unsigned short>> mStrips;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -71,20 +87,29 @@ namespace Nif
struct NiLinesData : public NiGeometryData struct NiLinesData : public NiGeometryData
{ {
// Lines, series of indices that correspond to connected vertices. // Lines, series of indices that correspond to connected vertices.
std::vector<unsigned short> lines; // NB: assumes <=65536 number of vertices
std::vector<uint16_t> mLines;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct NiParticlesData : public NiGeometryData struct NiParticlesData : public NiGeometryData
{ {
int numParticles{ 0 }; uint16_t mNumParticles{ 0 };
uint16_t mActiveCount;
int activeCount{ 0 }; std::vector<float> mRadii;
std::vector<float> mSizes;
std::vector<osg::Quat> mRotations;
std::vector<float> mRotationAngles;
std::vector<osg::Vec3f> mRotationAxes;
std::vector<float> particleRadii, sizes, rotationAngles; bool mHasTextureIndices{ false };
std::vector<osg::Quat> rotations; std::vector<osg::Vec4f> mSubtextureOffsets;
std::vector<osg::Vec3f> rotationAxes; float mAspectRatio{ 1.f };
uint16_t mAspectFlags{ 0 };
float mAspectRatio2;
float mAspectSpeed, mAspectSpeed2;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -103,7 +128,7 @@ namespace Nif
struct NiUVData : public Record struct NiUVData : public Record
{ {
FloatKeyMapPtr mKeyList[4]; std::array<FloatKeyMapPtr, 4> mKeyList;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -115,37 +140,94 @@ namespace Nif
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct NiPixelFormat
{
enum class Format : uint32_t
{
RGB = 0,
RGBA = 1,
Palette = 2,
PaletteAlpha = 3,
BGR = 4,
BGRA = 5,
DXT1 = 6,
DXT3 = 7,
DXT5 = 8,
};
struct ChannelData
{
enum class Type : uint32_t
{
Red = 0,
Green = 1,
Blue = 2,
Alpha = 3,
Compressed = 4,
OffsetU = 5,
OffsetV = 6,
OffsetW = 7,
OffsetQ = 8,
Luma = 9,
Height = 10,
VectorX = 11,
VectorY = 12,
VectorZ = 13,
Padding = 14,
Intensity = 15,
Index = 16,
Depth = 17,
Stencil = 18,
Empty = 19,
};
enum class Convention : uint32_t
{
NormInt = 0,
Half = 1,
Float = 2,
Index = 3,
Compressed = 4,
Unknown = 5,
Int = 6,
};
Type mType;
Convention mConvention;
uint8_t mBitsPerChannel;
bool mSigned;
void read(NIFStream* nif);
};
Format mFormat{ Format::RGB };
std::array<uint32_t, 4> mColorMasks;
uint32_t mBitsPerPixel{ 0 };
uint32_t mPixelTiling{ 0 };
std::array<uint32_t, 2> mCompareBits;
uint32_t mRendererHint{ 0 };
uint32_t mExtraData{ 0 };
uint8_t mFlags{ 0 };
bool mUseSrgb{ false };
std::array<ChannelData, 4> mChannels;
void read(NIFStream* nif);
};
struct NiPixelData : public Record struct NiPixelData : public Record
{ {
enum Format
{
NIPXFMT_RGB8,
NIPXFMT_RGBA8,
NIPXFMT_PAL8,
NIPXFMT_PALA8,
NIPXFMT_BGR8,
NIPXFMT_BGRA8,
NIPXFMT_DXT1,
NIPXFMT_DXT3,
NIPXFMT_DXT5
};
Format fmt{ NIPXFMT_RGB8 };
unsigned int colorMask[4]{ 0 };
unsigned int bpp{ 0 }, pixelTiling{ 0 };
bool sRGB{ false };
NiPalettePtr palette;
unsigned int numberOfMipmaps{ 0 };
struct Mipmap struct Mipmap
{ {
int width, height; uint32_t mWidth, mHeight;
int dataOffset; uint32_t mOffset;
}; };
std::vector<Mipmap> mipmaps;
std::vector<unsigned char> data; NiPixelFormat mPixelFormat;
NiPalettePtr mPalette;
uint32_t mBytesPerPixel;
std::vector<Mipmap> mMipmaps;
uint32_t mNumFaces{ 1 };
std::vector<uint8_t> mData;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
@ -160,22 +242,18 @@ namespace Nif
struct NiVisData : public Record struct NiVisData : public Record
{ {
struct VisData // TODO: investigate possible use of ByteKeyMap
{ std::shared_ptr<std::map<float, bool>> mKeys;
float time;
bool isSet;
};
std::vector<VisData> mVis;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct NiSkinInstance : public Record struct NiSkinInstance : public Record
{ {
NiSkinDataPtr data; NiSkinDataPtr mData;
NiSkinPartitionPtr partitions; NiSkinPartitionPtr mPartitions;
NodePtr root; NodePtr mRoot;
NodeList bones; NodeList mBones;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
@ -183,28 +261,31 @@ namespace Nif
struct BSDismemberSkinInstance : public NiSkinInstance struct BSDismemberSkinInstance : public NiSkinInstance
{ {
struct BodyPart
{
uint16_t mFlags;
uint16_t mType;
};
std::vector<BodyPart> mParts;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct NiSkinData : public Record struct NiSkinData : public Record
{ {
struct VertWeight using VertWeight = std::pair<unsigned short, float>;
{
unsigned short vertex;
float weight;
};
struct BoneInfo struct BoneInfo
{ {
Transformation trafo; Transformation mTransform;
osg::Vec3f boundSphereCenter; osg::BoundingSpheref mBoundSphere;
float boundSphereRadius; std::vector<VertWeight> mWeights;
std::vector<VertWeight> weights;
}; };
Transformation trafo; Transformation mTransform;
std::vector<BoneInfo> bones; std::vector<BoneInfo> mBones;
NiSkinPartitionPtr partitions; NiSkinPartitionPtr mPartitions;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
@ -214,20 +295,20 @@ namespace Nif
{ {
struct Partition struct Partition
{ {
std::vector<unsigned short> bones; std::vector<unsigned short> mBones;
std::vector<unsigned short> vertexMap; std::vector<unsigned short> mVertexMap;
std::vector<float> weights; std::vector<float> mWeights;
std::vector<std::vector<unsigned short>> strips; std::vector<std::vector<unsigned short>> mStrips;
std::vector<unsigned short> triangles; std::vector<unsigned short> mTriangles;
std::vector<unsigned short> trueTriangles; std::vector<char> mBoneIndices;
std::vector<char> boneIndices;
BSVertexDesc mVertexDesc; BSVertexDesc mVertexDesc;
std::vector<unsigned short> mTrueTriangles;
std::vector<std::vector<unsigned short>> mTrueStrips;
uint8_t mLODLevel;
bool mGlobalVB;
void read(NIFStream* nif); void read(NIFStream* nif);
std::vector<unsigned short> getTrueTriangles() const;
std::vector<std::vector<unsigned short>> getTrueStrips() const;
}; };
unsigned int mPartitionNum;
std::vector<Partition> mPartitions; std::vector<Partition> mPartitions;
unsigned int mDataSize; unsigned int mDataSize;
@ -245,6 +326,8 @@ namespace Nif
FloatKeyMapPtr mKeyFrames; FloatKeyMapPtr mKeyFrames;
std::vector<osg::Vec3f> mVertices; std::vector<osg::Vec3f> mVertices;
}; };
uint8_t mRelativeTargets;
std::vector<MorphData> mMorphs; std::vector<MorphData> mMorphs;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
@ -262,7 +345,7 @@ namespace Nif
Vector3KeyMapPtr mTranslations; Vector3KeyMapPtr mTranslations;
FloatKeyMapPtr mScales; FloatKeyMapPtr mScales;
enum class AxisOrder enum class AxisOrder : uint32_t
{ {
Order_XYZ = 0, Order_XYZ = 0,
Order_XZY = 1, Order_XZY = 1,
@ -283,14 +366,15 @@ namespace Nif
struct NiPalette : public Record struct NiPalette : public Record
{ {
// 32-bit RGBA colors that correspond to 8-bit indices // 32-bit RGBA colors that correspond to 8-bit indices
std::vector<unsigned int> colors; std::vector<uint32_t> mColors;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct NiStringPalette : public Record struct NiStringPalette : public Record
{ {
std::string palette; std::string mPalette;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -324,8 +408,7 @@ namespace Nif
struct BSMultiBoundSphere : public BSMultiBoundData struct BSMultiBoundSphere : public BSMultiBoundData
{ {
osg::Vec3f mCenter; osg::BoundingSpheref mSphere;
float mRadius;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };

View file

@ -49,7 +49,9 @@ namespace Nif
using ValueType = T; using ValueType = T;
using KeyType = KeyT<T>; using KeyType = KeyT<T>;
unsigned int mInterpolationType = InterpolationType_Unknown; std::string mFrameName;
float mLegacyWeight;
uint32_t mInterpolationType = InterpolationType_Unknown;
MapType mKeys; MapType mKeys;
// Read in a KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html) // Read in a KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html)
@ -57,21 +59,25 @@ namespace Nif
{ {
assert(nif); assert(nif);
if (morph && nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 106)) if (morph)
nif->getString(); // Frame name
if (morph && nif->getVersion() > NIFStream::generateVersion(10, 1, 0, 0))
{ {
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 104) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 106))
&& nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 2) && nif->getBethVersion() < 10) nif->read(mFrameName);
nif->getFloat(); // Legacy weight
return; if (nif->getVersion() > NIFStream::generateVersion(10, 1, 0, 0))
{
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 104)
&& nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 2) && nif->getBethVersion() < 10)
nif->read(mLegacyWeight);
return;
}
} }
size_t count = nif->getUInt(); uint32_t count;
nif->read(count);
if (count != 0 || morph) if (count != 0 || morph)
mInterpolationType = nif->getUInt(); nif->read(mInterpolationType);
KeyType key = {}; KeyType key = {};
@ -79,7 +85,8 @@ namespace Nif
{ {
for (size_t i = 0; i < count; i++) for (size_t i = 0; i < count; i++)
{ {
float time = nif->getFloat(); float time;
nif->read(time);
readValue(*nif, key); readValue(*nif, key);
mKeys[time] = key; mKeys[time] = key;
} }
@ -88,7 +95,8 @@ namespace Nif
{ {
for (size_t i = 0; i < count; i++) for (size_t i = 0; i < count; i++)
{ {
float time = nif->getFloat(); float time;
nif->read(time);
readQuadratic(*nif, key); readQuadratic(*nif, key);
mKeys[time] = key; mKeys[time] = key;
} }
@ -97,7 +105,8 @@ namespace Nif
{ {
for (size_t i = 0; i < count; i++) for (size_t i = 0; i < count; i++)
{ {
float time = nif->getFloat(); float time;
nif->read(time);
readTBC(*nif, key); readTBC(*nif, key);
mKeys[time] = key; mKeys[time] = key;
} }
@ -134,16 +143,16 @@ namespace Nif
static void readTBC(NIFStream& nif, KeyT<T>& key) static void readTBC(NIFStream& nif, KeyT<T>& key)
{ {
readValue(nif, key); readValue(nif, key);
/*key.mTension = */ nif.getFloat(); /*key.mTension = */ nif.get<float>();
/*key.mBias = */ nif.getFloat(); /*key.mBias = */ nif.get<float>();
/*key.mContinuity = */ nif.getFloat(); /*key.mContinuity = */ nif.get<float>();
} }
}; };
using FloatKeyMap = KeyMapT<float, &NIFStream::getFloat>; using FloatKeyMap = KeyMapT<float, &NIFStream::get<float>>;
using Vector3KeyMap = KeyMapT<osg::Vec3f, &NIFStream::getVector3>; using Vector3KeyMap = KeyMapT<osg::Vec3f, &NIFStream::get<osg::Vec3f>>;
using Vector4KeyMap = KeyMapT<osg::Vec4f, &NIFStream::getVector4>; using Vector4KeyMap = KeyMapT<osg::Vec4f, &NIFStream::get<osg::Vec4f>>;
using QuaternionKeyMap = KeyMapT<osg::Quat, &NIFStream::getQuaternion>; using QuaternionKeyMap = KeyMapT<osg::Quat, &NIFStream::get<osg::Quat>>;
using ByteKeyMap = KeyMapT<char, &NIFStream::getChar>; using ByteKeyMap = KeyMapT<char, &NIFStream::get<char>>;
using FloatKeyMapPtr = std::shared_ptr<FloatKeyMap>; using FloatKeyMapPtr = std::shared_ptr<FloatKeyMap>;
using Vector3KeyMapPtr = std::shared_ptr<Vector3KeyMap>; using Vector3KeyMapPtr = std::shared_ptr<Vector3KeyMap>;

View file

@ -122,6 +122,13 @@ namespace Nif
quat.z() = data[3]; quat.z() = data[3];
} }
template <>
void NIFStream::read<osg::BoundingSpheref>(osg::BoundingSpheref& sphere)
{
read(sphere.center());
read(sphere.radius());
}
template <> template <>
void NIFStream::read<Transformation>(Transformation& t) void NIFStream::read<Transformation>(Transformation& t)
{ {
@ -178,6 +185,12 @@ namespace Nif
readRange(*this, dest, size); readRange(*this, dest, size);
} }
template <>
void NIFStream::read<osg::BoundingSpheref>(osg::BoundingSpheref* dest, size_t size)
{
readRange(*this, dest, size);
}
template <> template <>
void NIFStream::read<Transformation>(Transformation* dest, size_t size) void NIFStream::read<Transformation>(Transformation* dest, size_t size)
{ {

View file

@ -15,6 +15,7 @@
#include <components/misc/endianness.hpp> #include <components/misc/endianness.hpp>
#include <components/misc/float16.hpp> #include <components/misc/float16.hpp>
#include <osg/BoundingSphere>
#include <osg/Quat> #include <osg/Quat>
#include <osg/Vec3f> #include <osg/Vec3f>
#include <osg/Vec4f> #include <osg/Vec4f>
@ -174,6 +175,8 @@ namespace Nif
template <> template <>
void NIFStream::read<osg::Quat>(osg::Quat& quat); void NIFStream::read<osg::Quat>(osg::Quat& quat);
template <> template <>
void NIFStream::read<osg::BoundingSpheref>(osg::BoundingSpheref& sphere);
template <>
void NIFStream::read<Transformation>(Transformation& t); void NIFStream::read<Transformation>(Transformation& t);
template <> template <>
void NIFStream::read<bool>(bool& data); void NIFStream::read<bool>(bool& data);
@ -191,6 +194,8 @@ namespace Nif
template <> template <>
void NIFStream::read<osg::Quat>(osg::Quat* dest, size_t size); void NIFStream::read<osg::Quat>(osg::Quat* dest, size_t size);
template <> template <>
void NIFStream::read<osg::BoundingSpheref>(osg::BoundingSpheref* dest, size_t size);
template <>
void NIFStream::read<Transformation>(Transformation* dest, size_t size); void NIFStream::read<Transformation>(Transformation* dest, size_t size);
template <> template <>
void NIFStream::read<bool>(bool* dest, size_t size); void NIFStream::read<bool>(bool* dest, size_t size);

View file

@ -19,7 +19,7 @@ namespace Nif
break; break;
case SPHERE_BV: case SPHERE_BV:
{ {
sphere.read(nif); nif->read(sphere);
break; break;
} }
case BOX_BV: case BOX_BV:
@ -336,7 +336,7 @@ namespace Nif
void BSTriShape::read(NIFStream* nif) void BSTriShape::read(NIFStream* nif)
{ {
Node::read(nif); Node::read(nif);
mBoundingSphere.read(nif); nif->read(mBoundingSphere);
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_F76) if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_F76)
{ {
@ -412,12 +412,6 @@ namespace Nif
mFlags = (data & 0xFFF00000000000) >> 0x2C; mFlags = (data & 0xFFF00000000000) >> 0x2C;
} }
void NiBoundingVolume::NiSphereBV::read(NIFStream* nif)
{
nif->read(center);
nif->read(radius);
}
void BSVertexData::read(NIFStream* nif, uint16_t flags) void BSVertexData::read(NIFStream* nif, uint16_t flags)
{ {
uint16_t vertexFlag = flags & BSVertexDesc::VertexAttribute::Vertex; uint16_t vertexFlag = flags & BSVertexDesc::VertexAttribute::Vertex;

View file

@ -26,13 +26,6 @@ namespace Nif
HALFSPACE_BV = 5 HALFSPACE_BV = 5
}; };
struct NiSphereBV
{
osg::Vec3f center;
float radius{ 0.f };
void read(NIFStream* nif);
};
struct NiBoxBV struct NiBoxBV
{ {
osg::Vec3f center; osg::Vec3f center;
@ -59,7 +52,7 @@ namespace Nif
}; };
unsigned int type; unsigned int type;
NiSphereBV sphere; osg::BoundingSpheref sphere;
NiBoxBV box; NiBoxBV box;
NiCapsuleBV capsule; NiCapsuleBV capsule;
NiLozengeBV lozenge; NiLozengeBV lozenge;
@ -355,7 +348,7 @@ namespace Nif
struct BSTriShape : Node struct BSTriShape : Node
{ {
NiBoundingVolume::NiSphereBV mBoundingSphere; osg::BoundingSpheref mBoundingSphere;
std::array<float, 6> mBoundMinMax; std::array<float, 6> mBoundMinMax;
NiSkinInstancePtr mSkin; NiSkinInstancePtr mSkin;

View file

@ -10,12 +10,12 @@ namespace Nif
NiTexture::read(nif); NiTexture::read(nif);
nif->read(mExternal); nif->read(mExternal);
if (mExternal || nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
nif->read(mFile);
bool hasData = nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 4); bool hasData = nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 4);
if (!hasData && !mExternal) if (!hasData && !mExternal)
nif->read(hasData); hasData = nif->get<uint8_t>() != 0;
if (mExternal || nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
nif->read(mFile);
if (hasData) if (hasData)
mData.read(nif); mData.read(nif);

View file

@ -35,18 +35,18 @@ namespace
void prepareTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriBasedGeomData& data) void prepareTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriBasedGeomData& data)
{ {
// FIXME: copying vertices/indices individually is unreasonable // FIXME: copying vertices/indices individually is unreasonable
const std::vector<osg::Vec3f>& vertices = data.vertices; const std::vector<osg::Vec3f>& vertices = data.mVertices;
mesh.preallocateVertices(static_cast<int>(vertices.size())); mesh.preallocateVertices(static_cast<int>(vertices.size()));
for (const osg::Vec3f& vertex : vertices) for (const osg::Vec3f& vertex : vertices)
mesh.findOrAddVertex(Misc::Convert::toBullet(vertex), false); mesh.findOrAddVertex(Misc::Convert::toBullet(vertex), false);
mesh.preallocateIndices(static_cast<int>(data.mNumTriangles * 3)); mesh.preallocateIndices(static_cast<int>(data.mNumTriangles) * 3);
} }
void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriShapeData& data) void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriShapeData& data)
{ {
prepareTriangleMesh(mesh, data); prepareTriangleMesh(mesh, data);
const std::vector<unsigned short>& triangles = data.triangles; const std::vector<unsigned short>& triangles = data.mTriangles;
for (std::size_t i = 0; i < triangles.size(); i += 3) for (std::size_t i = 0; i < triangles.size(); i += 3)
mesh.addTriangleIndices(triangles[i + 0], triangles[i + 1], triangles[i + 2]); mesh.addTriangleIndices(triangles[i + 0], triangles[i + 1], triangles[i + 2]);
} }
@ -54,7 +54,7 @@ namespace
void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data) void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data)
{ {
prepareTriangleMesh(mesh, data); prepareTriangleMesh(mesh, data);
for (const std::vector<unsigned short>& strip : data.strips) for (const std::vector<unsigned short>& strip : data.mStrips)
{ {
if (strip.size() < 3) if (strip.size() < 3)
continue; continue;
@ -87,7 +87,7 @@ namespace
return {}; return {};
auto data = static_cast<const Nif::NiTriShapeData*>(geometry.data.getPtr()); auto data = static_cast<const Nif::NiTriShapeData*>(geometry.data.getPtr());
if (data->triangles.empty()) if (data->mTriangles.empty())
return {}; return {};
return function(static_cast<const Nif::NiTriShapeData&>(*data)); return function(static_cast<const Nif::NiTriShapeData&>(*data));
@ -99,7 +99,7 @@ namespace
return {}; return {};
auto data = static_cast<const Nif::NiTriStripsData*>(geometry.data.getPtr()); auto data = static_cast<const Nif::NiTriStripsData*>(geometry.data.getPtr());
if (data->strips.empty()) if (data->mStrips.empty())
return {}; return {};
return function(static_cast<const Nif::NiTriStripsData&>(*data)); return function(static_cast<const Nif::NiTriStripsData&>(*data));
@ -381,7 +381,7 @@ namespace NifBullet
if (args.mHasMarkers && Misc::StringUtils::ciStartsWith(niGeometry.name, "EditorMarker")) if (args.mHasMarkers && Misc::StringUtils::ciStartsWith(niGeometry.name, "EditorMarker"))
return; return;
if (niGeometry.data.empty() || niGeometry.data->vertices.empty()) if (niGeometry.data.empty() || niGeometry.data->mVertices.empty())
return; return;
if (!niGeometry.skin.empty()) if (!niGeometry.skin.empty())

View file

@ -319,7 +319,7 @@ namespace NifOsg
= ByteInterpolator(static_cast<const Nif::NiBoolInterpolator*>(ctrl->mInterpolator.getPtr())); = ByteInterpolator(static_cast<const Nif::NiBoolInterpolator*>(ctrl->mInterpolator.getPtr()));
} }
else if (!ctrl->mData.empty()) else if (!ctrl->mData.empty())
mData = ctrl->mData->mVis; mData = ctrl->mData->mKeys;
} }
VisController::VisController() {} VisController::VisController() {}
@ -338,15 +338,13 @@ namespace NifOsg
if (!mInterpolator.empty()) if (!mInterpolator.empty())
return mInterpolator.interpKey(time); return mInterpolator.interpKey(time);
if (mData.size() == 0) if (mData->empty())
return true; return true;
for (size_t i = 1; i < mData.size(); i++) auto iter = mData->upper_bound(time);
{ if (iter != mData->begin())
if (mData[i].time > time) --iter;
return mData[i - 1].isSet; return iter->second;
}
return mData.back().isSet;
} }
void VisController::operator()(osg::Node* node, osg::NodeVisitor* nv) void VisController::operator()(osg::Node* node, osg::NodeVisitor* nv)

View file

@ -1,19 +1,18 @@
#ifndef COMPONENTS_NIFOSG_CONTROLLER_H #ifndef COMPONENTS_NIFOSG_CONTROLLER_H
#define COMPONENTS_NIFOSG_CONTROLLER_H #define COMPONENTS_NIFOSG_CONTROLLER_H
#include <components/nif/controller.hpp>
#include <components/nif/data.hpp>
#include <components/nif/nifkey.hpp>
#include <components/sceneutil/keyframe.hpp>
#include <components/sceneutil/nodecallback.hpp>
#include <components/sceneutil/statesetupdater.hpp>
#include <set> #include <set>
#include <type_traits> #include <type_traits>
#include <osg/Texture2D> #include <osg/Texture2D>
#include <components/nif/controller.hpp>
#include <components/nif/data.hpp>
#include <components/nif/nifkey.hpp>
#include <components/sceneutil/keyframe.hpp>
#include <components/sceneutil/nodecallback.hpp>
#include <components/sceneutil/statesetupdater.hpp>
namespace osg namespace osg
{ {
class Material; class Material;
@ -283,7 +282,7 @@ namespace NifOsg
class VisController : public SceneUtil::NodeCallback<VisController>, public SceneUtil::Controller class VisController : public SceneUtil::NodeCallback<VisController>, public SceneUtil::Controller
{ {
private: private:
std::vector<Nif::NiVisData::VisData> mData; std::shared_ptr<std::map<float, bool>> mData;
ByteInterpolator mInterpolator; ByteInterpolator mInterpolator;
unsigned int mMask{ 0u }; unsigned int mMask{ 0u };

View file

@ -1133,20 +1133,20 @@ namespace NifOsg
} }
auto particledata = static_cast<const Nif::NiParticlesData*>(particleNode->data.getPtr()); auto particledata = static_cast<const Nif::NiParticlesData*>(particleNode->data.getPtr());
partsys->setQuota(particledata->numParticles); partsys->setQuota(particledata->mNumParticles);
osg::BoundingBox box; osg::BoundingBox box;
int i = 0; int i = 0;
for (const auto& particle : partctrl->particles) for (const auto& particle : partctrl->particles)
{ {
if (i++ >= particledata->activeCount) if (i++ >= particledata->mActiveCount)
break; break;
if (particle.lifespan <= 0) if (particle.lifespan <= 0)
continue; continue;
if (particle.vertex >= particledata->vertices.size()) if (particle.vertex >= particledata->mVertices.size())
continue; continue;
ParticleAgeSetter particletemplate(std::max(0.f, particle.lifetime)); ParticleAgeSetter particletemplate(std::max(0.f, particle.lifetime));
@ -1158,22 +1158,22 @@ namespace NifOsg
// which can not be done in this loader since we are not attached to the scene yet. Will be fixed up // which can not be done in this loader since we are not attached to the scene yet. Will be fixed up
// post-load in the SceneManager. // post-load in the SceneManager.
created->setVelocity(particle.velocity); created->setVelocity(particle.velocity);
const osg::Vec3f& position = particledata->vertices[particle.vertex]; const osg::Vec3f& position = particledata->mVertices[particle.vertex];
created->setPosition(position); created->setPosition(position);
created->setColorRange(osgParticle::rangev4(partctrl->color, partctrl->color)); created->setColorRange(osgParticle::rangev4(partctrl->color, partctrl->color));
created->setAlphaRange(osgParticle::rangef(1.f, 1.f)); created->setAlphaRange(osgParticle::rangef(1.f, 1.f));
float size = partctrl->size; float size = partctrl->size;
if (particle.vertex < particledata->sizes.size()) if (particle.vertex < particledata->mSizes.size())
size *= particledata->sizes[particle.vertex]; size *= particledata->mSizes[particle.vertex];
created->setSizeRange(osgParticle::rangef(size, size)); created->setSizeRange(osgParticle::rangef(size, size));
box.expandBy(osg::BoundingSphere(position, size)); box.expandBy(osg::BoundingSphere(position, size));
} }
// radius may be used to force a larger bounding box // radius may be used to force a larger bounding box
box.expandBy(osg::BoundingSphere(osg::Vec3(0, 0, 0), particledata->radius)); box.expandBy(osg::BoundingSphere(osg::Vec3(0, 0, 0), particledata->mBoundingSphere.radius()));
partsys->setInitialBound(box); partsys->setInitialBound(box);
} }
@ -1346,9 +1346,9 @@ namespace NifOsg
void handleNiGeometryData(osg::Geometry* geometry, const Nif::NiGeometryData* data, void handleNiGeometryData(osg::Geometry* geometry, const Nif::NiGeometryData* data,
const std::vector<unsigned int>& boundTextures, const std::string& name) const std::vector<unsigned int>& boundTextures, const std::string& name)
{ {
const auto& vertices = data->vertices; const auto& vertices = data->mVertices;
const auto& normals = data->normals; const auto& normals = data->mNormals;
const auto& colors = data->colors; const auto& colors = data->mColors;
if (!vertices.empty()) if (!vertices.empty())
geometry->setVertexArray(new osg::Vec3Array(vertices.size(), vertices.data())); geometry->setVertexArray(new osg::Vec3Array(vertices.size(), vertices.data()));
if (!normals.empty()) if (!normals.empty())
@ -1357,7 +1357,7 @@ namespace NifOsg
if (!colors.empty()) if (!colors.empty())
geometry->setColorArray(new osg::Vec4Array(colors.size(), colors.data()), osg::Array::BIND_PER_VERTEX); geometry->setColorArray(new osg::Vec4Array(colors.size(), colors.data()), osg::Array::BIND_PER_VERTEX);
const auto& uvlist = data->uvlist; const auto& uvlist = data->mUVList;
int textureStage = 0; int textureStage = 0;
for (std::vector<unsigned int>::const_iterator it = boundTextures.begin(); it != boundTextures.end(); for (std::vector<unsigned int>::const_iterator it = boundTextures.begin(); it != boundTextures.end();
++it, ++textureStage) ++it, ++textureStage)
@ -1391,30 +1391,30 @@ namespace NifOsg
const Nif::NiSkinInstance* skin = niGeometry->skin.getPtr(); const Nif::NiSkinInstance* skin = niGeometry->skin.getPtr();
const Nif::NiSkinData* data = nullptr; const Nif::NiSkinData* data = nullptr;
const Nif::NiSkinPartition* partitions = nullptr; const Nif::NiSkinPartition* partitions = nullptr;
if (!skin->data.empty()) if (!skin->mData.empty())
{ {
data = skin->data.getPtr(); data = skin->mData.getPtr();
if (!data->partitions.empty()) if (!data->mPartitions.empty())
partitions = data->partitions.getPtr(); partitions = data->mPartitions.getPtr();
} }
if (!partitions && !skin->partitions.empty()) if (!partitions && !skin->mPartitions.empty())
partitions = skin->partitions.getPtr(); partitions = skin->mPartitions.getPtr();
hasPartitions = partitions != nullptr; hasPartitions = partitions != nullptr;
if (hasPartitions) if (hasPartitions)
{ {
std::vector<unsigned short> trueTriangles;
for (const Nif::NiSkinPartition::Partition& partition : partitions->mPartitions) for (const Nif::NiSkinPartition::Partition& partition : partitions->mPartitions)
{ {
trueTriangles = partition.getTrueTriangles(); const std::vector<unsigned short>& trueTriangles = partition.mTrueTriangles;
if (!trueTriangles.empty()) if (!trueTriangles.empty())
{ {
geometry->addPrimitiveSet(new osg::DrawElementsUShort( geometry->addPrimitiveSet(new osg::DrawElementsUShort(
osg::PrimitiveSet::TRIANGLES, trueTriangles.size(), trueTriangles.data())); osg::PrimitiveSet::TRIANGLES, trueTriangles.size(), trueTriangles.data()));
} }
const std::vector<std::vector<unsigned short>> trueStrips = partition.getTrueStrips(); for (const auto& strip : partition.mTrueStrips)
for (const auto& strip : trueStrips)
{ {
if (strip.size() < 3)
continue;
geometry->addPrimitiveSet(new osg::DrawElementsUShort( geometry->addPrimitiveSet(new osg::DrawElementsUShort(
osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), strip.data())); osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), strip.data()));
} }
@ -1430,7 +1430,7 @@ namespace NifOsg
if (niGeometryData->recType != Nif::RC_NiTriShapeData) if (niGeometryData->recType != Nif::RC_NiTriShapeData)
return; return;
auto data = static_cast<const Nif::NiTriShapeData*>(niGeometryData); auto data = static_cast<const Nif::NiTriShapeData*>(niGeometryData);
const std::vector<unsigned short>& triangles = data->triangles; const std::vector<unsigned short>& triangles = data->mTriangles;
if (triangles.empty()) if (triangles.empty())
return; return;
geometry->addPrimitiveSet( geometry->addPrimitiveSet(
@ -1442,7 +1442,7 @@ namespace NifOsg
return; 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->strips) for (const std::vector<unsigned short>& strip : data->mStrips)
{ {
if (strip.size() < 3) if (strip.size() < 3)
continue; continue;
@ -1458,7 +1458,7 @@ namespace NifOsg
if (niGeometryData->recType != Nif::RC_NiLinesData) if (niGeometryData->recType != Nif::RC_NiLinesData)
return; return;
auto data = static_cast<const Nif::NiLinesData*>(niGeometryData); auto data = static_cast<const Nif::NiLinesData*>(niGeometryData);
const auto& line = data->lines; const auto& line = data->mLines;
if (line.empty()) if (line.empty())
return; return;
geometry->addPrimitiveSet( geometry->addPrimitiveSet(
@ -1473,7 +1473,7 @@ namespace NifOsg
// above the actual renderable would be tedious. // above the actual renderable would be tedious.
std::vector<const Nif::Property*> drawableProps; std::vector<const Nif::Property*> drawableProps;
collectDrawableProperties(nifNode, parent, drawableProps); collectDrawableProperties(nifNode, parent, drawableProps);
applyDrawableProperties(parentNode, drawableProps, composite, !niGeometryData->colors.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::Node* nifNode, const Nif::Parent* parent, osg::Group* parentNode,
@ -1549,21 +1549,16 @@ namespace NifOsg
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->data.getPtr(); const Nif::NiSkinData* data = skin->mData.getPtr();
const Nif::NodeList& bones = skin->bones; const Nif::NodeList& bones = skin->mBones;
for (std::size_t i = 0, n = bones.size(); i < n; ++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()->name);
SceneUtil::RigGeometry::BoneInfluence influence; SceneUtil::RigGeometry::BoneInfluence influence;
const std::vector<Nif::NiSkinData::VertWeight>& weights = data->bones[i].weights; influence.mWeights = data->mBones[i].mWeights;
for (size_t j = 0; j < weights.size(); j++) influence.mInvBindMatrix = data->mBones[i].mTransform.toMatrix();
{ influence.mBoundSphere = data->mBones[i].mBoundSphere;
influence.mWeights.emplace_back(weights[j].vertex, weights[j].weight);
}
influence.mInvBindMatrix = data->bones[i].trafo.toMatrix();
influence.mBoundSphere
= osg::BoundingSpheref(data->bones[i].boundSphereCenter, data->bones[i].boundSphereRadius);
map->mData.emplace_back(boneName, influence); map->mData.emplace_back(boneName, influence);
} }
@ -1680,71 +1675,76 @@ namespace NifOsg
osg::ref_ptr<osg::Image> handleInternalTexture(const Nif::NiPixelData* pixelData) osg::ref_ptr<osg::Image> handleInternalTexture(const Nif::NiPixelData* pixelData)
{ {
osg::ref_ptr<osg::Image> image(new osg::Image); if (pixelData->mMipmaps.empty())
return nullptr;
// Pixel row alignment, defining it to be consistent with OSG DDS plugin // Not fatal, but warn the user
int packing = 1; if (pixelData->mNumFaces != 1)
Log(Debug::Info) << "Unsupported multifaceted internal texture in " << mFilename;
using Nif::NiPixelFormat;
NiPixelFormat niPixelFormat = pixelData->mPixelFormat;
GLenum pixelformat = 0; GLenum pixelformat = 0;
switch (pixelData->fmt) // Pixel row alignment. Defining it to be consistent with OSG DDS plugin
int packing = 1;
switch (niPixelFormat.mFormat)
{ {
case Nif::NiPixelData::NIPXFMT_RGB8: case NiPixelFormat::Format::RGB:
pixelformat = GL_RGB; pixelformat = GL_RGB;
break; break;
case Nif::NiPixelData::NIPXFMT_RGBA8: case NiPixelFormat::Format::RGBA:
pixelformat = GL_RGBA; pixelformat = GL_RGBA;
break; break;
case Nif::NiPixelData::NIPXFMT_PAL8: case NiPixelFormat::Format::Palette:
case Nif::NiPixelData::NIPXFMT_PALA8: case NiPixelFormat::Format::PaletteAlpha:
pixelformat = GL_RED; // Each color is defined by a byte. pixelformat = GL_RED; // Each color is defined by a byte.
break; break;
case Nif::NiPixelData::NIPXFMT_BGR8: case NiPixelFormat::Format::BGR:
pixelformat = GL_BGR; pixelformat = GL_BGR;
break; break;
case Nif::NiPixelData::NIPXFMT_BGRA8: case NiPixelFormat::Format::BGRA:
pixelformat = GL_BGRA; pixelformat = GL_BGRA;
break; break;
case Nif::NiPixelData::NIPXFMT_DXT1: case NiPixelFormat::Format::DXT1:
pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
packing = 2; packing = 2;
break; break;
case Nif::NiPixelData::NIPXFMT_DXT3: case NiPixelFormat::Format::DXT3:
pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
packing = 4; packing = 4;
break; break;
case Nif::NiPixelData::NIPXFMT_DXT5: case NiPixelFormat::Format::DXT5:
pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
packing = 4; packing = 4;
break; break;
default: default:
Log(Debug::Info) << "Unhandled internal pixel format " << pixelData->fmt << " in " << mFilename; Log(Debug::Info) << "Unhandled internal pixel format "
<< static_cast<uint32_t>(niPixelFormat.mFormat) << " in " << mFilename;
return nullptr; return nullptr;
} }
if (pixelData->mipmaps.empty())
return nullptr;
int width = 0; int width = 0;
int height = 0; int height = 0;
std::vector<unsigned int> mipmapVector; std::vector<unsigned int> mipmapOffsets;
for (unsigned int i = 0; i < pixelData->mipmaps.size(); ++i) for (unsigned int i = 0; i < pixelData->mMipmaps.size(); ++i)
{ {
const Nif::NiPixelData::Mipmap& mip = pixelData->mipmaps[i]; const Nif::NiPixelData::Mipmap& mip = pixelData->mMipmaps[i];
size_t mipSize = osg::Image::computeImageSizeInBytes( size_t mipSize = osg::Image::computeImageSizeInBytes(
mip.width, mip.height, 1, pixelformat, GL_UNSIGNED_BYTE, packing); mip.mWidth, mip.mHeight, 1, pixelformat, GL_UNSIGNED_BYTE, packing);
if (mipSize + mip.dataOffset > pixelData->data.size()) if (mipSize + mip.mOffset > pixelData->mData.size())
{ {
Log(Debug::Info) << "Internal texture's mipmap data out of bounds, ignoring texture"; Log(Debug::Info) << "Internal texture's mipmap data out of bounds, ignoring texture";
return nullptr; return nullptr;
} }
if (i != 0) if (i != 0)
mipmapVector.push_back(mip.dataOffset); mipmapOffsets.push_back(mip.mOffset);
else else
{ {
width = mip.width; width = mip.mWidth;
height = mip.height; height = mip.mHeight;
} }
} }
@ -1754,16 +1754,17 @@ namespace NifOsg
return nullptr; return nullptr;
} }
const std::vector<unsigned char>& pixels = pixelData->data; osg::ref_ptr<osg::Image> image(new osg::Image);
switch (pixelData->fmt) const std::vector<unsigned char>& pixels = pixelData->mData;
switch (niPixelFormat.mFormat)
{ {
case Nif::NiPixelData::NIPXFMT_RGB8: case NiPixelFormat::Format::RGB:
case Nif::NiPixelData::NIPXFMT_RGBA8: case NiPixelFormat::Format::RGBA:
case Nif::NiPixelData::NIPXFMT_BGR8: case NiPixelFormat::Format::BGR:
case Nif::NiPixelData::NIPXFMT_BGRA8: case NiPixelFormat::Format::BGRA:
case Nif::NiPixelData::NIPXFMT_DXT1: case NiPixelFormat::Format::DXT1:
case Nif::NiPixelData::NIPXFMT_DXT3: case NiPixelFormat::Format::DXT3:
case Nif::NiPixelData::NIPXFMT_DXT5: case NiPixelFormat::Format::DXT5:
{ {
unsigned char* data = new unsigned char[pixels.size()]; unsigned char* data = new unsigned char[pixels.size()];
memcpy(data, pixels.data(), pixels.size()); memcpy(data, pixels.data(), pixels.size());
@ -1771,18 +1772,18 @@ namespace NifOsg
osg::Image::USE_NEW_DELETE, packing); osg::Image::USE_NEW_DELETE, packing);
break; break;
} }
case Nif::NiPixelData::NIPXFMT_PAL8: case NiPixelFormat::Format::Palette:
case Nif::NiPixelData::NIPXFMT_PALA8: case NiPixelFormat::Format::PaletteAlpha:
{ {
if (pixelData->palette.empty() || pixelData->bpp != 8) if (pixelData->mPalette.empty() || niPixelFormat.mBitsPerPixel != 8)
{ {
Log(Debug::Info) << "Palettized texture in " << mFilename << " is invalid, ignoring"; Log(Debug::Info) << "Palettized texture in " << mFilename << " is invalid, ignoring";
return nullptr; return nullptr;
} }
pixelformat = pixelData->fmt == Nif::NiPixelData::NIPXFMT_PAL8 ? GL_RGB : GL_RGBA; pixelformat = niPixelFormat.mFormat == NiPixelFormat::Format::PaletteAlpha ? GL_RGBA : GL_RGB;
// We're going to convert the indices that pixel data contains // We're going to convert the indices that pixel data contains
// into real colors using the palette. // into real colors using the palette.
const auto& palette = pixelData->palette->colors; const auto& palette = pixelData->mPalette->mColors;
const int numChannels = pixelformat == GL_RGBA ? 4 : 3; const int numChannels = pixelformat == GL_RGBA ? 4 : 3;
unsigned char* data = new unsigned char[pixels.size() * numChannels]; unsigned char* data = new unsigned char[pixels.size() * numChannels];
unsigned char* pixel = data; unsigned char* pixel = data;
@ -1791,7 +1792,7 @@ namespace NifOsg
memcpy(pixel, &palette[index], sizeof(unsigned char) * numChannels); memcpy(pixel, &palette[index], sizeof(unsigned char) * numChannels);
pixel += numChannels; pixel += numChannels;
} }
for (unsigned int& offset : mipmapVector) for (unsigned int& offset : mipmapOffsets)
offset *= numChannels; offset *= numChannels;
image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data,
osg::Image::USE_NEW_DELETE, packing); osg::Image::USE_NEW_DELETE, packing);
@ -1801,7 +1802,7 @@ namespace NifOsg
return nullptr; return nullptr;
} }
image->setMipmapLevels(mipmapVector); image->setMipmapLevels(mipmapOffsets);
image->flipVertical(); image->flipVertical();
return image; return image;