diff --git a/components/nif/data.cpp b/components/nif/data.cpp index c7f9e71fda..34ec3b2831 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -13,8 +13,8 @@ namespace Nif if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 114)) nif->read(mGroupId); - // Note: has special meaning for NiPSysData (unimplemented) nif->read(mNumVertices); + bool hasData = recType != RC_NiPSysData || nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO3; if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) { @@ -22,7 +22,7 @@ namespace Nif nif->read(mCompressFlags); } - if (nif->get()) + if (nif->get() && hasData) nif->readVector(mVertices, mNumVertices); if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0)) @@ -34,7 +34,7 @@ namespace Nif nif->read(mMaterialHash); } - if (nif->get()) + if (nif->get() && hasData) { nif->readVector(mNormals, mNumVertices); if (mDataFlags & DataFlag_HasTangents) @@ -46,7 +46,7 @@ namespace Nif nif->read(mBoundingSphere); - if (nif->get()) + if (nif->get() && hasData) nif->readVector(mColors, mNumVertices); if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0)) @@ -64,13 +64,16 @@ namespace Nif else if (!nif->get()) numUVs = 0; - mUVList.resize(numUVs); - for (std::vector& list : mUVList) + if (hasData) { - 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(); + mUVList.resize(numUVs); + for (std::vector& list : mUVList) + { + 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)) @@ -146,64 +149,6 @@ namespace Nif mLines.shrink_to_fit(); } - void NiParticlesData::read(NIFStream* nif) - { - NiGeometryData::read(nif); - - // Should always match the number of vertices in theory, but doesn't: - // see mist.nif in Mistify mod (https://www.nexusmods.com/morrowind/mods/48112). - if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW) - nif->read(mNumParticles); - bool isBs202 = nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() != 0; - - bool numRadii = 1; - if (nif->getVersion() > NIFStream::generateVersion(10, 0, 1, 0)) - numRadii = (nif->get() && !isBs202) ? mNumVertices : 0; - nif->readVector(mRadii, numRadii); - nif->read(mActiveCount); - if (nif->get() && !isBs202) - nif->readVector(mSizes, mNumVertices); - - if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0)) - { - if (nif->get() && !isBs202) - nif->readVector(mRotations, mNumVertices); - if (nif->getVersion() >= NIFStream::generateVersion(20, 0, 0, 4)) - { - if (nif->get() && !isBs202) - nif->readVector(mRotationAngles, mNumVertices); - if (nif->get() && !isBs202) - nif->readVector(mRotationAxes, mNumVertices); - if (isBs202) - { - nif->read(mHasTextureIndices); - uint32_t numSubtextureOffsets; - if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3) - numSubtextureOffsets = nif->get(); - 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); - } - } - } - } - } - - void NiRotatingParticlesData::read(NIFStream* nif) - { - NiParticlesData::read(nif); - - if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0) && nif->get()) - nif->readVector(mRotations, mNumVertices); - } - void NiPosData::read(NIFStream* nif) { mKeyList = std::make_shared(); diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 1596579fdd..efab514223 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -70,32 +70,6 @@ namespace Nif void read(NIFStream* nif) override; }; - struct NiParticlesData : public NiGeometryData - { - uint16_t mNumParticles{ 0 }; - uint16_t mActiveCount; - - std::vector mRadii; - std::vector mSizes; - std::vector mRotations; - std::vector mRotationAngles; - std::vector mRotationAxes; - - bool mHasTextureIndices{ false }; - std::vector mSubtextureOffsets; - float mAspectRatio{ 1.f }; - uint16_t mAspectFlags{ 0 }; - float mAspectRatio2; - float mAspectSpeed, mAspectSpeed2; - - void read(NIFStream* nif) override; - }; - - struct NiRotatingParticlesData : public NiParticlesData - { - void read(NIFStream* nif) override; - }; - struct NiPosData : public Record { Vector3KeyMapPtr mKeyList; diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 66c4aaac7b..fe59bb8b9d 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -240,14 +240,8 @@ namespace Nif // GEOMETRY // 4.0.0.2 - { "NiAutoNormalParticles", &construct }, - { "NiAutoNormalParticlesData", &construct }, { "NiLines", &construct }, { "NiLinesData", &construct }, - { "NiParticles", &construct }, - { "NiParticlesData", &construct }, - { "NiRotatingParticles", &construct }, - { "NiRotatingParticlesData", &construct }, { "NiSkinData", &construct }, { "NiSkinInstance", &construct }, { "NiSkinPartition", &construct }, @@ -265,12 +259,26 @@ namespace Nif // PARTICLES + // Geometry, 4.0.0.2 + { "NiAutoNormalParticles", &construct }, + { "NiAutoNormalParticlesData", &construct }, + { "NiParticles", &construct }, + { "NiParticlesData", &construct }, + { "NiRotatingParticles", &construct }, + { "NiRotatingParticlesData", &construct }, + + // Geometry, Gamebryo + { "NiPSysData", &construct }, + // Modifiers, 4.0.0.2 { "NiGravity", &construct }, { "NiParticleColorModifier", &construct }, { "NiParticleGrowFade", &construct }, { "NiParticleRotation", &construct }, + // Modifier data, Gamebryo + { "NiPSysEmitterCtlrData", &construct }, + // Colliders, 4.0.0.2 { "NiPlanarCollider", &construct }, { "NiSphericalCollider", &construct }, diff --git a/components/nif/particle.cpp b/components/nif/particle.cpp index ae391c59e4..14898abf94 100644 --- a/components/nif/particle.cpp +++ b/components/nif/particle.cpp @@ -92,4 +92,97 @@ namespace Nif nif->read(mRotationSpeed); } + void NiParticlesData::read(NIFStream* nif) + { + NiGeometryData::read(nif); + + // Should always match the number of vertices in theory, but doesn't: + // see mist.nif in Mistify mod (https://www.nexusmods.com/morrowind/mods/48112). + if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW) + nif->read(mNumParticles); + bool isBs202 = nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() != 0; + + bool numRadii = 1; + if (nif->getVersion() > NIFStream::generateVersion(10, 0, 1, 0)) + numRadii = (nif->get() && !isBs202) ? mNumVertices : 0; + nif->readVector(mRadii, numRadii); + nif->read(mActiveCount); + if (nif->get() && !isBs202) + nif->readVector(mSizes, mNumVertices); + + if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0)) + { + if (nif->get() && !isBs202) + nif->readVector(mRotations, mNumVertices); + if (nif->getVersion() >= NIFStream::generateVersion(20, 0, 0, 4)) + { + if (nif->get() && !isBs202) + nif->readVector(mRotationAngles, mNumVertices); + if (nif->get() && !isBs202) + nif->readVector(mRotationAxes, mNumVertices); + if (isBs202) + { + nif->read(mHasTextureIndices); + uint32_t numSubtextureOffsets; + if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3) + numSubtextureOffsets = nif->get(); + 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); + } + } + } + } + } + + void NiRotatingParticlesData::read(NIFStream* nif) + { + NiParticlesData::read(nif); + + if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0) && nif->get()) + nif->readVector(mRotations, mNumVertices); + } + + void NiPSysData::read(NIFStream* nif) + { + NiParticlesData::read(nif); + + bool hasData = nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO3; + if (hasData) + { + mParticles.resize(mNumVertices); + for (NiParticleInfo& info : mParticles) + info.read(nif); + } + + if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76) + nif->skip(12); // Unknown + + if (nif->getVersion() >= NIFStream::generateVersion(20, 0, 0, 2) && nif->get() && hasData) + nif->readVector(mRotationSpeeds, mNumVertices); + + if (nif->getVersion() != NIFStream::generateVersion(20, 2, 0, 7) || nif->getBethVersion() == 0) + { + nif->read(mNumAddedParticles); + nif->read(mAddedParticlesBase); + } + } + + void NiPSysEmitterCtlrData::read(NIFStream* nif) + { + mFloatKeyList = std::make_shared(); + mVisKeyList = std::make_shared(); + uint32_t numVisKeys; + nif->read(numVisKeys); + for (size_t i = 0; i < numVisKeys; i++) + mVisKeyList->mKeys[nif->get()].mValue = nif->get() != 0; + } + } diff --git a/components/nif/particle.hpp b/components/nif/particle.hpp index 328498210a..084735a472 100644 --- a/components/nif/particle.hpp +++ b/components/nif/particle.hpp @@ -2,6 +2,7 @@ #define OPENMW_COMPONENTS_NIF_PARTICLE_HPP #include "base.hpp" +#include "data.hpp" namespace Nif { @@ -86,5 +87,49 @@ namespace Nif void read(NIFStream* nif) override; }; + struct NiParticlesData : NiGeometryData + { + uint16_t mNumParticles{ 0 }; + uint16_t mActiveCount; + + std::vector mRadii; + std::vector mSizes; + std::vector mRotations; + std::vector mRotationAngles; + std::vector mRotationAxes; + + bool mHasTextureIndices{ false }; + std::vector mSubtextureOffsets; + float mAspectRatio{ 1.f }; + uint16_t mAspectFlags{ 0 }; + float mAspectRatio2; + float mAspectSpeed, mAspectSpeed2; + + void read(NIFStream* nif) override; + }; + + struct NiRotatingParticlesData : NiParticlesData + { + void read(NIFStream* nif) override; + }; + + struct NiPSysData : NiParticlesData + { + std::vector mParticles; + std::vector mRotationSpeeds; + uint16_t mNumAddedParticles; + uint16_t mAddedParticlesBase; + + void read(NIFStream* nif) override; + }; + + struct NiPSysEmitterCtlrData : Record + { + FloatKeyMapPtr mFloatKeyList; + BoolKeyMapPtr mVisKeyList; + + void read(NIFStream* nif) override; + }; + } #endif diff --git a/components/nif/record.hpp b/components/nif/record.hpp index cecb9631d6..3013bef8de 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -169,6 +169,8 @@ namespace Nif RC_NiPlanarCollider, RC_NiPoint3Interpolator, RC_NiPosData, + RC_NiPSysEmitterCtlrData, + RC_NiPSysData, RC_NiRollController, RC_NiSequence, RC_NiSequenceStreamHelper,