17struct OperationParam {
28const std::vector<OperationSpec>& operationSpecs() {
29 static const std::vector<OperationSpec>
value = {
31 {
"get.spatial", 0, {{
"binding",
"string",
""}, {
"spacing",
"float",
"1"}, {
"seed",
"int",
"1"},
32 {
"jitter",
"float",
"0"}}},
33 {
"get.landscape", 0, {{
"binding",
"string",
""}, {
"spacing",
"float",
"1"}, {
"seed",
"int",
"1"},
34 {
"jitter",
"float",
"0"}}},
35 {
"get.spline", 0, {{
"binding",
"string",
""}, {
"spacing",
"float",
"1"}, {
"seed",
"int",
"1"},
36 {
"jitter",
"float",
"0"}}},
37 {
"get.points", 0, {{
"binding",
"string",
""}}},
38 {
"get.actor", 0, {{
"binding",
"string",
""}}},
39 {
"spatial.sample", 0, {{
"binding",
"string",
""}, {
"spacing",
"float",
"1"}, {
"seed",
"int",
"1"}, {
"jitter",
"float",
"0"}}},
40 {
"mesh.sample", 0, {{
"binding",
"string",
""}, {
"spacing",
"float",
"1"}, {
"seed",
"int",
"1"}, {
"jitter",
"float",
"0"}}},
41 {
"grid.sample", 0, {{
"width",
"int",
"8"}, {
"depth",
"int",
"8"}, {
"spacing",
"float",
"1"},
42 {
"seed",
"int",
"1"}, {
"jitter",
"float",
"0"},
43 {
"originX",
"float",
"0"}, {
"originY",
"float",
"0"}, {
"originZ",
"float",
"0"}}},
44 {
"poisson.sample", 0, {{
"width",
"int",
"32"}, {
"depth",
"int",
"32"}, {
"radius",
"float",
"2"},
45 {
"seed",
"int",
"1"}, {
"maxPoints",
"int",
"1000"},
46 {
"originX",
"float",
"0"}, {
"originY",
"float",
"0"}, {
"originZ",
"float",
"0"}}},
47 {
"spatial.filter", 1, {{
"binding",
"string",
""}, {
"invert",
"bool",
"false"}}},
48 {
"spatial.project", 1, {{
"binding",
"string",
""}}},
49 {
"spline.sample", 1, {{
"spacing",
"float",
"1"}, {
"seed",
"int",
"1"}, {
"lateralJitter",
"float",
"0"}}},
50 {
"spline.filter.distance", 2, {{
"minDistance",
"float",
"0"}, {
"maxDistance",
"float",
"1"}}},
51 {
"biome.generate", 0, {{
"binding",
"string",
""}, {
"spacing",
"float",
"1"}, {
"seed",
"int",
"1"},
52 {
"jitter",
"float",
"0"}}},
53 {
"grammar.generate", 1, {{
"grammar",
"string",
""}, {
"seed",
"int",
"1"},
54 {
"acceptIncomplete",
"bool",
"true"}}},
56 {
"points.union", 2, {}},
57 {
"points.intersect", 2, {}},
58 {
"points.difference", 2, {}},
59 {
"copy.points", 2, {{
"maxPoints",
"int",
"100000"},
60 {
"inheritTargetAttributes",
"bool",
"true"}}},
61 {
"density.remap", 1, {{
"inputMin",
"float",
"0"}, {
"inputMax",
"float",
"1"},
62 {
"outputMin",
"float",
"0"}, {
"outputMax",
"float",
"1"},
63 {
"clamp",
"bool",
"true"}}},
64 {
"density.from.normal", 1, {{
"minDegrees",
"float",
"0"}, {
"maxDegrees",
"float",
"90"},
65 {
"outputMin",
"float",
"0"}, {
"outputMax",
"float",
"1"},
66 {
"invert",
"bool",
"false"}}},
67 {
"landscape.sample", 1, {{
"binding",
"string",
""}, {
"project",
"bool",
"false"},
68 {
"attribute",
"string",
"$Density"},
69 {
"inputMin",
"float",
"0"}, {
"inputMax",
"float",
"0"},
70 {
"outputMin",
"float",
"0"}, {
"outputMax",
"float",
"1"},
71 {
"clamp",
"bool",
"true"}, {
"invert",
"bool",
"false"}}},
72 {
"texture.sample", 1, {{
"binding",
"string",
""}, {
"attribute",
"string",
"$Density"},
73 {
"inputMin",
"float",
"0"}, {
"inputMax",
"float",
"1"},
74 {
"outputMin",
"float",
"0"}, {
"outputMax",
"float",
"1"},
75 {
"clamp",
"bool",
"true"}, {
"invert",
"bool",
"false"}}},
76 {
"attribute.math.float", 1, {{
"attribute",
"string",
""},
77 {
"outputAttribute",
"string",
""},
78 {
"operation",
"string",
"multiply"},
79 {
"operand",
"float",
"1"},
80 {
"defaultValue",
"float",
"0"}}},
81 {
"transform", 1, {{
"x",
"float",
"0"}, {
"y",
"float",
"0"}, {
"z",
"float",
"0"},
82 {
"yaw",
"float",
"0"}, {
"scaleX",
"float",
"1"},
83 {
"scaleY",
"float",
"1"}, {
"scaleZ",
"float",
"1"}}},
84 {
"bounds.modify", 1, {{
"scaleX",
"float",
"1"}, {
"scaleY",
"float",
"1"},
85 {
"scaleZ",
"float",
"1"}, {
"padX",
"float",
"0"},
86 {
"padY",
"float",
"0"}, {
"padZ",
"float",
"0"}}},
87 {
"filter.float", 1, {{
"attribute",
"string",
""}, {
"min",
"float",
"0"},
88 {
"max",
"float",
"1"}, {
"invert",
"bool",
"false"}}},
89 {
"filter.slope", 1, {{
"minDegrees",
"float",
"0"},
90 {
"maxDegrees",
"float",
"90"}}},
91 {
"filter.string", 1, {{
"attribute",
"string",
""}, {
"value",
"string",
""},
92 {
"invert",
"bool",
"false"}}},
93 {
"attribute.set.float", 1, {{
"attribute",
"string",
""}, {
"value",
"float",
"0"}}},
94 {
"attribute.set.string", 1, {{
"attribute",
"string",
""}, {
"value",
"string",
""}}},
95 {
"attribute.set.int", 1, {{
"attribute",
"string",
""}, {
"value",
"int",
"0"}}},
96 {
"attribute.set.bool", 1, {{
"attribute",
"string",
""}, {
"value",
"bool",
"false"}}},
97 {
"attribute.set.vector", 1, {{
"attribute",
"string",
""}, {
"x",
"float",
"0"},
98 {
"y",
"float",
"0"}, {
"z",
"float",
"0"}}},
99 {
"attribute.copy", 1, {{
"source",
"string",
""}, {
"target",
"string",
""}}},
100 {
"attribute.rename", 1, {{
"from",
"string",
""}, {
"to",
"string",
""}}},
101 {
"attribute.delete", 1, {{
"attribute",
"string",
""}}},
102 {
"attribute.transfer", 2, {{
"attribute",
"string",
""}, {
"outputAttribute",
"string",
""},
103 {
"mode",
"string",
"nearest"}}},
104 {
"attribute.compare.float", 1, {{
"attribute",
"string",
""}, {
"comparison",
"string",
"gt"},
105 {
"operand",
"float",
"0"}, {
"outputAttribute",
"string",
"match"},
106 {
"defaultValue",
"float",
"0"}}},
107 {
"attribute.select.float", 1, {{
"conditionAttribute",
"string",
""},
108 {
"trueAttribute",
"string",
""},
109 {
"falseAttribute",
"string",
""},
110 {
"outputAttribute",
"string",
""},
111 {
"trueDefault",
"float",
"0"},
112 {
"falseDefault",
"float",
"0"}}},
113 {
"filter.int", 1, {{
"attribute",
"string",
""}, {
"min",
"int",
"0"},
114 {
"max",
"int",
"0"}, {
"invert",
"bool",
"false"}}},
115 {
"filter.bool", 1, {{
"attribute",
"string",
""}, {
"value",
"bool",
"true"},
116 {
"invert",
"bool",
"false"}}},
117 {
"attribute.partition", 1, {{
"attribute",
"string",
""}, {
"outputAttribute",
"string",
"partition"},
118 {
"mode",
"string",
"value"}}},
119 {
"attribute.noise.float", 1, {{
"attribute",
"string",
"noise"}, {
"seed",
"int",
"1"},
120 {
"frequency",
"float",
"1"}, {
"amplitude",
"float",
"1"},
121 {
"offset",
"float",
"0"}}},
122 {
"attribute.math.int", 1, {{
"attribute",
"string",
""}, {
"outputAttribute",
"string",
""},
123 {
"operation",
"string",
"add"}, {
"operand",
"int",
"1"},
124 {
"defaultValue",
"int",
"0"}}},
125 {
"attribute.math.vector", 1, {{
"attribute",
"string",
""}, {
"outputAttribute",
"string",
""},
126 {
"operation",
"string",
"scale"}, {
"operandX",
"float",
"1"},
127 {
"operandY",
"float",
"1"}, {
"operandZ",
"float",
"1"},
128 {
"defaultX",
"float",
"0"}, {
"defaultY",
"float",
"0"},
129 {
"defaultZ",
"float",
"0"}}},
130 {
"attribute.set.data.float", 1, {{
"attribute",
"string",
""}, {
"value",
"float",
"0"}}},
131 {
"attribute.set.data.int", 1, {{
"attribute",
"string",
""}, {
"value",
"int",
"0"}}},
132 {
"attribute.set.data.string", 1, {{
"attribute",
"string",
""}, {
"value",
"string",
""}}},
133 {
"spawn.mesh", 1, {{
"seed",
"int",
"1"}, {
"attribute",
"string",
"mesh"},
134 {
"mesh0",
"string",
""}, {
"weight0",
"float",
"1"},
135 {
"mesh1",
"string",
""}, {
"weight1",
"float",
"0"},
136 {
"mesh2",
"string",
""}, {
"weight2",
"float",
"0"},
137 {
"mesh3",
"string",
""}, {
"weight3",
"float",
"0"}}},
138 {
"density.cull", 1, {{
"seed",
"int",
"1"}, {
"multiplier",
"float",
"1"}}},
139 {
"self.prune", 1, {{
"radius",
"float",
"1"}}},
140 {
"jitter", 1, {{
"seed",
"int",
"1"}, {
"x",
"float",
"0"}, {
"z",
"float",
"0"}}},
141 {
"debug.disable", 1, {{
"enabled",
"bool",
"true"}}},
142 {
"debug.inspect", 1, {}},
143 {
"branch", 2, {{
"condition",
"bool",
"false"}}},
149bool operationKnown(
const std::string&
operation) {
150 const auto&
values = operationSpecs();
152 [&](
const OperationSpec&
value) { return operation == value.id; });
155const OperationSpec* operationSpec(
const std::string&
operation) {
156 const auto&
values = operationSpecs();
158 [&](
const OperationSpec&
value) { return operation == value.id; });
162const OperationParam* operationParam(
const OperationSpec* spec,
const std::string&
key) {
163 if (!spec)
return nullptr;
164 const auto found = std::find_if(spec->params.begin(), spec->params.end(),
165 [&](
const OperationParam&
value) { return key == value.key; });
166 return found == spec->params.end() ? nullptr : &*
found;
174 if (
id.empty() || !operationKnown(
operation) || nodes_.find(
id) != nodes_.end())
return false;
177 nodeOrder_.push_back(
id);
178 invalidateTopology(
id);
184 if (nodes_.find(
id) == nodes_.end())
return false;
187 nodeOrder_.erase(std::remove(nodeOrder_.begin(), nodeOrder_.end(),
id), nodeOrder_.end());
188 for (
auto it = parameterOrder_.begin(); it != parameterOrder_.end();) {
189 const auto binding = parameters_.find(*it);
191 floatOverrides_.erase(*it);
192 intOverrides_.erase(*it);
193 stringOverrides_.erase(*it);
195 it = parameterOrder_.erase(it);
200 for (
auto& [otherId,
node] : nodes_)
203 invalidateTopology(
id);
210 return index >= 0 &&
index < int(nodeOrder_.size()) ? nodeOrder_[size_t(
index)] : std::string();
213 const auto found = nodes_.find(
id);
214 return found == nodes_.end() ? std::string() :
found->second.operation;
218 const auto from = nodes_.find(fromId);
219 const auto to = nodes_.find(toId);
220 const auto* spec =
to == nodes_.end() ? nullptr : operationSpec(
to->second.operation);
221 if (
from == nodes_.end() ||
to == nodes_.end() || !spec || inputIndex < 0 ||
222 inputIndex >= spec->inputs || fromId == toId)
224 to->second.inputs[inputIndex] = fromId;
225 invalidateTopology(toId);
226 invalidateFrom(toId);
231 const auto to = nodes_.find(toId);
232 if (
to == nodes_.end() || inputIndex < 0 || inputIndex > 1 ||
233 to->second.inputs[inputIndex].empty())
235 to->second.inputs[inputIndex].clear();
236 invalidateTopology(toId);
237 invalidateFrom(toId);
243 return found != nodes_.end() && inputIndex >= 0 && inputIndex <= 1
244 ?
found->second.inputs[inputIndex]
249 const auto found = nodes_.find(
id);
250 if (
found == nodes_.end() || !
points)
return false;
251 if (maxNodeOutputPoints_ > 0 &&
points->getCount() > maxNodeOutputPoints_) {
252 error_ =
"input exceeds node point budget at node: " +
id;
256 found->second.hasPoints =
true;
262 const auto found = nodes_.find(
id);
264 found->second.spatial = std::make_shared<SpatialData>(*
spatial);
273 if (std::find(bindingOrder_.begin(), bindingOrder_.end(),
name) == bindingOrder_.end())
274 bindingOrder_.push_back(
name);
285 "binding name and point set are required",
"binding"));
287 if (std::find(bindingOrder_.begin(), bindingOrder_.end(),
name) == bindingOrder_.end())
288 bindingOrder_.push_back(
name);
298 if (
found == bindings_.end())
300 bindings_.erase(
found);
301 bindingOrder_.erase(std::remove(bindingOrder_.begin(), bindingOrder_.end(),
name), bindingOrder_.end());
307 if (bindings_.empty())
return;
309 bindingOrder_.clear();
316 return index >= 0 &&
index < int(bindingOrder_.size()) ? bindingOrder_[size_t(
index)] : std::string();
321 if (
found == bindings_.end())
return {};
322 return found->second.isSpatial ?
"spatial" :
"points";
327 const std::string
binding = stringValue(
node,
"binding", {});
332 if (
found == bindings_.end() || !
found->second.isSpatial || !
found->second.spatial)
338Result<std::shared_ptr<PointSet>> PointGraph::resolveBindingPoints(
const std::string&
name)
const {
340 if (
found == bindings_.end() ||
found->second.isSpatial || !
found->second.points)
341 return Result<std::shared_ptr<PointSet>>::failure(
343 return Result<std::shared_ptr<PointSet>>::success(
found->second.points);
349 const auto found = nodes_.find(
id);
350 if (
found == nodes_.end())
353 if (!
found->second.cacheValid)
359 const auto addBuiltin = [&](
const char*
name) {
362 info.type =
"builtin";
363 info.domain =
"builtin";
366 addBuiltin(
"$Density");
367 addBuiltin(
"$Position.X");
368 addBuiltin(
"$Position.Y");
369 addBuiltin(
"$Position.Z");
370 addBuiltin(
"$Normal.X");
371 addBuiltin(
"$Normal.Y");
372 addBuiltin(
"$Normal.Z");
373 addBuiltin(
"$Rotation.Yaw");
374 addBuiltin(
"$Scale.X");
378 for (std::size_t i = 0; i < table.columnCount(); ++i) {
380 info.
name = std::string(table.columnName(i));
381 info.domain =
"elements";
382 const auto type = table.typeOf(
info.name);
388 else info.type =
"string";
392 for (std::size_t i = 0; i < data.columnCount(); ++i) {
394 info.
name = std::string(data.columnName(i));
395 info.domain =
"data";
396 const auto type = data.typeOf(
info.name);
402 else info.type =
"string";
406 if (sampleLimit > 0 && report.
pointCount > 0) {
421 const auto found = nodes_.find(
id);
422 if (
found == nodes_.end() ||
found->second.operation !=
"biome.generate" || !rules)
424 found->second.biomeRules = std::make_shared<BiomeRules>(*rules);
430 const auto found = nodes_.find(
id);
431 if (
found == nodes_.end() ||
found->second.operation !=
"grammar.generate" || !grammar)
433 found->second.shapeGrammar = std::make_shared<ShapeGrammar>(*grammar);
439 const auto found = nodes_.find(
id);
440 const auto* spec =
found == nodes_.end() ? nullptr : operationSpec(
found->second.operation);
441 const auto* parameter = operationParam(spec,
key);
442 if (!parameter || std::string(parameter->kind) !=
"float")
return false;
449 const auto found = nodes_.find(
id);
450 const auto* spec =
found == nodes_.end() ? nullptr : operationSpec(
found->second.operation);
451 const auto* parameter = operationParam(spec,
key);
453 (std::string(parameter->kind) !=
"int" && std::string(parameter->kind) !=
"bool"))
461 const std::string&
value) {
462 const auto found = nodes_.find(
id);
463 const auto* spec =
found == nodes_.end() ? nullptr : operationSpec(
found->second.operation);
464 const auto* parameter = operationParam(spec,
key);
465 if (!parameter || std::string(parameter->kind) !=
"string")
return false;
472 const std::string&
key) {
474 if (
name.empty() ||
key.empty() ||
node == nodes_.end() || parameters_.count(
name) != 0)
476 const OperationSpec* spec = operationSpec(
node->second.operation);
477 if (!spec)
return false;
478 const auto* parameter = operationParam(spec,
key);
479 if (!parameter)
return false;
480 for (
const auto& [existingName,
binding] : parameters_)
482 parameters_.emplace(
name, ParameterBinding{
nodeId,
key, parameter->kind});
483 parameterOrder_.push_back(
name);
490 return index >= 0 &&
index < int(parameterOrder_.size()) ? parameterOrder_[size_t(
index)]
494 const auto found = parameters_.find(
name);
495 return found == parameters_.end() ? std::string() :
found->second.kind;
498 return floatOverrides_.count(
name) != 0 || intOverrides_.count(
name) != 0 ||
499 stringOverrides_.count(
name) != 0;
502 const auto found = parameters_.find(
name);
503 if (
found == parameters_.end() ||
found->second.kind !=
"float")
return false;
505 invalidateFrom(
found->second.nodeId);
509 const auto found = parameters_.find(
name);
510 if (
found == parameters_.end() || (
found->second.kind !=
"int" &&
511 found->second.kind !=
"bool"))
514 invalidateFrom(
found->second.nodeId);
518 const auto found = parameters_.find(
name);
519 if (
found == parameters_.end() ||
found->second.kind !=
"string")
return false;
521 invalidateFrom(
found->second.nodeId);
525 const auto found = parameters_.find(
name);
526 if (
found == parameters_.end() ||
found->second.kind !=
"float")
return fallback;
527 return floatValue(nodes_.at(
found->second.nodeId),
found->second.key, fallback);
530 const auto found = parameters_.find(
name);
531 if (
found == parameters_.end() || (
found->second.kind !=
"int" &&
532 found->second.kind !=
"bool"))
534 return intValue(nodes_.at(
found->second.nodeId),
found->second.key, fallback);
537 const std::string& fallback)
const {
538 const auto found = parameters_.find(
name);
539 if (
found == parameters_.end() ||
found->second.kind !=
"string")
return fallback;
540 return stringValue(nodes_.at(
found->second.nodeId),
found->second.key, fallback);
543 const auto found = parameters_.find(
name);
545 floatOverrides_.erase(
name);
546 intOverrides_.erase(
name);
547 stringOverrides_.erase(
name);
548 invalidateFrom(
found->second.nodeId);
553 const std::string& inputNode,
const std::string& outputNode) {
554 const auto found = nodes_.find(
id);
555 if (
found == nodes_.end() ||
found->second.operation !=
"subgraph" || !
graph ||
556 !
graph->hasNode(inputNode) || !
graph->hasNode(outputNode))
558 found->second.subgraph = std::make_shared<PointGraph>(*
graph);
559 found->second.subgraphInput = inputNode;
560 found->second.subgraphOutput = outputNode;
567 error_ = result.ok() ? std::string{} : result.status().describe();
568 if (!result.ok())
return nullptr;
569 return new PointSet(std::move(result).takeValue());
573 const std::string
output(outputId);
577 lastCancelled_ =
false;
578 computeFallbackReason_.clear();
580 if (cancelRequested_) {
581 lastCancelled_ =
true;
585 auto compiled = compileExecutionPlan(
output);
587 std::unordered_map<std::string, int> states;
588 auto result = evaluate(
output, states);
599 const int clamped = std::max(0,
points);
600 if (maxNodeOutputPoints_ == clamped)
return;
601 maxNodeOutputPoints_ = clamped;
606 if (policy !=
"auto" && policy !=
"gpu" && policy !=
"cpu")
return false;
607 if (computePolicy_ == policy)
return true;
608 computePolicy_ = policy;
614 const int clamped = std::max(0,
points);
615 if (computeMinimumPoints_ == clamped)
return;
616 computeMinimumPoints_ = clamped;
622 cancelRequested_ =
false;
623 lastCancelled_ =
false;
629 error_ = result.ok() ? std::string{} : result.status().describe();
634 std::unordered_map<std::string, int> states;
635 for (
const auto&
id : nodeOrder_) {
636 auto result = validateNode(
id, states);
637 if (!result.ok())
return result;
644 for (
auto& [
id,
node] : nodes_) {
645 node.cacheValid =
false;
646 node.deferredTransformValid =
false;
657 return index >= 0 &&
index < int(metrics_.size()) ? metrics_[size_t(
index)].id : std::string();
660 return index >= 0 &&
index < int(metrics_.size()) ? metrics_[size_t(
index)].outputCount : 0;
663 return index >= 0 &&
index < int(metrics_.size()) ? metrics_[size_t(
index)].milliseconds : 0.f;
666 return index >= 0 &&
index < int(metrics_.size()) && metrics_[size_t(
index)].cacheHit;
669 return index >= 0 &&
index < int(metrics_.size()) ? metrics_[size_t(
index)].backend : std::string();
679 return index >= 0 &&
index < int(metrics_.size()) ? metrics_[size_t(
index)].minX : 0.f;
682 return index >= 0 &&
index < int(metrics_.size()) ? metrics_[size_t(
index)].minY : 0.f;
685 return index >= 0 &&
index < int(metrics_.size()) ? metrics_[size_t(
index)].minZ : 0.f;
688 return index >= 0 &&
index < int(metrics_.size()) ? metrics_[size_t(
index)].maxX : 0.f;
691 return index >= 0 &&
index < int(metrics_.size()) ? metrics_[size_t(
index)].maxY : 0.f;
694 return index >= 0 &&
index < int(metrics_.size()) ? metrics_[size_t(
index)].maxZ : 0.f;
697 return index >= 0 &&
index < int(metrics_.size())
698 ? metrics_[size_t(
index)].averageDensity
703PointSet* PointGraph::materializeNodeOutput(
const std::string&
id)
const {
704 const auto found = nodes_.find(
id);
705 if (
found == nodes_.end())
return nullptr;
708 if (!
node.deferredTransformValid ||
node.operation !=
"transform" ||
node.inputs[0].empty())
return nullptr;
709 std::unique_ptr<PointSet>
input(materializeNodeOutput(
node.inputs[0]));
710 if (!
input)
return nullptr;
712 floatValue(
node,
"z", 0.f), floatValue(
node,
"yaw", 0.f),
713 floatValue(
node,
"scaleX", 1.f), floatValue(
node,
"scaleY", 1.f),
714 floatValue(
node,
"scaleZ", 1.f)));
718 std::ostringstream out;
719 out <<
"nodes=" << nodes_.size() <<
" executions=" << executionCount_ <<
" cacheHits=" << cacheHitCount_
721 for (
const auto& metric : metrics_)
723 << metric.id <<
" count=" << metric.outputCount <<
" ms=" << metric.milliseconds
724 <<
" backend=" << metric.backend << (metric.cacheHit ?
" cached" :
"");
725 if (!error_.empty()) out <<
"\nerror=" << error_;
732 ? operationSpecs()[size_t(
index)].id
736 const auto* spec = operationSpec(
operation);
737 return spec ? spec->inputs : -1;
740 const auto* spec = operationSpec(
operation);
741 return spec ? int(spec->params.size()) : 0;
744 const auto* spec = operationSpec(
operation);
745 return spec &&
index >= 0 &&
index < int(spec->params.size())
746 ? spec->params[size_t(
index)].key
750 const auto* spec = operationSpec(
operation);
751 return spec &&
index >= 0 &&
index < int(spec->params.size())
752 ? spec->params[size_t(
index)].kind
756 const auto* spec = operationSpec(
operation);
757 return spec &&
index >= 0 &&
index < int(spec->params.size())
758 ? spec->params[size_t(
index)].defaultValue
762void PointGraph::invalidate() {
clearCache(); }
764void PointGraph::invalidateFrom(
const std::string&
id) {
766 std::vector<std::string>
dirty{
id};
767 std::unordered_map<std::string, bool> visited;
770 if (visited[
current])
continue;
773 if (
found != nodes_.end()) {
774 found->second.cacheValid =
false;
775 found->second.deferredTransformValid =
false;
777 for (
const auto& [candidateId, candidate] : nodes_) {
778 if (candidate.inputs[0] ==
current || candidate.inputs[1] ==
current)
779 dirty.push_back(candidateId);
784float PointGraph::floatValue(
const Node&
node,
const std::string&
key,
float fallback)
const {
785 for (
const auto&
name : parameterOrder_) {
788 const auto overridden = floatOverrides_.find(
name);
789 if (overridden != floatOverrides_.end())
return overridden->second;
795int PointGraph::intValue(
const Node&
node,
const std::string&
key,
int fallback)
const {
796 for (
const auto&
name : parameterOrder_) {
799 const auto overridden = intOverrides_.find(
name);
800 if (overridden != intOverrides_.end())
return overridden->second;
806std::string PointGraph::stringValue(
const Node&
node,
const std::string&
key,
807 const std::string& fallback)
const {
808 for (
const auto&
name : parameterOrder_) {
811 const auto overridden = stringOverrides_.find(
name);
812 if (overridden != stringOverrides_.end())
return overridden->second;
ActionParameterOperation operation
eve::action::ActionSpatialBinding spatial
std::map< std::string, Var > values
HexCoordinates to
Cell the unit walks towards on this segment.
std::map< std::string, std::vector< std::string > > graph
eve::action::ActionVfxBinding binding
std::shared_ptr< const std::vector< glm::vec2 > > points
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.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
Schema-bearing column store aligned with a PointSet's point rows.
Data-driven biome distribution rules compatible with PointGraph and scene batches.
Backend-neutral GPU kernels used by PointGraph with deterministic CPU fallback.
EVENGINE_API_DOMAINS public API.
void resetCancellation()
Resets cancellation.
uint64_t getComputePeakBufferBytes() const
Return peak bytes reserved in each reusable point-compute buffer.
static std::string getOperationParamDefault(const std::string &operation, int index)
Returns the operation param default.
bool setParameterFloat(const std::string &name, float value)
Override an exposed floating-point parameter for this graph instance.
float getMetricMinZ(int index) const
Return the cached minimum output Z coordinate.
uint64_t getComputeBufferReuseCount() const
Return executions that reused allocated point-compute buffers.
bool setNodeFloat(const std::string &id, const std::string &key, float value)
Sets the node float.
bool removeNode(const std::string &id)
Remove a node and every edge that references it.
Result< PointSet > executeResult(std::string_view outputId)
Evaluate an output and return an independent owning point snapshot.
void setExecutionNodeBudget(int nodes)
Limit uncached nodes evaluated by one execute call; zero disables the limit.
bool exposeParameter(const std::string &name, const std::string &nodeId, const std::string &key)
Expose one reflected node parameter for per-instance overrides.
std::string getBindingName(int index) const
Return a binding name by stable insertion index.
float getMetricMilliseconds(int index) const
Returns the metric milliseconds.
void clearCache()
Clears cache.
static int getOperationCount()
Returns the operation count.
Result< void > clearBinding(const std::string &name)
Remove one named binding.
bool setNodeString(const std::string &id, const std::string &key, const std::string &value)
Sets the node string.
bool wasCancelled() const
Was cancelled.
float getMetricMaxX(int index) const
Return the cached maximum output X coordinate.
int getMetricCount() const
Returns the metric count.
std::string getInputNode(const std::string &nodeId, int inputIndex) const
Returns the input node.
std::string getNodeOperation(const std::string &id) const
Returns the node operation.
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.
uint64_t getRevision() const
Monotonic topology/parameter revision used by asset and preview caches.
std::string getComputeFallbackReason() const
Return why the latest eligible GPU node used the CPU fallback.
int getComputeMinimumPoints() const
Return the minimum point count for automatic GPU dispatch.
PointSet * getNodeOutput(const std::string &id) const
Copy the cached/debug output of a node after execution.
uint64_t getComputeReadbackCount() const
Return successful point-compute readbacks for this graph executor.
void requestCancel()
Cancel subsequent execution until resetCancellation is called.
int getExecutionCount() const
Returns the execution count.
uint64_t getExecutionPlanBuildCount() const
Return how many execution plans this graph instance has compiled.
int getNodeCount() const
Returns the node count.
bool setComputePolicy(const std::string &policy)
Select auto, gpu, or cpu point execution; GPU failures fall back to CPU.
PointSet * execute(const std::string &outputId)
Compatibility-only projection of executeResult; caller deletes the result, nullptr means failure.
std::string getBindingType(const std::string &name) const
Return spatial, points, or empty when the name is unbound.
int getLastFusedTransformCount() const
Return logical transforms in the latest successful fused dispatch.
bool isMetricCacheHit(int index) const
True when metric cache hit.
std::string getComputePolicy() const
Return the configured point compute policy.
bool validate()
Compatibility-only bool projection of validateResult; diagnostics are rendered by getError().
static int getOperationParamCount(const std::string &operation)
Returns the operation param count.
int getMaxNodeOutputPoints() const
Return the per-node point output limit; zero means unlimited.
static std::string getOperationId(int index)
Returns the operation id.
int getExecutionNodeBudget() const
Returns the execution node budget.
std::string getMetricBackend(int index) const
Return cpu, vulkan, or webgpu for one evaluated node.
float getMetricAverageDensity(int index) const
Return the mean point density of one node output.
Result< void > validateResult() const
Validate configured nodes and nested graphs without executing them.
int getBindingCount() const
Return the number of named bindings.
Result< void > setBindingSpatial(const std::string &name, SpatialData *spatial)
Bind named SpatialData for get.* / optional binding= parameters. External runtime slot: not serialize...
bool setNodeSubgraph(const std::string &id, PointGraph *graph, const std::string &inputNode, const std::string &outputNode)
Assign an immutable nested graph.
void setMaxNodeOutputPoints(int points)
Limit points produced by any node; zero disables the limit.
int getParameterCount() const
Return the number of exposed graph parameters.
int getCompiledSegmentCount() const
Return logical CPU/GPU segments in the execution plan compiled for the latest output.
float getMetricMinY(int index) const
Return the cached minimum output Y coordinate.
bool setNodeSpatial(const std::string &id, SpatialData *spatial)
Assign copied spatial data to a spatial sample, filter, projection, or biome node.
float getMetricMaxY(int index) const
Return the cached maximum output Y coordinate.
uint64_t getComputeUploadCount() const
Return successful point-compute uploads for this graph executor.
bool clearParameterOverride(const std::string &name)
Remove an instance override and restore the asset default.
static std::string getOperationParamKind(const std::string &operation, int index)
Returns the operation param kind.
float getParameterFloat(const std::string &name, float fallback) const
Return the effective floating-point parameter value.
Result< PointGraphInspectReport > inspectNode(const std::string &id, int sampleLimit=0) const
Build an inspect snapshot for a cached node output after execute.
std::string getParameterName(int index) const
Return an exposed parameter name by stable insertion index.
Result< void > setBindingPoints(const std::string &name, PointSet *points)
Bind named PointSet for get.points / get.actor. External runtime slot: not serialized with serializeD...
std::string getError() const
Render the last compatibility execute/validate or legacy authoring error; canonical Result calls do n...
int getCacheHitCount() const
Returns the cache hit count.
std::string getParameterKind(const std::string &name) const
Return the reflected kind of an exposed parameter.
bool disconnect(const std::string &toId, int inputIndex=0)
Remove one input connection.
bool hasParameterOverride(const std::string &name) const
Report whether an instance override is active.
bool setNodeInt(const std::string &id, const std::string &key, int value)
Sets the node int.
void clearBindings()
Remove every named binding.
float getMetricMaxZ(int index) const
Return the cached maximum output Z coordinate.
std::string getMetricNodeId(int index) const
Returns the metric node id.
std::string getNodeId(int index) const
Returns the node id.
float getMetricMinX(int index) const
Return cached output bounds for editor/debug visualization.
bool setNodeShapeGrammar(const std::string &id, ShapeGrammar *grammar)
Assign a copied shape grammar to a grammar.generate node.
bool setNodePoints(const std::string &id, PointSet *points)
Assign a copied PointSet to an input node.
std::string getParameterString(const std::string &name, const std::string &fallback) const
Return the effective string parameter value.
uint64_t getComputeDispatchCount() const
Return successful point-compute dispatches for this graph executor.
bool setParameterString(const std::string &name, const std::string &value)
Override an exposed string parameter for this graph instance.
int getParameterInt(const std::string &name, int fallback) const
Return the effective integer or Boolean parameter value.
static std::string getOperationParamKey(const std::string &operation, int index)
Returns the operation param key.
bool hasNode(const std::string &id) const
True when node.
int getMetricOutputCount(int index) const
Returns the metric output count.
bool setParameterInt(const std::string &name, int value)
Override an exposed integer or Boolean parameter for this graph instance.
bool addNode(const std::string &id, const std::string &operation)
Add a node using one of the operations returned by operationAt().
bool setNodeBiomeRules(const std::string &id, BiomeRules *rules)
Assign copied biome rules to a biome.generate node.
std::string debugReport() const
Debug report.
void setComputeMinimumPoints(int points)
Set the minimum point count for auto GPU dispatch.
static int getOperationInputCount(const std::string &operation)
Returns the operation input count.
Script-friendly collection of attributed 3D samples.
Module-based shape grammar expanded continuously along a 3D polyline.
Queryable spatial input for procedural pipelines.
const char * defaultValue
std::vector< ParamSpec > params
PointSet transformPointSet(const PointSet &input, float translateX, float translateY, float translateZ, float yawDegrees, float scaleX, float scaleY, float scaleZ)
Apply translation, yaw rotation and non-uniform scale to points and their transforms.
ActionSpatialBinding spatial
A data-flow graph for attributed procedural points.
Snapshot of one node output for editor Attribute/Inspect panels.
std::vector< float > sampleFloats
Optional sampled float values: columns-major flattened [column * sampleCount + point].
std::vector< PointGraphColumnInfo > columns