11double number(
const DecalDocumentTarget& document,
const char*
path) {
12 return *document.value(
path)->getIf<
double>();
19Result<graphics::Texture*> resolve(
const DecalDocumentTarget& document,
20 const IDecalRuntimeAssetResolver* assets,
const char*
path) {
21 const auto&
name = *document.value(
path)->getIf<std::string>();
22 if (
name.empty())
return eve::editing::applied<graphics::Texture*>(
nullptr);
24 return eve::editing::failed<graphics::Texture*>(EditorStatus::Rejected,
RuleId(
"editor.decal.assets"),
25 "Decal texture resolver is required");
26 return assets->texture(
name);
33 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId(
"editor.decal.manager"),
34 "Live DecalManager is required");
35 const auto diagnostics = document.
validate();
36 if (std::any_of(diagnostics.begin(), diagnostics.end(),
37 [](
const auto& diagnostic) { return diagnostic.severity() == DiagnosticSeverity::Error; }))
40 auto albedo = resolve(document, assets_,
"texture.albedo");
42 auto normal = resolve(document, assets_,
"texture.normal");
44 auto params = resolve(document, assets_,
"texture.params");
48 const auto&
position = array(document,
"transform.position");
49 const auto&
direction = array(document,
"transform.normal");
50 const auto&
uv = array(document,
"texture.uvRect");
51 candidate.
x =
static_cast<float>(*
position[0].getIf<
double>());
52 candidate.
y =
static_cast<float>(*
position[1].getIf<
double>());
53 candidate.
z =
static_cast<float>(*
position[2].getIf<
double>());
54 candidate.
nx =
static_cast<float>(*
direction[0].getIf<
double>());
55 candidate.
ny =
static_cast<float>(*
direction[1].getIf<
double>());
56 candidate.
nz =
static_cast<float>(*
direction[2].getIf<
double>());
57 candidate.
yaw =
static_cast<float>(
number(document,
"transform.yaw"));
58 candidate.
size =
static_cast<float>(
number(document,
"projection.size"));
59 candidate.
depth =
static_cast<float>(
number(document,
"projection.depth"));
60 candidate.
kind = *document.
value(
"decal.kind")->
getIf<std::string>();
71 const auto& projectionMode = *document.
value(
"projection.mode")->
getIf<std::string>();
72 candidate.
projectionMode = projectionMode ==
"triplanar" ? 1 : projectionMode ==
"spherical" ? 2 :
73 projectionMode ==
"world" ? 3 : 0;
79 candidate.
lifetime =
static_cast<float>(
number(document,
"lifetime.seconds"));
80 candidate.
fadeIn =
static_cast<float>(
number(document,
"lifetime.fadeIn"));
81 candidate.
fadeOut =
static_cast<float>(
number(document,
"lifetime.fadeOut"));
83 const int next = manager_->
replace(runtimeId_, std::move(candidate));
85 return eve::editing::failed<void>(EditorStatus::Conflict, RuleId(
"editor.decal.replace"),
86 "Decal runtime generation is stale or invalid");
88 return eve::editing::applied<void>(diagnostics);
93 if (!manager_ || !manager_->
remove(runtimeId_))
94 return eve::editing::failed<void>(EditorStatus::NotFound, RuleId(
"editor.decal.runtime-stale"),
95 "Published decal generation no longer exists");
97 return eve::editing::applied<void>();
103 auto applied = candidate->applyDomainOperation(
operation);
104 if (!applied.ok())
return applied;
109 auto candidate = std::make_unique<DecalPublishingTarget>(*
this);
110 candidate->staging_ =
true;
115 std::unique_ptr<IDomainOperationTarget> candidate) {
117 if (!typed || typed->targetId() !=
targetId() || typed->sink_ != sink_ || !typed->staging_)
118 return eve::editing::failed<void>(EditorStatus::Conflict, RuleId(
"editor.decal.publish-candidate"),
119 "Decal publishing candidate mismatch");
121 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId(
"editor.decal.publish-sink"),
122 "Decal publishing target requires a runtime sink");
123 auto published = sink_->
publish(typed->document_);
124 if (!published.ok())
return published;
125 document_ = typed->document_;
126 return eve::editing::applied<void>();
ActionParameterOperation operation
graphics::Texture * albedo
std::array< float, 3 > position
RoadLaneDirection direction
Move-only operation result carrying either a value or Status.
static Result failure(Status status)
Construct a failed result from a structured status.
Structured status and zero or more diagnostics for an operation.
int replace(int previousId, DecalInstance candidate)
Atomically install a fully configured instance and remove a previous generation.
bool remove(int id)
Removes .
Stable, serializable authoring document for one projected decal.
const EditorValue * value(const std::string &path) const
Read a renderer-neutral property by stable path.
Result< void > applyDomainOperation(const DomainOperation &operation) override
Applies domain operation.
std::vector< EditorDiagnostic > validate() const
Validate projection geometry, fades, UVs and texture-channel rules.
Candidate-first operation target synchronizing author state and a live decal.
Result< void > commitDomainState(std::unique_ptr< IDomainOperationTarget > candidate) override
Commits domain state.
Result< void > applyDomainOperation(const DomainOperation &operation) override
Applies domain operation.
std::unique_ptr< IDomainOperationTarget > cloneDomainState() const override
Clones domain state.
TargetId targetId() const override
Target id.
Result< void > publish(const DecalDocumentTarget &document) override
Publish a complete replacement; failure preserves the previous generation.
Result< void > clear()
Remove the currently published generation, if any.
virtual Result< void > publish(const DecalDocumentTarget &document)=0
Publish a candidate while preserving runtime state on rejection.
std::vector< Value > Array
const T * getIf() const
Return an immediately borrowed typed value.
std::vector< ParamSpec > params
Result< void > noOp(std::vector< Diagnostic > diagnostics={})
Construct a successful void result for an operation that made no change.
One live decal instance (CPU-side state; GPU draw happens per frame).
graphics::Texture * normal
graphics::Texture * albedo
graphics::Texture * params
DomainOperation public API.