载入中...
搜索中...
未找到
BuildLayerStack.cpp
浏览该文件的文档.
2
3#include "common/Diagnostic.h"
4#include "procgen/MeshGraph.h"
5
6#include <cmath>
7#include <iomanip>
8#include <limits>
9#include <sstream>
10
11namespace eve::procgen {
12namespace {
13
14std::string hexEncode(std::string_view value) {
15 constexpr char digits[] = "0123456789abcdef";
16 std::string output;
17 output.reserve(value.size() * 2);
18 for (const unsigned char byte : value) {
19 output.push_back(digits[byte >> 4U]);
20 output.push_back(digits[byte & 15U]);
21 }
22 return output;
23}
24
25Result<std::string> hexDecode(std::string_view value) {
26 if (value.size() % 2 != 0)
28 DiagnosticCode::ParseError, "odd embedded definition size", {}, {}, "procgen.buildLayers"));
29 const auto nibble = [](char c) -> int {
30 if (c >= '0' && c <= '9') return c - '0';
31 if (c >= 'a' && c <= 'f') return c - 'a' + 10;
32 if (c >= 'A' && c <= 'F') return c - 'A' + 10;
33 return -1;
34 };
35 std::string output(value.size() / 2, '\0');
36 for (std::size_t index = 0; index < value.size(); index += 2) {
37 const int high = nibble(value[index]);
38 const int low = nibble(value[index + 1]);
39 if (high < 0 || low < 0)
41 DiagnosticCode::ParseError, "invalid embedded definition", {}, {}, "procgen.buildLayers"));
42 output[index / 2] = char((high << 4) | low);
43 }
44 return Result<std::string>::success(std::move(output));
45}
46
47} // namespace
48
49int BuildLayerExecution::getCount() const noexcept { return int(artifacts_.size()); }
50
51std::string BuildLayerExecution::getId(int index) const {
52 return index >= 0 && index < int(artifacts_.size()) ? artifacts_[std::size_t(index)].id : std::string();
53}
54
55std::string BuildLayerExecution::getType(int index) const {
56 if (index < 0 || index >= int(artifacts_.size())) return {};
57 return std::holds_alternative<MeshBuild>(artifacts_[std::size_t(index)].value) ? "mesh" : "points";
58}
59
61 if (index < 0 || index >= int(artifacts_.size()))
63 DiagnosticCode::InvalidArgument, "artifact index is out of range", {}, {}, "procgen.buildLayers"));
64 const auto* value = std::get_if<MeshBuild>(&artifacts_[std::size_t(index)].value);
65 if (!value)
67 Diagnostic::error(DiagnosticCode::TypeMismatch, "artifact is not a mesh", {}, {}, "procgen.buildLayers"));
69}
70
72 if (index < 0 || index >= int(artifacts_.size()))
74 DiagnosticCode::InvalidArgument, "artifact index is out of range", {}, {}, "procgen.buildLayers"));
75 const auto* value = std::get_if<PointSet>(&artifacts_[std::size_t(index)].value);
76 if (!value)
78 {}, {}, "procgen.buildLayers"));
80}
81
82bool BuildLayerStack::contains(std::string_view id) const {
83 for (const auto& layer : layers_)
84 if (layer.id == id) return true;
85 return false;
86}
87
88Result<void> BuildLayerStack::addTileLayer(std::string id, bool enabled, float cellSize, float height,
89 std::string group) {
90 if (id.empty())
92 Diagnostic::error(DiagnosticCode::InvalidArgument, "layer id is empty", {}, {}, "procgen.buildLayers"));
93 if (contains(id))
95 Diagnostic::error(DiagnosticCode::Conflict, "duplicate layer id", id, {}, "procgen.buildLayers"));
96 if (!std::isfinite(cellSize) || cellSize <= 0.f || !std::isfinite(height))
98 id, {}, "procgen.buildLayers"));
99 layers_.push_back({std::move(id), enabled, TileLayer{cellSize, height, std::move(group)}});
100 return Result<void>::success();
101}
102
104 if (id.empty())
106 Diagnostic::error(DiagnosticCode::InvalidArgument, "layer id is empty", {}, {}, "procgen.buildLayers"));
107 if (contains(id))
109 Diagnostic::error(DiagnosticCode::Conflict, "duplicate layer id", id, {}, "procgen.buildLayers"));
110 layers_.push_back({std::move(id), enabled, layer});
111 return Result<void>::success();
112}
113
115 for (auto& layer : layers_) {
116 if (layer.id != id) continue;
117 layer.enabled = enabled;
118 return Result<void>::success();
119 }
121 Diagnostic::error(DiagnosticCode::NotFound, "unknown layer", std::string(id), {}, "procgen.buildLayers"));
122}
123
124void BuildLayerStack::clear() { layers_.clear(); }
125int BuildLayerStack::getLayerCount() const noexcept { return int(layers_.size()); }
126
127std::string BuildLayerStack::getLayerId(int index) const {
128 return index >= 0 && index < int(layers_.size()) ? layers_[std::size_t(index)].id : std::string();
129}
130
131std::string BuildLayerStack::getLayerType(int index) const {
132 if (index < 0 || index >= int(layers_.size())) return {};
133 return std::holds_alternative<TileLayer>(layers_[std::size_t(index)].definition) ? "tiles" : "objects";
134}
135
136bool BuildLayerStack::isLayerEnabled(int index) const noexcept {
137 return index >= 0 && index < int(layers_.size()) && layers_[std::size_t(index)].enabled;
138}
139
141 const PointSet* orientation) const {
142 return executeRegion(grid, points,
143 {0, 0, grid.getWidth(), grid.getHeight(), -std::numeric_limits<float>::infinity(),
144 -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(),
145 std::numeric_limits<float>::infinity()},
146 orientation);
147}
148
150 const BuildLayerRegion& region,
151 const PointSet* orientation) const {
152 if (region.minCellX < 0 || region.minCellY < 0 || region.maxCellX < region.minCellX ||
153 region.maxCellY < region.minCellY || region.maxCellX > grid.getWidth() || region.maxCellY > grid.getHeight() ||
154 !(region.minWorldX < region.maxWorldX) || !(region.minWorldZ < region.maxWorldZ))
156 DiagnosticCode::InvalidArgument, "invalid build-layer region", {}, {}, "procgen.buildLayers"));
157 PointSet regionPoints;
158 regionPoints.reserve(points.points().size());
159 for (std::size_t index = 0; index < points.points().size(); ++index) {
160 const auto& point = points.points()[index];
161 if (point.x < region.minWorldX || point.x >= region.maxWorldX || point.z < region.minWorldZ ||
162 point.z >= region.maxWorldZ)
163 continue;
164 auto appended = regionPoints.appendPointFrom(points, index);
165 if (!appended.ok()) return Result<BuildLayerExecution>::failure(appended.status());
166 }
167 BuildLayerExecution execution;
168 execution.artifacts_.reserve(layers_.size());
169 for (const auto& layer : layers_) {
170 if (!layer.enabled) continue;
171 if (const auto* tile = std::get_if<TileLayer>(&layer.definition)) {
173 auto result = graph.addNode("grid", "grid.input");
174 if (result.ok()) result = graph.addNode("tiles", "mesh.grid_tiles");
175 if (result.ok()) result = graph.setNodeGrid("grid", grid);
176 if (result.ok()) result = graph.setNodeFloat("tiles", "cellSize", tile->cellSize);
177 if (result.ok()) result = graph.setNodeFloat("tiles", "height", tile->height);
178 if (result.ok()) result = graph.setNodeFloat("tiles", "minX", float(region.minCellX));
179 if (result.ok()) result = graph.setNodeFloat("tiles", "minY", float(region.minCellY));
180 if (result.ok()) result = graph.setNodeFloat("tiles", "maxX", float(region.maxCellX));
181 if (result.ok()) result = graph.setNodeFloat("tiles", "maxY", float(region.maxCellY));
182 if (result.ok()) result = graph.setNodeString("tiles", "group", tile->group);
183 if (result.ok()) result = graph.connect("grid", "tiles");
184 if (!result.ok()) return Result<BuildLayerExecution>::failure(result.status());
185 auto mesh = graph.execute("tiles");
186 if (!mesh.ok()) return Result<BuildLayerExecution>::failure(mesh.status());
187 execution.artifacts_.push_back({layer.id, std::move(mesh).takeValue()});
188 } else {
189 auto built = std::get<ObjectBuildLayer>(layer.definition).build(regionPoints, orientation);
190 if (!built.ok()) return Result<BuildLayerExecution>::failure(built.status());
191 execution.artifacts_.push_back({layer.id, std::move(built).takeValue()});
192 }
193 }
194 return Result<BuildLayerExecution>::success(std::move(execution));
195}
196
198 std::ostringstream out;
199 out << "EVPCG_BUILD_LAYERS 1\n" << std::setprecision(9);
200 for (const auto& layer : layers_) {
201 if (const auto* tile = std::get_if<TileLayer>(&layer.definition)) {
202 out << "TILE " << std::quoted(layer.id) << ' ' << int(layer.enabled) << ' ' << tile->cellSize << ' '
203 << tile->height << ' ' << std::quoted(tile->group) << '\n';
204 } else {
205 const auto& object = std::get<ObjectBuildLayer>(layer.definition);
206 out << "OBJECT " << std::quoted(layer.id) << ' ' << int(layer.enabled) << ' '
207 << hexEncode(object.serializeDefinition()) << '\n';
208 }
209 }
210 out << "END\n";
211 return out.str();
212}
213
215 BuildLayerStack replacement;
216 std::istringstream input{std::string(definition)};
217 std::string magic;
218 int version = 0;
219 if (!(input >> magic >> version) || magic != "EVPCG_BUILD_LAYERS" || version != 1)
221 Diagnostic::error(DiagnosticCode::ParseError, "invalid build-layer header", {}, {}, "procgen.buildLayers"));
222 std::string line;
223 std::getline(input, line);
224 bool ended = false;
225 while (std::getline(input, line)) {
226 if (line.empty()) continue;
227 std::istringstream record(line);
228 std::string kind;
229 record >> kind;
230 if (kind == "END") {
231 ended = true;
232 break;
233 }
234 std::string id;
235 int enabled = 0;
236 if (kind == "TILE") {
237 float cellSize = 0.f, height = 0.f;
238 std::string group;
239 if (!(record >> std::quoted(id) >> enabled >> cellSize >> height >> std::quoted(group)) ||
240 (enabled != 0 && enabled != 1))
242 {}, "procgen.buildLayers"));
243 auto applied = replacement.addTileLayer(std::move(id), enabled != 0, cellSize, height, std::move(group));
244 if (!applied.ok()) return applied;
245 } else if (kind == "OBJECT") {
246 std::string encoded;
247 if (!(record >> std::quoted(id) >> enabled >> encoded) || (enabled != 0 && enabled != 1))
248 return Result<void>::failure(Diagnostic::error(DiagnosticCode::ParseError, "invalid OBJECT record", {},
249 {}, "procgen.buildLayers"));
250 auto decoded = hexDecode(encoded);
251 if (!decoded.ok()) return Result<void>::failure(decoded.status());
253 auto applied = object.deserializeDefinition(decoded.value());
254 if (!applied.ok()) return applied;
255 auto added = replacement.addObjectLayer(std::move(id), enabled != 0, object);
256 if (!added.ok()) return added;
257 } else {
259 DiagnosticCode::ParseError, "unknown build-layer record: " + kind, {}, {}, "procgen.buildLayers"));
260 }
261 record >> std::ws;
262 if (!record.eof())
264 DiagnosticCode::ParseError, "trailing build-layer record data", {}, {}, "procgen.buildLayers"));
265 }
266 if (!ended)
267 return Result<void>::failure(Diagnostic::error(DiagnosticCode::ParseError, "build-layer END record is missing",
268 {}, {}, "procgen.buildLayers"));
269 *this = std::move(replacement);
270 return Result<void>::success();
271}
272
273} // namespace eve::procgen
double value
std::string output
building::EdgeCurveGroup group
Stable, structured diagnostics shared by engine modules.
EvpackChunkInput input
Definition Evpack.cpp:170
std::int32_t c
std::uint32_t height
TokenKind kind
bool ended
std::map< std::string, std::vector< std::string > > graph
Definition Package.cpp:59
TileLayer * layer
std::string id
Definition PlayHost.cpp:108
std::shared_ptr< const std::vector< glm::vec2 > > points
Mesh * mesh
Json object
float size
Definition TreeMesh.cpp:156
uint32_t index
glm::vec3 point
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
Immutable-by-convention result of one complete build-stack execution.
int getCount() const noexcept
Returns the count.
std::string getType(int index) const
Returns the type.
Result< PointSet > getPoints(int index) const
Copy a point artifact, or return a typed failure for a bad index/type.
Result< MeshBuild > getMesh(int index) const
Copy a mesh artifact, or return a typed failure for a bad index/type.
std::string getId(int index) const
Returns the id.
Ordered, versioned executor for procedural tile and object build layers.
Result< BuildLayerExecution > execute(const Grid2D &grid, const PointSet &points, const PointSet *orientation=nullptr) const
Execute every enabled layer in stable insertion order.
Result< void > setEnabled(std::string_view id, bool enabled)
std::string serializeDefinition() const
Serialize ordered definitions using EVPCG_BUILD_LAYERS schema version 1.
bool isLayerEnabled(int index) const noexcept
True when layer enabled.
Result< void > addObjectLayer(std::string id, bool enabled, const ObjectBuildLayer &layer)
Append an object layer by value. Layer ids are unique.
int getLayerCount() const noexcept
Returns the layer count.
Result< void > addTileLayer(std::string id, bool enabled, float cellSize, float height, std::string group)
Append a grid-to-mesh tile layer. Layer ids are unique.
std::string getLayerId(int index) const
Returns the layer id.
std::string getLayerType(int index) const
Returns the layer type.
Result< void > deserializeDefinition(std::string_view definition)
Atomically restore version 1; unknown fields and malformed embedded layers are rejected.
Result< BuildLayerExecution > executeRegion(const Grid2D &grid, const PointSet &points, const BuildLayerRegion &region, const PointSet *orientation=nullptr) const
Execute enabled layers for one half-open cluster region.
Intermediate 2D generation result. cells store semantic ids (see Semantic.h), not tile GIDs — convert...
Definition Grid2D.h:36
Typed procedural mesh graph composing grid, point and topology domains.
Definition MeshGraph.h:21
Result< void > addNode(std::string id, std::string operation)
Adds node.
Deterministic TileWorldCreator-style object build layer.
Result< void > deserializeDefinition(std::string_view definition)
Atomically replace settings from schema version 1; unknown records are rejected.
Script-friendly collection of attributed 3D samples.
Definition PointSet.h:59
Result< int > appendPointFrom(const PointSet &source, std::size_t sourceIndex)
Append a point and its attributes from another set.
Definition PointSet.cpp:69
void reserve(std::size_t count)
Reserve point storage without changing point or attribute row counts.
Definition PointSet.cpp:60
@ TypeMismatch
A stable reference resolved to a different canonical domain type.
bool enabled
Half-open grid and world-space bounds used for one incremental cluster build.