💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

387 lines
8.5 KiB

  1. /*
  2. * Copyright (C) 2021 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #ifndef ANTKEEPER_ANIMATION_HPP
  20. #define ANTKEEPER_ANIMATION_HPP
  21. #include "animation-channel.hpp"
  22. #include <algorithm>
  23. #include <functional>
  24. #include <type_traits>
  25. #include <unordered_map>
  26. /**
  27. * Abstract base class for keyframe animations.
  28. */
  29. class animation_base
  30. {
  31. public:
  32. animation_base();
  33. /**
  34. * Advances the animation position (t) by @p dt.
  35. *
  36. * @param dt Delta time by which the animation position will be advanced.
  37. */
  38. virtual void advance(double dt) = 0;
  39. /**
  40. * Sets the animation position to @p t.
  41. *
  42. * @param t Position in time to which the animation position will be set.
  43. */
  44. void seek(double t);
  45. /// Sets the animation position to `0.0`.
  46. void rewind();
  47. /// Enables or disables looping of the animation.
  48. void loop(bool enabled);
  49. /// Pauses the animation.
  50. void pause();
  51. /// Plays the animation.
  52. void play();
  53. /// Stops the animation, rewinds it, and resets the loop count.
  54. void stop();
  55. /**
  56. * Sets the speed of the animation.
  57. *
  58. * @param speed Speed multiplier.
  59. */
  60. void set_speed(double speed);
  61. /// Returns `true` if looping of the animation is enabled, `false` otherwise.
  62. bool is_looped() const;
  63. /// Returns `true` if the animation is paused, `false` otherwise.
  64. bool is_paused() const;
  65. /// Returns `true` if the animation is stopped, `false` otherwise.
  66. bool is_stopped() const;
  67. /// Returns the current position in time of the animation.
  68. double get_position() const;
  69. /// Returns the current loop count of the animation.
  70. int get_loop_count() const;
  71. /// Returns the duration of the animation.
  72. virtual double get_duration() const = 0;
  73. /// Sets the callback that's executed when the animation is started from a stopped state.
  74. void set_start_callback(std::function<void()> callback);
  75. /// Sets the callback that's executed when a non-looped animation has finished.
  76. void set_end_callback(std::function<void()> callback);
  77. /**
  78. * Sets the callback that's executed when the animation loops.
  79. *
  80. * @param callback Loop callback function which is passed the current loop count.
  81. */
  82. void set_loop_callback(std::function<void(int)> callback);
  83. protected:
  84. bool looped;
  85. int loop_count;
  86. bool paused;
  87. bool stopped;
  88. double position;
  89. double speed;
  90. std::function<void()> start_callback;
  91. std::function<void()> end_callback;
  92. std::function<void(int)> loop_callback;
  93. };
  94. inline bool animation_base::is_looped() const
  95. {
  96. return looped;
  97. }
  98. inline bool animation_base::is_paused() const
  99. {
  100. return paused;
  101. }
  102. inline bool animation_base::is_stopped() const
  103. {
  104. return stopped;
  105. }
  106. inline double animation_base::get_position() const
  107. {
  108. return position;
  109. }
  110. inline int animation_base::get_loop_count() const
  111. {
  112. return loop_count;
  113. }
  114. /**
  115. * Keyframe animation.
  116. *
  117. * @tparam T Animated data type.
  118. */
  119. template <typename T>
  120. class animation: public animation_base
  121. {
  122. public:
  123. /// Channel for this animation type.
  124. typedef animation_channel<T> channel;
  125. // Keyframe type for this animation.
  126. typedef typename channel::keyframe keyframe;
  127. /// Interpolator function type.
  128. typedef typename std::decay<std::function<T(const T&, const T&, double)>>::type interpolator_type;
  129. /// Creates an animation.
  130. animation();
  131. /// @copydoc animation_base::advance()
  132. virtual void advance(double dt);
  133. /**
  134. * Adds a channel to the animation.
  135. *
  136. * @param id ID of the channel.
  137. * @return Added or pre-existing channel.
  138. */
  139. channel* add_channel(int id);
  140. /**
  141. * Removes a channel from the animation.
  142. *
  143. * @param id ID of the channel to remove.
  144. */
  145. void remove_channel(int id);
  146. /// Removes all channels from the animation.
  147. void remove_channels();
  148. /**
  149. * Sets the frame interpolator function object.
  150. *
  151. * @param interpolator Frame interpolator function object.
  152. */
  153. void set_interpolator(interpolator_type interpolator);
  154. /**
  155. * Sets the callback that's executed on each frame of animation.
  156. *
  157. * @param callback Frame callback which receives the index of an animation channel and value of an interpolated frame.
  158. */
  159. void set_frame_callback(std::function<void(int, const T&)> callback);
  160. /**
  161. * Returns the channel with the specified ID.
  162. *
  163. * @param id ID of the channel to get.
  164. */
  165. const channel* get_channel(int id) const;
  166. /// @copydoc animation::get_channel(int) const
  167. channel* get_channel(int id);
  168. /// @copydoc animation_base::get_duration() const
  169. virtual double get_duration() const;
  170. private:
  171. std::unordered_map<int, channel> channels;
  172. interpolator_type interpolator;
  173. std::function<void(int, const T&)> frame_callback;
  174. };
  175. template <typename T>
  176. animation<T>::animation():
  177. interpolator(nullptr),
  178. frame_callback(nullptr)
  179. {}
  180. template <typename T>
  181. void animation<T>::advance(double dt)
  182. {
  183. if (paused || stopped)
  184. {
  185. return;
  186. }
  187. // Advance position by dt
  188. position += dt * speed;
  189. // Determine duration of the animation
  190. double duration = get_duration();
  191. if (position < duration)
  192. {
  193. if (frame_callback != nullptr && interpolator != nullptr)
  194. {
  195. for (std::size_t i = 0; i < channels.size(); ++i)
  196. {
  197. auto frames = channels[i].find_keyframes(position);
  198. if (frames[0] != nullptr && frames[1] != nullptr)
  199. {
  200. // Calculate interpolated frame
  201. double t0 = std::get<0>(*frames[0]);
  202. double t1 = std::get<0>(*frames[1]);
  203. double alpha = (position - t0) / (t1 - t0);
  204. T frame = interpolator(std::get<1>(*frames[0]), std::get<1>(*frames[1]), alpha);
  205. // Pass frame to frame callback
  206. frame_callback(static_cast<int>(i), frame);
  207. }
  208. else if (frames[0] != nullptr)
  209. {
  210. // Pass frame to frame callback
  211. frame_callback(static_cast<int>(i), std::get<1>(*frames[0]));
  212. }
  213. else if (frames[1] != nullptr)
  214. {
  215. // Pass frame to frame callback
  216. frame_callback(static_cast<int>(i), std::get<1>(*frames[1]));
  217. }
  218. }
  219. }
  220. }
  221. else
  222. {
  223. if (looped)
  224. {
  225. ++loop_count;
  226. // Subtract duration of animation from position
  227. position -= duration;
  228. // Execute loop callback
  229. if (loop_callback)
  230. {
  231. loop_callback(loop_count);
  232. }
  233. // Call frame callback on looped frame
  234. if (frame_callback)
  235. {
  236. advance(0.0);
  237. }
  238. }
  239. else
  240. {
  241. // Call frame callback for end frame
  242. if (frame_callback != nullptr)
  243. {
  244. for (std::size_t i = 0; i < channels.size(); ++i)
  245. {
  246. auto frames = channels[i].find_keyframes(channels[i].get_duration());
  247. if (frames[0] != nullptr)
  248. {
  249. frame_callback(static_cast<int>(i), std::get<1>(*frames[0]));
  250. }
  251. }
  252. }
  253. stopped = true;
  254. // Call end callback
  255. if (end_callback)
  256. {
  257. end_callback();
  258. }
  259. }
  260. }
  261. }
  262. template <typename T>
  263. typename animation<T>::channel* animation<T>::add_channel(int id)
  264. {
  265. return &(*channels.emplace(id, id).first).second;
  266. }
  267. template <typename T>
  268. void animation<T>::remove_channel(int id)
  269. {
  270. auto it = channels.find(id);
  271. if (it != channels.end())
  272. {
  273. channels.erase(it);
  274. }
  275. }
  276. template <typename T>
  277. void animation<T>::remove_channels()
  278. {
  279. channels.clear();
  280. }
  281. template <typename T>
  282. void animation<T>::set_interpolator(interpolator_type interpolator)
  283. {
  284. this->interpolator = interpolator;
  285. }
  286. template <typename T>
  287. void animation<T>::set_frame_callback(std::function<void(int, const T&)> callback)
  288. {
  289. this->frame_callback = callback;
  290. }
  291. template <typename T>
  292. const typename animation<T>::channel* animation<T>::get_channel(int id) const
  293. {
  294. auto it = channels.find(id);
  295. if (it != channels.end())
  296. {
  297. return &it->second;
  298. }
  299. return nullptr;
  300. }
  301. template <typename T>
  302. typename animation<T>::channel* animation<T>::get_channel(int id)
  303. {
  304. auto it = channels.find(id);
  305. if (it != channels.end())
  306. {
  307. return &it->second;
  308. }
  309. return nullptr;
  310. }
  311. template <typename T>
  312. double animation<T>::get_duration() const
  313. {
  314. double duration = 0.0;
  315. for (auto it = channels.begin(); it != channels.end(); ++it)
  316. {
  317. duration = std::max<double>(duration, it->second.get_duration());
  318. }
  319. return duration;
  320. }
  321. #endif // ANTKEEPER_ANIMATION_HPP