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)
{
mKeys = std::make_shared<std::map<float, bool>>();
uint32_t numKeys;
nif->read(numKeys);
for (size_t i = 0; i < numKeys; i++)
mKeys = std::make_shared<std::vector<std::pair<float, bool>>>(nif->get<uint32_t>());
for (auto& [time, value] : *mKeys)
{
float time;
char value;
nif->read(time);
nif->read(value);
(*mKeys)[time] = (value != 0);
value = nif->get<uint8_t>() != 0;
}
}

View File

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

View File

@ -3,7 +3,7 @@
#ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP
#define OPENMW_COMPONENTS_NIF_NIFKEY_HPP
#include <map>
#include <utility>
#include <vector>
#include "exception.hpp"
@ -46,7 +46,8 @@ namespace Nif
template <typename T, T (NIFStream::*getValue)()>
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 KeyType = KeyT<T>;
@ -78,9 +79,13 @@ namespace Nif
uint32_t count;
nif->read(count);
if (count != 0 || morph)
if (count == 0 && !morph)
return;
nif->read(mInterpolationType);
mKeys.reserve(count);
KeyType key = {};
if (mInterpolationType == InterpolationType_Linear || mInterpolationType == InterpolationType_Constant)
@ -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<T>& key : tcbKeys)
mKeys[key.mTime] = KeyType{ std::move(key.mValue), std::move(key.mInTan), std::move(key.mOutTan) };
for (TCBKey<T>& 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:

View File

@ -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<FloatKeyMap>();
mFloatKeyList->read(nif);
mVisKeyList = std::make_shared<BoolKeyMap>();
uint32_t numVisKeys;
nif->read(numVisKeys);
for (size_t i = 0; i < numVisKeys; i++)
mVisKeyList->mKeys[nif->get<float>()].mValue = nif->get<uint8_t>() != 0;
mVisKeyList->mKeys.resize(nif->get<uint32_t>());
for (auto& [time, key] : mVisKeyList->mKeys)
{
nif->read(time);
key.mValue = nif->get<uint8_t>() != 0;
}
}
void NiPSysCollider::read(NIFStream* nif)

View File

@ -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<float, bool>& key) { return time < key.first; });
if (iter != mData->begin())
--iter;
return iter->second;

View File

@ -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<VisController>, public SceneUtil::Controller
{
private:
std::shared_ptr<std::map<float, bool>> mData;
std::shared_ptr<std::vector<std::pair<float, bool>>> mData;
BoolInterpolator mInterpolator;
unsigned int mMask{ 0u };