diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 29b11bd806..a134749bdc 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -247,16 +247,11 @@ namespace Nif void NiVisData::read(NIFStream* nif) { - mKeys = std::make_shared>(); - uint32_t numKeys; - nif->read(numKeys); - for (size_t i = 0; i < numKeys; i++) + mKeys = std::make_shared>>(nif->get()); + for (auto& [time, value] : *mKeys) { - float time; - char value; nif->read(time); - nif->read(value); - (*mKeys)[time] = (value != 0); + value = nif->get() != 0; } } diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 1ccd2919b7..4000055e8c 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -193,8 +193,8 @@ namespace Nif struct NiVisData : public Record { - // TODO: investigate possible use of BoolKeyMap - std::shared_ptr> mKeys; + // This is theoretically a "flat map" sorted by time + std::shared_ptr>> mKeys; void read(NIFStream* nif) override; }; diff --git a/components/nif/nifkey.hpp b/components/nif/nifkey.hpp index 5a755c8d0d..e32ef76d95 100644 --- a/components/nif/nifkey.hpp +++ b/components/nif/nifkey.hpp @@ -3,7 +3,7 @@ #ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP #define OPENMW_COMPONENTS_NIF_NIFKEY_HPP -#include +#include #include #include "exception.hpp" @@ -46,7 +46,8 @@ namespace Nif template struct KeyMapT { - using MapType = std::map>; + // This is theoretically a "flat map" sorted by time + using MapType = std::vector>>; using ValueType = T; using KeyType = KeyT; @@ -78,8 +79,12 @@ namespace Nif uint32_t count; nif->read(count); - if (count != 0 || morph) - nif->read(mInterpolationType); + if (count == 0 && !morph) + return; + + nif->read(mInterpolationType); + + mKeys.reserve(count); KeyType key = {}; @@ -90,7 +95,7 @@ namespace Nif float time; nif->read(time); readValue(*nif, key); - mKeys[time] = key; + mKeys.emplace_back(time, key); } } else if (mInterpolationType == InterpolationType_Quadratic) @@ -100,7 +105,7 @@ namespace Nif float time; nif->read(time); readQuadratic(*nif, key); - mKeys[time] = key; + mKeys.emplace_back(time, key); } } else if (mInterpolationType == InterpolationType_TCB) @@ -115,8 +120,9 @@ namespace Nif nif->read(tcbKey.mBias); } generateTCBTangents(tcbKeys); - for (TCBKey& key : tcbKeys) - mKeys[key.mTime] = KeyType{ std::move(key.mValue), std::move(key.mInTan), std::move(key.mOutTan) }; + for (TCBKey& tcbKey : tcbKeys) + mKeys.emplace_back(std::move(tcbKey.mTime), + KeyType{ std::move(tcbKey.mValue), std::move(tcbKey.mInTan), std::move(tcbKey.mOutTan) }); } else if (mInterpolationType == InterpolationType_XYZ) { @@ -132,6 +138,8 @@ namespace Nif throw Nif::Exception("Unhandled interpolation type: " + std::to_string(mInterpolationType), nif->getFile().getFilename()); } + + // Note: NetImmerse does NOT sort keys or remove duplicates } private: diff --git a/components/nif/particle.cpp b/components/nif/particle.cpp index d81d423fb6..9249541717 100644 --- a/components/nif/particle.cpp +++ b/components/nif/particle.cpp @@ -676,12 +676,16 @@ namespace Nif void NiPSysEmitterCtlrData::read(NIFStream* nif) { + // TODO: this is not used in the official files and needs verification mFloatKeyList = std::make_shared(); + mFloatKeyList->read(nif); 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; + mVisKeyList->mKeys.resize(nif->get()); + for (auto& [time, key] : mVisKeyList->mKeys) + { + nif->read(time); + key.mValue = nif->get() != 0; + } } void NiPSysCollider::read(NIFStream* nif) diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 7e4c5da7a0..ee3d0dd45e 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -374,7 +374,8 @@ namespace NifOsg if (mData->empty()) return true; - auto iter = mData->upper_bound(time); + auto iter = std::upper_bound(mData->begin(), mData->end(), time, + [](float time, const std::pair& key) { return time < key.first; }); if (iter != mData->begin()) --iter; return iter->second; diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index 468668ce76..41c8027eea 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -54,7 +54,8 @@ namespace NifOsg return mLastHighKey; } - return mKeys->mKeys.lower_bound(time); + return std::lower_bound(mKeys->mKeys.begin(), mKeys->mKeys.end(), time, + [](const typename MapT::MapType::value_type& key, float t) { return key.first < t; }); } public: @@ -99,8 +100,8 @@ namespace NifOsg const typename MapT::MapType& keys = mKeys->mKeys; - if (time <= keys.begin()->first) - return keys.begin()->second.mValue; + if (time <= keys.front().first) + return keys.front().second.mValue; typename MapT::MapType::const_iterator it = retrieveKey(time); @@ -116,7 +117,7 @@ namespace NifOsg return interpolate(mLastLowKey->second, mLastHighKey->second, a, mKeys->mInterpolationType); } - return keys.rbegin()->second.mValue; + return keys.back().second.mValue; } bool empty() const { return !mKeys || mKeys->mKeys.empty(); } @@ -283,7 +284,7 @@ namespace NifOsg class VisController : public SceneUtil::NodeCallback, public SceneUtil::Controller { private: - std::shared_ptr> mData; + std::shared_ptr>> mData; BoolInterpolator mInterpolator; unsigned int mMask{ 0u };