From 9c70ea9b6d63f0379f58849dc8b33ef26c7c3d06 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Sat, 10 Jan 2026 20:29:50 +0800 Subject: [PATCH] add timeline for anime loader --- .../ChickenNuggetAnimeLoader/main.cpp | 23 +++--- .../Shared/basalt/anime_loader.cpp | 70 +++++++++++++------ .../Shared/basalt/anime_loader.hpp | 30 ++++---- 3 files changed, 78 insertions(+), 45 deletions(-) diff --git a/BasaltPresenter/Plugins/AnimeLoader/ChickenNuggetAnimeLoader/main.cpp b/BasaltPresenter/Plugins/AnimeLoader/ChickenNuggetAnimeLoader/main.cpp index 96694df..bf8e6f3 100644 --- a/BasaltPresenter/Plugins/AnimeLoader/ChickenNuggetAnimeLoader/main.cpp +++ b/BasaltPresenter/Plugins/AnimeLoader/ChickenNuggetAnimeLoader/main.cpp @@ -19,17 +19,18 @@ public: virtual void load(AnimeLoaderConfig&& config) override { IAnimeLoader::load(std::move(config)); - this->frames.emplace(60 * 0, KeyFrame{.position = {F(0), F(-10), F(10)}, .rotation = {F(0.382683), F(0), F(0), F(0.92388)}}); - this->frames.emplace(60 * 1, - KeyFrame{.position = {F(-10), F(0), F(10)}, - .rotation = {F(0.270598), F(-0.270598), F(-0.653282), F(0.653282)}}); - this->frames.emplace(60 * 2, KeyFrame{.position = {F(0), F(10), F(10)}, .rotation = {F(0), F(-0.382683), F(-0.92388), F(0)}}); - this->frames.emplace(60 * 3, - KeyFrame{.position = {F(10), F(0), F(10)}, - .rotation = {F(-0.270598), F(-0.270598), F(-0.653282), F(-0.653282)}}); - this->frames.emplace(60 * 4, - KeyFrame{.position = {F(0), F(-10), F(10)}, - .rotation = {F(0.382683), F(0), F(0), F(0.92388)}}); // Same as the first + this->timeline.add_key_frame(60 * 0, KeyFrame{.position = {F(0), F(-10), F(10)}, .rotation = {F(0.382683), F(0), F(0), F(0.92388)}}); + this->timeline.add_key_frame(60 * 1, + KeyFrame{.position = {F(-10), F(0), F(10)}, + .rotation = {F(0.270598), F(-0.270598), F(-0.653282), F(0.653282)}}); + this->timeline.add_key_frame(60 * 2, + KeyFrame{.position = {F(0), F(10), F(10)}, .rotation = {F(0), F(-0.382683), F(-0.92388), F(0)}}); + this->timeline.add_key_frame(60 * 3, + KeyFrame{.position = {F(10), F(0), F(10)}, + .rotation = {F(-0.270598), F(-0.270598), F(-0.653282), F(-0.653282)}}); + this->timeline.add_key_frame(60 * 4, + KeyFrame{.position = {F(0), F(-10), F(10)}, + .rotation = {F(0.382683), F(0), F(0), F(0.92388)}}); // Same as the first } }; diff --git a/BasaltPresenter/Shared/basalt/anime_loader.cpp b/BasaltPresenter/Shared/basalt/anime_loader.cpp index 21f5f6c..07efc5d 100644 --- a/BasaltPresenter/Shared/basalt/anime_loader.cpp +++ b/BasaltPresenter/Shared/basalt/anime_loader.cpp @@ -4,42 +4,42 @@ using ::basalt::shared::math::FloatPoint; namespace basalt::shared::anime_loader { -#pragma region Anime Loader +#pragma region Timeline - IAnimeLoader::IAnimeLoader() : status(AnimeLoaderStatus::Ready), time(0) {} - - 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; + 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}}; } - KeyFrameSpan IAnimeLoader::tick() { - if (this->status != AnimeLoaderStatus::Loaded) throw std::runtime_error("unexpected anime loader status"); + Timeline::~Timeline() {} - // Get last frame index - auto last_pair = this->frames.end(); - --last_pair; - auto last_index = last_pair->first; - // Accumulate time - ++this->time; - if (this->time > last_index) { - this->time = 0; + 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->frames.lower_bound(this->time); + auto lower_bound = this->keyframes.lower_bound(frame_index); // 获取下一帧,如果没有下一帧,就转换为自身 auto upper_bound = lower_bound; ++upper_bound; - if (upper_bound == this->frames.end()) { + if (upper_bound == this->keyframes.end()) { upper_bound = lower_bound; } // 计算归一化时间 - FloatPoint next_time = static_cast(this->time - lower_bound->first) + 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; @@ -56,4 +56,30 @@ namespace basalt::shared::anime_loader { #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 diff --git a/BasaltPresenter/Shared/basalt/anime_loader.hpp b/BasaltPresenter/Shared/basalt/anime_loader.hpp index 1934458..b997f78 100644 --- a/BasaltPresenter/Shared/basalt/anime_loader.hpp +++ b/BasaltPresenter/Shared/basalt/anime_loader.hpp @@ -6,15 +6,6 @@ namespace basalt::shared::anime_loader { - //struct KeyFrame { - // math::Index frame_index; - // math::Matrix4x4 transform; - //}; - - //struct KeyFrameCompare { - // bool operator()(const KeyFrame& lhs, const KeyFrame& rhs) const { return lhs.time < rhs.time; } - //}; - struct KeyFrame { math::Vector3 position; math::Quaternion rotation; @@ -29,6 +20,21 @@ namespace basalt::shared::anime_loader { math::Quaternion next_rotation; ///< 后一帧的摄像机旋转。 }; + class Timeline { + public: + Timeline(); + ~Timeline(); + + public: + void add_key_frame(math::Index frame_index, KeyFrame frame_data); + math::Index max_frame() const; + KeyFrameSpan tick(math::Index frame_index) const; + + private: + math::Index max_frame_cache; + std::map keyframes; + }; + struct AnimeLoaderConfig { char_types::BSString filename; ///< The file to be loaded by loader. }; @@ -53,8 +59,8 @@ namespace basalt::shared::anime_loader { /** * @brief * @remarks - * \li 重写时只允许往frames里插入数据。 - * \li 重写时务必保证frames里插入的数据大于2个,且初始节点的Key总是0。动画帧个数必须大于4。 + * \li 重写时只允许往timeline里插入数据。 + * \li 重写时务必保证timeline里插入的数据大于2个,且初始节点的Key总是0。动画帧个数必须大于4。 */ virtual void load(AnimeLoaderConfig&& config); KeyFrameSpan tick(); @@ -63,7 +69,7 @@ namespace basalt::shared::anime_loader { AnimeLoaderStatus status; AnimeLoaderConfig config; math::Index time; - std::map frames; + Timeline timeline; }; } // namespace basalt::shared::anime_loader