载入中...
搜索中...
未找到
UrbanOutput.cpp
浏览该文件的文档.
2
4#include "procgen/Grid2D.h"
5#include "procgen/MeshBuild.h"
6#include "procgen/Params.h"
7#include "procgen/Semantic.h"
12
13#include <algorithm>
14#include <cmath>
15#include <cstdint>
16#include <limits>
17#include <sstream>
18#include <string>
19#include <vector>
20
21namespace eve::procgen::urban {
22namespace {
23
24constexpr double kPi = 3.14159265358979323846;
25
26std::vector<std::string> split(const std::string& s, char sep) {
27 std::vector<std::string> parts;
28 std::string cur;
29 for (const char c : s) {
30 if (c == sep) {
31 if (!cur.empty()) parts.push_back(cur);
32 cur.clear();
33 } else {
34 cur.push_back(c);
35 }
36 }
37 if (!cur.empty()) parts.push_back(cur);
38 return parts;
39}
40
41bool parsePoints(const std::string& text, Polygon& out) {
42 out.clear();
43 for (const std::string& pair : split(text, ';')) {
44 const auto xy = split(pair, ',');
45 if (xy.size() != 2) return false;
46 try {
47 out.push_back({std::stod(xy[0]), std::stod(xy[1])});
48 } catch (...) {
49 return false;
50 }
51 }
52 return out.size() >= 3;
53}
54
55bool makePresetLand(const std::string& preset, double w, double h, Polygon& out) {
56 out.clear();
57 const std::string p = preset.empty() ? "rect" : preset;
58 if (p == "rect") {
59 out = {{0, 0}, {w, 0}, {w, h}, {0, h}};
60 } else if (p == "triangle") {
61 out = {{0, 0}, {w, 0}, {w * 0.5, h}};
62 } else if (p == "ellipse" || p == "circle") {
63 const int segs = 28;
64 const double rx = w * 0.5;
65 const double ry = h * 0.5;
66 for (int i = 0; i < segs; ++i) {
67 const double a = double(i) * 2.0 * kPi / double(segs);
68 out.push_back({w * 0.5 + rx * std::cos(a), h * 0.5 + ry * std::sin(a)});
69 }
70 } else if (p == "l") {
71 out = {{0, 0}, {w, 0}, {w, h * 0.55}, {w * 0.45, h * 0.55}, {w * 0.45, h}, {0, h}};
72 } else if (p == "hexagon") {
73 const double cx = w * 0.5;
74 const double cy = h * 0.5;
75 for (int i = 0; i < 6; ++i) {
76 const double a = -kPi * 0.5 + double(i) * kPi / 3.0;
77 out.push_back({cx + w * 0.5 * std::cos(a), cy + h * 0.5 * std::sin(a)});
78 }
79 } else {
80 return false;
81 }
82 return out.size() >= 3;
83}
84
85void addQuad(MeshBuild& m, const Vec2& a, const Vec2& b, const Vec2& c, const Vec2& d, float y, float uScale) {
86 const int base = m.getVertexCount();
87 const float ax = float(a.x), az = float(a.y);
88 const float bx = float(b.x), bz = float(b.y);
89 const float cx = float(c.x), cz = float(c.y);
90 const float dx = float(d.x), dz = float(d.y);
91 m.addVertex(ax, y, az, 0.f, 1.f, 0.f, ax * uScale, az * uScale);
92 m.addVertex(bx, y, bz, 0.f, 1.f, 0.f, bx * uScale, bz * uScale);
93 m.addVertex(cx, y, cz, 0.f, 1.f, 0.f, cx * uScale, cz * uScale);
94 m.addVertex(dx, y, dz, 0.f, 1.f, 0.f, dx * uScale, dz * uScale);
95 m.addTriangle(uint32_t(base), uint32_t(base + 1), uint32_t(base + 2));
96 m.addTriangle(uint32_t(base), uint32_t(base + 2), uint32_t(base + 3));
97}
98
99} // namespace
100
101bool parseUrbanOptions(const Params& params, UrbanOptions& opts, std::string& error) {
102 opts = UrbanOptions{};
103 opts.seed = params.getSeed();
104 const double w = std::max(1.0, double(params.getFloat("landWidth", 100.f)));
105 const double h = std::max(1.0, double(params.getFloat("landHeight", 60.f)));
106 const std::string landParam = params.getString("land", "rect");
107 std::string landText = params.getString("landPoints", "");
108 if (landText.empty()) landText = landParam;
109 if (!makePresetLand(landParam, w, h, opts.land)) {
110 if (!parsePoints(landText, opts.land)) {
111 error = "urban: unknown land preset '" + landParam +
112 "' (use rect|triangle|ellipse|l|hexagon or explicit 'x,y;x,y;...' points)";
113 return false;
114 }
115 }
116 ensureCCW(opts.land);
117 opts.minParcelArea = std::max(0.01, double(params.getFloat("minParcelArea", 4.f)));
118 opts.targetParcels = std::max(0, params.getInt("targetParcels", 120));
119 opts.maxLevels = std::max(1, params.getInt("maxLevels", 10));
120
121 opts.lambdaSize = std::max(0.0, double(params.getFloat("lambdaSize", 0.3f)));
122 opts.lambdaRegu = std::max(0.0, double(params.getFloat("lambdaRegu", 0.5f)));
123 opts.lambdaAcce = std::max(0.0, double(params.getFloat("lambdaAcce", 0.2f)));
124 opts.lambdaOrient = std::max(0.0, double(params.getFloat("lambdaOrient", 0.f)));
125 opts.gammaAngle = std::clamp(double(params.getFloat("gammaAngle", 0.75f)), 0.0, 100.0);
126 opts.gammaSide = std::clamp(double(params.getFloat("gammaSide", 0.25f)), 0.0, 100.0);
127 opts.accessThreshold = std::clamp(double(params.getFloat("accessThreshold", 0.5f)), 0.01, 10.0);
128 opts.shortEdgeFactor = std::clamp(double(params.getFloat("shortEdgeFactor", 0.2f)), 0.0, 1.0);
129 opts.streetWidth = std::max(0.1, double(params.getFloat("streetWidth", 1.f)));
130 opts.dijkstraJunctionWeight = std::max(0.0, double(params.getFloat("dijkstraJunctionWeight", 1.5f)));
131 opts.boundaryStreetFraction = std::clamp(double(params.getFloat("boundaryStreetFraction", 0.5f)), 0.0, 1.0);
132
133 const std::string pattern = params.getString("streetPattern", "default");
134 if (pattern == "loop")
135 opts.streetPattern = 1;
136 else if (pattern == "culdesac")
137 opts.streetPattern = 2;
138 else if (pattern == "tree")
139 opts.streetPattern = 3;
140 else
141 opts.streetPattern = 0;
142 opts.culDeSacAfterLevel = std::max(1, params.getInt("culDeSacAfterLevel", 4));
143
144 const std::string orient = params.getString("orientation", "none");
145 opts.orientation = orient == "east-west" ? 1 : orient == "north-south" ? 2 : 0;
146
147 const std::string boundary = params.getString("boundaryStreet", "all");
148 opts.boundaryStreetMode = boundary == "none" ? 1 : boundary == "random" ? 2 : 0;
149
150 opts.optimize = params.getInt("optimize", 1) != 0;
151 opts.optimizeIterations = std::clamp(params.getInt("optimizeIterations", 160), 0, 2000);
152 opts.optRegu = std::max(0.0, double(params.getFloat("optRegu", 0.2f)));
153 opts.optSide = std::max(0.0, double(params.getFloat("optSide", 1.f)));
154 opts.optStre = std::max(0.0, double(params.getFloat("optStre", 1.f)));
155 opts.optJunc = std::max(0.0, double(params.getFloat("optJunc", 0.5f)));
156 opts.optClose = std::max(0.0, double(params.getFloat("optClose", 0.3f)));
157 return true;
158}
159
160bool generateUrbanGrid(const Params& params, Grid2D& out, std::string& error) {
161 UrbanOptions opts;
162 if (!parseUrbanOptions(params, opts, error)) return false;
163 UrbanGenerator gen(opts);
164 if (!gen.generate(&error)) return false;
165 const UrbanLayout& layout = gen.layout();
166 if (layout.parcels.empty()) {
167 error = "urban.parcels: no parcels generated";
168 return false;
169 }
170
171 double minX = opts.land[0].x, minY = opts.land[0].y;
172 double maxX = opts.land[0].x, maxY = opts.land[0].y;
173 for (const Vec2& p : opts.land) {
174 minX = std::min(minX, p.x);
175 minY = std::min(minY, p.y);
176 maxX = std::max(maxX, p.x);
177 maxY = std::max(maxY, p.y);
178 }
179 const double landW = std::max(1e-6, maxX - minX);
180 const double landH = std::max(1e-6, maxY - minY);
181 const double cellSize = std::max(0.05, double(params.getFloat("cellSize", 1.f)));
182 const int gridW = std::clamp(int(std::ceil(landW / cellSize)) + 2, 3, 1024);
183 const int gridH = std::clamp(int(std::ceil(landH / cellSize)) + 2, 3, 1024);
184 const double ox = minX - cellSize;
185 const double oy = minY - cellSize;
186 const double halfStreet = opts.streetWidth * 0.5;
187
188 out.resize(gridW, gridH);
189 out.fill(Semantic::Wall);
190 for (int gy = 0; gy < gridH; ++gy) {
191 for (int gx = 0; gx < gridW; ++gx) {
192 const Vec2 c{ox + (double(gx) + 0.5) * cellSize, oy + (double(gy) + 0.5) * cellSize};
193 if (!pointInPolygon(c, opts.land)) continue;
194 bool onStreet = false;
195 for (const Street& s : layout.streets) {
196 for (size_t i = 1; i < s.pts.size(); ++i) {
197 if (distanceToSegment(c, s.pts[i - 1], s.pts[i]) <= halfStreet) {
198 onStreet = true;
199 break;
200 }
201 }
202 if (onStreet) break;
203 }
204 if (onStreet) {
205 out.setCell(gx, gy, int(Semantic::Road));
206 continue;
207 }
208 int pid = 0;
209 for (size_t p = 0; p < layout.parcels.size(); ++p) {
210 Polygon poly;
211 for (const int ci : layout.parcels[p].ring) poly.push_back(layout.corners[size_t(ci)]);
212 if (pointInPolygon(c, poly)) {
213 pid = int(p) + 1;
214 break;
215 }
216 }
217 out.setCell(gx, gy, pid > 0 ? int(Semantic::Floor) : int(Semantic::Wall));
218 if (pid > 0) out.setDetail(gx, gy, std::min(pid, 254));
219 }
220 }
221
222 // Parcel anchors for building placement.
223 out.clearObjects();
224 for (size_t p = 0; p < layout.parcels.size(); ++p) {
225 Polygon poly;
226 for (const int ci : layout.parcels[p].ring) poly.push_back(layout.corners[size_t(ci)]);
227 const Vec2 c = centroid(poly);
228 out.addObjectAt("parcel" + std::to_string(p), "parcel", float((c.x - ox) / cellSize),
229 float((c.y - oy) / cellSize));
230 }
231
232 out.setMeta("algorithm", "urban.parcels");
233 out.setMeta("seed", std::to_string(opts.seed));
234 out.setMeta("parcels", std::to_string(layout.parcels.size()));
235 out.setMeta("streets", std::to_string(layout.streets.size()));
236 out.setMeta("junctions", std::to_string(layout.streetJunctions));
237 out.setMeta("streetLength", std::to_string(layout.totalStreetLength));
238 out.setMeta("avgIrregularity", std::to_string(layout.avgIrregularity));
239 out.setMeta("levels", std::to_string(layout.levelsUsed));
240 out.setMeta("streetPattern", params.getString("streetPattern", "default"));
241 out.setMeta("optimize", opts.optimize ? "1" : "0");
242 out.setMeta("cellSize", std::to_string(cellSize));
243 return true;
244}
245
246bool generateUrbanMesh(const Params& params, MeshBuild& out, std::string& error) {
247 UrbanOptions opts;
248 if (!parseUrbanOptions(params, opts, error)) return false;
249 UrbanGenerator gen(opts);
250 if (!gen.generate(&error)) return false;
251 const UrbanLayout& layout = gen.layout();
252 if (layout.parcels.empty()) {
253 error = "mesh.urban: no parcels generated";
254 return false;
255 }
256
257 out.clear();
258 const float extrude = std::max(0.f, params.getFloat("extrude", 0.f));
259 const float uvScale = std::max(0.001f, params.getFloat("uvScale", 0.1f));
260
261 // Street ribbons (slightly above the ground plane).
262 for (const Street& s : layout.streets) {
263 if (s.pts.size() < 2) continue;
264 const double half = s.width * 0.5;
265 for (size_t i = 1; i < s.pts.size(); ++i) {
266 const Vec2 dir = normalize(s.pts[i] - s.pts[i - 1]);
267 const Vec2 perp = perpendicular(dir);
268 const Vec2 l0 = s.pts[i - 1] + perp * half;
269 const Vec2 r0 = s.pts[i - 1] - perp * half;
270 const Vec2 l1 = s.pts[i] + perp * half;
271 const Vec2 r1 = s.pts[i] - perp * half;
272 addQuad(out, l0, l1, r1, r0, 0.01f, uvScale);
273 }
274 }
275
276 for (size_t p = 0; p < layout.parcels.size(); ++p) {
277 Polygon poly;
278 for (const int ci : layout.parcels[p].ring) poly.push_back(layout.corners[size_t(ci)]);
279 std::vector<int> tris;
280 if (!triangulatePolygon(poly, tris)) {
281 error = "mesh.urban: failed to triangulate parcel " + std::to_string(p);
282 return false;
283 }
284 auto emitVert = [&](const Vec2& v, float y, float nx, float ny, float nz) {
285 out.addVertex(float(v.x), y, float(v.y), nx, ny, nz, float(v.x) * uvScale, float(v.y) * uvScale);
286 };
287 if (extrude <= 0.f) {
288 const int base = out.getVertexCount();
289 for (size_t i = 0; i < poly.size(); ++i) emitVert(poly[i], 0.f, 0.f, 1.f, 0.f);
290 for (size_t t = 0; t + 2 < tris.size(); t += 3)
291 // Ear-clipping emits CCW (x,y) which is -Y in the XZ plane; flip for +Y.
292 out.addTriangle(uint32_t(base + tris[t]), uint32_t(base + tris[t + 2]), uint32_t(base + tris[t + 1]));
293 } else {
294 const double yTop = double(extrude);
295 const int baseTop = out.getVertexCount();
296 for (size_t i = 0; i < poly.size(); ++i) emitVert(poly[i], float(yTop), 0.f, 1.f, 0.f);
297 for (size_t t = 0; t + 2 < tris.size(); t += 3)
298 out.addTriangle(uint32_t(baseTop + tris[t]), uint32_t(baseTop + tris[t + 2]),
299 uint32_t(baseTop + tris[t + 1]));
300 const int baseBottom = out.getVertexCount();
301 for (size_t i = 0; i < poly.size(); ++i) emitVert(poly[i], 0.f, 0.f, -1.f, 0.f);
302 for (size_t t = 0; t + 2 < tris.size(); t += 3)
303 out.addTriangle(uint32_t(baseBottom + tris[t]), uint32_t(baseBottom + tris[t + 1]),
304 uint32_t(baseBottom + tris[t + 2]));
305 const size_t m = poly.size();
306 for (size_t i = 0; i < m; ++i) {
307 const Vec2& a = poly[i];
308 const Vec2& b = poly[(i + 1) % m];
309 const Vec2 n = normalize(perpendicular(b - a));
310 const int base = out.getVertexCount();
311 emitVert(a, 0.f, float(n.x), 0.f, float(n.y));
312 emitVert(b, 0.f, float(n.x), 0.f, float(n.y));
313 emitVert(b, float(yTop), float(n.x), 0.f, float(n.y));
314 emitVert(a, float(yTop), float(n.x), 0.f, float(n.y));
315 out.addTriangle(uint32_t(base), uint32_t(base + 1), uint32_t(base + 2));
316 out.addTriangle(uint32_t(base), uint32_t(base + 2), uint32_t(base + 3));
317 }
318 }
319 }
320
321 out.setMeta("algorithm", "mesh.urban");
322 out.setMeta("seed", std::to_string(opts.seed));
323 out.setMeta("parcels", std::to_string(layout.parcels.size()));
324 out.setMeta("streets", std::to_string(layout.streets.size()));
325 out.setMeta("junctions", std::to_string(layout.streetJunctions));
326 out.setMeta("streetLength", std::to_string(layout.totalStreetLength));
327 out.setMeta("avgIrregularity", std::to_string(layout.avgIrregularity));
328 out.setMeta("extrude", std::to_string(extrude));
329 return true;
330}
331
332static RecipeDescriptor makeUrbanDescriptor(std::string id, std::string name,
333 std::string category) {
334 RecipeDescriptor descriptor{std::move(id), std::move(name), std::move(category), {}};
335 descriptor.params.push_back(ParamDescriptor::integer("seed", "Seed", 1, 0, 2147483647));
336 descriptor.params.push_back(ParamDescriptor::choice("land", "Land Shape", "rect",
337 {"rect", "triangle", "ellipse", "l", "hexagon"}));
338 descriptor.params.push_back(ParamDescriptor::floating("landWidth", "Land Width", 100.f, 1.f, 10000.f, 1.f));
339 descriptor.params.push_back(ParamDescriptor::floating("landHeight", "Land Height", 60.f, 1.f, 10000.f, 1.f));
340 descriptor.params.push_back(ParamDescriptor::text("landPoints", "Custom Land Points", ""));
341 descriptor.params.push_back(ParamDescriptor::integer("targetParcels", "Target Parcels", 120, 0, 10000));
342 descriptor.params.push_back(ParamDescriptor::floating("minParcelArea", "Minimum Parcel Area", 4.f, 0.01f,
343 10000.f, 0.1f));
344 descriptor.params.push_back(ParamDescriptor::integer("maxLevels", "Maximum Levels", 10, 1, 128));
345 descriptor.params.push_back(ParamDescriptor::choice("streetPattern", "Street Pattern", "default",
346 {"default", "loop", "culdesac", "tree"}));
347 descriptor.params.push_back(ParamDescriptor::floating("streetWidth", "Street Width", 1.f, 0.1f, 100.f,
348 0.1f));
349 descriptor.params.push_back(ParamDescriptor::choice("orientation", "Orientation", "none",
350 {"none", "east-west", "north-south"}));
351 descriptor.params.push_back(ParamDescriptor::choice("boundaryStreet", "Boundary Streets", "all",
352 {"all", "none", "random"}));
353 descriptor.params.push_back(ParamDescriptor::floating("cellSize", "Grid Cell Size", 1.f, 0.05f, 100.f,
354 0.05f));
355 descriptor.params.push_back(ParamDescriptor::boolean("optimize", "Optimize", true));
356 auto addAdvanced = [&](ParamDescriptor param) {
357 param.advanced = true;
358 descriptor.params.push_back(std::move(param));
359 };
360 addAdvanced(ParamDescriptor::floating("lambdaSize", "Size Weight", 0.3f, 0.f, 100.f, 0.01f));
361 addAdvanced(ParamDescriptor::floating("lambdaRegu", "Regularity Weight", 0.5f, 0.f, 100.f, 0.01f));
362 addAdvanced(ParamDescriptor::floating("lambdaAcce", "Access Weight", 0.2f, 0.f, 100.f, 0.01f));
363 addAdvanced(ParamDescriptor::floating("lambdaOrient", "Orientation Weight", 0.f, 0.f, 100.f, 0.01f));
364 addAdvanced(ParamDescriptor::floating("gammaAngle", "Angle Weight", 0.75f, 0.f, 100.f, 0.01f));
365 addAdvanced(ParamDescriptor::floating("gammaSide", "Side Weight", 0.25f, 0.f, 100.f, 0.01f));
366 addAdvanced(ParamDescriptor::floating("accessThreshold", "Access Threshold", 0.5f, 0.01f, 10.f, 0.01f));
367 addAdvanced(ParamDescriptor::floating("shortEdgeFactor", "Short Edge Factor", 0.2f, 0.f, 1.f, 0.01f));
368 addAdvanced(ParamDescriptor::floating("dijkstraJunctionWeight", "Junction Weight", 1.5f, 0.f, 100.f,
369 0.1f));
370 addAdvanced(ParamDescriptor::floating("boundaryStreetFraction", "Boundary Street Fraction", 0.5f, 0.f,
371 1.f, 0.01f));
372 addAdvanced(ParamDescriptor::integer("culDeSacAfterLevel", "Cul-de-sac Level", 4, 1, 128));
373 addAdvanced(ParamDescriptor::integer("optimizeIterations", "Optimize Iterations", 160, 0, 2000));
374 addAdvanced(ParamDescriptor::floating("optRegu", "Optimize Regularity", 0.2f, 0.f, 100.f, 0.01f));
375 addAdvanced(ParamDescriptor::floating("optSide", "Optimize Sides", 1.f, 0.f, 100.f, 0.01f));
376 addAdvanced(ParamDescriptor::floating("optStre", "Optimize Streets", 1.f, 0.f, 100.f, 0.01f));
377 addAdvanced(ParamDescriptor::floating("optJunc", "Optimize Junctions", 0.5f, 0.f, 100.f, 0.01f));
378 addAdvanced(ParamDescriptor::floating("optClose", "Optimize Closure", 0.3f, 0.f, 100.f, 0.01f));
379 return descriptor;
380}
381
383 registry.registerAlgorithm(
384 makeUrbanDescriptor("urban.parcels", "Urban Parcels", "Urban"), generateUrbanGrid);
385}
386
389 makeUrbanDescriptor("mesh.urban", "Urban Blocks", "Urban");
390 descriptor.params.push_back(
391 ParamDescriptor::floating("extrude", "Extrusion Height", 0.f, 0.f, 1000.f, 0.1f));
392 descriptor.params.push_back(
393 ParamDescriptor::floating("uvScale", "UV Scale", 0.1f, 0.f, 100.f, 0.01f));
394 registry.registerRecipe(std::move(descriptor), generateUrbanMesh);
395}
396
397} // namespace eve::procgen::urban
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
std::vector< eve::artifact::PartView > parts
std::string descriptor
const std::string & s
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
int bz
Definition CaveMesh.cpp:114
int ax
Definition CaveMesh.cpp:113
int bx
Definition CaveMesh.cpp:114
bool split
Definition CaveMesh.cpp:123
int az
Definition CaveMesh.cpp:113
float nx
float nz
float ny
glm::vec4 p[6]
std::string pattern
std::string layout
glm::vec3 n
Definition Grass.cpp:63
float v
std::int32_t c
int h
std::string text
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
Vec3 centroid
std::string error
Definition Package.cpp:60
float d
float t
float dz
float dx
CommandLogBoundary boundary
Anchor rule, see above.
V3 dir
Definition TreeMesh.cpp:150
const char * category
double oy
double ox
float m[16]
Registry for executable generators and their reflection metadata.
void registerAlgorithm(const std::string &id, GeneratorFn fn)
Register an algorithm without metadata for backward compatibility.
Intermediate 2D generation result. cells store semantic ids (see Semantic.h), not tile GIDs — convert...
Definition Grid2D.h:36
void setDetail(int x, int y, int value)
Per-cell detail layer (0..255), parallel to cells. Semantics stay in cells (palette/GID compatible); ...
Definition Grid2D.cpp:37
void clearObjects()
Clears objects.
Definition Grid2D.cpp:54
void resize(int width, int height)
Resize.
Definition Grid2D.cpp:13
void fill(int semantic)
Fill.
Definition Grid2D.cpp:33
void setCell(int x, int y, int semantic)
Sets the cell.
Definition Grid2D.cpp:23
void addObjectAt(const std::string &name, const std::string &type, float x, float y)
Script-friendly: name/type + tile coords.
Definition Grid2D.cpp:58
void setMeta(const std::string &key, const std::string &value)
Sets the meta.
Definition Grid2D.cpp:47
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
Definition MeshBuild.h:19
void addVertex(float px, float py, float pz, float nx, float ny, float nz, float u, float v)
Adds vertex.
Definition MeshBuild.cpp:33
void addTriangle(uint32_t i0, uint32_t i1, uint32_t i2)
Adds triangle.
Definition MeshBuild.cpp:46
void setMeta(const std::string &key, const std::string &value)
Sets the meta.
void clear()
Clears .
Definition MeshBuild.cpp:8
EVENGINE_API_DOMAINS public API.
void registerRecipe(const std::string &id, MeshRecipeFn fn)
Register a recipe without metadata.
Owning, typed generation parameters.
Definition Params.h:27
Hierarchical co-generation of parcels and streets (paper Section 4) plus the global geometric optimiz...
const UrbanLayout & layout() const
Layout.
bool generate(std::string *error=nullptr)
Run the pipeline. On failure error receives a human-readable reason.
std::vector< ParamSpec > params
constexpr uint32_t Floor
Definition Semantic.h:14
constexpr uint32_t Wall
Definition Semantic.h:13
constexpr uint32_t Road
Definition Semantic.h:23
void registerUrbanMeshRecipes(MeshRecipeRegistry &registry)
Registers urban mesh recipes.
Vec2 perpendicular(const Vec2 &a)
Perpendicular.
Definition UrbanTypes.h:50
bool pointInPolygon(const Vec2 &p, const Polygon &poly)
Point-in-polygon test (ray casting; boundary counts as inside).
bool triangulatePolygon(const Polygon &poly, std::vector< int > &outTriangles)
Triangulate a simple polygon by ear clipping; returns CCW triangles (3*i..3*i+2).
bool parseUrbanOptions(const Params &params, UrbanOptions &opts, std::string &error)
Parse Procgen Params into urban generator options. Params: land/landPoints, minParcelArea,...
double distanceToSegment(const Vec2 &p, const Vec2 &a, const Vec2 &b)
Raster helper: does the pixel-center fall within eps of segment a-b?
bool generateUrbanMesh(const Params &params, MeshBuild &out, std::string &error)
MeshBuild of parcel blocks + street ribbons (flat or extruded, Y-up).
Vec2 normalize(const Vec2 &a)
Normalize.
Definition UrbanTypes.h:44
std::vector< Vec2 > Polygon
Closed polygon ring, stored CCW, without repeating the first point.
Definition UrbanTypes.h:55
bool generateUrbanGrid(const Params &params, Grid2D &out, std::string &error)
Grid2D rasterization of the urban layout (Semantic::Road/Floor/Wall + parcel detail).
void registerUrbanGenerators(GeneratorRegistry &registry)
Register "urban.parcels" (Grid2D) and "mesh.urban" (MeshBuild) builtins.
bool ensureCCW(Polygon &poly)
Ensure the ring is CCW (positive signed area); returns whether it was flipped.
static ParamDescriptor boolean(std::string key, std::string label, bool defaultValue)
Construct a Boolean descriptor stored as zero or one in Params.
static ParamDescriptor choice(std::string key, std::string label, std::string defaultValue, std::vector< std::string > choices)
Construct a finite-choice string descriptor.
static ParamDescriptor floating(std::string key, std::string label, float defaultValue, float minimum, float maximum, float step)
Construct a bounded floating-point descriptor.
static ParamDescriptor integer(std::string key, std::string label, int defaultValue, int minimum, int maximum, int step=1)
Construct a bounded integer descriptor.
static ParamDescriptor text(std::string key, std::string label, std::string defaultValue)
Construct a free-form string descriptor.
Complete metadata and parameter schema shared by every procgen recipe family.
Definition ParamSchema.h:52
std::vector< ParamDescriptor > params
Definition ParamSchema.h:56
A street decomposed from the street network graph: consecutive street edges whose included angle is l...
Definition UrbanTypes.h:63
Structured urban layout produced by the hierarchical co-generation (paper Section 4) plus the geometr...
Definition UrbanTypes.h:84
All user-facing controls for the urban generator. Defaults follow the paper (λ=0.3/0....
Definition UrbanTypes.h:117
Minimal 2D vector used by the urban layout algorithms (paper coordinates).
Definition UrbanTypes.h:15