Download unify/engine/animation/animation.cpp from Snapkitty/repoverse: direct link, hf CLI and curl.
- Browser
- Download file 6.38 kB
-
https://huggingface.co/Snapkitty/repoverse/resolve/main/unify/engine/animation/animation.cpp
- Command line
-
hf download hf://Snapkitty/repoverse/unify/engine/animation/animation.cpp
-
curl -L -o animation.cpp https://huggingface.co/Snapkitty/repoverse/resolve/main/unify/engine/animation/animation.cpp
6.38 kB
| namespace unify { | |
| float Track::sample(float t) const { | |
| if (keys.empty()) return 0; | |
| if (t <= keys.front().time) return keys.front().value; | |
| if (t >= keys.back().time) return keys.back().value; | |
| auto it = std::upper_bound(keys.begin(), keys.end(), t, [](float x, const Keyframe& k) { return x < k.time; }); | |
| const Keyframe& b = *it; | |
| const Keyframe& a = *(it - 1); | |
| float span = b.time - a.time; | |
| return span > 0 ? a.value + (b.value - a.value) * ((t - a.time) / span) : b.value; | |
| } | |
| float AnimationClip::length() const { | |
| float frames_len = 0; | |
| for (auto& f : frames) frames_len += f.duration; | |
| float track_len = 0; | |
| for (auto& t : tracks) if (!t.keys.empty()) track_len = std::max(track_len, t.keys.back().time); | |
| return std::max(frames_len, track_len); | |
| } | |
| void AnimationSystem::add_clip(AnimationClip c) { clips_[c.name] = std::move(c); } | |
| const AnimationClip* AnimationSystem::clip(const std::string& name) const { | |
| auto it = clips_.find(name); | |
| return it == clips_.end() ? nullptr : &it->second; | |
| } | |
| uint32_t AnimationSystem::create_player(const std::string& clip) { | |
| uint32_t id; | |
| if (!free_.empty()) { id = free_.back(); free_.pop_back(); } | |
| else { id = uint32_t(players_.size()); players_.emplace_back(); live_.push_back(0); } | |
| players_[id] = AnimationPlayer{}; | |
| players_[id].clip = clip; | |
| live_[id] = 1; | |
| return id + 1; // 0 means "no player" | |
| } | |
| bool AnimationSystem::destroy_player(uint32_t id) { | |
| if (id == 0 || id > players_.size() || !live_[id - 1]) return false; | |
| live_[id - 1] = 0; | |
| free_.push_back(id - 1); | |
| return true; | |
| } | |
| AnimationPlayer* AnimationSystem::player(uint32_t id) { | |
| return (id == 0 || id > players_.size() || !live_[id - 1]) ? nullptr : &players_[id - 1]; | |
| } | |
| size_t AnimationSystem::live_players() const { return size_t(std::count(live_.begin(), live_.end(), 1)); } | |
| void AnimationSystem::play(uint32_t id, const std::string& name, float blend_seconds, bool restart) { | |
| AnimationPlayer* p = player(id); | |
| if (!p) return; | |
| if (p->clip == name && !restart) return; | |
| if (blend_seconds > 0) { | |
| p->from_clip = p->clip; | |
| p->from_time = p->time; | |
| p->blend = 0; | |
| p->blend_duration = blend_seconds; | |
| } else { | |
| p->from_clip.clear(); | |
| p->blend = 1; | |
| } | |
| p->clip = name; | |
| p->time = 0; | |
| p->finished = false; | |
| } | |
| // Maps an unbounded play time into clip time according to the play mode. | |
| float AnimationSystem::wrap(const AnimationClip& c, float t, bool* finished) { | |
| float len = c.length(); | |
| if (len <= 0) return 0; | |
| switch (c.mode) { | |
| case PlayMode::Once: | |
| if (t >= len) { if (finished) *finished = true; return len; } | |
| return t; | |
| case PlayMode::Loop: return std::fmod(t, len); | |
| case PlayMode::PingPong: { | |
| float m = std::fmod(t, 2 * len); | |
| return m <= len ? m : 2 * len - m; | |
| } | |
| } | |
| return t; | |
| } | |
| void AnimationSystem::advance(float dt) { | |
| for (size_t i = 0; i < players_.size(); i++) { | |
| if (!live_[i]) continue; | |
| AnimationPlayer& p = players_[i]; | |
| const AnimationClip* c = clip(p.clip); | |
| if (!c) continue; | |
| if (!p.finished) { | |
| p.time += dt * p.speed; | |
| if (c->mode == PlayMode::Once && p.time >= c->length()) { p.time = c->length(); p.finished = true; } | |
| // Keep loop time bounded so float precision does not degrade over long sessions. | |
| if (c->mode == PlayMode::Loop && c->length() > 0) p.time = std::fmod(p.time, c->length()); | |
| if (c->mode == PlayMode::PingPong && c->length() > 0) p.time = std::fmod(p.time, 2 * c->length()); | |
| } | |
| if (p.blend < 1) { | |
| p.from_time += dt * p.speed; | |
| p.blend = p.blend_duration > 0 ? std::min(1.0f, p.blend + dt / p.blend_duration) : 1.0f; | |
| if (p.blend >= 1) p.from_clip.clear(); | |
| } | |
| } | |
| } | |
| uint32_t AnimationSystem::sprite_region(uint32_t id) const { | |
| const AnimationPlayer* p = const_cast<AnimationSystem*>(this)->player(id); | |
| if (!p) return 0; | |
| const AnimationClip* c = clip(p->clip); | |
| if (!c || c->frames.empty()) return 0; | |
| float t = wrap(*c, p->time, nullptr); | |
| float acc = 0; | |
| for (auto& f : c->frames) { | |
| acc += f.duration; | |
| if (t < acc) return f.region; | |
| } | |
| return c->frames.back().region; | |
| } | |
| float AnimationSystem::value(uint32_t id, const std::string& property, float fallback) const { | |
| const AnimationPlayer* p = const_cast<AnimationSystem*>(this)->player(id); | |
| if (!p) return fallback; | |
| auto sample = [&](const std::string& name, float time, bool& found) { | |
| const AnimationClip* c = clip(name); | |
| if (!c) return fallback; | |
| for (auto& tr : c->tracks) | |
| if (tr.property == property) { found = true; return tr.sample(wrap(*c, time, nullptr)); } | |
| return fallback; | |
| }; | |
| bool found_to = false, found_from = false; | |
| float to = sample(p->clip, p->time, found_to); | |
| if (p->blend >= 1 || p->from_clip.empty()) return to; | |
| float from = sample(p->from_clip, p->from_time, found_from); | |
| if (!found_to && !found_from) return fallback; | |
| return from + (to - from) * p->blend; | |
| } | |
| void AnimationSystem::serialize(BinaryWriter& w) const { | |
| w.u32(uint32_t(players_.size())); | |
| for (size_t i = 0; i < players_.size(); i++) { | |
| w.u8(live_[i]); | |
| if (!live_[i]) continue; | |
| const AnimationPlayer& p = players_[i]; | |
| w.str(p.clip); w.f32(p.time); w.f32(p.speed); w.boolean(p.finished); | |
| w.str(p.from_clip); w.f32(p.from_time); w.f32(p.blend); w.f32(p.blend_duration); | |
| } | |
| w.u32(uint32_t(free_.size())); | |
| for (uint32_t f : free_) w.u32(f); | |
| } | |
| void AnimationSystem::deserialize(BinaryReader& r) { | |
| uint32_t n = r.u32(); | |
| players_.assign(n, AnimationPlayer{}); | |
| live_.assign(n, 0); | |
| for (uint32_t i = 0; i < n; i++) { | |
| live_[i] = r.u8(); | |
| if (!live_[i]) continue; | |
| AnimationPlayer& p = players_[i]; | |
| p.clip = r.str(); p.time = r.f32(); p.speed = r.f32(); p.finished = r.boolean(); | |
| p.from_clip = r.str(); p.from_time = r.f32(); p.blend = r.f32(); p.blend_duration = r.f32(); | |
| } | |
| free_.resize(r.u32()); | |
| for (auto& f : free_) f = r.u32(); | |
| } | |
| } // namespace unify | |