1
0

add timeline for anime loader

This commit is contained in:
2026-01-10 20:29:50 +08:00
parent 2379a23477
commit 9c70ea9b6d
3 changed files with 78 additions and 45 deletions

View File

@@ -19,17 +19,18 @@ public:
virtual void load(AnimeLoaderConfig&& config) override { virtual void load(AnimeLoaderConfig&& config) override {
IAnimeLoader::load(std::move(config)); 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->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->frames.emplace(60 * 1, this->timeline.add_key_frame(60 * 1,
KeyFrame{.position = {F(-10), F(0), F(10)}, KeyFrame{.position = {F(-10), F(0), F(10)},
.rotation = {F(0.270598), F(-0.270598), F(-0.653282), F(0.653282)}}); .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->timeline.add_key_frame(60 * 2,
this->frames.emplace(60 * 3, KeyFrame{.position = {F(0), F(10), F(10)}, .rotation = {F(0), F(-0.382683), F(-0.92388), F(0)}});
KeyFrame{.position = {F(10), F(0), F(10)}, this->timeline.add_key_frame(60 * 3,
.rotation = {F(-0.270598), F(-0.270598), F(-0.653282), F(-0.653282)}}); KeyFrame{.position = {F(10), F(0), F(10)},
this->frames.emplace(60 * 4, .rotation = {F(-0.270598), F(-0.270598), F(-0.653282), F(-0.653282)}});
KeyFrame{.position = {F(0), F(-10), F(10)}, this->timeline.add_key_frame(60 * 4,
.rotation = {F(0.382683), F(0), F(0), F(0.92388)}}); // Same as the first KeyFrame{.position = {F(0), F(-10), F(10)},
.rotation = {F(0.382683), F(0), F(0), F(0.92388)}}); // Same as the first
} }
}; };

View File

@@ -4,42 +4,42 @@ using ::basalt::shared::math::FloatPoint;
namespace basalt::shared::anime_loader { namespace basalt::shared::anime_loader {
#pragma region Anime Loader #pragma region Timeline
IAnimeLoader::IAnimeLoader() : status(AnimeLoaderStatus::Ready), time(0) {} Timeline::Timeline() : keyframes(), max_frame_cache(0) {
// insert default frame for first index
IAnimeLoader::~IAnimeLoader() {} this->keyframes[0] = KeyFrame{.position = {0, 0, 0}, .rotation = {0, 0, 0, 1}};
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() { Timeline::~Timeline() {}
if (this->status != AnimeLoaderStatus::Loaded) throw std::runtime_error("unexpected anime loader status");
// Get last frame index void Timeline::add_key_frame(math::Index frame_index, KeyFrame frame_data) {
auto last_pair = this->frames.end(); // insert data
--last_pair; this->keyframes[frame_index] = frame_data;
auto last_index = last_pair->first; // update max frame
// Accumulate time this->max_frame_cache = keyframes.rbegin()->first;
++this->time; }
if (this->time > last_index) {
this->time = 0; 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获取到不小于自身的项目肯定能获取到 // 首先用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; auto upper_bound = lower_bound;
++upper_bound; ++upper_bound;
if (upper_bound == this->frames.end()) { if (upper_bound == this->keyframes.end()) {
upper_bound = lower_bound; upper_bound = lower_bound;
} }
// 计算归一化时间 // 计算归一化时间
FloatPoint next_time = static_cast<FloatPoint>(this->time - lower_bound->first) FloatPoint next_time = static_cast<FloatPoint>(frame_index - lower_bound->first)
/ static_cast<FloatPoint>(upper_bound->first - lower_bound->first + 1); / static_cast<FloatPoint>(upper_bound->first - lower_bound->first + 1);
FloatPoint prev_time = static_cast<FloatPoint>(1) - next_time; FloatPoint prev_time = static_cast<FloatPoint>(1) - next_time;
@@ -56,4 +56,30 @@ namespace basalt::shared::anime_loader {
#pragma endregion #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 } // namespace basalt::shared::anime_loader

View File

@@ -6,15 +6,6 @@
namespace basalt::shared::anime_loader { 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 { struct KeyFrame {
math::Vector3 position; math::Vector3 position;
math::Quaternion rotation; math::Quaternion rotation;
@@ -29,6 +20,21 @@ namespace basalt::shared::anime_loader {
math::Quaternion next_rotation; ///< 后一帧的摄像机旋转。 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<math::Index, KeyFrame> keyframes;
};
struct AnimeLoaderConfig { struct AnimeLoaderConfig {
char_types::BSString filename; ///< The file to be loaded by loader. char_types::BSString filename; ///< The file to be loaded by loader.
}; };
@@ -53,8 +59,8 @@ namespace basalt::shared::anime_loader {
/** /**
* @brief * @brief
* @remarks * @remarks
* \li 重写时只允许往frames里插入数据。 * \li 重写时只允许往timeline里插入数据。
* \li 重写时务必保证frames里插入的数据大于2个且初始节点的Key总是0。动画帧个数必须大于4。 * \li 重写时务必保证timeline里插入的数据大于2个且初始节点的Key总是0。动画帧个数必须大于4。
*/ */
virtual void load(AnimeLoaderConfig&& config); virtual void load(AnimeLoaderConfig&& config);
KeyFrameSpan tick(); KeyFrameSpan tick();
@@ -63,7 +69,7 @@ namespace basalt::shared::anime_loader {
AnimeLoaderStatus status; AnimeLoaderStatus status;
AnimeLoaderConfig config; AnimeLoaderConfig config;
math::Index time; math::Index time;
std::map<math::Index, KeyFrame> frames; Timeline timeline;
}; };
} // namespace basalt::shared::anime_loader } // namespace basalt::shared::anime_loader