载入中...
搜索中...
未找到
SpriteAnimation.cpp
浏览该文件的文档.
2#include <algorithm>
3#include <cmath>
5
6namespace eve::asset {
7namespace {
8const Value* field(const Value& value, const char* name) { return value.isObject() ? value.find(name) : nullptr; }
9bool number(const Value* v, double& n) {
10 if (!v || !v->isNumeric()) return false;
11 n = v->isInt64() ? double(v->asInt()) : v->asDouble();
12 return std::isfinite(n);
13}
14template <std::size_t N>
15bool array(const Value* v, std::array<double, N>& out) {
16 const auto* a = v ? v->getIf<Value::Array>() : nullptr;
17 if (!a || a->size() != N) return false;
18 for (std::size_t i = 0; i < N; ++i)
19 if (!number(&(*a)[i], out[i])) return false;
20 return true;
21}
22} // namespace
24 const auto* schema = field(value, "schema");
25 const auto* version = field(value, "schemaVersion");
26 const auto* loop = field(value, "loop");
27 const auto* keys = field(value, "frames");
28 const auto* frames = keys ? keys->getIf<Value::Array>() : nullptr;
30 if (!schema || !schema->isString() || schema->asString() != "eve.sprite-animation" || !version ||
31 !version->isInt64() || version->asInt() != 1 || !loop || !loop->isBool() ||
32 !number(field(value, "duration"), out.duration_) || out.duration_ <= 0 || !frames || frames->empty() ||
33 frames->size() > 100000)
35 Diagnostic::error(DiagnosticCode::ParseError, "invalid sprite timeline", {}, {}, "asset.sprite-animation"));
36 out.loop_ = loop->asBool();
37 for (const auto& key : *frames) {
38 const auto* image = field(key, "image");
39 const auto* name = field(key, "name");
40 const auto* filter = field(key, "filter");
41 if (!image || !image->isString())
43 DiagnosticCode::ParseError, "missing sprite image reference", {}, {}, "asset.sprite-animation"));
44 auto ref = AssetRef::parse(image->asString());
45 if (!ref) return Result<SpriteAnimationClip>::failure(ref.status());
46 SpriteAnimationFrame frame{std::move(ref).takeValue()};
47 if (!image || !image->isString() || !name || !name->isString() || !filter || !filter->isString() ||
48 (filter->asString() != "nearest" && filter->asString() != "linear") ||
49 !number(field(key, "time"), frame.time) || !number(field(key, "pixelsPerUnit"), frame.pixelsPerUnit) ||
50 !array(field(key, "rect"), frame.rect) || !array(field(key, "pivot"), frame.pivot) ||
51 !array(field(key, "imageSize"), frame.imageSize))
53 DiagnosticCode::ParseError, "invalid sprite key fields", {}, {}, "asset.sprite-animation"));
54 frame.name = name->asString();
55 frame.nearest = filter->asString() == "nearest";
56 if (frame.time < 0 || frame.time >= out.duration_ ||
57 (out.frames_.empty() ? frame.time != 0 : frame.time <= out.frames_.back().time) ||
58 frame.pixelsPerUnit <= 0 || frame.rect[0] < 0 || frame.rect[1] < 0 || frame.rect[2] <= 0 ||
59 frame.rect[3] <= 0 || frame.imageSize[0] <= 0 || frame.imageSize[1] <= 0 ||
60 frame.rect[0] + frame.rect[2] > frame.imageSize[0] || frame.rect[1] + frame.rect[3] > frame.imageSize[1] ||
61 frame.pivot[0] < 0 || frame.pivot[0] > 1 || frame.pivot[1] < 0 || frame.pivot[1] > 1)
63 DiagnosticCode::ParseError, "sprite key bounds or timing invalid", {}, {}, "asset.sprite-animation"));
64 out.frames_.push_back(std::move(frame));
65 }
66 return Result<SpriteAnimationClip>::success(std::move(out));
67}
69 const EvpackCapabilities& caps) {
70 auto payload = reader.read(ref, "eve.sprite-animation/1", caps, 64 * 1024 * 1024);
72 if (payload.value().chunks.size() != 1 || payload.value().chunks.front().kind != EvpackChunkKind::Definition)
74 DiagnosticCode::ParseError, "sprite timeline requires one definition", {}, {}, "asset.sprite-animation"));
75 auto value = decodeRuntimeDefinition(payload.value().chunks.front().bytes);
77 return decode(value.value());
78}
80 if (!std::isfinite(seconds) || seconds < 0 || frames_.empty())
82 DiagnosticCode::ParseError, "invalid sprite sample time or empty clip", {}, {}, "asset.sprite-animation"));
83 const double time = loop_ ? std::fmod(seconds, duration_) : seconds;
84 auto it = std::upper_bound(frames_.begin(), frames_.end(), time,
85 [](double t, const auto& frame) { return t < frame.time; });
86 return Result<SpriteAnimationFrame>::success(*(it == frames_.begin() ? it : std::prev(it)));
87}
88} // namespace eve::asset
double value
Value::Object payload
std::uint32_t key
vk::UniqueImage image
glm::vec3 n
Definition Grass.cpp:63
float v
std::string name
MeleePoint3 a
Definition MeleeHit.cpp:40
float t
double number
JSON-free versioned runtime metadata codec.
std::string filter
int caps
Definition TreeMesh.cpp:162
Stable asset identity backed by PersistentId.
Definition ResourceRef.h:53
static Result< AssetRef > parse(std::string_view text)
Parse an asset://<canonical UUID> reference.
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
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
std::vector< Value > Array
Definition Value.h:33
Immutable reader retaining an admitted pack by shared ownership.
Result< RuntimeAssetPayload > read(const AssetRef &asset, std::string_view expectedType, const EvpackCapabilities &capabilities, std::uint64_t maximumDecodedBytes) const
Resolve, type-check, variant-select and decode a canonical runtime asset.
Immutable validated sprite timeline. No GPU/ECS objects, scheduler, callbacks or mutable playback clo...
static Result< SpriteAnimationClip > decode(const Value &definition)
Decode eve.sprite-animation/1; reject unknown versions, ignore unknown fields.
static Result< SpriteAnimationClip > load(const EvpackResourceReader &reader, const AssetRef &asset, const EvpackCapabilities &capabilities)
Load an immutable runtime definition; reader is borrowed only during this call.
Result< SpriteAnimationFrame > sample(double seconds) const
Sample a held key; loop wraps at duration, non-loop playback holds the final frame.
Result< Value > decodeRuntimeDefinition(std::span< const std::uint8_t > bytes, const RuntimeDefinitionLimits &limits)
Decode a bounded EVDEF\0\1 metadata tree without a JSON parser.
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
const EditorValue * field(const EditorValue &value, const char *name)
Actual runtime device capabilities used for variant selection.
Definition Evpack.h:92
Owning sprite key snapshot; pixel rectangles and pivots use top-left coordinates.