vkmEngine 1.0.0
A C++ game engine · vkmengine.com
Loading...
Searching...
No Matches
animation_track.h
1#pragma once
2
3#include <algorithm>
4#include <cmath>
5#include <cstddef>
6#include <vector>
7
8#include <glm/gtc/quaternion.hpp>
9
10#include "core/math/easing.h"
11
12namespace Vkm::Engine {
13
21template<typename T>
22class AnimationTrack {
23 public:
25 static constexpr float SAME_INSTANT_SECONDS = 1e-4f;
26
27 public:
28 AnimationTrack() = default;
29
35 explicit AnimationTrack(Easing easing) : m_easing(easing) {}
36
37 ~AnimationTrack() = default;
38
39 AnimationTrack(const AnimationTrack& other) = default;
40 AnimationTrack& operator=(const AnimationTrack& other) = default;
41
42 AnimationTrack(AnimationTrack && other) noexcept = default;
43 AnimationTrack& operator=(AnimationTrack && other) noexcept = default;
44
45 public:
56 size_t addKeyframe(float time, const T& value) {
57 auto it = std::upper_bound(m_times.begin(), m_times.end(), time);
58 auto index = static_cast<size_t>(it - m_times.begin());
59 m_times.insert(it, time);
60 m_values.insert(m_values.begin() + index, value);
61 return index;
62 }
63
72 void setKeyframe(float time, const T& value) {
73 for (size_t i = 0; i < m_times.size(); ++i) {
74 if (std::fabs(m_times[i] - time) < SAME_INSTANT_SECONDS) {
75 m_values[i] = value;
76 return;
77 }
78 }
79 addKeyframe(time, value);
80 }
81
87 void removeKeyframe(size_t index) {
88 if (index >= m_times.size()) return;
89 m_times.erase(m_times.begin() + static_cast<std::ptrdiff_t>(index));
90 m_values.erase(m_values.begin() + static_cast<std::ptrdiff_t>(index));
91 }
92
99 void setKeyframeValue(size_t index, const T& value) {
100 if (index >= m_values.size()) return;
101 m_values[index] = value;
102 }
103
113 size_t setKeyframeTime(size_t index, float time) {
114 if (index >= m_times.size()) return index;
115 T value = m_values[index];
116 removeKeyframe(index);
117 return addKeyframe(time, value);
118 }
119
120 void clear() {
121 m_times.clear();
122 m_values.clear();
123 }
124
130 void setEasing(Easing easing) {
131 m_easing = easing;
132 }
133
142 T getValue(float time) const {
143 if (m_times.empty()) {
144 return T{};
145 }
146
147 if (m_times.size() == 1) {
148 return m_values[0];
149 }
150
151 // Otherwise a first key past 0 reaches upper_bound == begin() and underflows prevIndex.
152 if (time <= m_times.front()) {
153 return m_values.front();
154 }
155
156 if (time >= m_times.back()) {
157 return m_values.back();
158 }
159
160 // time < m_times.back() here, so upper_bound never returns end().
161 auto it = std::upper_bound(m_times.begin(), m_times.end(), time);
162
163 const size_t nextIndex = static_cast<size_t>(it - m_times.begin());
164 const size_t prevIndex = nextIndex - 1;
165
166 float segmentDuration = m_times[nextIndex] - m_times[prevIndex];
167 if (segmentDuration <= 0.0f) {
168 return m_values[prevIndex];
169 }
170
171 float t = (time - m_times[prevIndex]) / segmentDuration;
172
173 const float eased = easingFunction(m_easing)(t);
174
175 return interpolate(m_values[prevIndex], m_values[nextIndex], eased);
176 }
177
183 float getDuration() const {
184 if (m_times.empty()) {
185 return 0.0f;
186 }
187 return m_times.back();
188 }
189
190 bool isEmpty() const {
191 return m_times.empty();
192 }
193
194 size_t keyframeCount() const {
195 return m_times.size();
196 }
197
203 Easing getEasing() const {
204 return m_easing;
205 }
206
212 const std::vector<float>& getTimes() const { return m_times; }
213
219 const std::vector<T>& getValues() const { return m_values; }
220
221 private:
231 template<typename U = T>
232 static U interpolate(const U& a, const U& b, float t) {
233 if constexpr (std::is_same_v<U, glm::quat>) {
234 return glm::slerp(a, b, t);
235 } else {
236 return glm::mix(a, b, t);
237 }
238 }
239
240 private:
241 std::vector<float> m_times;
242 std::vector<T> m_values;
243 Easing m_easing = Easing::Linear;
244};
245
246} // namespace Vkm::Engine
247
Keyframes of type T sorted by time, sampled through an easing curve.
Definition animation_track.h:22
size_t addKeyframe(float time, const T &value)
Add a keyframe at time.
Definition animation_track.h:56
float getDuration() const
The track's length.
Definition animation_track.h:183
size_t setKeyframeTime(size_t index, float time)
Move the keyframe at index to time.
Definition animation_track.h:113
const std::vector< float > & getTimes() const
The keyframe times, ascending.
Definition animation_track.h:212
static constexpr float SAME_INSTANT_SECONDS
Two keyframe times closer than this, in seconds, are one instant.
Definition animation_track.h:25
void removeKeyframe(size_t index)
Remove the keyframe at index; out of range is a no-op.
Definition animation_track.h:87
Easing getEasing() const
The curve this track eases with.
Definition animation_track.h:203
void setKeyframeValue(size_t index, const T &value)
Replace the value of the keyframe at index; out of range is a no-op.
Definition animation_track.h:99
void setKeyframe(float time, const T &value)
Set the value at time, replacing any keyframe already there.
Definition animation_track.h:72
T getValue(float time) const
Sample the track at time.
Definition animation_track.h:142
void setEasing(Easing easing)
Choose the curve this track eases its interpolation with.
Definition animation_track.h:130
const std::vector< T > & getValues() const
The keyframe values, parallel to getTimes.
Definition animation_track.h:219
AnimationTrack(Easing easing)
Construct a track that interpolates with easing.
Definition animation_track.h:35