Merge branch 'nif' into 'master'

Modernize NIF loader, part 2

See merge request OpenMW/openmw!3404
macos_ci_fix
psi29a 1 year ago
commit 0a47e927d5
No known key found for this signature in database

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

@ -323,22 +323,22 @@ namespace
init(mController);
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.triangles = { 0, 1, 2 };
mNiTriShapeData.mTriangles = { 0, 1, 2 };
mNiTriShape.data = Nif::NiGeometryDataPtr(&mNiTriShapeData);
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.triangles = { 0, 1, 2 };
mNiTriShapeData2.mTriangles = { 0, 1, 2 };
mNiTriShape2.data = Nif::NiGeometryDataPtr(&mNiTriShapeData2);
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) };
mNiTriStripsData.mNumTriangles = 2;
mNiTriStripsData.strips = { { 0, 1, 2, 3 } };
mNiTriStripsData.mStrips = { { 0, 1, 2, 3 } };
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)
{
auto data = static_cast<Nif::NiTriShapeData*>(mNiTriShape.data.getPtr());
data->triangles.clear();
data->mTriangles.clear();
mNiTriShape.parents.push_back(&mNiNode);
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)
{
mNiTriStripsData.strips.clear();
mNiTriStripsData.mStrips.clear();
Nif::NIFFile file("test.nif");
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)
{
mNiTriStripsData.strips.front() = { 0, 1 };
mNiTriStripsData.mStrips.front() = { 0, 1 };
Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiTriStrips);
@ -1293,7 +1293,7 @@ namespace
mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) }));
mNiNode.recType = Nif::RC_AvoidNode;
mNiTriStripsData.strips.front() = { 0, 1 };
mNiTriStripsData.mStrips.front() = { 0, 1 };
Nif::NIFFile file("test.nif");
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)
{
mNiTriStripsData.strips.front() = { 0, 1 };
mNiTriStripsData.mStrips.front() = { 0, 1 };
mNiTriStrips.parents.push_back(&mNiNode);
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)
{
mNiTriStripsData.strips.front() = { 0, 1 };
mNiTriStripsData.mStrips.front() = { 0, 1 };
mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) }));

