From 89774716fb451d77887ec1e36d034d239b76f3a5 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Tue, 5 Sep 2023 00:56:20 +0300 Subject: [PATCH] Modernize NiGeometryData --- .../nifloader/testbulletnifloader.cpp | 6 +- components/nif/data.cpp | 94 ++++++++++--------- components/nif/data.hpp | 26 ++++- components/nifbullet/bulletnifloader.cpp | 4 +- components/nifosg/nifloader.cpp | 16 ++-- 5 files changed, 84 insertions(+), 62 deletions(-) diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp index 022ba5dbbb..99f91aacb9 100644 --- a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -323,19 +323,19 @@ 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.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.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.mStrips = { { 0, 1, 2, 3 } }; diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 185684e4a3..7522543a58 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -11,72 +11,79 @@ namespace Nif 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 + 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); + bool hasVertices; + nif->read(hasVertices); + if (hasVertices) + 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 + nif->read(mMaterialHash); - if (nif->getBoolean()) + bool hasNormals; + nif->read(hasNormals); + if (hasNormals) { - 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(mCenter); + nif->read(mRadius); - if (nif->getBoolean()) - nif->readVector(colors, verts); + bool hasColors; + nif->read(hasColors); + if (hasColors) + 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; } bool hasUVs = true; if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW) - hasUVs = nif->getBoolean(); + nif->read(hasUVs); if (hasUVs) { - uvlist.resize(numUVs); - for (unsigned int i = 0; i < numUVs; i++) + mUVList.resize(numUVs); + for (std::vector& list : mUVList) { - nif->readVector(uvlist[i], verts); + nif->readVector(list, mNumVertices); // 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()); - } + for (osg::Vec2f& uv : list) + uv.y() = 1.f - uv.y(); } } if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0)) - nif->getUShort(); // Consistency flags + nif->read(mConsistencyType); if (nif->getVersion() >= NIFStream::generateVersion(20, 0, 0, 4)) nif->skip(4); // Additional data @@ -130,14 +137,13 @@ namespace Nif { NiGeometryData::read(nif); - size_t num = vertices.size(); std::vector 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; // 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) { @@ -146,9 +152,9 @@ namespace Nif } } // 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) { - mLines.emplace_back(num - 1); + mLines.emplace_back(mNumVertices - 1); mLines.emplace_back(0); } } @@ -164,21 +170,21 @@ namespace Nif 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()); + nif->readVector(particleRadii, mNumVertices); activeCount = nif->getUShort(); // Particle sizes if (nif->getBoolean()) - nif->readVector(sizes, vertices.size()); + nif->readVector(sizes, mNumVertices); if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0) && nif->getBoolean()) - nif->readVector(rotations, vertices.size()); + nif->readVector(rotations, mNumVertices); if (nif->getVersion() >= NIFStream::generateVersion(20, 0, 0, 4)) { if (nif->getBoolean()) - nif->readVector(rotationAngles, vertices.size()); + nif->readVector(rotationAngles, mNumVertices); if (nif->getBoolean()) - nif->readVector(rotationAxes, vertices.size()); + nif->readVector(rotationAxes, mNumVertices); } } @@ -192,7 +198,7 @@ namespace Nif bool hasRotations; nif->read(hasRotations); if (hasRotations) - nif->readVector(rotations, vertices.size()); + nif->readVector(rotations, mNumVertices); } void NiPosData::read(NIFStream* nif) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 3f548e361c..6791265b45 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -35,11 +35,27 @@ namespace Nif // Common ancestor for several data classes struct NiGeometryData : public Record { - std::vector vertices, normals, tangents, bitangents; - std::vector colors; - std::vector> 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 mVertices; + uint16_t mDataFlags{ 0 }; + uint32_t mMaterialHash; + std::vector mNormals, mTangents, mBitangents; + osg::Vec3f mCenter; + float mRadius; + std::vector mColors; + std::vector> mUVList; + uint16_t mConsistencyType; void read(NIFStream* nif) override; }; diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 51f8cae6c3..be27b04603 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -35,7 +35,7 @@ namespace void prepareTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriBasedGeomData& data) { // FIXME: copying vertices/indices individually is unreasonable - const std::vector& vertices = data.vertices; + const std::vector& vertices = data.mVertices; mesh.preallocateVertices(static_cast(vertices.size())); for (const osg::Vec3f& vertex : vertices) mesh.findOrAddVertex(Misc::Convert::toBullet(vertex), false); @@ -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()) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 87d6d19ab0..e56583e493 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1146,7 +1146,7 @@ namespace NifOsg 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,7 +1158,7 @@ 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)); @@ -1173,7 +1173,7 @@ namespace NifOsg } // 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->mRadius)); partsys->setInitialBound(box); } @@ -1346,9 +1346,9 @@ namespace NifOsg void handleNiGeometryData(osg::Geometry* geometry, const Nif::NiGeometryData* data, const std::vector& 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::const_iterator it = boundTextures.begin(); it != boundTextures.end(); ++it, ++textureStage) @@ -1473,7 +1473,7 @@ namespace NifOsg // above the actual renderable would be tedious. std::vector 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,