载入中...
搜索中...
未找到
AnimCurveLibrary.cpp
浏览该文件的文档.
2#include <algorithm>
3#include <bit>
4#include <cmath>
5#include <cstdint>
6#include <map>
7#include <stdexcept>
8#include <string>
9#include <vector>
11
12namespace eve::animation {
13namespace {
14struct Key {
15 float time, value;
16};
17struct Clip {
18 float duration;
19 std::map<std::string, std::vector<Key>, std::less<>> channels;
20};
21class Reader {
22public:
23 explicit Reader(std::span<const std::byte> data) : bytes(data) {}
24 std::uint32_t integer() {
25 require(4);
26 std::uint32_t result = 0;
27 for (unsigned i = 0; i < 4; ++i) result |= std::to_integer<std::uint32_t>(bytes[offset++]) << (8 * i);
28 return result;
29 }
30 float number() {
31 float value = std::bit_cast<float>(integer());
32 if (!std::isfinite(value)) throw std::runtime_error("nonfinite animation curve value");
33 return value;
34 }
35 std::string string() {
36 auto count = integer();
37 if (count == 0 || count > 4096) throw std::runtime_error("invalid animation curve name length");
38 require(count);
39 std::string value(reinterpret_cast<const char*>(bytes.data() + offset), count);
40 offset += count;
42 throw std::runtime_error("invalid animation curve UTF-8");
43 return value;
44 }
45 void require(std::size_t count) const {
46 if (count > bytes.size() - offset) throw std::runtime_error("truncated animation curves");
47 }
48 bool finished() const { return offset == bytes.size(); }
49
50private:
51 std::span<const std::byte> bytes;
52 std::size_t offset = 0;
53};
54eve::Diagnostic invalid(std::string message) {
56 "animation");
57}
58} // namespace
60 std::map<std::string, Clip, std::less<>> clips;
61};
62AnimCurveLibrary::AnimCurveLibrary() : impl_(std::make_unique<Impl>()) {}
64eve::Result<void> AnimCurveLibrary::load(std::span<const std::byte> bytes) {
65 try {
66 if (bytes.size() > 64 * 1024 * 1024) throw std::runtime_error("animation curves exceed 64 MiB");
67 Reader reader(bytes);
68 if (reader.integer() != 0x43465645 || reader.integer() != 1)
69 throw std::runtime_error("unsupported animation curves format");
70 auto count = reader.integer();
71 if (count > 100000) throw std::runtime_error("too many animation curve sources");
72 Impl next;
73 for (std::uint32_t i = 0; i < count; ++i) {
74 auto source = reader.string();
75 Clip clip;
76 clip.duration = reader.number();
77 auto channelCount = reader.integer();
78 if (channelCount > 64 || clip.duration < 0 || (channelCount && clip.duration <= 0))
79 throw std::runtime_error("invalid animation curve duration or channel count");
80 for (std::uint32_t j = 0; j < channelCount; ++j) {
81 auto name = reader.string();
82 auto keyCount = reader.integer();
83 if (keyCount < 2 || keyCount > 1000000) throw std::runtime_error("invalid animation curve key count");
84 reader.require(std::size_t(keyCount) * 8);
85 std::vector<Key> keys;
86 keys.reserve(keyCount);
87 for (std::uint32_t k = 0; k < keyCount; ++k) {
88 Key key{reader.number(), reader.number()};
89 if (key.time < 0 || key.time > clip.duration || (!keys.empty() && key.time <= keys.back().time))
90 throw std::runtime_error("unordered animation curve time");
91 keys.push_back(key);
92 }
93 if (keys.front().time != 0 || keys.back().time != clip.duration)
94 throw std::runtime_error("animation curve endpoints do not span duration");
95 if (!clip.channels.emplace(name, std::move(keys)).second)
96 throw std::runtime_error("duplicate animation curve channel");
97 }
98 if (!next.clips.emplace(source, std::move(clip)).second)
99 throw std::runtime_error("duplicate animation curve source");
100 }
101 if (!reader.finished()) throw std::runtime_error("trailing animation curve bytes");
102 impl_->clips.swap(next.clips);
104 } catch (const std::exception& error) {
105 return eve::Result<void>::failure(invalid(error.what()));
106 }
107}
108bool AnimCurveLibrary::contains(std::string_view source) const { return impl_->clips.contains(source); }
109std::size_t AnimCurveLibrary::size() const noexcept { return impl_->clips.size(); }
110eve::Result<std::optional<float>> AnimCurveLibrary::sample(std::string_view source, std::string_view channel,
111 double seconds, bool loop) const {
113 if (!std::isfinite(seconds)) return Result::failure(invalid("nonfinite animation curve time"));
114 auto found = impl_->clips.find(source);
115 if (found == impl_->clips.end())
116 return Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "animation curve source not found",
117 "source", {}, "animation"));
118 const auto& clip = found->second;
119 auto curve = clip.channels.find(channel);
120 if (curve == clip.channels.end()) return Result::success(std::optional<float>{});
121 if (loop) {
122 seconds = std::fmod(seconds, double(clip.duration));
123 if (seconds < 0) seconds += clip.duration;
124 }
125 const auto& keys = curve->second;
126 if (seconds <= 0) return Result::success(keys.front().value);
127 if (seconds >= clip.duration) return Result::success(keys.back().value);
128 auto end = std::upper_bound(keys.begin(), keys.end(), seconds,
129 [](double time, const Key& key) { return time < key.time; });
130 const auto& a = *(end - 1);
131 const auto& b = *end;
132 double alpha = (seconds - a.time) / (double(b.time) - a.time);
133 return Result::success(float(double(a.value) * (1 - alpha) + double(b.value) * alpha));
134}
135} // namespace eve::animation
double value
float duration
std::map< std::string, std::vector< Key >, std::less<> > channels
std::string message
std::uint32_t key
glm::vec4 clip
size_t offset
std::uint64_t bytes
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
std::string error
Definition Package.cpp:60
double number
bool found
std::string string
std::uint32_t count
Heightmap curve
const UnitySourceAsset & source
Strict allocation-free UTF-8 validation.
A structured explanation of a failed, degraded, or noteworthy result.
Definition Diagnostic.h:94
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
AnimCurveLibrary()
Construct an empty library with independent lifetime.
eve::Result< std::optional< float > > sample(std::string_view source, std::string_view channel, double seconds, bool loop) const
Evaluate one scalar channel with linear interpolation.
std::size_t size() const noexcept
Return the number of source records currently owned.
bool contains(std::string_view source) const
Query whether a source record exists, including records without channels.
eve::Result< void > load(std::span< const std::byte > bytes)
Atomically replace data from little-endian eve.animation-curves/1 bytes.
~AnimCurveLibrary()
Destroy owned curve data without accessing external objects.
Result< int > invalid(std::string message)
Invalid.
int64_t integer(const RuntimeTensor &v, size_t i=0)
Integer.
bool isValidUtf8(std::string_view text, Utf8NullPolicy nullPolicy) noexcept
Validate shortest-form UTF-8, Unicode scalar range and optional NUL exclusion.
std::map< std::string, Clip, std::less<> > clips