@ -1,200 +1,149 @@
#include "data.hpp"
#include <components/debug/debuglog.hpp>
#include "exception.hpp"
#include "nifkey.hpp"
#include "node.hpp"
#include <components/debug/debuglog.hpp>
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)
{
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))
nif->skip(2); // Keep flags and compress flags
{
nif->read(mKeepFlags);
nif->read(mCompressFlags);
}
if (nif->getBoolean())
nif->readVector(vertices, verts);
if (nif->get<bool>())
nif->readVector(mVertices, mNumVertices);
unsigned int dataFlags = 0;
if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0))
dataFlags = nif->getUShort();
{
nif->read(mDataFlags);
if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS
&& nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
nif->getUInt(); // Material CRC
if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS
&& nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
nif->read(mMaterialHash);
}
if (nif->getBoolean())
if (nif->get<bool>())
{
nif->readVector(normals, verts);
if (dataFlags & 0x1000)
nif->readVector(mNormals, mNumVertices);
if (mDataFlags & DataFlag_HasTangents)
{
nif->readVector(tangents, verts);
nif->readVector(bitangents, verts);
nif->readVector(mTangents, mNumVertices);
nif->readVector(mBitangents, mNumVertices);
}
}
center = nif->getVector3();
radius = nif->getFloat();
nif->read(mBoundingSphere);
if (nif->getBoolean())
nif->readVector(colors, verts);
if (nif->get<bool>())
nif->readVector(mColors, mNumVertices);
unsigned int numUVs = dataFlags;
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 later games only the first 6 bits are used as a count and the rest are flags.
// In 4.0.0.2 the flags field corresponds to the number of UV sets.
// In later revisions the part that corresponds to the number is narrower.
uint16_t numUVs = mDataFlags;
if (nif->getVersion() > NIFFile::NIFVersion::VER_MW)
{
numUVs &= 0x3f;
numUVs &= DataFlag_NumUVsMask;
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;
if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW)
hasUVs = nif->getBoolean();
if (hasUVs)
mUVList.resize(numUVs);
for (std::vector<osg::Vec2f>& list : mUVList)
{
uvlist.resize(numUVs);
for (unsigned int i = 0; i < numUVs; i++)
{
nif->readVector(uvlist[i], verts);
// 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());
}
}
nif->readVector(list, mNumVertices);
// flip the texture coordinates to convert them to the OpenGL convention of bottom-left image origin
for (osg::Vec2f& uv : list)
uv.y() = 1.f - uv.y();
}
if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0))
nif->getUShort(); // Consistency flags
if (nif->getVersion() >= NIFStream::generateVersion(20, 0, 0, 4))
nif->skip(4); // Additional data
{
nif->read(mConsistencyType);
if (nif->getVersion() >= NIFStream::generateVersion(20, 0, 0, 4))
nif->skip(4); // Additional data
}
}
void NiTriBasedGeomData::read(NIFStream* nif)
{
NiGeometryData::read(nif);
mNumTriangles = nif->getUShort();
nif->read(mNumTriangles);
}
void NiTriShapeData::read(NIFStream* nif)
{
NiTriBasedGeomData::read(nif);
// We have three times as many vertices as triangles, so this
// is always equal to mNumTriangles * 3.
int cnt = nif->getInt();
bool hasTriangles = true;
if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD)
hasTriangles = nif->getBoolean();
if (hasTriangles)
nif->readVector(triangles, cnt);
// 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));
}
uint32_t numIndices;
nif->read(numIndices);
if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD && !nif->get<bool>())
numIndices = 0;
nif->readVector(mTriangles, numIndices);
mMatchGroups.resize(nif->get<uint16_t>());
for (auto& group : mMatchGroups)
nif->readVector(group, nif->get<uint16_t>());
}
void NiTriStripsData::read(NIFStream* nif)
{
NiTriBasedGeomData::read(nif);
// Number of triangle strips
int numStrips = nif->getUShort();
std::vector<unsigned short> lengths;
uint16_t numStrips;
nif->read(numStrips);
std::vector<uint16_t> lengths;
nif->readVector(lengths, numStrips);
// "Has Strips" flag. Exceptionally useful.
bool hasStrips = true;
if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD)
hasStrips = nif->getBoolean();
if (!hasStrips || !numStrips)
return;
strips.resize(numStrips);
if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD && !nif->get<bool>())
numStrips = 0;
mStrips.resize(numStrips);
for (int i = 0; i < numStrips; i++)
nif->readVector(strips[i], lengths[i]);
nif->readVector(mStrips[i], lengths[i]);
}
void NiLinesData::read(NIFStream* nif)
{
NiGeometryData::read(nif);
size_t num = vertices.size();
std::vector<uint8_t> flags;
nif->readVector(flags, num);
nif->readVector(flags, mNumVertices);
// Can't construct a line from a single vertex.
if (num < 2)
if (mNumVertices < 2)
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.
for (size_t i = 0; i < num - 1; ++i)
for (uint16_t i = 0; i < mNumVertices - 1; ++i)
{
if (flags[i] & 1)
{
lines.emplace_back(i);
lines.emplace_back(i + 1);
mLines.emplace_back(i);
mLines.emplace_back(i + 1);
}
}
// 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);
lines.emplace_back(0);
mLines.emplace_back(mNumVertices - 1);
mLines.emplace_back(0);
}
mLines.shrink_to_fit();
}
void NiParticlesData::read(NIFStream* nif)
@ -203,26 +152,47 @@ namespace Nif
// Should always match the number of vertices
if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW)
numParticles = nif->getUShort();
if (nif->getVersion() <= NIFStream::generateVersion(10, 0, 1, 0))
std::fill(particleRadii.begin(), particleRadii.end(), nif->getFloat());
else if (nif->getBoolean())
nif->readVector(particleRadii, vertices.size());
activeCount = nif->getUShort();
nif->read(mNumParticles);
else if (nif->getVersion() != NIFFile::NIFVersion::VER_BGS || nif->getBethVersion() == 0)
mNumParticles = mNumVertices;
bool numRadii = 1;
if (nif->getVersion() > NIFStream::generateVersion(10, 0, 1, 0))
numRadii = nif->get<bool>() ? mNumParticles : 0;
nif->readVector(mRadii, numRadii);
nif->read(mActiveCount);
if (nif->get<bool>())
nif->readVector(mSizes, mNumParticles);
// Particle sizes
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->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0))
{
if (nif->getBoolean())
nif->readVector(rotationAngles, vertices.size());
if (nif->getBoolean())
nif->readVector(rotationAxes, vertices.size());
if (nif->get<bool>())
nif->readVector(mRotations, mNumParticles);
if (nif->getVersion() >= NIFStream::generateVersion(20, 0, 0, 4))
{
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);
if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0) && nif->getBoolean())
nif->readVector(rotations, vertices.size());
if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0) && nif->get<bool>())
nif->readVector(mRotations, mNumParticles);
}
void NiPosData::read(NIFStream* nif)
@ -242,10 +212,10 @@ namespace Nif
void NiUVData::read(NIFStream* nif)
{
for (int i = 0; i < 4; i++)
for (FloatKeyMapPtr& keys : mKeyList)
{
mKeyList[i] = std::make_shared<FloatKeyMap>();
mKeyList[i]->read(nif);
keys = std::make_shared<FloatKeyMap>();
keys->read(nif);
}
}
@ -255,58 +225,61 @@ namespace Nif
mKeyList->read(nif);
}
void NiPixelData::read(NIFStream* nif)
void NiPixelFormat::read(NIFStream* nif)
{
fmt = (Format)nif->getUInt();
if (nif->getVersion() < NIFStream::generateVersion(10, 4, 0, 2))
mFormat = static_cast<Format>(nif->get<uint32_t>());
if (nif->getVersion() <= NIFStream::generateVersion(10, 4, 0, 1))
{
for (unsigned int i = 0; i < 4; ++i)
colorMask[i] = nif->getUInt();
bpp = nif->getUInt();
nif->skip(8); // "Old Fast Compare". Whatever that means.
nif->readArray(mColorMasks);
nif->read(mBitsPerPixel);
nif->readArray(mCompareBits);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
pixelTiling = nif->getUInt();
nif->read(mPixelTiling);
}
else // TODO: see if anything from here needs to be implemented
else
{
bpp = nif->getChar();
nif->skip(4); // Renderer hint
nif->skip(4); // Extra data
nif->skip(4); // Flags
pixelTiling = nif->getUInt();
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))
sRGB = nif->getBoolean();
nif->skip(4 * 10); // Channel data
nif->read(mUseSrgb);
for (int i = 0; i < 4; i++)
mChannels[i].read(nif);
}
}
palette.read(nif);
numberOfMipmaps = nif->getUInt();
// Bytes per pixel, should be bpp / 8
/* int bytes = */ nif->getUInt();
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);
}
for (unsigned int i = 0; i < numberOfMipmaps; i++)
void NiPixelData::read(NIFStream* nif)
{
mPixelFormat.read(nif);
mPalette.read(nif);
mMipmaps.resize(nif->get<uint32_t>());
nif->read(mBytesPerPixel);
for (Mipmap& mip : mMipmaps)
{
// 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);
nif->read(mip.mWidth);
nif->read(mip.mHeight);
nif->read(mip.mOffset);
}
// 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);
uint32_t numPixels;
nif->read(numPixels);
if (nif->getVersion() >= NIFStream::generateVersion(10, 4, 0, 2))
nif->read(mNumFaces);
nif->readVector(mData, numPixels * mNumFaces);
}
void NiPixelData::post(Reader& nif)
{
palette.post(nif);
mPalette.post(nif);
}
void NiColorData::read(NIFStream* nif)
@ -317,62 +290,110 @@ namespace Nif
void NiVisData::read(NIFStream* nif)
{
int count = nif->getInt();
mVis.resize(count);
for (size_t i = 0; i < mVis.size(); i++)
mKeys = std::make_shared<std::map<float, bool>>();
uint32_t numKeys;
nif->read(numKeys);
for (size_t i = 0; i < numKeys; i++)
{
mVis[i].time = nif->getFloat();
mVis[i].isSet = (nif->getChar() != 0);
float time;
char value;
nif->read(time);
nif->read(value);
(*mKeys)[time] = (value != 0);
}
}
void NiSkinData::read(NIFStream* nif)
void NiSkinInstance::read(NIFStream* nif)
{
trafo.rotation = nif->getMatrix3();
trafo.pos = nif->getVector3();
trafo.scale = nif->getFloat();
mData.read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 101))
mPartitions.read(nif);
mRoot.read(nif);
readRecordList(nif, mBones);
}
int boneNum = nif->getInt();
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW
&& nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 0))
partitions.read(nif);
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)
{
nif->read(mTransform.rotation);
nif->read(mTransform.pos);
nif->read(mTransform.scale);
uint32_t numBones;
nif->read(numBones);
bool hasVertexWeights = true;
if (nif->getVersion() > NIFStream::generateVersion(4, 2, 1, 0))
hasVertexWeights = nif->getBoolean();
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW)
{
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 0))
mPartitions.read(nif);
bones.resize(boneNum);
for (BoneInfo& bi : bones)
if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 1, 0))
nif->read(hasVertexWeights);
}
mBones.resize(numBones);
for (BoneInfo& bi : mBones)
{
bi.trafo.rotation = nif->getMatrix3();
bi.trafo.pos = nif->getVector3();
bi.trafo.scale = nif->getFloat();
bi.boundSphereCenter = nif->getVector3();
bi.boundSphereRadius = nif->getFloat();
nif->read(bi.mTransform.rotation);
nif->read(bi.mTransform.pos);
nif->read(bi.mTransform.scale);
nif->read(bi.mBoundSphere);
size_t numVertices = nif->getUShort();
uint16_t numVertices;
nif->read(numVertices);
if (!hasVertexWeights)
continue;
bi.weights.resize(numVertices);
for (size_t j = 0; j < bi.weights.size(); j++)
bi.mWeights.resize(numVertices);
for (auto& [vertex, weight] : bi.mWeights)
{
bi.weights[j].vertex = nif->getUShort();
bi.weights[j].weight = nif->getFloat();
nif->read(vertex);
nif->read(weight);
}
}
}
void NiSkinData::post(Reader& nif)
{
partitions.post(nif);
mPartitions.post(nif);
}
void NiSkinPartition::read(NIFStream* nif)
{
nif->read(mPartitionNum);
mPartitions.resize(mPartitionNum);
mPartitions.resize(nif->get<uint32_t>());
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE)
{
@ -391,103 +412,76 @@ namespace Nif
void NiSkinPartition::Partition::read(NIFStream* nif)
{
size_t numVertices = nif->getUShort();
size_t numTriangles = nif->getUShort();
size_t numBones = nif->getUShort();
size_t numStrips = nif->getUShort();
size_t bonesPerVertex = nif->getUShort();
nif->readVector(bones, numBones);
bool hasVertexMap = true;
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
hasVertexMap = nif->getBoolean();
if (hasVertexMap)
nif->readVector(vertexMap, numVertices);
bool hasVertexWeights = true;
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
hasVertexWeights = nif->getBoolean();
if (hasVertexWeights)
nif->readVector(weights, numVertices * bonesPerVertex);
uint16_t numVertices, numTriangles, numBones, numStrips, bonesPerVertex;
nif->read(numVertices);
nif->read(numTriangles);
nif->read(numBones);
nif->read(numStrips);
nif->read(bonesPerVertex);
nif->readVector(mBones, numBones);
bool hasPresenceFlags = nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0);
if (!hasPresenceFlags || nif->get<bool>())
nif->readVector(mVertexMap, numVertices);
if (!hasPresenceFlags || nif->get<bool>())
nif->readVector(mWeights, numVertices * bonesPerVertex);
std::vector<unsigned short> stripLengths;
nif->readVector(stripLengths, numStrips);
bool hasFaces = true;
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
hasFaces = nif->getBoolean();
if (hasFaces)
if (!hasPresenceFlags || nif->get<bool>())
{
if (numStrips)
{
strips.resize(numStrips);
mStrips.resize(numStrips);
for (size_t i = 0; i < numStrips; i++)
nif->readVector(strips[i], stripLengths[i]);
nif->readVector(mStrips[i], stripLengths[i]);
}
else
nif->readVector(triangles, numTriangles * 3);
nif->readVector(mTriangles, numTriangles * 3);
}
bool hasBoneIndices = nif->getChar() != 0;
if (hasBoneIndices)
nif->readVector(boneIndices, numVertices * bonesPerVertex);
if (nif->get<uint8_t>() != 0)
nif->readVector(mBoneIndices, numVertices * bonesPerVertex);
if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
{
nif->getChar(); // LOD level
nif->getBoolean(); // Global VB
nif->read(mLODLevel);
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);
nif->readVector(trueTriangles, numTriangles * 3);
if (!mStrips.empty())
{
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)
{
int morphCount = nif->getInt();
int vertCount = nif->getInt();
nif->getChar(); // Relative targets, always 1
uint32_t numMorphs, numVerts;
nif->read(numMorphs);
nif->read(numVerts);
nif->read(mRelativeTargets);
mMorphs.resize(morphCount);
for (int i = 0; i < morphCount; i++)
mMorphs.resize(numMorphs);
for (MorphData& morph : mMorphs)
{
mMorphs[i].mKeyFrames = std::make_shared<FloatKeyMap>();
mMorphs[i].mKeyFrames->read(nif, /*morph*/ true);
nif->readVector(mMorphs[i].mVertices, vertCount);
morph.mKeyFrames = std::make_shared<FloatKeyMap>();
morph.mKeyFrames->read(nif, /*morph*/ true);
nif->readVector(morph.mVertices, numVerts);
}
}
@ -498,7 +492,7 @@ namespace Nif
if (mRotations->mInterpolationType == InterpolationType_XYZ)
{
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>();
mYRotations = std::make_shared<FloatKeyMap>();
mZRotations = std::make_shared<FloatKeyMap>();
@ -514,18 +508,24 @@ namespace 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.
colors.resize(256);
unsigned int numEntries = nif->getUInt();
for (unsigned int i = 0; i < numEntries; i++)
colors[i] = nif->getUInt() | alphaMask;
mColors.resize(numEntries > 256 ? numEntries : 256);
for (uint32_t i = 0; i < numEntries; i++)
{
nif->read(mColors[i]);
mColors[i] |= alphaMask;
}
}
void NiStringPalette::read(NIFStream* nif)
{
palette = nif->getStringPalette();
if (nif->getUInt() != palette.size())
mPalette = nif->getStringPalette();
if (nif->get<uint32_t>() != mPalette.size())
Log(Debug::Warning) << "NIFFile Warning: Failed size check in NiStringPalette. File: "
<< nif->getFile().getFilename();
}
@ -548,15 +548,14 @@ namespace Nif
void BSMultiBoundOBB::read(NIFStream* nif)
{
mCenter = nif->getVector3();
mSize = nif->getVector3();
mRotation = nif->getMatrix3();
nif->read(mCenter);
nif->read(mSize);
nif->read(mRotation);
}
void BSMultiBoundSphere::read(NIFStream* nif)
{
mCenter = nif->getVector3();
mRadius = nif->getFloat();
nif->read(mSphere);
}
} // Namespace

