Assume NIF controller data is already sorted (#8545)

This commit is contained in:
Alexei Kotov 2025-07-08 22:36:43 +03:00
parent c90ae89381
commit 93cb69b012
6 changed files with 37 additions and 28 deletions

View File

@ -247,16 +247,11 @@ namespace Nif
void NiVisData::read(NIFStream* nif) void NiVisData::read(NIFStream* nif)
{ {
mKeys = std::make_shared<std::map<float, bool>>(); mKeys = std::make_shared<std::vector<std::pair<float, bool>>>(nif->get<uint32_t>());
uint32_t numKeys; for (auto& [time, value] : *mKeys)
nif->read(numKeys);
for (size_t i = 0; i < numKeys; i++)
{ {
float time;
char value;
nif->read(time); nif->read(time);
nif->read(value); value = nif->get<uint8_t>() != 0;
(*mKeys)[time] = (value != 0);
} }
} }

View File

@ -193,8 +193,8 @@ namespace Nif
struct NiVisData : public Record struct NiVisData : public Record
{ {
// TODO: investigate possible use of BoolKeyMap // This is theoretically a "flat map" sorted by time
std::shared_ptr<std::map<float, bool>> mKeys; std::shared_ptr<std::vector<std::pair<float, bool>>> mKeys;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };

View File

@ -3,7 +3,7 @@
#ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP #ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP
#define OPENMW_COMPONENTS_NIF_NIFKEY_HPP #define OPENMW_COMPONENTS_NIF_NIFKEY_HPP
#include <map> #include <utility>
#include <vector> #include <vector>
#include "exception.hpp" #include "exception.hpp"
@ -46,7 +46,8 @@ namespace Nif
template <typename T, T (NIFStream::*getValue)()> template <typename T, T (NIFStream::*getValue)()>
struct KeyMapT struct KeyMapT
{ {
using MapType = std::map<float, KeyT<T>>; // This is theoretically a "flat map" sorted by time
using MapType = std::vector<std::pair<float, KeyT<T>>>;
using ValueType = T; using ValueType = T;
using KeyType = KeyT<T>; using KeyType = KeyT<T>;
@ -78,8 +79,12 @@ namespace Nif
uint32_t count; uint32_t count;
nif->read(count); nif->read(count);
if (count != 0 || morph) if (count == 0 && !morph)
nif->read(mInterpolationType); return;
nif->read(mInterpolationType);
mKeys.reserve(count);
KeyType key = {}; KeyType key = {};
@ -90,7 +95,7 @@ namespace Nif
float time; float time;
nif->read(time); nif->read(time);
readValue(*nif, key); readValue(*nif, key);
mKeys[time] = key; mKeys.emplace_back(time, key);
} }
} }
else if (mInterpolationType == InterpolationType_Quadratic) else if (mInterpolationType == InterpolationType_Quadratic)
@ -100,7 +105,7 @@ namespace Nif
float time; float time;
nif->read(time); nif->read(time);
readQuadratic(*nif, key); readQuadratic(*nif, key);
mKeys[time] = key; mKeys.emplace_back(time, key);
} }
} }
else if (mInterpolationType == InterpolationType_TCB) else if (mInterpolationType == InterpolationType_TCB)
@ -115,8 +120,9 @@ namespace Nif
nif->read(tcbKey.mBias); nif->read(tcbKey.mBias);
} }
generateTCBTangents(tcbKeys); generateTCBTangents(tcbKeys);
for (TCBKey<T>& key : tcbKeys) for (TCBKey<T>& tcbKey : tcbKeys)
mKeys[key.mTime] = KeyType{ std::move(key.mValue), std::move(key.mInTan), std::move(key.mOutTan) }; 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) else if (mInterpolationType == InterpolationType_XYZ)
{ {
@ -132,6 +138,8 @@ namespace Nif
throw Nif::Exception("Unhandled interpolation type: " + std::to_string(mInterpolationType), throw Nif::Exception("Unhandled interpolation type: " + std::to_string(mInterpolationType),
nif->getFile().getFilename()); nif->getFile().getFilename());
} }
// Note: NetImmerse does NOT sort keys or remove duplicates
} }
private: private:

View File

@ -676,12 +676,16 @@ namespace Nif
void NiPSysEmitterCtlrData::read(NIFStream* nif) void NiPSysEmitterCtlrData::read(NIFStream* nif)
{ {
// TODO: this is not used in the official files and needs verification
mFloatKeyList = std::make_shared<FloatKeyMap>(); mFloatKeyList = std::make_shared<FloatKeyMap>();
mFloatKeyList->read(nif);
mVisKeyList = std::make_shared<BoolKeyMap>(); mVisKeyList = std::make_shared<BoolKeyMap>();
uint32_t numVisKeys; mVisKeyList->mKeys.resize(nif->get<uint32_t>());
nif->read(numVisKeys); for (auto& [time, key] : mVisKeyList->mKeys)
for (size_t i = 0; i < numVisKeys; i++) {
mVisKeyList->mKeys[nif->get<float>()].mValue = nif->get<uint8_t>() != 0; nif->read(time);
key.mValue = nif->get<uint8_t>() != 0;
}
} }
void NiPSysCollider::read(NIFStream* nif) void NiPSysCollider::read(NIFStream* nif)

View File

@ -374,7 +374,8 @@ namespace NifOsg
if (mData->empty()) if (mData->empty())
return true; return true;
auto iter = mData->upper_bound(time); auto iter = std::upper_bound(mData->begin(), mData->end(), time,
[](float time, const std::pair<float, bool>& key) { return time < key.first; });
if (iter != mData->begin()) if (iter != mData->begin())
--iter; --iter;
return iter->second; return iter->second;

View File

@ -54,7 +54,8 @@ namespace NifOsg
return mLastHighKey; 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: public:
@ -99,8 +100,8 @@ namespace NifOsg
const typename MapT::MapType& keys = mKeys->mKeys; const typename MapT::MapType& keys = mKeys->mKeys;
if (time <= keys.begin()->first) if (time <= keys.front().first)
return keys.begin()->second.mValue; return keys.front().second.mValue;
typename MapT::MapType::const_iterator it = retrieveKey(time); typename MapT::MapType::const_iterator it = retrieveKey(time);
@ -116,7 +117,7 @@ namespace NifOsg
return interpolate(mLastLowKey->second, mLastHighKey->second, a, mKeys->mInterpolationType); 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(); } bool empty() const { return !mKeys || mKeys->mKeys.empty(); }
@ -283,7 +284,7 @@ namespace NifOsg
class VisController : public SceneUtil::NodeCallback<VisController>, public SceneUtil::Controller class VisController : public SceneUtil::NodeCallback<VisController>, public SceneUtil::Controller
{ {
private: private:
std::shared_ptr<std::map<float, bool>> mData; std::shared_ptr<std::vector<std::pair<float, bool>>> mData;
BoolInterpolator mInterpolator; BoolInterpolator mInterpolator;
unsigned int mMask{ 0u }; unsigned int mMask{ 0u };