2026-01-08 19:23:19 +08:00
|
|
|
|
#include "anime_loader.hpp"
|
|
|
|
|
|
|
2026-01-09 16:40:30 +08:00
|
|
|
|
using ::basalt::shared::math::FloatPoint;
|
|
|
|
|
|
|
2026-01-08 19:23:19 +08:00
|
|
|
|
namespace basalt::shared::anime_loader {
|
|
|
|
|
|
|
2026-01-10 20:29:50 +08:00
|
|
|
|
#pragma region Timeline
|
2026-01-09 16:40:30 +08:00
|
|
|
|
|
2026-01-10 20:29:50 +08:00
|
|
|
|
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}};
|
|
|
|
|
|
}
|
2026-01-09 16:40:30 +08:00
|
|
|
|
|
2026-01-10 20:29:50 +08:00
|
|
|
|
Timeline::~Timeline() {}
|
2026-01-09 16:40:30 +08:00
|
|
|
|
|
2026-01-10 20:29:50 +08:00
|
|
|
|
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;
|
2026-01-08 19:23:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-10 20:29:50 +08:00
|
|
|
|
math::Index Timeline::max_frame() const {
|
|
|
|
|
|
return this->max_frame_cache;
|
|
|
|
|
|
}
|
2026-01-08 19:23:19 +08:00
|
|
|
|
|
2026-01-10 20:29:50 +08:00
|
|
|
|
KeyFrameSpan Timeline::tick(math::Index frame_index) const {
|
|
|
|
|
|
if (this->keyframes.empty()) [[unlikely]] {
|
|
|
|
|
|
throw std::runtime_error("No key frames in timeline");
|
2026-01-09 16:40:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 首先用lower_bound获取到不小于自身的项目(肯定能获取到)
|
2026-01-10 20:29:50 +08:00
|
|
|
|
auto lower_bound = this->keyframes.lower_bound(frame_index);
|
2026-01-09 16:40:30 +08:00
|
|
|
|
// 获取下一帧,如果没有下一帧,就转换为自身
|
|
|
|
|
|
auto upper_bound = lower_bound;
|
|
|
|
|
|
++upper_bound;
|
2026-01-10 20:29:50 +08:00
|
|
|
|
if (upper_bound == this->keyframes.end()) {
|
2026-01-09 16:40:30 +08:00
|
|
|
|
upper_bound = lower_bound;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算归一化时间
|
2026-01-10 20:29:50 +08:00
|
|
|
|
FloatPoint next_time = static_cast<FloatPoint>(frame_index - lower_bound->first)
|
2026-01-09 16:40:30 +08:00
|
|
|
|
/ static_cast<FloatPoint>(upper_bound->first - lower_bound->first + 1);
|
|
|
|
|
|
FloatPoint prev_time = static_cast<FloatPoint>(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,
|
|
|
|
|
|
};
|
2026-01-08 19:23:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 16:40:30 +08:00
|
|
|
|
#pragma endregion
|
|
|
|
|
|
|
2026-01-10 20:29:50 +08:00
|
|
|
|
#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
|
|
|
|
|
|
|
2026-01-09 16:40:30 +08:00
|
|
|
|
} // namespace basalt::shared::anime_loader
|