@ -35,11 +35,26 @@ namespace Nif
// Common ancestor for several data classes
struct NiGeometryData : public Record
{
std::vector<osg::Vec3f> vertices, normals, tangents, bitangents;
std::vector<osg::Vec4f> colors;
std::vector<std::vector<osg::Vec2f>> uvlist;
osg::Vec3f center;
float radius;
// Interpretation of Flags field differs depending on the version
enum DataFlags
{
DataFlag_HasUV = 0x0001,
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;
};
@ -47,7 +62,7 @@ namespace Nif
// Abstract
struct NiTriBasedGeomData : public NiGeometryData
{
size_t mNumTriangles;
uint16_t mNumTriangles;
void read(NIFStream* nif) override;
};
@ -55,7 +70,8 @@ namespace Nif
struct NiTriShapeData : public NiTriBasedGeomData
{
// 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;
};
@ -63,7 +79,7 @@ namespace Nif
struct NiTriStripsData : public NiTriBasedGeomData
{
// 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;
};
@ -71,20 +87,29 @@ namespace Nif
struct NiLinesData : public NiGeometryData
{
// 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;
};
struct NiParticlesData : public NiGeometryData
{
int numParticles{ 0 };
int activeCount{ 0 };
std::vector<float> particleRadii, sizes, rotationAngles;
std::vector<osg::Quat> rotations;
std::vector<osg::Vec3f> rotationAxes;
uint16_t mNumParticles{ 0 };
uint16_t mActiveCount;
std::vector<float> mRadii;
std::vector<float> mSizes;
std::vector<osg::Quat> mRotations;
std::vector<float> mRotationAngles;
std::vector<osg::Vec3f> mRotationAxes;
bool mHasTextureIndices{ false };
std::vector<osg::Vec4f> mSubtextureOffsets;
float mAspectRatio{ 1.f };
uint16_t mAspectFlags{ 0 };
float mAspectRatio2;
float mAspectSpeed, mAspectSpeed2;
void read(NIFStream* nif) override;
};
@ -103,7 +128,7 @@ namespace Nif
struct NiUVData : public Record
{
FloatKeyMapPtr mKeyList[4];
std::array<FloatKeyMapPtr, 4> mKeyList;
void read(NIFStream* nif) override;
};
@ -115,37 +140,94 @@ namespace Nif
void read(NIFStream* nif) override;
};
struct NiPixelData : public Record
struct NiPixelFormat
{
enum Format
enum class Format : uint32_t
{
NIPXFMT_RGB8,
NIPXFMT_RGBA8,
NIPXFMT_PAL8,
NIPXFMT_PALA8,
NIPXFMT_BGR8,
NIPXFMT_BGRA8,
NIPXFMT_DXT1,
NIPXFMT_DXT3,
NIPXFMT_DXT5
RGB = 0,
RGBA = 1,
Palette = 2,
PaletteAlpha = 3,
BGR = 4,
BGRA = 5,
DXT1 = 6,
DXT3 = 7,
DXT5 = 8,
};
Format fmt{ NIPXFMT_RGB8 };
unsigned int colorMask[4]{ 0 };
unsigned int bpp{ 0 }, pixelTiling{ 0 };
bool sRGB{ false };
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);
};
NiPalettePtr palette;
unsigned int numberOfMipmaps{ 0 };
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 Mipmap
{
int width, height;
int dataOffset;
uint32_t mWidth, mHeight;
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 post(Reader& nif) override;
@ -160,22 +242,18 @@ namespace Nif
struct NiVisData : public Record
{
struct VisData
{
float time;
bool isSet;
};
std::vector<VisData> mVis;
// TODO: investigate possible use of ByteKeyMap
std::shared_ptr<std::map<float, bool>> mKeys;
void read(NIFStream* nif) override;
};
struct NiSkinInstance : public Record
{
NiSkinDataPtr data;
NiSkinPartitionPtr partitions;
NodePtr root;
NodeList bones;
NiSkinDataPtr mData;
NiSkinPartitionPtr mPartitions;
NodePtr mRoot;
NodeList mBones;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
@ -183,28 +261,31 @@ namespace Nif
struct BSDismemberSkinInstance : public NiSkinInstance
{
struct BodyPart
{
uint16_t mFlags;
uint16_t mType;
};
std::vector<BodyPart> mParts;
void read(NIFStream* nif) override;
};
struct NiSkinData : public Record
{
struct VertWeight
{
unsigned short vertex;
float weight;
};
using VertWeight = std::pair<unsigned short, float>;
struct BoneInfo
{
Transformation trafo;
osg::Vec3f boundSphereCenter;
float boundSphereRadius;
std::vector<VertWeight> weights;
Transformation mTransform;
osg::BoundingSpheref mBoundSphere;
std::vector<VertWeight> mWeights;
};
Transformation trafo;
std::vector<BoneInfo> bones;
NiSkinPartitionPtr partitions;
Transformation mTransform;
std::vector<BoneInfo> mBones;
NiSkinPartitionPtr mPartitions;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
@ -214,20 +295,20 @@ namespace Nif
{
struct Partition
{
std::vector<unsigned short> bones;
std::vector<unsigned short> vertexMap;
std::vector<float> weights;
std::vector<std::vector<unsigned short>> strips;
std::vector<unsigned short> triangles;
std::vector<unsigned short> trueTriangles;
std::vector<char> boneIndices;
std::vector<unsigned short> mBones;
std::vector<unsigned short> mVertexMap;
std::vector<float> mWeights;
std::vector<std::vector<unsigned short>> mStrips;
std::vector<unsigned short> mTriangles;
std::vector<char> mBoneIndices;
BSVertexDesc mVertexDesc;
std::vector<unsigned short> mTrueTriangles;
std::vector<std::vector<unsigned short>> mTrueStrips;
uint8_t mLODLevel;
bool mGlobalVB;
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;
unsigned int mDataSize;
@ -245,6 +326,8 @@ namespace Nif
FloatKeyMapPtr mKeyFrames;
std::vector<osg::Vec3f> mVertices;
};
uint8_t mRelativeTargets;
std::vector<MorphData> mMorphs;
void read(NIFStream* nif) override;
@ -262,7 +345,7 @@ namespace Nif
Vector3KeyMapPtr mTranslations;
FloatKeyMapPtr mScales;
enum class AxisOrder
enum class AxisOrder : uint32_t
{
Order_XYZ = 0,
Order_XZY = 1,
@ -283,14 +366,15 @@ namespace Nif
struct NiPalette : public Record
{
// 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;
};
struct NiStringPalette : public Record
{
std::string palette;
std::string mPalette;
void read(NIFStream* nif) override;
};
@ -324,8 +408,7 @@ namespace Nif
struct BSMultiBoundSphere : public BSMultiBoundData
{
osg::Vec3f mCenter;
float mRadius;
osg::BoundingSpheref mSphere;
void read(NIFStream* nif) override;
};

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

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

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

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

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

@ -10,12 +10,12 @@ namespace Nif
NiTexture::read(nif);
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);
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)
mData.read(nif);

@ -35,18 +35,18 @@ namespace
void prepareTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriBasedGeomData& data)
{
// 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()));
for (const osg::Vec3f& vertex : vertices)
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)
{
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)
mesh.addTriangleIndices(triangles[i + 0], triangles[i + 1], triangles[i + 2]);
}
@ -54,7 +54,7 @@ namespace
void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& 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)
continue;
@ -87,7 +87,7 @@ namespace
return {};
auto data = static_cast<const Nif::NiTriShapeData*>(geometry.data.getPtr());
if (data->triangles.empty())
if (data->mTriangles.empty())
return {};
return function(static_cast<const Nif::NiTriShapeData&>(*data));
@ -99,7 +99,7 @@ namespace
return {};
auto data = static_cast<const Nif::NiTriStripsData*>(geometry.data.getPtr());
if (data->strips.empty())
if (data->mStrips.empty())
return {};
return function(static_cast<const Nif::NiTriStripsData&>(*data));
@ -381,7 +381,7 @@ namespace NifBullet
if (args.mHasMarkers && Misc::StringUtils::ciStartsWith(niGeometry.name, "EditorMarker"))
return;
if (niGeometry.data.empty() || niGeometry.data->vertices.empty())
if (niGeometry.data.empty() || niGeometry.data->mVertices.empty())
return;
if (!niGeometry.skin.empty())

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

