431a9c00a5
On Linux and macOS, using Clang to compile OpenStarbound produces about 400 MB worth of warnings during the build, making the compiler output unreadable and slowing the build down considerably. 99% of the warnings were unqualified uses of std::move and std::forward, which are now all properly qualified. Fixed a few other minor warnings about non-virtual destructors and some uses of std::move preventing copy elision on temporary objects. Most remaining warnings are now unused parameters.
247 lines
7.2 KiB
C++
247 lines
7.2 KiB
C++
#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 = std::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
|