载入中...
搜索中...
未找到
TextureRecipeTarget.cpp
浏览该文件的文档.
2#include <charconv>
3#include <cmath>
4#include <cstdlib>
5#include <stdexcept>
6#include <utility>
7#include "image/ImageData.h"
9#include "procgen/Params.h"
11namespace eve::procgen_editing {
12namespace {
13EditorDiagnostic diagnostic(const char* rule, DiagnosticSeverity severity, std::string message) {
15 severity, std::move(message));
16}
17const EditorValue* field(const EditorValue& v, const char* k) {
18 auto* o = v.getIf<EditorValue::Object>();
19 if (!o) return nullptr;
20 auto i = o->find(k);
21 return i == o->end() ? nullptr : &i->second;
22}
23EditorValue defaultValue(const procgen::ParamDescriptor& p) {
24 try {
25 switch (p.kind) {
26 case procgen::ParamKind::Integer: return int64_t{std::stoll(p.defaultValue)};
27 case procgen::ParamKind::Float: return std::stod(p.defaultValue);
28 case procgen::ParamKind::Boolean: return p.defaultValue == "1" || p.defaultValue == "true";
30 case procgen::ParamKind::Choice: return p.defaultValue;
31 }
32 } catch (...) {
33 return {};
34 }
35 return {};
36}
37PropertyType propertyType(procgen::ParamKind k) {
38 switch (k) {
39 case procgen::ParamKind::Integer: return PropertyType::Int;
40 case procgen::ParamKind::Float: return PropertyType::Float;
41 case procgen::ParamKind::Boolean: return PropertyType::Bool;
42 case procgen::ParamKind::Choice: return PropertyType::Enum;
43 case procgen::ParamKind::String: return PropertyType::String;
44 }
45 return PropertyType::String;
46}
47bool hasErrors(const std::vector<EditorDiagnostic>& d) {
48 for (const auto& x : d)
49 if (x.severity() == DiagnosticSeverity::Error) return true;
50 return false;
51}
52} // namespace
53TextureRecipeTarget::TextureRecipeTarget(std::string id, std::string recipe)
54 : id_(std::move(id)), recipe_(std::move(recipe)) {
55 auto initialized = initializeDefaults();
56 if (!initialized.ok()) throw std::invalid_argument("Texture recipe is not registered: " + recipe_);
57}
58Result<void> TextureRecipeTarget::initializeDefaults() {
60 r.registerBuiltins();
61 const auto* d = r.descriptor(recipe_);
62 if (!d)
63 return eve::editing::failed<void>(EditorStatus::NotFound, RuleId("editor.texture-recipe.unknown"),
64 "Texture recipe is not registered");
65 values_.clear();
66 for (const auto& p : d->params) values_[p.key] = defaultValue(p);
67 return eve::editing::applied<void>();
68}
70 return {TargetId(id_),
71 "texture-recipe",
73 false,
74 {CapabilityId("eve.editor.target.texture-recipe-properties")}};
75}
77 return c == CapabilityId("eve.editor.target.texture-recipe-properties") ? static_cast<IPropertyProvider*>(this)
78 : nullptr;
79}
80bool TextureRecipeTarget::matches(const SelectionSnapshot& s) const {
81 return s.items.size() == 1 && s.items[0].target == TargetId(id_) && s.items[0].type == "texture-recipe";
82}
92 out.typeId = "texture-recipe";
94 const auto* d = r.descriptor(recipe_);
95 if (!d) return out;
96 for (const auto& p : d->params) {
98 x.path = PropertyPath("param." + p.key);
99 x.displayNameKey = p.displayName;
100 x.descriptionKey = p.description;
101 x.category = p.category;
102 x.type = propertyType(p.kind);
103 x.defaultValue = defaultValue(p);
104 x.flags = PropertyFlag::Runtime;
105 x.enumItems = p.choices;
106 if (p.hasMinimum) x.numeric.minimum = p.minimum;
107 if (p.hasMaximum) x.numeric.maximum = p.maximum;
108 x.numeric.step = p.step;
109 out.properties.push_back(std::move(x));
110 }
111 return out;
112}
114 if (!matches(s) || p.value().rfind("param.", 0) != 0) return {};
115 auto i = values_.find(p.value().substr(6));
116 return i == values_.end() ? PropertyReadResult{} : PropertyReadResult{PropertyReadState::Value, i->second, {}};
117}
118EditorValue TextureRecipeTarget::contentValue() const {
119 return EditorValue::Object{{"recipe", recipe_}, {"values", values_}};
120}
122 const PropertyPath& p,
123 const EditorValue& v,
124 PropertySetMode mode) const {
125 if (mode == PropertySetMode::Reset) return makeReset(s, p);
126 auto d = schema(s).find(p);
127 if (!matches(s) || !d || mode != PropertySetMode::Absolute || !validatePropertyValue(*d, v).ok())
128 return eve::editing::failed<DomainOperation>(EditorStatus::Rejected, RuleId("editor.texture-recipe.set"),
129 "Texture recipe property edit is invalid");
130 auto values = values_;
131 values[p.value().substr(6)] = v;
133 op.type = "texture-recipe.replace.v1";
134 op.inverseType = op.type;
135 op.target = TargetId(id_);
136 op.payload = EditorValue::Object{{"recipe", recipe_}, {"values", std::move(values)}};
137 op.inverse = contentValue();
138 op.hasInverse = true;
139 op.affectedProperties.push_back(p.value());
140 op.mergeKey = "texture-recipe:" + id_ + ":" + p.value();
141 return eve::editing::applied<DomainOperation>(std::move(op));
142}
144 auto d = schema(s).find(p);
145 if (!d)
146 return eve::editing::failed<DomainOperation>(
147 EditorStatus::Unsupported, RuleId("editor.texture-recipe.property"), "Unknown texture recipe property");
148 return makeSet(s, p, d->defaultValue, PropertySetMode::Absolute);
149}
150std::vector<EditorDiagnostic> TextureRecipeTarget::validate() const {
151 std::vector<EditorDiagnostic> d;
153 const auto* desc = r.descriptor(recipe_);
154 if (!desc) {
155 d.push_back(diagnostic("editor.texture-recipe.unknown", DiagnosticSeverity::Error,
156 "Texture recipe is not registered"));
157 return d;
158 }
159 const auto s = schema({});
160 for (const auto& p : desc->params) {
161 auto i = values_.find(p.key);
162 auto x = s.find(PropertyPath("param." + p.key));
163 if (i == values_.end() || !x || !validatePropertyValue(*x, i->second).ok())
164 d.push_back(diagnostic("editor.texture-recipe.parameter", DiagnosticSeverity::Error,
165 "Invalid texture parameter: " + p.key));
166 }
167 if (values_.size() != desc->params.size())
168 d.push_back(diagnostic("editor.texture-recipe.extra", DiagnosticSeverity::Error,
169 "Texture recipe contains unknown parameters"));
170 return d;
171}
173 if (op.target != TargetId(id_) || op.type != "texture-recipe.replace.v1")
174 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.texture-recipe.operation"),
175 "Texture recipe operation is invalid");
176 const auto *recipe = field(op.payload, "recipe"), *values = field(op.payload, "values");
177 const auto* rs = recipe ? recipe->getIf<std::string>() : nullptr;
178 const auto* vo = values ? values->getIf<EditorValue::Object>() : nullptr;
179 if (!rs || *rs != recipe_ || !vo || vo->size() > 128)
180 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.texture-recipe.payload"),
181 "Texture recipe payload is invalid");
182 TextureRecipeTarget candidate = *this;
183 candidate.values_ = *vo;
184 if (hasErrors(candidate.validate()))
185 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.texture-recipe.invalid"),
186 "Texture recipe validation failed");
187 values_ = std::move(candidate.values_);
188 bumpRevision();
189 widenDirty(0, 0);
190 return eve::editing::applied<void>();
191}
192std::unique_ptr<IDomainOperationTarget> TextureRecipeTarget::cloneDomainState() const {
193 return std::make_unique<TextureRecipeTarget>(*this);
194}
196 std::unique_ptr<IDomainOperationTarget> c) {
197 auto* t = dynamic_cast<TextureRecipeTarget*>(c.get());
198 if (!t || t->id_ != id_ || t->recipe_ != recipe_)
199 return eve::editing::failed<void>(EditorStatus::Conflict, RuleId("editor.texture-recipe.candidate"),
200 "Texture recipe candidate mismatch");
201 *this = *t;
202 return eve::editing::applied<void>();
203}
205 return EditorValue::Object{{"schemaVersion", int64_t{1}}, {"content", contentValue()}};
206}
208 const auto *v = field(s, "schemaVersion"), *c = field(s, "content");
209 const auto* version = v ? v->getIf<int64_t>() : nullptr;
210 if (!version || *version != 1 || !c)
211 return eve::editing::failed<void>(EditorStatus::Unsupported, RuleId("editor.texture-recipe.snapshot"),
212 "Unsupported texture recipe snapshot");
214 op.target = TargetId(id_);
215 op.type = "texture-recipe.replace.v1";
216 op.payload = *c;
217 auto result = applyDomainOperation(op);
218 if (result.ok()) clearDirtyRegion();
219 return result;
220}
225 const auto diagnostics = target.validate();
226 if (hasErrors(diagnostics)) {
228 eve::Status(EditorStatus::Rejected, diagnostics));
229 }
231 for (const auto& [key, v] : target.values()) {
232 if (auto* x = v.getIf<int64_t>())
233 params.setInt(key, static_cast<int>(*x));
234 else if (auto* x = v.getIf<double>())
235 params.setFloat(key, static_cast<float>(*x));
236 else if (auto* x = v.getIf<bool>())
237 params.setBool(key, *x);
238 else if (auto* x = v.getIf<std::string>())
239 params.setString(key, *x);
240 }
241 std::string error;
243 if (!candidate)
244 return eve::editing::failed<TextureRecipePreviewArtifact>(EditorStatus::Failed,
245 RuleId("editor.texture-recipe.generate"), error);
246 std::uint64_t hash = 1469598103934665603ull;
247 auto* bytes = static_cast<const unsigned char*>(candidate->getData());
248 for (std::size_t i = 0; i < candidate->getSize(); ++i) {
249 hash ^= bytes[i];
250 hash *= 1099511628211ull;
251 }
252 TextureRecipePreviewArtifact artifact{target.revision(), candidate->getWidth(), candidate->getHeight(), hash};
253 image_ = std::move(candidate);
254 revision_ = target.revision();
255 return eve::editing::applied<TextureRecipePreviewArtifact>(artifact, diagnostics);
256}
257} // namespace eve::procgen_editing
LogicalId target
float x
Definition AnimClip.cpp:738
const std::string & s
glm::vec4 p[6]
scene::NodeDesc desc
std::array< std::uint8_t, 32 > hash
Definition Evpack.cpp:172
std::string message
std::uint32_t key
double r
float v
std::int32_t c
std::uint64_t bytes
std::string error
Definition Package.cpp:60
std::string id
Definition PlayHost.cpp:108
float d
float t
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
bool ok() const noexcept
Whether this result represents a non-failure outcome.
Definition Result.h:480
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
Structured status and zero or more diagnostics for an operation.
Definition Status.h:68
UUID-backed identifier adapter for legacy textual boundaries.
Definition Identity.h:314
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::map< std::string, Value > Object
const T * getIf() const
Return an immediately borrowed typed value.
Owning, typed generation parameters.
Definition Params.h:27
void setInt(const std::string &key, int value)
Store an Int64 parameter, preserving its numeric Value kind.
Definition Params.cpp:149
std::unique_ptr< image::ImageData > generate(const std::string &id, const Params &params, std::string &error) const
Generates an image and transfers its unique ownership to the caller.
static TextureRecipeRegistry & instance()
Access the process-wide texture recipe registry.
TextureRecipePreviewRuntime()
Texture recipe preview runtime.
Result< TextureRecipePreviewArtifact > generate(const TextureRecipeTarget &)
Generate.
~TextureRecipePreviewRuntime()
Texture recipe preview runtime.
Revisioned, schema-driven procedural texture recipe asset.
Result< DomainOperation > makeReset(const SelectionSnapshot &, const PropertyPath &) const override
Make reset.
Result< DomainOperation > makeSet(const SelectionSnapshot &, const PropertyPath &, const EditorValue &, PropertySetMode) const override
Make set.
EditorValue snapshotValue() const
Snapshot value.
PropertySchema schema(const SelectionSnapshot &) const override
Schema.
const std::string & recipe() const
Recipe.
PropertyReadResult read(const SelectionSnapshot &, const PropertyPath &) const override
Reads .
const EditorValue::Object & values() const
Values.
Result< void > applyDomainOperation(const DomainOperation &) override
Applies domain operation.
TextureRecipeTarget(std::string id, std::string recipe)
Construct a registered procedural texture recipe target.
Result< void > commitDomainState(std::unique_ptr< IDomainOperationTarget >) override
Commits domain state.
std::vector< EditorDiagnostic > validate() const
Validate.
TargetDescriptor describe() const override
Describe.
Result< void > loadSnapshot(const EditorValue &)
Loads snapshot.
void * queryCapability(const CapabilityId &) override
Query an optional target capability.
std::unique_ptr< IDomainOperationTarget > cloneDomainState() const override
Clones domain state.
eve::Result< eve::Revision > currentRevision(const SelectionSnapshot &) const override
Current revision.
const char * defaultValue
std::vector< ParamSpec > params
EVENGINE_API_PLATFORM bool hasErrors(const std::vector< DnutDiagnostic > &diagnostics)
Return whether any diagnostic is an error.
eve::Severity DiagnosticSeverity
StrongId< TargetIdTag > TargetId
Definition EditingIds.h:57
PropertyType
PropertyType public API.
StrongId< RuleIdTag > RuleId
Definition EditingIds.h:60
PropertySetMode
PropertySetMode public API.
StrongId< CapabilityIdTag > CapabilityId
Definition EditingIds.h:61
Result< void > validatePropertyValue(const PropertyDescriptor &descriptor, const Value &value)
Validate property value.
Diagnostic ruleDiagnostic(eve::DiagnosticCode code, RuleId rule, DiagnosticSeverity severity, std::string message)
Build a common diagnostic carrying an open editing rule identity.
const EditorValue * field(const EditorValue &value, const char *name)
editing::Diagnostic EditorDiagnostic
ParamKind
Semantic type used by schema-driven recipe parameter editors.
Definition ParamSchema.h:11
DomainOperation public API.
std::vector< std::string > affectedProperties
PropertyDescriptor public API.
PropertyReadResult public API.
EVENGINE_API_PLATFORM public API.
std::vector< PropertyDescriptor > properties
PropertyDescriptorLookup find(const PropertyPath &path) const
Finds find.
SelectionSnapshot public API.
Stable discovery metadata exposed by every editable target.
TextureRecipePreviewArtifact public API.