@ -1,19 +1,18 @@
#ifndef COMPONENTS_NIFOSG_CONTROLLER_H
#define COMPONENTS_NIFOSG_CONTROLLER_H
#include <set>
#include <type_traits>
#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>
#include <set>
#include <type_traits>
#include <osg/Texture2D>
namespace osg
{
class Material;
@ -283,7 +282,7 @@ namespace NifOsg
class VisController : public SceneUtil::NodeCallback<VisController>, public SceneUtil::Controller
{
private:
std::vector<Nif::NiVisData::VisData> mData;
std::shared_ptr<std::map<float, bool>> mData;
ByteInterpolator mInterpolator;
unsigned int mMask{ 0u };

@ -1133,20 +1133,20 @@ namespace NifOsg
}
auto particledata = static_cast<const Nif::NiParticlesData*>(particleNode->data.getPtr());
partsys->setQuota(particledata->numParticles);
partsys->setQuota(particledata->mNumParticles);
osg::BoundingBox box;
int i = 0;
for (const auto& particle : partctrl->particles)
{
if (i++ >= particledata->activeCount)
if (i++ >= particledata->mActiveCount)
break;
if (particle.lifespan <= 0)
continue;
if (particle.vertex >= particledata->vertices.size())
if (particle.vertex >= particledata->mVertices.size())
continue;
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
// post-load in the SceneManager.
created->setVelocity(particle.velocity);
const osg::Vec3f& position = particledata->vertices[particle.vertex];
const osg::Vec3f& position = particledata->mVertices[particle.vertex];
created->setPosition(position);
created->setColorRange(osgParticle::rangev4(partctrl->color, partctrl->color));
created->setAlphaRange(osgParticle::rangef(1.f, 1.f));
float size = partctrl->size;
if (particle.vertex < particledata->sizes.size())
size *= particledata->sizes[particle.vertex];
if (particle.vertex < particledata->mSizes.size())
size *= particledata->mSizes[particle.vertex];
created->setSizeRange(osgParticle::rangef(size, size));
box.expandBy(osg::BoundingSphere(position, size));
}
// 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);
}
@ -1346,9 +1346,9 @@ namespace NifOsg
void handleNiGeometryData(osg::Geometry* geometry, const Nif::NiGeometryData* data,
const std::vector<unsigned int>& boundTextures, const std::string& name)
{
const auto& vertices = data->vertices;
const auto& normals = data->normals;
const auto& colors = data->colors;
const auto& vertices = data->mVertices;
const auto& normals = data->mNormals;
const auto& colors = data->mColors;
if (!vertices.empty())
geometry->setVertexArray(new osg::Vec3Array(vertices.size(), vertices.data()));
if (!normals.empty())
@ -1357,7 +1357,7 @@ namespace NifOsg
if (!colors.empty())
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;
for (std::vector<unsigned int>::const_iterator it = boundTextures.begin(); it != boundTextures.end();
++it, ++textureStage)
@ -1391,30 +1391,30 @@ namespace NifOsg
const Nif::NiSkinInstance* skin = niGeometry->skin.getPtr();
const Nif::NiSkinData* data = nullptr;
const Nif::NiSkinPartition* partitions = nullptr;
if (!skin->data.empty())
if (!skin->mData.empty())
{
data = skin->data.getPtr();
if (!data->partitions.empty())
partitions = data->partitions.getPtr();
data = skin->mData.getPtr();
if (!data->mPartitions.empty())
partitions = data->mPartitions.getPtr();
}
if (!partitions && !skin->partitions.empty())
partitions = skin->partitions.getPtr();
if (!partitions && !skin->mPartitions.empty())
partitions = skin->mPartitions.getPtr();
hasPartitions = partitions != nullptr;
if (hasPartitions)
{
std::vector<unsigned short> trueTriangles;
for (const Nif::NiSkinPartition::Partition& partition : partitions->mPartitions)
{
trueTriangles = partition.getTrueTriangles();
const std::vector<unsigned short>& trueTriangles = partition.mTrueTriangles;
if (!trueTriangles.empty())
{
geometry->addPrimitiveSet(new osg::DrawElementsUShort(
osg::PrimitiveSet::TRIANGLES, trueTriangles.size(), trueTriangles.data()));
}
const std::vector<std::vector<unsigned short>> trueStrips = partition.getTrueStrips();
for (const auto& strip : trueStrips)
for (const auto& strip : partition.mTrueStrips)
{
if (strip.size() < 3)
continue;
geometry->addPrimitiveSet(new osg::DrawElementsUShort(
osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), strip.data()));
}
@ -1430,7 +1430,7 @@ namespace NifOsg
if (niGeometryData->recType != Nif::RC_NiTriShapeData)
return;
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())
return;
geometry->addPrimitiveSet(
@ -1442,7 +1442,7 @@ namespace NifOsg
return;
auto data = static_cast<const Nif::NiTriStripsData*>(niGeometryData);
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)
continue;
@ -1458,7 +1458,7 @@ namespace NifOsg
if (niGeometryData->recType != Nif::RC_NiLinesData)
return;
auto data = static_cast<const Nif::NiLinesData*>(niGeometryData);
const auto& line = data->lines;
const auto& line = data->mLines;
if (line.empty())
return;
geometry->addPrimitiveSet(
@ -1473,7 +1473,7 @@ namespace NifOsg
// above the actual renderable would be tedious.
std::vector<const Nif::Property*> 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,
@ -1549,21 +1549,16 @@ namespace NifOsg
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::NiSkinData* data = skin->data.getPtr();
const Nif::NodeList& bones = skin->bones;
for (std::size_t i = 0, n = bones.size(); i < n; ++i)
const Nif::NiSkinData* data = skin->mData.getPtr();
const Nif::NodeList& bones = skin->mBones;
for (std::size_t i = 0; i < bones.size(); ++i)
{
std::string boneName = Misc::StringUtils::lowerCase(bones[i].getPtr()->name);
SceneUtil::RigGeometry::BoneInfluence influence;
const std::vector<Nif::NiSkinData::VertWeight>& weights = data->bones[i].weights;
for (size_t j = 0; j < weights.size(); j++)
{
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);
influence.mWeights = data->mBones[i].mWeights;
influence.mInvBindMatrix = data->mBones[i].mTransform.toMatrix();
influence.mBoundSphere = data->mBones[i].mBoundSphere;
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> image(new osg::Image);
if (pixelData->mMipmaps.empty())
return nullptr;
// Pixel row alignment, defining it to be consistent with OSG DDS plugin
int packing = 1;
// Not fatal, but warn the user
if (pixelData->mNumFaces != 1)
Log(Debug::Info) << "Unsupported multifaceted internal texture in " << mFilename;
using Nif::NiPixelFormat;
NiPixelFormat niPixelFormat = pixelData->mPixelFormat;
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;
break;
case Nif::NiPixelData::NIPXFMT_RGBA8:
case NiPixelFormat::Format::RGBA:
pixelformat = GL_RGBA;
break;
case Nif::NiPixelData::NIPXFMT_PAL8:
case Nif::NiPixelData::NIPXFMT_PALA8:
case NiPixelFormat::Format::Palette:
case NiPixelFormat::Format::PaletteAlpha:
pixelformat = GL_RED; // Each color is defined by a byte.
break;
case Nif::NiPixelData::NIPXFMT_BGR8:
case NiPixelFormat::Format::BGR:
pixelformat = GL_BGR;
break;
case Nif::NiPixelData::NIPXFMT_BGRA8:
case NiPixelFormat::Format::BGRA:
pixelformat = GL_BGRA;
break;
case Nif::NiPixelData::NIPXFMT_DXT1:
case NiPixelFormat::Format::DXT1:
pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
packing = 2;
break;
case Nif::NiPixelData::NIPXFMT_DXT3:
case NiPixelFormat::Format::DXT3:
pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
packing = 4;
break;
case Nif::NiPixelData::NIPXFMT_DXT5:
case NiPixelFormat::Format::DXT5:
pixelformat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
packing = 4;
break;
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;
}
if (pixelData->mipmaps.empty())
return nullptr;
int width = 0;
int height = 0;
std::vector<unsigned int> mipmapVector;
for (unsigned int i = 0; i < pixelData->mipmaps.size(); ++i)
std::vector<unsigned int> mipmapOffsets;
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(
mip.width, mip.height, 1, pixelformat, GL_UNSIGNED_BYTE, packing);
if (mipSize + mip.dataOffset > pixelData->data.size())
mip.mWidth, mip.mHeight, 1, pixelformat, GL_UNSIGNED_BYTE, packing);
if (mipSize + mip.mOffset > pixelData->mData.size())
{
Log(Debug::Info) << "Internal texture's mipmap data out of bounds, ignoring texture";
return nullptr;
}
if (i != 0)
mipmapVector.push_back(mip.dataOffset);
mipmapOffsets.push_back(mip.mOffset);
else
{
width = mip.width;
height = mip.height;
width = mip.mWidth;
height = mip.mHeight;
}
}
@ -1754,16 +1754,17 @@ namespace NifOsg
return nullptr;
}
const std::vector<unsigned char>& pixels = pixelData->data;
switch (pixelData->fmt)
osg::ref_ptr<osg::Image> image(new osg::Image);
const std::vector<unsigned char>& pixels = pixelData->mData;
switch (niPixelFormat.mFormat)
{
case Nif::NiPixelData::NIPXFMT_RGB8:
case Nif::NiPixelData::NIPXFMT_RGBA8:
case Nif::NiPixelData::NIPXFMT_BGR8:
case Nif::NiPixelData::NIPXFMT_BGRA8:
case Nif::NiPixelData::NIPXFMT_DXT1:
case Nif::NiPixelData::NIPXFMT_DXT3:
case Nif::NiPixelData::NIPXFMT_DXT5:
case NiPixelFormat::Format::RGB:
case NiPixelFormat::Format::RGBA:
case NiPixelFormat::Format::BGR:
case NiPixelFormat::Format::BGRA:
case NiPixelFormat::Format::DXT1:
case NiPixelFormat::Format::DXT3:
case NiPixelFormat::Format::DXT5:
{
unsigned char* data = new unsigned char[pixels.size()];
memcpy(data, pixels.data(), pixels.size());
@ -1771,18 +1772,18 @@ namespace NifOsg
osg::Image::USE_NEW_DELETE, packing);
break;
}
case Nif::NiPixelData::NIPXFMT_PAL8:
case Nif::NiPixelData::NIPXFMT_PALA8:
case NiPixelFormat::Format::Palette:
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";
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
// 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;
unsigned char* data = new unsigned char[pixels.size() * numChannels];
unsigned char* pixel = data;
@ -1791,7 +1792,7 @@ namespace NifOsg
memcpy(pixel, &palette[index], sizeof(unsigned char) * numChannels);
pixel += numChannels;
}
for (unsigned int& offset : mipmapVector)
for (unsigned int& offset : mipmapOffsets)
offset *= numChannels;
image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data,
osg::Image::USE_NEW_DELETE, packing);
@ -1801,7 +1802,7 @@ namespace NifOsg
return nullptr;
}
image->setMipmapLevels(mipmapVector);
image->setMipmapLevels(mipmapOffsets);
image->flipVertical();
return image;

Loading…
Cancel
Save