载入中...
搜索中...
未找到
Hd2dTarget.cpp
浏览该文件的文档.
2#include <algorithm>
3#include <cmath>
4#include <utility>
5namespace eve::hd2d_editing {
6namespace {
7const EditorValue* field(const EditorValue& v, const char* k) {
8 const auto* o = v.getIf<EditorValue::Object>();
9 if (!o) return nullptr;
10 const auto i = o->find(k);
11 return i == o->end() ? nullptr : &i->second;
12}
13EditorValue array(const float* v, int n) {
15 for (int i = 0; i < n; ++i) a.push_back(v[i]);
16 return a;
17}
18bool readArray(const EditorValue* v, float* out, int n) {
19 const auto* a = v ? v->getIf<EditorValue::Array>() : nullptr;
20 if (!a || static_cast<int>(a->size()) != n) return false;
21 for (int i = 0; i < n; ++i) {
22 const auto* x = (*a)[i].getIf<double>();
23 if (!x || !std::isfinite(*x)) return false;
24 out[i] = static_cast<float>(*x);
25 }
26 return true;
27}
28PropertyDescriptor prop(std::string p, PropertyType t, EditorValue v, const char* c, double min = -1e9,
29 double max = 1e9) {
32 d.displayNameKey = "editor.hd2d." + p;
33 d.category = c;
34 d.type = t;
35 d.defaultValue = std::move(v);
36 d.flags = PropertyFlag::Runtime;
37 d.numeric.minimum = min;
38 d.numeric.maximum = max;
39 return d;
40}
41bool errors(const std::vector<EditorDiagnostic>& d) {
42 return std::any_of(d.begin(), d.end(), [](const auto& v) { return v.severity() == DiagnosticSeverity::Error; });
43}
44} // namespace
45Hd2dDocumentTarget::Hd2dDocumentTarget(std::string id) : id_(std::move(id)) {}
47 return {TargetId(id_), "hd2d-asset", revisionValue(), false, {CapabilityId("eve.editor.target.hd2d-properties")}};
48}
50 return c == CapabilityId("eve.editor.target.hd2d-properties") ? static_cast<IPropertyProvider*>(this) : nullptr;
51}
52bool Hd2dDocumentTarget::matches(const SelectionSnapshot& s) const {
53 return s.items.size() == 1 && s.items.front().target == TargetId(id_) && s.items.front().item.value() == id_;
54}
63 s.typeId = "hd2d.asset";
64 s.properties = {prop("asset.kind", PropertyType::Enum, "sprite", "asset"),
65 prop("asset.source", PropertyType::AssetRef, "", "asset"),
66 prop("sprite.grid", PropertyType::Vec2, EditorValue::Array{1.0, 1.0}, "sprite", 1, 4096),
67 prop("sprite.frame", PropertyType::Int, int64_t{0}, "sprite", 0, 16777215),
68 prop("sprite.animation", PropertyType::Vec2, EditorValue::Array{0.0, 0.0}, "sprite", 0, 16777215),
69 prop("sprite.fps", PropertyType::Float, 12.0, "sprite", .01, 1000),
70 prop("sprite.flipX", PropertyType::Bool, false, "sprite"),
71 prop("sprite.flipY", PropertyType::Bool, false, "sprite"),
72 prop("sprite.size", PropertyType::Vec2, EditorValue::Array{1.0, 1.0}, "sprite", .001, 100000),
73 prop("sprite.tint", PropertyType::Color, EditorValue::Array{1.0, 1.0, 1.0, 1.0}, "sprite", 0, 1),
74 prop("sprite.visible", PropertyType::Bool, true, "sprite"),
75 prop("tile.sideDepth", PropertyType::Float, 6.0, "tilemap", 0, 100000),
76 prop("tile.heightScale", PropertyType::Float, 1.0, "tilemap", 0, 100000),
77 prop("tile.wallUv", PropertyType::Vec4, EditorValue::Array{0.0, 0.0, .05, .05}, "tilemap", 0, 1)};
78 s.properties[0].enumItems = {"sprite", "tilemap"};
79 s.properties[1].assetTypeFilters = {"image", "texture", "map-layer"};
80 return s;
81}
82EditorValue Hd2dDocumentTarget::contentValue() const {
83 const auto& v = value_;
84 return EditorValue::Object{{"kind", v.kind},
85 {"source", v.sourceAsset},
86 {"columns", int64_t{v.columns}},
87 {"rows", int64_t{v.rows}},
88 {"frame", int64_t{v.frame}},
89 {"animStart", int64_t{v.animStart}},
90 {"animEnd", int64_t{v.animEnd}},
91 {"fps", v.fps},
92 {"flipX", v.flipX},
93 {"flipY", v.flipY},
94 {"size", array(v.size.data(), 2)},
95 {"tint", array(v.tint.data(), 4)},
96 {"visible", v.visible},
97 {"sideDepth", v.sideDepth},
98 {"heightScale", v.heightScale},
99 {"wallUv", array(v.wallUv.data(), 4)}};
100}
102 if (!matches(s) || !schema(s).find(p)) return {};
103 const auto& v = value_;
105 if (p == PropertyPath("asset.kind"))
106 value = v.kind;
107 else if (p == PropertyPath("asset.source"))
109 else if (p == PropertyPath("sprite.grid"))
110 value = EditorValue::Array{static_cast<double>(v.columns), static_cast<double>(v.rows)};
111 else if (p == PropertyPath("sprite.frame"))
112 value = int64_t{v.frame};
113 else if (p == PropertyPath("sprite.animation"))
114 value = EditorValue::Array{static_cast<double>(v.animStart), static_cast<double>(v.animEnd)};
115 else if (p == PropertyPath("sprite.fps"))
116 value = v.fps;
117 else if (p == PropertyPath("sprite.flipX"))
118 value = v.flipX;
119 else if (p == PropertyPath("sprite.flipY"))
120 value = v.flipY;
121 else if (p == PropertyPath("sprite.size"))
122 value = array(v.size.data(), 2);
123 else if (p == PropertyPath("sprite.tint"))
124 value = array(v.tint.data(), 4);
125 else if (p == PropertyPath("sprite.visible"))
126 value = v.visible;
127 else if (p == PropertyPath("tile.sideDepth"))
128 value = v.sideDepth;
129 else if (p == PropertyPath("tile.heightScale"))
131 else
132 value = array(v.wallUv.data(), 4);
133 return {PropertyReadState::Value, std::move(value), {}};
134}
135Result<DomainOperation> Hd2dDocumentTarget::replacement(EditorValue c, std::string p) const {
137 op.type = "hd2d.document.replace.v1";
138 op.inverseType = op.type;
139 op.target = TargetId(id_);
140 op.payload = std::move(c);
141 op.inverse = contentValue();
142 op.hasInverse = true;
143 op.affectedProperties.push_back(p);
144 op.mergeKey = "hd2d:" + id_ + ":" + p;
145 return eve::editing::applied<DomainOperation>(std::move(op));
146}
148 const EditorValue& value, PropertySetMode mode) const {
149 if (mode == PropertySetMode::Reset) return makeReset(s, p);
150 auto d = schema(s).find(p);
151 if (!matches(s) || !d || mode != PropertySetMode::Absolute || !validatePropertyValue(*d, value).ok())
152 return eve::editing::failed<DomainOperation>(EditorStatus::Rejected, RuleId("editor.hd2d.set"),
153 "HD2D property is invalid");
154 auto c = *this;
155 auto& v = c.value_;
156 if (p == PropertyPath("asset.kind"))
157 v.kind = *value.getIf<std::string>();
158 else if (p == PropertyPath("asset.source"))
159 v.sourceAsset = *value.getIf<std::string>();
160 else if (p == PropertyPath("sprite.grid")) {
161 const auto& a = *value.getIf<EditorValue::Array>();
162 v.columns = static_cast<int>(*a[0].getIf<double>());
163 v.rows = static_cast<int>(*a[1].getIf<double>());
164 } else if (p == PropertyPath("sprite.frame"))
165 v.frame = static_cast<int>(*value.getIf<int64_t>());
166 else if (p == PropertyPath("sprite.animation")) {
167 const auto& a = *value.getIf<EditorValue::Array>();
168 v.animStart = static_cast<int>(*a[0].getIf<double>());
169 v.animEnd = static_cast<int>(*a[1].getIf<double>());
170 } else if (p == PropertyPath("sprite.fps"))
171 v.fps = static_cast<float>(*value.getIf<double>());
172 else if (p == PropertyPath("sprite.flipX"))
173 v.flipX = *value.getIf<bool>();
174 else if (p == PropertyPath("sprite.flipY"))
175 v.flipY = *value.getIf<bool>();
176 else if (p == PropertyPath("sprite.size"))
177 readArray(&value, v.size.data(), 2);
178 else if (p == PropertyPath("sprite.tint"))
179 readArray(&value, v.tint.data(), 4);
180 else if (p == PropertyPath("sprite.visible"))
181 v.visible = *value.getIf<bool>();
182 else if (p == PropertyPath("tile.sideDepth"))
183 v.sideDepth = static_cast<float>(*value.getIf<double>());
184 else if (p == PropertyPath("tile.heightScale"))
185 v.heightScale = static_cast<float>(*value.getIf<double>());
186 else
187 readArray(&value, v.wallUv.data(), 4);
188 if (errors(c.validate()))
189 return eve::editing::failed<DomainOperation>(EditorStatus::Rejected, RuleId("editor.hd2d.invalid"),
190 "HD2D edit produces an invalid preset");
191 return replacement(c.contentValue(), p.value());
192}
194 auto d = schema(s).find(p);
195 if (!d)
196 return eve::editing::failed<DomainOperation>(EditorStatus::Unsupported, RuleId("editor.hd2d.property"),
197 "Unknown HD2D property");
198 return makeSet(s, p, d->defaultValue, PropertySetMode::Absolute);
199}
200std::vector<EditorDiagnostic> Hd2dDocumentTarget::validate() const {
201 std::vector<EditorDiagnostic> d;
202 const auto& v = value_;
203 if (v.kind != "sprite" && v.kind != "tilemap")
205 DiagnosticSeverity::Error, "HD2D kind must be sprite or tilemap"));
206 if (v.sourceAsset.empty())
208 RuleId("editor.hd2d.source"), DiagnosticSeverity::Warning,
209 "HD2D source asset is not assigned"));
210 const long long frames = static_cast<long long>(v.columns) * v.rows;
211 if (v.columns < 1 || v.rows < 1 || frames > 16777216 || v.frame < 0 || v.frame >= frames || v.animStart < 0 ||
212 v.animEnd < v.animStart || v.animEnd >= frames || !std::isfinite(v.fps) || v.fps <= 0 || v.fps > 1000)
214 DiagnosticSeverity::Error,
215 "HD2D sprite grid, frame or animation range is invalid"));
216 const bool sizeOk = std::all_of(v.size.begin(), v.size.end(), [](float n) { return std::isfinite(n) && n > 0; });
217 const bool tintOk =
218 std::all_of(v.tint.begin(), v.tint.end(), [](float n) { return std::isfinite(n) && n >= 0 && n <= 1; });
219 const bool uvFinite =
220 std::all_of(v.wallUv.begin(), v.wallUv.end(), [](float n) { return std::isfinite(n) && n >= 0 && n <= 1; });
221 if (!sizeOk || !tintOk || !std::isfinite(v.sideDepth) || v.sideDepth < 0 || !std::isfinite(v.heightScale) ||
222 v.heightScale < 0 || !uvFinite || v.wallUv[0] >= v.wallUv[2] || v.wallUv[1] >= v.wallUv[3])
224 RuleId("editor.hd2d.presentation"), DiagnosticSeverity::Error,
225 "HD2D size, tint or tile extrusion values are invalid"));
226 return d;
227}
229 if (op.target != TargetId(id_) || op.type != "hd2d.document.replace.v1" || !op.payload.isWithinLimits(5, 64, 4096))
230 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.hd2d.operation"),
231 "HD2D operation is invalid");
233 const auto * kind = field(op.payload, "kind"), *source = field(op.payload, "source"),
234 *fx = field(op.payload, "flipX"), *fy = field(op.payload, "flipY"),
235 *visible = field(op.payload, "visible");
236 const auto* k = kind ? kind->getIf<std::string>() : nullptr;
237 const auto* s = source ? source->getIf<std::string>() : nullptr;
238 const auto* xb = fx ? fx->getIf<bool>() : nullptr;
239 const auto* yb = fy ? fy->getIf<bool>() : nullptr;
240 const auto* vb = visible ? visible->getIf<bool>() : nullptr;
241 if (!k || !s || !xb || !yb || !vb)
242 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.hd2d.payload"),
243 "HD2D text/bool field is invalid");
244 v.kind = *k;
245 v.sourceAsset = *s;
246 v.flipX = *xb;
247 v.flipY = *yb;
248 v.visible = *vb;
249 int* ints[] = {&v.columns, &v.rows, &v.frame, &v.animStart, &v.animEnd};
250 const char* ifields[] = {"columns", "rows", "frame", "animStart", "animEnd"};
251 for (int i = 0; i < 5; ++i) {
252 const auto* x = field(op.payload, ifields[i]);
253 const auto* n = x ? x->getIf<int64_t>() : nullptr;
254 if (!n)
255 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.hd2d.integer"),
256 "HD2D integer is invalid");
257 *ints[i] = static_cast<int>(*n);
258 }
259 float* floats[] = {&v.fps, &v.sideDepth, &v.heightScale};
260 const char* ffields[] = {"fps", "sideDepth", "heightScale"};
261 for (int i = 0; i < 3; ++i) {
262 const auto* x = field(op.payload, ffields[i]);
263 const auto* n = x ? x->getIf<double>() : nullptr;
264 if (!n || !std::isfinite(*n))
265 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.hd2d.number"),
266 "HD2D number is invalid");
267 *floats[i] = static_cast<float>(*n);
268 }
269 if (!readArray(field(op.payload, "size"), v.size.data(), 2) ||
270 !readArray(field(op.payload, "tint"), v.tint.data(), 4) ||
271 !readArray(field(op.payload, "wallUv"), v.wallUv.data(), 4))
272 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.hd2d.vector"),
273 "HD2D vector is invalid");
275 c.value_ = v;
276 if (errors(c.validate()))
277 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.hd2d.invalid"),
278 "HD2D document validation failed");
279 value_ = v;
280 bumpRevision();
281 widenDirty(0, 0);
282 return eve::editing::applied<void>();
283}
284std::unique_ptr<IDomainOperationTarget> Hd2dDocumentTarget::cloneDomainState() const {
285 return std::make_unique<Hd2dDocumentTarget>(*this);
286}
287Result<void> Hd2dDocumentTarget::commitDomainState(std::unique_ptr<IDomainOperationTarget> c) {
288 auto* t = dynamic_cast<Hd2dDocumentTarget*>(c.get());
289 if (!t || t->id_ != id_)
290 return eve::editing::failed<void>(EditorStatus::Conflict, RuleId("editor.hd2d.candidate"),
291 "HD2D candidate mismatch");
292 *this = *t;
293 return eve::editing::applied<void>();
294}
296 return EditorValue::Object{{"schemaVersion", int64_t{1}}, {"content", contentValue()}};
297}
299 const auto *v = field(s, "schemaVersion"), *content = field(s, "content");
300 const auto* version = v ? v->getIf<int64_t>() : nullptr;
301 if (!version || *version != 1 || !content)
302 return eve::editing::failed<void>(EditorStatus::Unsupported, RuleId("editor.hd2d.snapshot"),
303 "Unsupported HD2D snapshot");
305 op.target = TargetId(id_);
306 op.type = "hd2d.document.replace.v1";
307 op.payload = *content;
308 auto result = applyDomainOperation(op);
309 if (result.ok()) clearDirtyRegion();
310 return result;
311}
313 if (d.value().kind != "sprite" || !std::isfinite(time) || time < 0 || time > 86400)
314 return eve::editing::failed<Hd2dFramePreview>(EditorStatus::Rejected, RuleId("editor.hd2d.preview"),
315 "HD2D frame preview requires a sprite and bounded time");
316 const auto diagnostics = d.validate();
317 if (errors(diagnostics))
318 return eve::editing::failed<Hd2dFramePreview>(EditorStatus::Rejected, RuleId("editor.hd2d.preview-invalid"),
319 "HD2D preset is invalid");
320 const auto& v = d.value();
321 const int span = v.animEnd - v.animStart + 1;
322 const int frame = v.animStart + (static_cast<long long>(std::floor(time * v.fps)) % span);
323 const int x = frame % v.columns, y = frame / v.columns;
325 p.frame = frame;
326 p.uv = {static_cast<float>(x) / v.columns, static_cast<float>(y) / v.rows, static_cast<float>(x + 1) / v.columns,
327 static_cast<float>(y + 1) / v.rows};
328 if (v.flipX) std::swap(p.uv[0], p.uv[2]);
329 if (v.flipY) std::swap(p.uv[1], p.uv[3]);
330 return eve::editing::applied<Hd2dFramePreview>(p);
331}
332} // namespace eve::hd2d_editing
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
const std::string & s
glm::vec4 p[6]
glm::vec3 n
Definition Grass.cpp:63
float v
std::int32_t c
TokenKind kind
MeleePoint3 a
Definition MeleeHit.cpp:40
std::string id
Definition PlayHost.cpp:108
float d
float t
bool visible
const UnitySourceAsset & source
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
UUID-backed identifier adapter for legacy textual boundaries.
Definition Identity.h:314
const std::string & value() const noexcept
Returns the exact legacy or canonical compatibility spelling.
Definition Identity.h:362
void bumpRevision()
Advance the revision without touching the dirty region.
Revision revisionValue() const noexcept
Return the revision as the shared protocol revision type.
void widenDirty()
Mark an unknown area dirty without advancing the revision.
virtual void clearDirtyRegion()=0
Clears dirty region.
UI-independent property access for an immutable selection snapshot.
Dotted property path (layout.size) compared and persisted as text.
Definition EditingIds.h:78
Deterministic owning value tree shared by authoring hosts.
std::vector< Value > Array
bool isWithinLimits(size_t maxDepth, size_t maxElements, size_t maxStringBytes) const
Validate nesting, element count and approximate string byte limits.
std::map< std::string, Value > Object
const T * getIf() const
Return an immediately borrowed typed value.
Revisioned HD-2D sprite/tilemap presentation asset.
Definition Hd2dTarget.h:62
Hd2dDocumentTarget(std::string id)
Hd 2 d document target.
EditorValue snapshotValue() const
Snapshot value.
void * queryCapability(const CapabilityId &) override
Query an optional stable editing capability.
eve::Result< eve::Revision > currentRevision(const SelectionSnapshot &) const override
Current revision.
Result< void > commitDomainState(std::unique_ptr< IDomainOperationTarget >) override
Commits domain state.
std::vector< EditorDiagnostic > validate() const
Validate.
TargetDescriptor describe() const override
Describe.
PropertySchema schema(const SelectionSnapshot &) const override
Schema.
Result< void > applyDomainOperation(const DomainOperation &) override
Applies domain operation.
Result< void > loadSnapshot(const EditorValue &)
Loads snapshot.
std::unique_ptr< IDomainOperationTarget > cloneDomainState() const override
Clones domain state.
PropertyReadResult read(const SelectionSnapshot &, const PropertyPath &) const override
Reads .
Result< DomainOperation > makeSet(const SelectionSnapshot &, const PropertyPath &, const EditorValue &, PropertySetMode) const override
Make set.
Result< DomainOperation > makeReset(const SelectionSnapshot &, const PropertyPath &) const override
Make reset.
const Hd2dAssetValue & value() const
Value.
Definition Hd2dTarget.h:94
Result< Hd2dFramePreview > evaluate(const Hd2dDocumentTarget &, float time) const
Evaluate.
PropertySetMode
PropertySetMode public API.
Diagnostic ruleDiagnostic(eve::DiagnosticCode code, RuleId rule, DiagnosticSeverity severity, std::string message)
Build a common diagnostic carrying an open editing rule identity.
editing::RuleId RuleId
Definition Hd2dTarget.h:40
editing::PropertyPath PropertyPath
Definition Hd2dTarget.h:33
editing::Value EditorValue
Definition Hd2dTarget.h:26
editing::PropertyDescriptor PropertyDescriptor
Definition Hd2dTarget.h:31
editing::TargetId TargetId
Definition Hd2dTarget.h:43
editing::CapabilityId CapabilityId
Definition Hd2dTarget.h:19
editing::PropertyType PropertyType
Definition Hd2dTarget.h:38
const EditorValue * field(const EditorValue &value, const char *name)
DomainOperation public API.
std::vector< std::string > affectedProperties
PropertyReadResult public API.
EVENGINE_API_PLATFORM public API.
PropertyDescriptorLookup find(const PropertyPath &path) const
Finds find.
SelectionSnapshot public API.
Stable discovery metadata exposed by every editable target.
Authored HD-2D sprite-sheet and tile-extrusion preset.
Definition Hd2dTarget.h:46
std::array< float, 4 > wallUv
Definition Hd2dTarget.h:55
std::array< float, 4 > tint
Definition Hd2dTarget.h:53
std::array< float, 2 > size
Definition Hd2dTarget.h:52
Deterministically sampled sprite-sheet frame and UV rectangle.
Definition Hd2dTarget.h:113