mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-31 19:45:34 +00:00
Merge branch 'nif' into 'master'
Modernize NIF loader, part 2 See merge request OpenMW/openmw!3404
This commit is contained in:
commit
0a47e927d5
14 changed files with 645 additions and 551 deletions
|
@ -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
|
||||
|
||||
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);
|
||||
if (dataFlags & 0x1000)
|
||||
nif->read(mKeepFlags);
|
||||
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(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();
|
||||
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))
|
||||
std::fill(particleRadii.begin(), particleRadii.end(), nif->getFloat());
|
||||
else if (nif->getBoolean())
|
||||
nif->readVector(particleRadii, vertices.size());
|
||||
activeCount = nif->getUShort();
|
||||
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 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)
|
||||
{
|
||||
fmt = (Format)nif->getUInt();
|
||||
|
||||
if (nif->getVersion() < NIFStream::generateVersion(10, 4, 0, 2))
|
||||
mPixelFormat.read(nif);
|
||||
mPalette.read(nif);
|
||||
mMipmaps.resize(nif->get<uint32_t>());
|
||||
nif->read(mBytesPerPixel);
|
||||
for (Mipmap& mip : mMipmaps)
|
||||
{
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
colorMask[i] = nif->getUInt();
|
||||
bpp = nif->getUInt();
|
||||
nif->skip(8); // "Old Fast Compare". Whatever that means.
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
|
||||
pixelTiling = nif->getUInt();
|
||||
nif->read(mip.mWidth);
|
||||
nif->read(mip.mHeight);
|
||||
nif->read(mip.mOffset);
|
||||
}
|
||||
else // TODO: see if anything from here needs to be implemented
|
||||
{
|
||||
bpp = nif->getChar();
|
||||
nif->skip(4); // Renderer hint
|
||||
nif->skip(4); // Extra data
|
||||
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);
|
||||
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 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)
|
||||
{
|
||||
trafo.rotation = nif->getMatrix3();
|
||||
trafo.pos = nif->getVector3();
|
||||
trafo.scale = nif->getFloat();
|
||||
|
||||
int boneNum = nif->getInt();
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW
|
||||
&& nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 0))
|
||||
partitions.read(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();
|
||||
|
||||
bones.resize(boneNum);
|
||||
for (BoneInfo& bi : bones)
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW)
|
||||
{
|
||||
bi.trafo.rotation = nif->getMatrix3();
|
||||
bi.trafo.pos = nif->getVector3();
|
||||
bi.trafo.scale = nif->getFloat();
|
||||
bi.boundSphereCenter = nif->getVector3();
|
||||
bi.boundSphereRadius = nif->getFloat();
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 0))
|
||||
mPartitions.read(nif);
|
||||
|
||||
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)
|
||||
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 };
|
||||
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;
|
||||
std::vector<osg::Quat> rotations;
|
||||
std::vector<osg::Vec3f> rotationAxes;
|
||||
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 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
|
||||
{
|
||||
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
|
||||
{
|
||||
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 <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>
|
||||
|
||||
#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
|
||||
{
|
||||
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…
Reference in a new issue