载入中...
搜索中...
未找到
AnimClipBinary.cpp
浏览该文件的文档.
2#include <bit>
3#include <cmath>
4#include <cstdint>
5#include <cstring>
6#include <stdexcept>
7#include <string>
8#include <vector>
13
14namespace eve::animation {
16public:
17 static void resize(AnimClip& clip, int bone, int kind, std::size_t count) {
18 clip.ensureBone(bone);
19 auto& track = clip.tracks_[bone];
20 if (kind == 0)
21 track.positions.resize(count);
22 else if (kind == 1)
23 track.rotations.resize(count);
24 else
25 track.scales.resize(count);
26 }
27 static void write(AnimClip& clip, int bone, int kind, std::size_t key, float time, float x, float y, float z,
28 float w) {
29 auto& track = clip.tracks_[bone];
30 if (kind == 0)
31 track.positions[key] = {time, x, y, z};
32 else if (kind == 2)
33 track.scales[key] = {time, x, y, z};
34 else {
36 rotation.qx = x;
37 rotation.qy = y;
38 rotation.qz = z;
39 rotation.qw = w;
40 rotation.normalizeRotation();
41 track.rotations[key] = {time, rotation.qx, rotation.qy, rotation.qz, rotation.qw};
42 }
43 }
44};
45namespace {
46class Reader {
47public:
48 explicit Reader(std::span<const std::byte> input) : data(input) {}
49 std::uint32_t integer() {
50 require(4);
51 std::uint32_t value = 0;
52 if constexpr (std::endian::native == std::endian::little) {
53 std::memcpy(&value, data.data() + offset, 4);
54 offset += 4;
55 } else {
56 for (int i = 0; i < 4; ++i) value |= std::to_integer<std::uint32_t>(data[offset++]) << (i * 8);
57 }
58 return value;
59 }
60 float number() {
61 const float value = std::bit_cast<float>(integer());
62 if (!std::isfinite(value)) throw std::runtime_error("nonfinite track value");
63 return value;
64 }
65 std::string string() {
66 const auto length = integer();
67 if (length == 0 || length > 4096) throw std::runtime_error("invalid track name length");
68 require(length);
69 std::string value(reinterpret_cast<const char*>(data.data() + offset), length);
70 offset += length;
71 if (!eve::isValidUtf8(value, eve::Utf8NullPolicy::Reject)) throw std::runtime_error("invalid UTF-8 name");
72 return value;
73 }
74 void require(std::size_t count) const {
75 if (count > data.size() - offset) throw std::runtime_error("truncated animation tracks");
76 }
77 bool finished() const { return offset == data.size(); }
78
79private:
80 std::span<const std::byte> data;
81 std::size_t offset = 0;
82};
83} // namespace
84
85eve::Result<int> loadAnimationTrackBatch(std::span<const AnimationTrackInput> inputs, const AnimSkeleton& skeleton,
86 int workerCount) {
87 try {
88 if (workerCount < 0 || workerCount > 8 || inputs.size() > 64)
89 throw std::runtime_error("invalid animation batch size or worker count");
90 std::size_t total = 0;
91 for (std::size_t i = 0; i < inputs.size(); ++i) {
92 if (inputs[i].bytes.size() > 256 * 1024 * 1024 - total)
93 throw std::runtime_error("animation batch exceeds 256 MiB limit");
94 total += inputs[i].bytes.size();
95 for (std::size_t j = 0; j < i; ++j)
96 if (&inputs[i].destination.get() == &inputs[j].destination.get())
97 throw std::runtime_error("duplicate animation batch destination");
98 }
99 std::vector<AnimClip> candidates(inputs.size());
100 const int workers = detail::parallelAnimationItems(inputs.size(), workerCount, [&](std::size_t i) {
101 auto loaded = loadAnimationTracks(candidates[i], inputs[i].bytes, skeleton);
102 if (!loaded.ok()) throw std::runtime_error(loaded.status().describe());
103 });
104 for (std::size_t i = 0; i < inputs.size(); ++i) inputs[i].destination.get().adopt(candidates[i]);
106 } catch (const std::exception& error) {
108 "animation-track-batch", {}, "animation"));
109 }
110}
111
112eve::Result<void> loadAnimationTracks(AnimClip& destination, std::span<const std::byte> bytes,
113 const AnimSkeleton& skeleton) {
114 try {
115 if (bytes.size() > 128 * 1024 * 1024) throw std::runtime_error("animation track file exceeds 128 MiB limit");
116 Reader reader(bytes);
117 if (reader.integer() != 0x43415645 || reader.integer() != 1)
118 throw std::runtime_error("unsupported animation track schema");
119 const auto count = reader.integer();
120 if (count > static_cast<unsigned>(skeleton.getBoneCount())) throw std::runtime_error("too many bone tracks");
121 const float duration = reader.number(), rate = reader.number();
122 const auto flags = reader.integer();
123 if (duration < 0 || rate <= 0 || flags > 1) throw std::runtime_error("invalid animation settings");
124 AnimClip candidate(reader.string());
125 candidate.setDuration(duration);
126 candidate.setSampleRate(rate);
127 candidate.setLoop(flags != 0);
128 std::vector<bool> seen(skeleton.getBoneCount(), false);
129 for (unsigned track = 0; track < count; ++track) {
130 const int bone = skeleton.findBone(reader.string());
131 if (bone < 0 || seen[bone]) throw std::runtime_error("unknown or duplicate bone track");
132 seen[bone] = true;
133 for (int kind = 0; kind < 3; ++kind) {
134 const auto keys = reader.integer();
135 const int components = kind == 1 ? 4 : 3;
136 if (keys > 1000000) throw std::runtime_error("too many animation keys");
137 reader.require(static_cast<std::size_t>(keys) * (components + 1) * 4);
138 AnimClipBinaryAccess::resize(candidate, bone, kind, keys);
139 float previous = -1.f;
140 for (unsigned key = 0; key < keys; ++key) {
141 const float time = reader.number();
142 if (time < 0 || time > duration || time <= previous)
143 throw std::runtime_error("invalid key time order");
144 previous = time;
145 const float x = reader.number(), y = reader.number(), z = reader.number();
146 float w = 1.f;
147 if (kind == 1) {
148 w = reader.number();
149 const float norm = x * x + y * y + z * z + w * w;
150 if (!std::isfinite(norm) || norm < 1e-12f) throw std::runtime_error("invalid key quaternion");
151 }
152 AnimClipBinaryAccess::write(candidate, bone, kind, key, time, x, y, z, w);
153 }
154 }
155 }
156 if (!reader.finished()) throw std::runtime_error("unknown trailing animation data");
157 destination.adopt(candidate);
159 } catch (const std::exception& error) {
161 "animation-tracks", {}, "animation"));
162 }
163}
164} // namespace eve::animation
double value
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
float duration
float length
Definition CaveMesh.cpp:94
EvpackChunkInput input
Definition Evpack.cpp:170
std::uint32_t key
glm::vec4 clip
int inputs
Definition GridGraph.cpp:23
TokenKind kind
size_t offset
std::array< float, 4 > rotation
std::uint32_t bone
std::uint64_t bytes
graphics::Canvas * previous
std::string error
Definition Package.cpp:60
double number
std::string string
std::uint32_t count
Strict allocation-free UTF-8 validation.
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
static void resize(AnimClip &clip, int bone, int kind, std::size_t count)
static void write(AnimClip &clip, int bone, int kind, std::size_t key, float time, float x, float y, float z, float w)
Keyframed skeletal animation clip (local TRS tracks per bone). Script type: AnimClip.
Definition AnimClip.h:145
void setDuration(float seconds)
Sets the duration.
Definition AnimClip.cpp:22
void adopt(AnimClip &other)
Replace this clip's content with other's (payload moved, not copied). Used by hot reload so registere...
Definition AnimClip.cpp:981
void setSampleRate(float hz)
Sample rate hint used by MotionDatabase baking (Hz).
Definition AnimClip.cpp:33
void setLoop(bool loop)
Sets the loop.
Definition AnimClip.h:166
3D bone hierarchy + bind-pose local TRS for skeletal animation. Independent of ik::Skeleton3D (FABRIK...
int getBoneCount() const
Returns the bone count.
int findBone(const std::string &name) const
Finds bone.
int parallelAnimationItems(std::size_t count, int requested, Body &&body)
Parallel animation items.
eve::Result< int > loadAnimationTrackBatch(std::span< const AnimationTrackInput > inputs, const AnimSkeleton &skeleton, int workerCount)
Decode independent clips in parallel, then publish all or none in input order.
eve::Result< void > loadAnimationTracks(AnimClip &destination, std::span< const std::byte > bytes, const AnimSkeleton &skeleton)
Atomically replace a clip from the little-endian eve.animation-tracks/1 format (EVAC,...
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.
Local TRS used by skeletal animation (quaternion xyzw).
Definition AnimMath.h:9