载入中...
搜索中...
未找到
MaterialTarget.cpp
浏览该文件的文档.
2
3#include <tuple>
4#include <utility>
5
6namespace eve::material_editing {
7namespace {
8
9PropertyDescriptor property(const char* path, const char* label, const char* category,
10 PropertyType type, EditorValue defaultValue,
11 PropertyFlag flags = PropertyFlag::Runtime) {
12 PropertyDescriptor result;
13 result.path = PropertyPath(path);
14 result.displayNameKey = label;
15 result.category = category;
16 result.type = type;
17 result.flags = flags;
18 result.defaultValue = std::move(defaultValue);
19 return result;
20}
21
22const EditorValue* objectField(const EditorValue& value, const char* key) {
23 const auto* object = value.getIf<EditorValue::Object>();
24 if (!object) return nullptr;
25 const auto found = object->find(key);
26 return found == object->end() ? nullptr : &found->second;
27}
28
29} // namespace
30
32 : id_(std::move(id)), values_(defaults()) {}
33
35 TargetDescriptor result;
36 result.id = TargetId(id_);
37 result.type = "material-document";
38 result.revision = revisionValue();
41 return result;
42}
43
44void* MaterialDocumentTarget::queryCapability(const CapabilityId& capability) {
46 return static_cast<IPropertyProvider*>(this);
48 return static_cast<eve::editing::IEditingSnapshotProvider*>(this);
49 return nullptr;
50}
51
53 if (operation.target != TargetId(id_))
54 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.material.target-mismatch"),
55 "Material operation targets another document");
56 if (operation.type != "material.property.set.v1")
57 return eve::editing::failed<void>(EditorStatus::Unsupported, RuleId("editor.material.operation-unsupported"),
58 "Unsupported material operation: " + operation.type);
59 const EditorValue* pathValue = objectField(operation.payload, "path");
60 const EditorValue* value = objectField(operation.payload, "value");
61 const auto* path = pathValue ? pathValue->getIf<std::string>() : nullptr;
62 if (!path || !value)
63 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.material.operation-payload"),
64 "Material operation requires path and value");
65 auto descriptor = materialSchema().find(PropertyPath(*path));
66 if (!descriptor)
67 return eve::editing::failed<void>(EditorStatus::Unsupported, RuleId("editor.material.property-unsupported"),
68 "Unknown material property: " + *path);
69 auto valid = validateAssignment(*descriptor, *value);
70 if (!valid.ok()) return valid;
71 values_[*path] = *value;
73 widenDirty(0, 0);
74 return eve::editing::applied<void>();
75}
76
77std::unique_ptr<IDomainOperationTarget> MaterialDocumentTarget::cloneDomainState() const {
78 return std::make_unique<MaterialDocumentTarget>(*this);
79}
80
82 std::unique_ptr<IDomainOperationTarget> candidate) {
83 auto* typed = dynamic_cast<MaterialDocumentTarget*>(candidate.get());
84 if (!typed || typed->id_ != id_)
85 return eve::editing::failed<void>(EditorStatus::Conflict, RuleId("editor.material.candidate-mismatch"),
86 "Material candidate belongs to another target");
87 *this = *typed;
88 return eve::editing::applied<void>();
89}
90
92 if (!selectionMatches(selection))
94 eve::DiagnosticCode::InvalidArgument, "Selection does not belong to this material",
95 "editor.material.selection", {}, "editor.MaterialDocumentTarget"));
97}
98
99PropertySchema MaterialDocumentTarget::schema(const SelectionSnapshot&) const { return materialSchema(); }
100
102 const PropertyPath& path) const {
103 if (!selectionMatches(selection)) return {};
104 const auto found = values_.find(path.value());
105 if (found == values_.end()) return {};
106 return {PropertyReadState::Value, found->second, {}};
107}
108
110 const PropertyPath& path,
111 const EditorValue& value,
112 PropertySetMode mode) const {
113 if (!selectionMatches(selection))
114 return eve::editing::failed<DomainOperation>(EditorStatus::Rejected, RuleId("editor.material.selection"),
115 "Selection does not belong to this material");
116 if (mode == PropertySetMode::Reset) return makeReset(selection, path);
117 if (mode != PropertySetMode::Absolute)
118 return eve::editing::failed<DomainOperation>(EditorStatus::Unsupported, RuleId("editor.material.set-mode"),
119 "Material properties require absolute assignment");
120 auto descriptor = materialSchema().find(path);
121 if (!descriptor)
122 return eve::editing::failed<DomainOperation>(EditorStatus::Unsupported,
123 RuleId("editor.material.property-unsupported"),
124 "Unknown material property: " + path.value());
125 auto valid = validateAssignment(*descriptor, value);
126 if (!valid.ok()) return Result<DomainOperation>::failure(valid.status());
127 const auto previous = values_.find(path.value());
128 if (previous == values_.end())
129 return eve::editing::failed<DomainOperation>(EditorStatus::NotFound, RuleId("editor.material.property-missing"),
130 "Material property has no current value");
131 auto payload = [&](const EditorValue& assigned) {
133 object["path"] = path.value();
134 object["value"] = assigned;
135 return EditorValue(std::move(object));
136 };
138 operation.type = "material.property.set.v1";
139 operation.target = TargetId(id_);
140 operation.payload = payload(value);
141 operation.inverse = payload(previous->second);
142 operation.hasInverse = true;
143 operation.affectedProperties.push_back(path.value());
144 operation.mergeKey = "material:" + id_ + ":" + path.value();
145 return eve::editing::applied<DomainOperation>(std::move(operation));
146}
147
149 const PropertyPath& path) const {
150 auto descriptor = materialSchema().find(path);
151 if (!descriptor)
152 return eve::editing::failed<DomainOperation>(EditorStatus::Unsupported,
153 RuleId("editor.material.property-unsupported"),
154 "Unknown material property: " + path.value());
155 return makeSet(selection, path, descriptor->defaultValue, PropertySetMode::Absolute);
156}
157
160 for (const auto& [path, value] : values_) properties[path] = value;
162 root["schemaVersion"] = 9;
163 root["properties"] = EditorValue(std::move(properties));
164 return EditorValue(std::move(root));
165}
166
168 const EditorValue* versionValue = objectField(snapshot, "schemaVersion");
169 const EditorValue* propertiesValue = objectField(snapshot, "properties");
170 const auto* version = versionValue ? versionValue->getIf<int64_t>() : nullptr;
171 const auto* properties = propertiesValue ? propertiesValue->getIf<EditorValue::Object>() : nullptr;
172 if (!version || (*version < 1 || *version > 9) || !properties)
173 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.material.snapshot-format"),
174 "Material snapshot requires schemaVersion 1 through 9 and properties");
175 auto candidate = defaults();
176 const PropertySchema schemaValue = materialSchema();
177 for (const auto& [path, value] : *properties) {
178 auto descriptor = schemaValue.find(PropertyPath(path));
179 if (!descriptor)
180 return eve::editing::failed<void>(EditorStatus::Unsupported, RuleId("editor.material.snapshot-property"),
181 "Material snapshot contains unknown property: " + path);
182 auto valid = validateAssignment(*descriptor, value);
183 if (!valid.ok()) return valid;
184 candidate[path] = value;
185 }
186 values_ = std::move(candidate);
187 bumpRevision();
189 return eve::editing::applied<void>();
190}
191
192std::vector<EditorDiagnostic> MaterialDocumentTarget::validate() const {
193 std::vector<EditorDiagnostic> diagnostics;
194 const auto stringValue = [&](const char* path) -> std::string {
195 const auto found = values_.find(path);
196 if (found == values_.end()) return {};
197 const auto* value = found->second.getIf<std::string>();
198 return value ? *value : std::string{};
199 };
200 if (stringValue("shading.model") == "custom" && stringValue("textures.shader").empty())
201 diagnostics.push_back(eve::editing::ruleDiagnostic(
202 eve::DiagnosticCode::PreconditionViolation, RuleId("editor.material.custom-shader-required"),
203 DiagnosticSeverity::Error, "Custom shading requires a shader asset"));
204 if (stringValue("surface.mode") == "masked" && stringValue("textures.albedo").empty())
205 diagnostics.push_back(eve::editing::ruleDiagnostic(
206 eve::DiagnosticCode::PreconditionViolation, RuleId("editor.material.mask-without-albedo"),
207 DiagnosticSeverity::Warning, "Masked material has no albedo texture providing alpha"));
208 if (!stringValue("textures.height").empty()) {
209 const auto* scale = values_.at("parallax.scale").getIf<double>();
210 if (scale && *scale == 0.0)
211 diagnostics.push_back(eve::editing::ruleDiagnostic(
212 eve::DiagnosticCode::PreconditionViolation, RuleId("editor.material.height-without-parallax"),
213 DiagnosticSeverity::Info, "Height texture is assigned while parallax scale is zero"));
214 }
215 const auto boolValue = [&](const char* path) {
216 const auto found = values_.find(path);
217 const auto* value = found == values_.end() ? nullptr : found->second.getIf<bool>();
218 return value && *value;
219 };
220 if (boolValue("vegetation.alpha.enabled") && stringValue("vegetation.alpha.noise").empty())
221 diagnostics.push_back(eve::editing::ruleDiagnostic(
222 eve::DiagnosticCode::PreconditionViolation, RuleId("editor.material.vegetation-alpha-noise"),
223 DiagnosticSeverity::Warning,
224 "Vegetation alpha fading uses the neutral noise volume until a 3D noise asset is assigned"));
225 return diagnostics;
226}
227
228PropertySchema MaterialDocumentTarget::materialSchema() {
229 PropertySchema schema;
230 schema.typeId = "graphics.material";
231 schema.version = 9;
232 auto shading = property("shading.model", "editor.material.shading-model", "shading",
233 PropertyType::Enum, "pbr");
234 shading.enumItems = {"pbr", "unlit", "hair", "custom"};
235 schema.properties.push_back(std::move(shading));
236 auto tint = property("shading.tint", "editor.material.tint", "shading", PropertyType::Color,
237 EditorValue::Array{1.0, 1.0, 1.0, 1.0});
238 schema.properties.push_back(std::move(tint));
239 auto numeric = [&](const char* path, const char* label, double value, double minimum, double maximum) {
240 auto descriptor = property(path, label, "shading", PropertyType::Float, value);
241 descriptor.numeric.minimum = minimum;
242 descriptor.numeric.maximum = maximum;
243 descriptor.numeric.step = 0.01;
244 schema.properties.push_back(std::move(descriptor));
245 };
246 numeric("shading.metallic", "editor.material.metallic", 0.0, 0.0, 1.0);
247 numeric("shading.roughness", "editor.material.roughness", 0.45, 0.04, 1.0);
248 for (const auto& [path, label] : {std::pair{"textures.albedo", "editor.material.albedo"},
249 {"textures.normal", "editor.material.normal"},
250 {"textures.height", "editor.material.height"},
251 {"textures.orm", "editor.material.orm"},
252 {"textures.emissive", "editor.material.emissive"},
253 {"textures.shader", "editor.material.shader"}}) {
254 auto descriptor = property(path, label, "textures", PropertyType::AssetRef, "");
255 descriptor.assetTypeFilters = path == std::string("textures.shader")
256 ? std::vector<std::string>{"shader"}
257 : std::vector<std::string>{"texture"};
258 schema.properties.push_back(std::move(descriptor));
259 }
260 auto surface = property("surface.mode", "editor.material.surface-mode", "surface",
261 PropertyType::Enum, "opaque");
262 surface.enumItems = {"opaque", "masked", "transparent"};
263 schema.properties.push_back(std::move(surface));
264 auto blend = property("surface.blend", "editor.material.blend-mode", "surface",
265 PropertyType::Enum, "alpha");
266 blend.enumItems = {"alpha", "premultiplied", "additive", "multiply"};
267 schema.properties.push_back(std::move(blend));
268 numeric("surface.alpha-cutoff", "editor.material.alpha-cutoff", 0.5, 0.0, 1.0);
269 numeric("parallax.scale", "editor.material.parallax-scale", 0.0, 0.0, 0.25);
270 for (const auto& [path, label, value] : {
271 std::tuple{"surface.depth-write", "editor.material.depth-write", false},
272 {"surface.double-sided", "editor.material.double-sided", false},
273 {"lighting.receive", "editor.material.receive-light", true},
274 {"shadow.cast", "editor.material.cast-shadow", true},
275 {"shadow.receive", "editor.material.receive-shadow", true}})
276 schema.properties.push_back(property(path, label, "surface", PropertyType::Bool, value));
277 schema.properties.push_back(property("vegetation.alpha.enabled", "editor.material.vegetation-alpha-enabled",
278 "vegetation-alpha", PropertyType::Bool, false));
279 auto fadeNoise = property("vegetation.alpha.noise", "editor.material.vegetation-alpha-noise",
280 "vegetation-alpha", PropertyType::AssetRef, "");
281 fadeNoise.assetTypeFilters = {"texture3d"};
282 schema.properties.push_back(std::move(fadeNoise));
283 auto vegetationNumeric = [&](const char* path, const char* label, double value, double minimum, double maximum) {
284 auto descriptor = property(path, label, "vegetation-alpha", PropertyType::Float, value);
285 descriptor.numeric.minimum = minimum;
286 descriptor.numeric.maximum = maximum;
287 descriptor.numeric.step = 0.01;
288 schema.properties.push_back(std::move(descriptor));
289 };
290 vegetationNumeric("vegetation.alpha.global", "editor.material.vegetation-alpha-global", 0.0, 0.0, 1.0);
291 vegetationNumeric("vegetation.alpha.variation", "editor.material.vegetation-alpha-variation", 0.0, 0.0, 1.0);
292 vegetationNumeric("vegetation.alpha.glancing", "editor.material.vegetation-alpha-glancing", 0.0, 0.0, 1.0);
293 vegetationNumeric("vegetation.alpha.camera", "editor.material.vegetation-alpha-camera", 1.0, 0.0, 1.0);
294 vegetationNumeric("vegetation.alpha.constant", "editor.material.vegetation-alpha-constant", 0.0, 0.0, 1.0);
295 vegetationNumeric("vegetation.alpha.camera-min", "editor.material.vegetation-alpha-camera-min", 0.0, 0.0,
296 1000000.0);
297 vegetationNumeric("vegetation.alpha.camera-max", "editor.material.vegetation-alpha-camera-max", 100.0, 0.0,
298 1000000.0);
299 vegetationNumeric("vegetation.alpha.noise-tiling", "editor.material.vegetation-alpha-noise-tiling", 1.0, 0.0,
300 1000000.0);
301 schema.properties.push_back(property("vegetation.alpha.detail-fade",
302 "editor.material.vegetation-alpha-detail-fade", "vegetation-alpha",
303 PropertyType::Bool, false));
304 schema.properties.push_back(property("vegetation.emission.enabled", "editor.material.vegetation-emission-enabled",
305 "vegetation-emission", PropertyType::Bool, false));
306 auto vegetationStageNumeric = [&](const char* path, const char* label, const char* category, double value) {
307 auto descriptor = property(path, label, category, PropertyType::Float, value);
308 descriptor.numeric.minimum = 0.0;
309 descriptor.numeric.maximum = 1.0;
310 descriptor.numeric.step = 0.01;
311 schema.properties.push_back(std::move(descriptor));
312 };
313 vegetationStageNumeric("vegetation.emission.minimum", "editor.material.vegetation-emission-minimum",
314 "vegetation-emission", 0.0);
315 vegetationStageNumeric("vegetation.emission.maximum", "editor.material.vegetation-emission-maximum",
316 "vegetation-emission", 1.0);
317 vegetationStageNumeric("vegetation.emission.phase", "editor.material.vegetation-emission-phase",
318 "vegetation-emission", 1.0);
319 vegetationStageNumeric("vegetation.emission.global", "editor.material.vegetation-emission-global",
320 "vegetation-emission", 1.0);
321 schema.properties.push_back(property("vegetation.gradient.enabled", "editor.material.vegetation-gradient-enabled",
322 "vegetation-gradient", PropertyType::Bool, false));
323 schema.properties.push_back(property("vegetation.gradient.color-one", "editor.material.vegetation-gradient-color-one",
324 "vegetation-gradient", PropertyType::Color,
325 EditorValue::Array{1.0, 1.0, 1.0, 1.0}));
326 schema.properties.push_back(property("vegetation.gradient.color-two", "editor.material.vegetation-gradient-color-two",
327 "vegetation-gradient", PropertyType::Color,
328 EditorValue::Array{1.0, 1.0, 1.0, 1.0}));
329 vegetationStageNumeric("vegetation.gradient.minimum", "editor.material.vegetation-gradient-minimum",
330 "vegetation-gradient", 0.0);
331 vegetationStageNumeric("vegetation.gradient.maximum", "editor.material.vegetation-gradient-maximum",
332 "vegetation-gradient", 1.0);
333 schema.properties.push_back(property("vegetation.translucency.enabled",
334 "editor.material.vegetation-translucency-enabled",
335 "vegetation-translucency", PropertyType::Bool, false));
336 schema.properties.push_back(property("vegetation.translucency.color",
337 "editor.material.vegetation-translucency-color",
338 "vegetation-translucency", PropertyType::Color,
339 EditorValue::Array{1.0, 1.0, 1.0, 1.0}));
340 auto translucencyNumeric = [&](const char* name, double value, double minimum, double maximum) {
341 const std::string path = std::string("vegetation.translucency.") + name;
342 const std::string label = std::string("editor.material.vegetation-translucency-") + name;
343 auto descriptor = property(path.c_str(), label.c_str(), "vegetation-translucency", PropertyType::Float, value);
344 descriptor.numeric.minimum = minimum;
345 descriptor.numeric.maximum = maximum;
346 descriptor.numeric.step = 0.01;
347 schema.properties.push_back(std::move(descriptor));
348 };
349 translucencyNumeric("intensity", 0.0, 0.0, 1.0);
350 translucencyNumeric("strength", 1.0, 0.0, 50.0);
351 translucencyNumeric("normal-distortion", 0.5, 0.0, 1.0);
352 translucencyNumeric("scattering", 2.0, 1.0, 50.0);
353 translucencyNumeric("direct", 0.9, 0.0, 1.0);
354 translucencyNumeric("ambient", 0.1, 0.0, 1.0);
355 translucencyNumeric("shadow", 0.5, 0.0, 1.0);
356 translucencyNumeric("mask-amount", 0.0, 0.0, 1.0);
357 translucencyNumeric("global", 1.0, 0.0, 1000000.0);
358 translucencyNumeric("overlay", 1.0, 0.0, 1000000.0);
359 translucencyNumeric("mask-minimum", 0.0, 0.0, 1.0);
360 translucencyNumeric("mask-maximum", 0.0, 0.0, 1.0);
361 schema.properties.push_back(property("vegetation.color.enabled", "editor.material.vegetation-color-enabled",
362 "vegetation-color", PropertyType::Bool, false));
363 for (const auto& [path, label, value] : {
364 std::tuple{"vegetation.color.field", "editor.material.vegetation-field-color",
365 EditorValue::Array{1.0, 1.0, 1.0, 0.0}},
366 {"vegetation.color.overlay-color", "editor.material.vegetation-overlay-color",
367 EditorValue::Array{1.0, 1.0, 1.0, 1.0}},
368 {"vegetation.color.vertex-occlusion-color", "editor.material.vegetation-vertex-occlusion-color",
369 EditorValue::Array{1.0, 1.0, 1.0, 1.0}}})
370 schema.properties.push_back(property(path, label, "vegetation-color", PropertyType::Color, value));
371 auto colorNumeric = [&](const char* name, double value, double minimum, double maximum) {
372 const std::string path = std::string("vegetation.color.") + name;
373 const std::string label = std::string("editor.material.vegetation-color-") + name;
374 auto descriptor = property(path.c_str(), label.c_str(), "vegetation-color", PropertyType::Float, value);
375 descriptor.numeric.minimum = minimum;
376 descriptor.numeric.maximum = maximum;
377 descriptor.numeric.step = 0.01;
378 schema.properties.push_back(std::move(descriptor));
379 };
380 for (const auto& [name, value] : {
381 std::pair{"overlay", 0.0}, {"wetness", 0.0}, {"overlay-variation", 0.5},
382 {"overlay-projection", 0.5}, {"vertex-occlusion-alpha", 0.5019608},
383 {"overlay-normal", 0.5}, {"wetness-normal", 0.5}, {"overlay-smoothness", 0.5},
384 {"wetness-contrast", 0.5}, {"overlay-subsurface", 0.5}, {"colors-coverage", 1.0},
385 {"colors-mask", 1.0}, {"colors-variation", 0.5}, {"color-mask-minimum", 0.1},
386 {"color-mask-maximum", 0.2}, {"overlay-mask-minimum", 0.1}, {"overlay-mask-maximum", 0.2},
387 {"vertex-occlusion-minimum", 0.0}, {"vertex-occlusion-maximum", 1.0}})
388 colorNumeric(name, value, 0.0, 1.0);
389 colorNumeric("colors-intensity", 1.0, 0.0, 2.0);
390 colorNumeric("alpha-threshold-offset", 0.0, -0.5, 0.5);
391 schema.properties.push_back(property("vegetation.color.invert-vertex-occlusion",
392 "editor.material.vegetation-color-invert-vertex-occlusion",
393 "vegetation-color", PropertyType::Bool, false));
394 schema.properties.push_back(property("vegetation.color.invert-vertex-occlusion-colors",
395 "editor.material.vegetation-color-invert-vertex-occlusion-colors",
396 "vegetation-color", PropertyType::Bool, false));
397 auto backface = property("vegetation.color.backface-normal", "editor.material.vegetation-backface-normal",
398 "vegetation-color", PropertyType::Enum, "flip");
399 backface.enumItems = {"flip", "mirror", "same"};
400 schema.properties.push_back(std::move(backface));
401 schema.properties.push_back(property("vegetation.detail.enabled", "editor.material.vegetation-detail-enabled",
402 "vegetation-detail", PropertyType::Bool, false));
403 for (const auto& [path, label] : {
404 std::pair{"vegetation.detail.albedo", "editor.material.vegetation-detail-albedo"},
405 {"vegetation.detail.normal", "editor.material.vegetation-detail-normal"},
406 {"vegetation.detail.mask", "editor.material.vegetation-detail-mask"}}) {
407 auto descriptor =
408 property(path, label, "vegetation-detail", PropertyType::AssetRef, EditorValue(std::string{}));
409 descriptor.assetTypeFilters = {"texture"};
410 schema.properties.push_back(std::move(descriptor));
411 }
412 schema.properties.push_back(property("vegetation.detail.color", "editor.material.vegetation-detail-color",
413 "vegetation-detail", PropertyType::Color,
414 EditorValue::Array{1.0, 1.0, 1.0, 1.0}));
415 schema.properties.push_back(property("vegetation.detail.color-two", "editor.material.vegetation-detail-color-two",
416 "vegetation-detail", PropertyType::Color,
417 EditorValue::Array{1.0, 1.0, 1.0, 1.0}));
418 schema.properties.push_back(property("vegetation.detail.uv-scale", "editor.material.vegetation-detail-uv-scale",
419 "vegetation-detail", PropertyType::Vec2,
420 EditorValue::Array{1.0, 1.0}));
421 schema.properties.push_back(property("vegetation.detail.uv-offset", "editor.material.vegetation-detail-uv-offset",
422 "vegetation-detail", PropertyType::Vec2,
423 EditorValue::Array{0.0, 0.0}));
424 schema.properties.push_back(property("vegetation.detail.inverse-uv-scale",
425 "editor.material.vegetation-detail-inverse-uv-scale",
426 "vegetation-detail", PropertyType::Bool, false));
427 auto detailEnum = [&](const char* name, std::vector<std::string> choices, const char* defaultValue) {
428 const std::string path = std::string("vegetation.detail.") + name;
429 const std::string label = std::string("editor.material.vegetation-detail-") + name;
430 auto descriptor = property(path.c_str(), label.c_str(), "vegetation-detail", PropertyType::Enum,
431 EditorValue(std::string(defaultValue)));
432 descriptor.enumItems = std::move(choices);
433 schema.properties.push_back(std::move(descriptor));
434 };
435 detailEnum("uv-mode", {"primary", "detail", "world"}, "primary");
436 detailEnum("color-mode", {"single", "variation"}, "single");
437 detailEnum("blend-mode", {"multiply", "replace"}, "multiply");
438 detailEnum("alpha-mode", {"main", "detail"}, "detail");
439 detailEnum("mask-mode", {"regular", "inverse"}, "regular");
440 detailEnum("mesh-mode", {"regular", "inverse"}, "regular");
441 auto detailNumeric = [&](const char* name, double value, double minimum, double maximum) {
442 const std::string path = std::string("vegetation.detail.") + name;
443 const std::string label = std::string("editor.material.vegetation-detail-") + name;
444 auto descriptor = property(path.c_str(), label.c_str(), "vegetation-detail", PropertyType::Float, value);
445 descriptor.numeric.minimum = minimum;
446 descriptor.numeric.maximum = maximum;
447 descriptor.numeric.step = 0.01;
448 schema.properties.push_back(std::move(descriptor));
449 };
450 detailNumeric("value", 1.0, 0.0, 1.0);
451 detailNumeric("normal-value", 1.0, -8.0, 8.0);
452 for (const auto& [name, value] : {
453 std::pair{"normal-blend", 1.0}, {"albedo-value", 1.0}, {"metallic", 0.0}, {"occlusion", 1.0},
454 {"smoothness", 1.0}, {"blend-minimum", 0.0}, {"blend-maximum", 1.0}, {"mask-minimum", 0.0},
455 {"mask-maximum", 1.0}, {"mesh-minimum", 0.0}, {"mesh-maximum", 1.0}})
456 detailNumeric(name, value, 0.0, 1.0);
457 const auto fieldGroup = [&](const char* group, EditorValue::Array fallback) {
458 const std::string prefix = std::string("vegetation.") + group;
459 const std::string label = std::string("editor.material.vegetation-") + group;
460 schema.properties.push_back(property((prefix + ".enabled").c_str(), (label + "-enabled").c_str(),
461 "vegetation-fields", PropertyType::Bool, false));
462 auto texture = property((prefix + ".texture").c_str(), (label + "-texture").c_str(),
463 "vegetation-fields", PropertyType::AssetRef, EditorValue(std::string{}));
464 texture.assetTypeFilters = {"texture2d-array"};
465 schema.properties.push_back(std::move(texture));
466 auto layer = property((prefix + ".layer").c_str(), (label + "-layer").c_str(),
467 "vegetation-fields", PropertyType::Int, std::int64_t(0));
468 layer.numeric.minimum = 0;
469 layer.numeric.maximum = 8;
470 layer.numeric.step = 1;
471 schema.properties.push_back(std::move(layer));
472 schema.properties.push_back(property((prefix + ".fallback").c_str(), (label + "-fallback").c_str(),
473 "vegetation-fields", PropertyType::Vec4, std::move(fallback)));
474 schema.properties.push_back(property((prefix + ".coords").c_str(), (label + "-coords").c_str(),
475 "vegetation-fields", PropertyType::Vec4,
476 EditorValue::Array{1.0, 1.0, 0.0, 0.0}));
477 schema.properties.push_back(property((prefix + ".use-pivot-position").c_str(),
478 (label + "-use-pivot-position").c_str(), "vegetation-fields",
479 PropertyType::Bool, false));
480 for (std::int64_t i = 0; i < 9; ++i) {
481 const std::string path = prefix + ".usage-" + std::to_string(i);
482 auto usage = property(path.c_str(), (label + "-usage").c_str(), "vegetation-fields",
483 PropertyType::Float, 0.0);
484 usage.numeric.minimum = 0.0;
485 usage.numeric.maximum = 1.0;
486 usage.numeric.step = 0.01;
487 schema.properties.push_back(std::move(usage));
488 }
489 };
490 fieldGroup("extras", EditorValue::Array{1.0, 0.0, 0.0, 1.0});
491 fieldGroup("colors", EditorValue::Array{1.0, 1.0, 1.0, 0.0});
492 schema.properties.push_back(property("vegetation.vertex.enabled", "editor.material.vegetation-vertex-enabled",
493 "vegetation-vertex", PropertyType::Bool, false));
494 auto vertexTexture = property("vegetation.vertex.texture", "editor.material.vegetation-vertex-texture",
495 "vegetation-vertex", PropertyType::AssetRef, EditorValue(std::string{}));
496 vertexTexture.assetTypeFilters = {"texture2d-array"};
497 schema.properties.push_back(std::move(vertexTexture));
498 auto vertexLayer = property("vegetation.vertex.layer", "editor.material.vegetation-vertex-layer",
499 "vegetation-vertex", PropertyType::Int, std::int64_t(0));
500 vertexLayer.numeric.minimum = 0;
501 vertexLayer.numeric.maximum = 8;
502 vertexLayer.numeric.step = 1;
503 schema.properties.push_back(std::move(vertexLayer));
504 schema.properties.push_back(property("vegetation.vertex.fallback", "editor.material.vegetation-vertex-fallback",
505 "vegetation-vertex", PropertyType::Vec4,
506 EditorValue::Array{0.0, 0.0, 0.0, 1.0}));
507 schema.properties.push_back(property("vegetation.vertex.coords", "editor.material.vegetation-vertex-coords",
508 "vegetation-vertex", PropertyType::Vec4,
509 EditorValue::Array{1.0, 1.0, 0.0, 0.0}));
510 for (std::int64_t i = 0; i < 9; ++i) {
511 const std::string path = "vegetation.vertex.usage-" + std::to_string(i);
512 auto usage = property(path.c_str(), "editor.material.vegetation-vertex-usage", "vegetation-vertex",
513 PropertyType::Float, 0.0);
514 usage.numeric.minimum = 0.0;
515 usage.numeric.maximum = 1.0;
516 usage.numeric.step = 0.01;
517 schema.properties.push_back(std::move(usage));
518 }
519 auto vertexSource = property("vegetation.vertex.source", "editor.material.vegetation-vertex-source",
520 "vegetation-vertex", PropertyType::Enum, "rest-mesh");
521 vertexSource.enumItems = {"rest-mesh", "cpu-deformed", "gpu-fields"};
522 schema.properties.push_back(std::move(vertexSource));
523 const auto vertexNumeric = [&](const char* name, double defaultValue, double minimum, double maximum) {
524 const std::string path = std::string("vegetation.vertex.") + name;
525 const std::string label = std::string("editor.material.vegetation-vertex-") + name;
526 auto descriptor = property(path.c_str(), label.c_str(), "vegetation-vertex", PropertyType::Float,
528 descriptor.numeric.minimum = minimum;
529 descriptor.numeric.maximum = maximum;
530 descriptor.numeric.step = 0.01;
531 schema.properties.push_back(std::move(descriptor));
532 };
533 vertexNumeric("global-size", 1.0, 0.0, 1.0);
534 vertexNumeric("size-fade-start", 0.0, 0.0, 1000000.0);
535 vertexNumeric("size-fade-end", 100.0, 0.0, 1000000.0);
536 vertexNumeric("distance-fade-bias", 1.0, 0.0001, 1000000.0);
537 auto motionMode = property("vegetation.motion.mode", "editor.material.vegetation-motion-mode",
538 "vegetation-motion", PropertyType::Enum, "disabled");
539 motionMode.enumItems = {"disabled", "object"};
540 schema.properties.push_back(std::move(motionMode));
541 for (const auto& [name, filter] : {
542 std::pair{"texture", "texture2d-array"}, {"noise", "texture2d"}}) {
543 const std::string path = std::string("vegetation.motion.") + name;
544 const std::string label = std::string("editor.material.vegetation-motion-") + name;
545 auto texture = property(path.c_str(), label.c_str(), "vegetation-motion", PropertyType::AssetRef,
546 EditorValue(std::string{}));
547 texture.assetTypeFilters = {filter};
548 schema.properties.push_back(std::move(texture));
549 }
550 auto motionLayer = property("vegetation.motion.layer", "editor.material.vegetation-motion-layer",
551 "vegetation-motion", PropertyType::Int, std::int64_t(0));
552 motionLayer.numeric.minimum = 0;
553 motionLayer.numeric.maximum = 8;
554 motionLayer.numeric.step = 1;
555 schema.properties.push_back(std::move(motionLayer));
556 schema.properties.push_back(property("vegetation.motion.fallback", "editor.material.vegetation-motion-fallback",
557 "vegetation-motion", PropertyType::Vec4,
558 EditorValue::Array{1.0, 0.0, 0.5, 0.0}));
559 schema.properties.push_back(property("vegetation.motion.coords", "editor.material.vegetation-motion-coords",
560 "vegetation-motion", PropertyType::Vec4,
561 EditorValue::Array{1.0, 1.0, 0.0, 0.0}));
562 schema.properties.push_back(property("vegetation.motion.global-direction",
563 "editor.material.vegetation-motion-global-direction", "vegetation-motion",
564 PropertyType::Vec2, EditorValue::Array{1.0, 0.0}));
565 schema.properties.push_back(property("vegetation.motion.world-origin",
566 "editor.material.vegetation-motion-world-origin", "vegetation-motion",
567 PropertyType::Vec3, EditorValue::Array{0.0, 0.0, 0.0}));
568 for (std::int64_t i = 0; i < 9; ++i) {
569 const std::string path = "vegetation.motion.usage-" + std::to_string(i);
570 auto usage = property(path.c_str(), "editor.material.vegetation-motion-usage", "vegetation-motion",
571 PropertyType::Float, 0.0);
572 usage.numeric.minimum = 0.0;
573 usage.numeric.maximum = 1.0;
574 usage.numeric.step = 0.01;
575 schema.properties.push_back(std::move(usage));
576 }
577 const auto motionNumeric = [&](const char* name, double defaultValue, double minimum, double maximum) {
578 const std::string path = std::string("vegetation.motion.") + name;
579 const std::string label = std::string("editor.material.vegetation-motion-") + name;
580 auto descriptor = property(path.c_str(), label.c_str(), "vegetation-motion", PropertyType::Float,
582 descriptor.numeric.minimum = minimum;
583 descriptor.numeric.maximum = maximum;
584 descriptor.numeric.step = 0.01;
585 schema.properties.push_back(std::move(descriptor));
586 };
587 motionNumeric("time", 0.0, -1000000000000.0, 1000000000000.0);
588 for (const auto& [name, value] : {
589 std::pair{"dynamic-mode", 0.0}, {"rigidity", 0.5}, {"facing", 0.5}, {"interaction-mask", 1.0}})
590 motionNumeric(name, value, 0.0, 1.0);
591 for (const auto& [name, value] : {
592 std::pair{"bending", 0.2}, {"bending-speed", 2.0}, {"bending-scale", 1.0},
593 {"bending-variation", 0.0}, {"branch", 0.2}, {"rolling", 0.2}, {"branch-speed", 6.0},
594 {"branch-scale", 3.0}, {"branch-variation", 0.0}, {"flutter", 0.2}, {"flutter-speed", 20.0},
595 {"flutter-scale", 10.0}, {"flutter-variation", 0.0}, {"global-bending", 1.0},
596 {"global-branch", 1.0}, {"global-flutter", 1.0}, {"noise-tiling", 1.0},
597 {"interaction", 1.0}, {"fade-distance", 100.0}, {"perspective-push", 0.0},
598 {"perspective-noise", 0.0}, {"perspective-angle", 1.0}})
599 motionNumeric(name, value, 0.0, 1000000.0);
600 return schema;
601}
602
603std::map<std::string, EditorValue> MaterialDocumentTarget::defaults() {
604 std::map<std::string, EditorValue> result;
605 for (const PropertyDescriptor& descriptor : materialSchema().properties)
607 return result;
608}
609
610Result<void> MaterialDocumentTarget::validateAssignment(const PropertyDescriptor& descriptor,
611 const EditorValue& value) {
613}
614
615bool MaterialDocumentTarget::selectionMatches(const SelectionSnapshot& selection) const {
616 if (selection.items.size() != 1) return false;
617 return selection.items.front().target == TargetId(id_);
618}
619
620} // namespace eve::material_editing
ActionParameterOperation operation
double value
Value::Object payload
int root
Definition AnimSmr.cpp:119
std::string usage
std::string descriptor
building::EdgeCurveGroup group
std::string label
float maximum[3]
float minimum[3]
std::uint32_t key
glm::vec4 tint
float blend
std::array< float, 3 > scale
std::map< std::string, PropertyRule > properties
std::set< std::string > choices
std::string name
bool valid
graphics::Canvas * previous
TileLayer * layer
std::string path
Definition PlayHost.cpp:110
std::string id
Definition PlayHost.cpp:108
bool found
std::string filter
Json object
const char * category
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
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.
eve::OptionalRef< C > capability()
Query a capability through its interface-owned stable identity.
virtual void clearDirtyRegion()=0
Clears dirty region.
Optional capability exposing a deterministic, persistence-safe editing snapshot.
static CapabilityId editingCapabilityId()
Stable capability identity shared by every editable domain target.
UI-independent property access for an immutable selection snapshot.
static CapabilityId editingCapabilityId()
Stable capability identity for generic property editing.
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
std::map< std::string, Value > Object
const T * getIf() const
Return an immediately borrowed typed value.
UI-neutral, serializable material authoring target.
TargetDescriptor describe() const override
Describe.
PropertySchema schema(const SelectionSnapshot &selection) const override
Schema.
PropertyReadResult read(const SelectionSnapshot &selection, const PropertyPath &path) const override
Reads .
std::vector< EditorDiagnostic > validate() const
Validate cross-field authoring rules without mutating the target.
eve::Result< eve::Revision > currentRevision(const SelectionSnapshot &selection) const override
Current revision.
void * queryCapability(const CapabilityId &capability) override
Query an optional target capability.
Result< void > applyDomainOperation(const DomainOperation &operation) override
Applies domain operation.
EditorValue snapshotValue() const override
Capture deterministic content suitable for DocumentService persistence.
Result< void > commitDomainState(std::unique_ptr< IDomainOperationTarget > candidate) override
Commits domain state.
MaterialDocumentTarget(std::string id)
Material document target.
Result< DomainOperation > makeReset(const SelectionSnapshot &selection, const PropertyPath &path) const override
Make reset.
Result< DomainOperation > makeSet(const SelectionSnapshot &selection, const PropertyPath &path, const EditorValue &value, PropertySetMode mode) const override
Make set.
Result< void > loadSnapshot(const EditorValue &snapshot)
Replace content when opening a persisted material document.
std::unique_ptr< IDomainOperationTarget > cloneDomainState() const override
Clones domain state.
const char * defaultValue
editing::PropertyDescriptor PropertyDescriptor
editing::PropertyPath PropertyPath
editing::TargetId TargetId
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.
editing::Value EditorValue
DomainOperation 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.
std::vector< CapabilityId > capabilities