diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
commit | 6352e8e3196f78388b6c771073f9e03eaa612673 (patch) | |
tree | e23772f79a7fbc41bc9108951e9e136857484bf4 /source/core/StarNetElementFloatFields.hpp | |
parent | 6741a057e5639280d85d0f88ba26f000baa58f61 (diff) |
everything everywhere
all at once
Diffstat (limited to 'source/core/StarNetElementFloatFields.hpp')
-rw-r--r-- | source/core/StarNetElementFloatFields.hpp | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/source/core/StarNetElementFloatFields.hpp b/source/core/StarNetElementFloatFields.hpp new file mode 100644 index 0000000..835b3ec --- /dev/null +++ b/source/core/StarNetElementFloatFields.hpp @@ -0,0 +1,246 @@ +#ifndef STAR_NET_ELEMENT_FLOAT_FIELDS_HPP +#define STAR_NET_ELEMENT_FLOAT_FIELDS_HPP + +#include <type_traits> + +#include "StarNetElement.hpp" +#include "StarInterpolation.hpp" + +namespace Star { + +STAR_EXCEPTION(StepStreamException, StarException); + +template <typename T> +class NetElementFloating : public NetElement { +public: + T get() const; + void set(T value); + + // If a fixed point base is given, then instead of transmitting the value as + // a float, it is transmitted as a VLQ of the value divided by the fixed + // point base. Any NetElementFloating that is transmitted to must also have + // the same fixed point base set. + void setFixedPointBase(Maybe<T> fixedPointBase = {}); + + // If interpolation is enabled on the NetStepStates parent, and an + // interpolator is set, then on steps in between data points this will be + // used to interpolate this value. It is not necessary that senders and + // receivers both have matching interpolation functions, or any interpolation + // functions at all. + void setInterpolator(function<T(T, T, T)> interpolator); + + void initNetVersion(NetElementVersion const* version = nullptr) override; + + // Values are never interpolated, but they will be delayed for the given + // interpolationTime. + void enableNetInterpolation(float extrapolationHint = 0.0f) override; + void disableNetInterpolation() override; + void tickNetInterpolation(float dt) override; + + void netStore(DataStream& ds) const override; + void netLoad(DataStream& ds) override; + + bool writeNetDelta(DataStream& ds, uint64_t fromVersion) const override; + void readNetDelta(DataStream& ds, float interpolationTime = 0.0f) override; + void blankNetDelta(float interpolationTime = 0.0f) override; + +private: + void writeValue(DataStream& ds, T t) const; + T readValue(DataStream& ds) const; + + T interpolate() const; + + Maybe<T> m_fixedPointBase; + NetElementVersion const* m_netVersion = nullptr; + uint64_t m_latestUpdateVersion = 0; + T m_value = T(); + + function<T(T, T, T)> m_interpolator; + float m_extrapolation = 0.0f; + Maybe<Deque<pair<float, T>>> m_interpolationDataPoints; +}; + +typedef NetElementFloating<float> NetElementFloat; +typedef NetElementFloating<double> NetElementDouble; + +template <typename T> +T NetElementFloating<T>::get() const { + return m_value; +} + +template <typename T> +void NetElementFloating<T>::set(T value) { + if (m_value != value) { + // Only mark the step as updated here if it actually would change the + // transmitted value. + if (!m_fixedPointBase || round(m_value / *m_fixedPointBase) != round(value / *m_fixedPointBase)) + m_latestUpdateVersion = m_netVersion ? m_netVersion->current() : 0; + + m_value = value; + + if (m_interpolationDataPoints) { + m_interpolationDataPoints->clear(); + m_interpolationDataPoints->append({0.0f, m_value}); + } + } +} + +template <typename T> +void NetElementFloating<T>::setFixedPointBase(Maybe<T> fixedPointBase) { + m_fixedPointBase = fixedPointBase; +} + +template <typename T> +void NetElementFloating<T>::setInterpolator(function<T(T, T, T)> interpolator) { + m_interpolator = move(interpolator); +} + +template <typename T> +void NetElementFloating<T>::initNetVersion(NetElementVersion const* version) { + m_netVersion = version; + m_latestUpdateVersion = 0; +} + +template <typename T> +void NetElementFloating<T>::enableNetInterpolation(float extrapolationHint) { + m_extrapolation = extrapolationHint; + if (!m_interpolationDataPoints) { + m_interpolationDataPoints.emplace(); + m_interpolationDataPoints->append({0.0f, m_value}); + } +} + +template <typename T> +void NetElementFloating<T>::disableNetInterpolation() { + if (m_interpolationDataPoints) { + m_value = m_interpolationDataPoints->last().second; + m_interpolationDataPoints.reset(); + } +} + +template <typename T> +void NetElementFloating<T>::tickNetInterpolation(float dt) { + if (m_interpolationDataPoints) { + for (auto& p : *m_interpolationDataPoints) + p.first -= dt; + + while (m_interpolationDataPoints->size() > 2 && (*m_interpolationDataPoints)[1].first <= 0.0f) + m_interpolationDataPoints->removeFirst(); + + m_value = interpolate(); + } +} + +template <typename T> +void NetElementFloating<T>::netStore(DataStream& ds) const { + if (m_interpolationDataPoints) + writeValue(ds, m_interpolationDataPoints->last().second); + else + writeValue(ds, m_value); +} + +template <typename T> +void NetElementFloating<T>::netLoad(DataStream& ds) { + m_value = readValue(ds); + m_latestUpdateVersion = m_netVersion ? m_netVersion->current() : 0; + if (m_interpolationDataPoints) { + m_interpolationDataPoints->clear(); + m_interpolationDataPoints->append({0.0f, m_value}); + } +} + +template <typename T> +bool NetElementFloating<T>::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { + if (m_latestUpdateVersion < fromVersion) + return false; + + if (m_interpolationDataPoints) + writeValue(ds, m_interpolationDataPoints->last().second); + else + writeValue(ds, m_value); + + return true; +} + +template <typename T> +void NetElementFloating<T>::readNetDelta(DataStream& ds, float interpolationTime) { + T t = readValue(ds); + + m_latestUpdateVersion = m_netVersion ? m_netVersion->current() : 0; + if (m_interpolationDataPoints) { + if (interpolationTime < m_interpolationDataPoints->last().first) + m_interpolationDataPoints->clear(); + m_interpolationDataPoints->append({interpolationTime, t}); + m_value = interpolate(); + } else { + m_value = t; + } +} + +template <typename T> +void NetElementFloating<T>::blankNetDelta(float interpolationTime) { + if (m_interpolationDataPoints) { + auto lastPoint = m_interpolationDataPoints->last(); + float lastTime = lastPoint.first; + lastPoint.first = interpolationTime; + if (interpolationTime < lastTime) + *m_interpolationDataPoints = {lastPoint}; + else + m_interpolationDataPoints->append(lastPoint); + + m_value = interpolate(); + } +} + +template <typename T> +void NetElementFloating<T>::writeValue(DataStream& ds, T t) const { + if (m_fixedPointBase) + ds.writeVlqI(round(t / *m_fixedPointBase)); + else + ds.write(t); +} + +template <typename T> +T NetElementFloating<T>::readValue(DataStream& ds) const { + T t; + if (m_fixedPointBase) + t = ds.readVlqI() * *m_fixedPointBase; + else + ds.read(t); + return t; +} + +template <typename T> +T NetElementFloating<T>::interpolate() const { + auto& dataPoints = *m_interpolationDataPoints; + + float ipos = inverseLinearInterpolateUpper(dataPoints.begin(), dataPoints.end(), 0.0f, + [](float lhs, auto const& rhs) { + return lhs < rhs.first; + }, [](auto const& dataPoint) { + return dataPoint.first; + }); + auto bound = getBound2(ipos, dataPoints.size(), BoundMode::Extrapolate); + + if (m_interpolator) { + auto const& minPoint = dataPoints[bound.i0]; + auto const& maxPoint = dataPoints[bound.i1]; + + // If step separation is less than 1.0, don't normalize extrapolation to + // the very small step difference, because this can result in large jumps + // during jitter. + float stepDist = max(maxPoint.first - minPoint.first, 1.0f); + float offset = clamp<float>(bound.offset, 0.0f, 1.0f + m_extrapolation / stepDist); + return m_interpolator(offset, minPoint.second, maxPoint.second); + + } else { + if (bound.offset < 1.0f) + return dataPoints[bound.i0].second; + else + return dataPoints[bound.i1].second; + } +} + +} + +#endif |