10#include <unordered_map>
23 [&](
const GraphNodeRecord&
node) { return node.id == id; });
29 const auto found = std::find_if(
node.pins.begin(),
node.pins.end(),
30 [&](
const GraphPinRecord& pin) { return pin.id == id; });
36int inputIndex(
const GraphNodeRecord&
node,
const GraphPinId& pin) {
38 for (
const auto& candidate :
node.pins) {
39 if (candidate.direction != GraphPinDirection::Input)
continue;
40 if (candidate.id == pin)
return index;
48 if (
kind ==
"int")
return EditorValue(
static_cast<int64_t
>(std::stoll(encoded)));
58 decision.
allowed =
from.direction == GraphPinDirection::Output &&
59 to.direction == GraphPinDirection::Input &&
from.type ==
"point" &&
60 to.type ==
"point" &&
from.node !=
to.node;
63 "editor.pcg.invalid-connection", DiagnosticSeverity::Error,
64 "PCG graph connections require point output-to-input pins on distinct nodes"));
71 return eve::editing::failed<GraphNodeRecord>(EditorStatus::Rejected,
RuleId(
"editor.pcg.unknown-operation"),
72 "Unknown PointGraph operation");
78 for (
int i = 0; i < parameterCount; ++i) {
90 for (
int i = 0; i <
inputs; ++i)
91 node.pins.push_back({GraphPinId(id.value() +
".in" + std::to_string(i)), id,
"point",
92 GraphPinDirection::Input});
95 return eve::editing::applied<GraphNodeRecord>(std::move(
node));
103 result.
status = EditorStatus::Rejected;
104 result.
diagnostics.push_back(pcgDiagnostic(
"editor.pcg.wrong-domain",
105 DiagnosticSeverity::Error,
106 "Graph is not a procgen.point document"));
109 if (
graph.schemaVersion == 1) {
110 result.
status = EditorStatus::NoOp;
113 if (
graph.schemaVersion != 0) {
114 result.
status = EditorStatus::Unsupported;
116 "editor.pcg.unsupported-schema", DiagnosticSeverity::Error,
117 "Unsupported procgen.point graph schemaVersion: " +
118 std::to_string(
graph.schemaVersion)));
122 std::unordered_map<std::string, GraphPinId> pinRemap;
123 std::vector<GraphNodeRecord> migratedNodes;
124 migratedNodes.reserve(
graph.nodes.size());
125 for (
const auto& legacy :
graph.nodes) {
126 auto canonical =
makeNode(legacy.id, legacy.type);
127 if (!canonical.ok()) {
128 result.
status = EditorStatus::Rejected;
130 "editor.pcg.migration-unknown-operation", DiagnosticSeverity::Error,
131 "Cannot migrate unknown PCG operation: " + legacy.type));
137 result.
status = EditorStatus::Rejected;
139 "editor.pcg.migration-invalid-properties", DiagnosticSeverity::Error,
140 "Legacy PCG node properties must be an object: " + legacy.id.value()));
148 for (
const auto& pin : legacy.pins) {
149 const int ordinal = pin.direction == GraphPinDirection::Input ?
input++ :
output++;
150 int canonicalOrdinal = 0;
151 const auto replacement = std::find_if(
152 canonical.value().pins.begin(), canonical.value().pins.end(),
154 if (candidate.direction != pin.direction) return false;
155 return canonicalOrdinal++ == ordinal;
157 if (replacement == canonical.value().pins.end())
continue;
158 if (!pinRemap.emplace(pin.id.value(), replacement->id).second) {
159 result.
status = EditorStatus::Rejected;
161 "editor.pcg.migration-duplicate-pin", DiagnosticSeverity::Error,
162 "Legacy PCG graph contains duplicate pin id: " + pin.id.value()));
166 migratedNodes.push_back(std::move(canonical.value()));
168 result.
graph.
nodes = std::move(migratedNodes);
170 const auto from = pinRemap.find(
edge.from.value());
171 const auto to = pinRemap.find(
edge.to.value());
173 if (
to != pinRemap.end())
edge.to =
to->second;
176 result.
status = EditorStatus::Applied;
177 result.
diagnostics.push_back(pcgDiagnostic(
"editor.pcg.migrated-v0-v1",
178 DiagnosticSeverity::Info,
179 "Migrated procgen.point graph schemaVersion 0 to 1"));
187 if (migration.status != EditorStatus::Applied && migration.status != EditorStatus::NoOp) {
188 result.
status = migration.status;
193 const auto&
source = migration.graph;
197 result.
status = EditorStatus::Failed;
198 result.
diagnostics.push_back(pcgDiagnostic(
"editor.pcg.invalid-node",
199 DiagnosticSeverity::Error,
200 "Unknown or duplicated PCG node: " +
206 result.
status = EditorStatus::Failed;
208 "editor.pcg.invalid-properties", DiagnosticSeverity::Error,
209 "PCG node properties must be an object: " +
node.id.value()));
213 const bool subgraphProperty =
214 node.type ==
"subgraph" &&
215 (
key ==
"definition" ||
key ==
"inputNode" ||
key ==
"outputNode");
216 if (subgraphProperty)
continue;
217 int parameterIndex = -1;
221 parameterIndex =
index;
224 const std::string
kind = parameterIndex >= 0
226 node.type, parameterIndex)
229 if (
kind ==
"bool") {
230 if (
const auto*
boolean =
value.getIf<
bool>())
232 }
else if (
kind ==
"int") {
233 if (
const auto* integer =
value.getIf<int64_t>();
234 integer && *integer >= std::numeric_limits<int>::min() &&
235 *integer <= std::numeric_limits<int>::max())
237 }
else if (
kind ==
"float") {
240 std::abs(*
number) <= std::numeric_limits<float>::max())
242 }
else if (
kind ==
"string") {
243 if (
const auto*
text =
value.getIf<std::string>())
247 result.
status = EditorStatus::Failed;
249 parameterIndex < 0 ?
"editor.pcg.unknown-property"
250 :
"editor.pcg.property-type-mismatch",
251 DiagnosticSeverity::Error,
252 "Invalid PCG property " +
node.id.value() +
"." +
key));
256 if (
node.type ==
"subgraph") {
257 const auto definition =
properties->find(
"definition");
258 const auto inputNode =
properties->find(
"inputNode");
259 const auto outputNode =
properties->find(
"outputNode");
260 const auto* definitionText =
261 definition ==
properties->end() ? nullptr : definition->second.getIf<std::string>();
262 const auto* inputText =
263 inputNode ==
properties->end() ? nullptr : inputNode->second.getIf<std::string>();
264 const auto* outputText =
265 outputNode ==
properties->end() ? nullptr : outputNode->second.getIf<std::string>();
267 if (!definitionText || !inputText || !outputText || definitionText->empty() ||
268 !nested.deserializeDefinition(*definitionText) ||
270 result.
status = EditorStatus::Failed;
272 "editor.pcg.invalid-subgraph", DiagnosticSeverity::Error,
273 "PCG subgraph node requires a valid definition and input/output node ids"));
281 result.
status = EditorStatus::Failed;
282 result.
diagnostics.push_back(pcgDiagnostic(
"editor.pcg.invalid-parameters",
283 DiagnosticSeverity::Error,
284 "PCG graph parameters must be an object"));
287 for (
const auto& [
name,
value] : *parameters) {
289 const auto node =
binding ?
binding->find(
"node") : EditorValue::Object::const_iterator{};
290 const auto key =
binding ?
binding->find(
"key") : EditorValue::Object::const_iterator{};
292 ?
node->second.getIf<std::string>()
295 ?
key->second.getIf<std::string>()
297 if (!
nodeId || !parameterKey ||
299 result.
status = EditorStatus::Failed;
301 "editor.pcg.invalid-parameter-binding", DiagnosticSeverity::Error,
302 "Invalid PCG graph parameter binding: " +
name));
307 std::unordered_map<std::string, StableId> connectedInputs;
312 const int slot = toNode &&
to ? inputIndex(*toNode,
to->
id) : -1;
313 const std::string inputKey =
to ?
to->id.value() : std::string();
314 if (!inputKey.empty() && connectedInputs.find(inputKey) != connectedInputs.end()) {
315 result.
status = EditorStatus::Failed;
317 "editor.pcg.duplicate-input", DiagnosticSeverity::Error,
318 "PCG input pin has more than one incoming edge: " + inputKey));
322 !compiled.
connect(
from->node.value(),
to->node.value(), slot)) {
323 result.
status = EditorStatus::Failed;
324 result.
diagnostics.push_back(pcgDiagnostic(
"editor.pcg.invalid-edge",
325 DiagnosticSeverity::Error,
326 "Invalid PCG graph edge: " +
330 connectedInputs.emplace(inputKey,
edge.id);
335 result.
status = EditorStatus::Failed;
336 result.
diagnostics.push_back(pcgDiagnostic(
"editor.pcg.cycle", DiagnosticSeverity::Error,
341 result.
status = EditorStatus::Applied;
346 const std::string& inputNode,
348 const std::string& outputNode,
349 int nodeBudget,
int pointBudget)
const {
354 if (compiled.status != EditorStatus::Applied) {
355 result.
status = compiled.status;
358 if (!previewInput || inputNode.
empty() || outputNode.empty()) {
359 result.
status = EditorStatus::Rejected;
361 "editor.pcg.preview-invalid-input", DiagnosticSeverity::Error,
362 "PCG preview requires input/output node ids and point input"));
366 if (!runtime.deserializeDefinition(compiled.definition)) {
367 result.
status = EditorStatus::Failed;
368 result.
diagnostics.push_back(pcgDiagnostic(
"editor.pcg.preview-bind-failed",
369 DiagnosticSeverity::Error,
370 "PCG preview definition could not be loaded"));
373 runtime.setExecutionNodeBudget(nodeBudget);
374 runtime.setMaxNodeOutputPoints(pointBudget);
375 if (!runtime.setNodePoints(inputNode, previewInput)) {
376 result.
status = EditorStatus::Failed;
378 "editor.pcg.preview-bind-failed", DiagnosticSeverity::Error,
379 runtime.getError().empty() ?
"PCG preview input node is invalid: " + inputNode
380 : runtime.getError()));
383 auto output = runtime.executeResult(outputNode);
386 result.
status = cancelled ? EditorStatus::Cancelled : EditorStatus::Failed;
388 cancelled ?
"editor.pcg.preview-cancelled" :
"editor.pcg.preview-execution-failed",
389 cancelled ? DiagnosticSeverity::Info : DiagnosticSeverity::Error,
output.status().describe()));
393 result.
metrics.reserve(
size_t(runtime.getMetricCount()));
400 metric.
minX = runtime.getMetricMinX(
index);
401 metric.
minY = runtime.getMetricMinY(
index);
402 metric.
minZ = runtime.getMetricMinZ(
index);
403 metric.
maxX = runtime.getMetricMaxX(
index);
404 metric.
maxY = runtime.getMetricMaxY(
index);
405 metric.
maxZ = runtime.getMetricMaxZ(
index);
407 result.
metrics.push_back(std::move(metric));
409 result.
status = EditorStatus::Applied;
ActionParameterOperation operation
HexCoordinates to
Cell the unit walks towards on this segment.
std::map< std::string, PropertyRule > properties
std::map< std::string, std::vector< std::string > > graph
eve::action::ActionVfxBinding binding
const UnitySourceAsset & source
Move-only operation result carrying either a value or Status.
UUID-backed identifier adapter for legacy textual boundaries.
std::map< std::string, Value > Object
EVENGINE_API_DOMAINS public API.
static std::string getOperationParamDefault(const std::string &operation, int index)
Returns the operation param default.
bool setNodeFloat(const std::string &id, const std::string &key, float value)
Sets the node float.
bool exposeParameter(const std::string &name, const std::string &nodeId, const std::string &key)
Expose one reflected node parameter for per-instance overrides.
bool setNodeString(const std::string &id, const std::string &key, const std::string &value)
Sets the node string.
bool connect(const std::string &fromId, const std::string &toId, int inputIndex=0)
Connect an output to input slot 0 or 1. Replaces that input connection.
std::string serializeDefinition() const
Serialize graph topology, parameters and nested graphs.
static int getOperationParamCount(const std::string &operation)
Returns the operation param count.
bool deserializeDefinition(const std::string &definition)
Transactionally replace this graph from serializeDefinition output.
bool setNodeSubgraph(const std::string &id, PointGraph *graph, const std::string &inputNode, const std::string &outputNode)
Assign an immutable nested graph.
static std::string getOperationParamKind(const std::string &operation, int index)
Returns the operation param kind.
std::string getError() const
Render the last compatibility execute/validate or legacy authoring error; canonical Result calls do n...
bool setNodeInt(const std::string &id, const std::string &key, int value)
Sets the node int.
static std::string getOperationParamKey(const std::string &operation, int index)
Returns the operation param key.
bool addNode(const std::string &id, const std::string &operation)
Add a node using one of the operations returned by operationAt().
static int getOperationInputCount(const std::string &operation)
Returns the operation input count.
Script-friendly collection of attributed 3D samples.
PcgGraphMigrationResult migrate(const GraphDocumentData &graph) const
Upgrade a legacy graph without mutating the source document.
Result< GraphNodeRecord > makeNode(const GraphNodeId &id, const std::string &operation) const
Construct a generic editor node from one reflected PointGraph operation.
std::string domain() const override
Domain.
PcgGraphCompileResult compile(const GraphDocumentData &graph) const
Compile topology and scalar properties into a reusable PointGraph definition.
PcgGraphPreviewResult preview(const GraphDocumentData &graph, const std::string &inputNode, procgen::PointSet *previewInput, const std::string &outputNode, int nodeBudget=0, int pointBudget=100000) const
Compile and execute an isolated point-input preview for editor visualization.
GraphConnectionDecision canConnect(const GraphPinRecord &from, const GraphPinRecord &to) const override
Can connect.
const char * defaultValue
eve::Severity DiagnosticSeverity
StrongId< RuleIdTag > RuleId
StrongId< GraphPinIdTag > GraphPinId
Result< T > applied(T value, std::vector< Diagnostic > diagnostics={})
Construct an Applied result with an owning payload and optional diagnostics.
StrongId< GraphNodeIdTag > GraphNodeId
Diagnostic ruleDiagnostic(eve::DiagnosticCode code, RuleId rule, DiagnosticSeverity severity, std::string message)
Build a common diagnostic carrying an open editing rule identity.
editing::GraphPinRecord GraphPinRecord
editing::GraphNodeRecord GraphNodeRecord
editing::Diagnostic EditorDiagnostic
Result of domain-specific connection validation.
std::vector< Diagnostic > diagnostics
Immutable graph document value sent to a domain compiler.
std::uint32_t schemaVersion
std::vector< GraphNodeRecord > nodes
std::vector< GraphEdgeRecord > edges
Generic graph node independent of its domain.
Stable typed pin owned by a graph node.
Result of compiling a generic editor graph into a PointGraph asset definition.
Revision documentRevision
std::vector< EditorDiagnostic > diagnostics
Transactional result of upgrading an editor PCG graph to the current schema.
std::vector< EditorDiagnostic > diagnostics
std::uint32_t fromVersion
One node's isolated PointGraph preview telemetry for graph overlays.
Revision-tagged result of compiling and executing an isolated editor preview.
std::vector< EditorDiagnostic > diagnostics
Revision documentRevision
std::vector< PcgGraphPreviewMetric > metrics