119 lines
3.3 KiB
C++
119 lines
3.3 KiB
C++
#pragma once
|
|
#include <string>
|
|
#include <vector>
|
|
#include <fstream>
|
|
#include <algorithm>
|
|
#include "../Defines.h"
|
|
#include "../Serializer.h"
|
|
|
|
// Path stores a recording detailing any type.
|
|
// Interpolator should be a functor with a method specification:
|
|
// T operator()(const T& a, const T& b, double time)
|
|
// Where a and b should be interpolated between, and time is a value between 0 and 1, indicating the interpolation value
|
|
template<typename T, typename Interpolator>
|
|
class Path
|
|
{
|
|
private:
|
|
std::vector<T> mFrames;
|
|
std::vector<double> mFrameTimes;
|
|
|
|
public:
|
|
Path() : mFrames(std::vector<T>()), mFrameTimes(std::vector<double>()) {}
|
|
~Path() {}
|
|
|
|
void SetStateAtTime(const double& time, const T& state)
|
|
{
|
|
// Find the position of this new state:
|
|
auto pos = std::lower_bound(mFrameTimes.begin(), mFrameTimes.end(), time);
|
|
size_t idx = pos - mFrameTimes.begin();
|
|
mFrames.insert(mFrames.begin() + idx, state);
|
|
mFrameTimes.insert(pos, time);
|
|
}
|
|
|
|
T GetStateAtTime(const double& time) const
|
|
{
|
|
// Find the frames between which the requested time takes place
|
|
size_t afterIdx = std::upper_bound(mFrameTimes.begin(), mFrameTimes.end(), time) - mFrameTimes.begin();
|
|
size_t beforeIdx = afterIdx - 1;
|
|
if (afterIdx == mFrameTimes.size()) // End of the recording, return the last camera state
|
|
return mFrames.back();
|
|
if (afterIdx == 0)
|
|
return mFrames.front();
|
|
|
|
// Now linearly interpolate the frames:
|
|
double t = ((time - mFrameTimes[beforeIdx]) / (mFrameTimes[afterIdx] - mFrameTimes[beforeIdx]));
|
|
|
|
const T& before = mFrames[beforeIdx];
|
|
const T& after = mFrames[afterIdx];
|
|
|
|
return Interpolator()(before, after, t);
|
|
}
|
|
|
|
double GetLength() const {
|
|
if (Empty()) return 0;
|
|
return mFrameTimes.back() - mFrameTimes.front();
|
|
}
|
|
|
|
size_t Size() const { return mFrames.size(); }
|
|
|
|
/// Removes the camera path node at index i
|
|
void RemoveAt(size_t i)
|
|
{
|
|
mFrames.erase(mFrames.begin() + i);
|
|
mFrameTimes.erase(mFrames.begin() + i);
|
|
}
|
|
|
|
/// Removes the last stored camera path node
|
|
void RemoveLast()
|
|
{
|
|
mFrames.pop_back();
|
|
mFrameTimes.pop_back();
|
|
}
|
|
|
|
// Makes sure the frametimes go from 0 to the length of the recording
|
|
void Normalize()
|
|
{
|
|
if (Empty()) return;
|
|
double offset = mFrameTimes[0];
|
|
for (size_t i = 0; i < mFrameTimes.size(); i++)
|
|
mFrameTimes[i] = mFrameTimes[i] - offset;
|
|
}
|
|
bool Empty() const { return mFrames.empty(); }
|
|
void Clear()
|
|
{
|
|
mFrames.clear();
|
|
mFrameTimes.clear();
|
|
}
|
|
|
|
void WriteToFile(const std::string& filename)
|
|
{
|
|
std::ofstream file(filename, std::ios::binary);
|
|
unsigned32 recordingLength = (unsigned32)mFrames.size();
|
|
Serializer<unsigned32>::Serialize(recordingLength, file);
|
|
if (recordingLength > 0)
|
|
{
|
|
Serializer<T*>::Serialize(&mFrames[0], mFrames.size(), file);
|
|
Serializer<double*>::Serialize(&mFrameTimes[0], mFrames.size(), file);
|
|
}
|
|
file.close();
|
|
}
|
|
|
|
bool ReadFromFile(const std::string& filename)
|
|
{
|
|
std::ifstream file(filename, std::ios::binary);
|
|
if (file.good()) {
|
|
unsigned32 recordingLength = 0;
|
|
Serializer<unsigned32>::Deserialize(recordingLength, file);
|
|
mFrames.resize(recordingLength);
|
|
mFrameTimes.resize(recordingLength);
|
|
if (recordingLength > 0)
|
|
{
|
|
Serializer<T*>::Deserialize(&mFrames[0], mFrames.size(), file);
|
|
Serializer<double*>::Deserialize(&mFrameTimes[0], mFrames.size(), file);
|
|
}
|
|
file.close();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}; |