#include "anime_loader.hpp" using ::basalt::shared::math::FloatPoint; namespace basalt::shared::anime_loader { #pragma region Timeline Timeline::Timeline() : keyframes(), max_frame_cache(0) { // insert default frame for first index this->keyframes[0] = KeyFrame{.position = {0, 0, 0}, .rotation = {0, 0, 0, 1}}; } Timeline::~Timeline() {} void Timeline::add_key_frame(math::Index frame_index, KeyFrame frame_data) { // insert data this->keyframes[frame_index] = frame_data; // update max frame this->max_frame_cache = keyframes.rbegin()->first; } math::Index Timeline::max_frame() const { return this->max_frame_cache; } KeyFrameSpan Timeline::tick(math::Index frame_index) const { if (this->keyframes.empty()) [[unlikely]] { throw std::runtime_error("No key frames in timeline"); } // 首先用lower_bound获取到不小于自身的项目(肯定能获取到) auto lower_bound = this->keyframes.lower_bound(frame_index); // 获取下一帧,如果没有下一帧,就转换为自身 auto upper_bound = lower_bound; ++upper_bound; if (upper_bound == this->keyframes.end()) { upper_bound = lower_bound; } // 计算归一化时间 FloatPoint next_time = static_cast(frame_index - lower_bound->first) / static_cast(upper_bound->first - lower_bound->first + 1); FloatPoint prev_time = static_cast(1) - next_time; // 返回结构 return KeyFrameSpan{ .prev_time = prev_time, .next_time = next_time, .prev_position = lower_bound->second.position, .next_position = upper_bound->second.position, .prev_rotation = lower_bound->second.rotation, .next_rotation = upper_bound->second.rotation, }; } #pragma endregion #pragma region Anime Loader IAnimeLoader::IAnimeLoader() : status(AnimeLoaderStatus::Ready), time(0), timeline() {} IAnimeLoader::~IAnimeLoader() {} void IAnimeLoader::load(AnimeLoaderConfig&& config) { if (this->status != AnimeLoaderStatus::Ready) throw std::runtime_error("unexpected anime loader status"); this->config = std::move(config); this->status = AnimeLoaderStatus::Loaded; } KeyFrameSpan IAnimeLoader::tick() { if (this->status != AnimeLoaderStatus::Loaded) throw std::runtime_error("unexpected anime loader status"); // Accumulate time ++this->time; if (this->time > this->timeline.max_frame()) { this->time = 0; } // Return from timeline return this->timeline.tick(this->time); } #pragma endregion } // namespace basalt::shared::anime_loader