载入中...
搜索中...
未找到
MarchingCubes.cpp
浏览该文件的文档.
7
8#include <algorithm>
9#include <cmath>
10#include <cstdint>
11#include <map>
12
13namespace eve::procgen {
14namespace {
15
16#include "procgen/algorithms/MarchingCubesTables.inc"
17
18using eve::procgen::mc_tables::kEdgeTable;
19using eve::procgen::mc_tables::kTriTable;
20
21// Edge endpoints for the 12 cube edges (corner index pairs).
22constexpr int kEdgeCorners[12][2] = {
23 {0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 4}, {1, 5}, {2, 6}, {3, 7},
24};
25
26// Corner offsets in unit cube (x,y,z).
27constexpr float kCornerOffset[8][3] = {
28 {0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}, {0, 0, 1}, {1, 0, 1}, {1, 1, 1}, {0, 1, 1},
29};
30
31inline float lerp(float a, float b, float t) { return a + (b - a) * t; }
32
33inline void normalize3(float &x, float &y, float &z) {
34 const float len = std::sqrt(x * x + y * y + z * z);
35 if (len > 1e-8f) {
36 x /= len;
37 y /= len;
38 z /= len;
39 } else {
40 x = 0.f;
41 y = 1.f;
42 z = 0.f;
43 }
44}
45
46inline float fade(float t) { return t * t * t * (t * (t * 6.f - 15.f) + 10.f); }
47
48inline float grad3(uint32_t h, float x, float y, float z) {
49 const uint32_t g = h & 15u;
50 const float u = g < 8 ? x : y;
51 const float v = g < 4 ? y : (g == 12 || g == 14 ? x : z);
52 return ((g & 1u) ? -u : u) + ((g & 2u) ? -v : v);
53}
54
55float valueNoise3(float x, float y, float z, uint32_t seed) {
56 const int xi = int(std::floor(x));
57 const int yi = int(std::floor(y));
58 const int zi = int(std::floor(z));
59 const float xf = x - float(xi);
60 const float yf = y - float(yi);
61 const float zf = z - float(zi);
62 const float u = fade(xf);
63 const float v = fade(yf);
64 const float w = fade(zf);
65
66 auto h = [&](int ix, int iy, int iz) -> uint32_t {
67 return uint32_t(ix) * 374761393u + uint32_t(iy) * 668265263u + uint32_t(iz) * 1274126177u +
68 seed * 2246822519u;
69 };
70
71 const float n000 = grad3(h(xi, yi, zi), xf, yf, zf);
72 const float n100 = grad3(h(xi + 1, yi, zi), xf - 1, yf, zf);
73 const float n010 = grad3(h(xi, yi + 1, zi), xf, yf - 1, zf);
74 const float n110 = grad3(h(xi + 1, yi + 1, zi), xf - 1, yf - 1, zf);
75 const float n001 = grad3(h(xi, yi, zi + 1), xf, yf, zf - 1);
76 const float n101 = grad3(h(xi + 1, yi, zi + 1), xf - 1, yf, zf - 1);
77 const float n011 = grad3(h(xi, yi + 1, zi + 1), xf, yf - 1, zf - 1);
78 const float n111 = grad3(h(xi + 1, yi + 1, zi + 1), xf - 1, yf - 1, zf - 1);
79
80 const float x00 = lerp(n000, n100, u);
81 const float x10 = lerp(n010, n110, u);
82 const float x01 = lerp(n001, n101, u);
83 const float x11 = lerp(n011, n111, u);
84 const float y0 = lerp(x00, x10, v);
85 const float y1 = lerp(x01, x11, v);
86 return lerp(y0, y1, w); // roughly [-1,1]
87}
88
89float fbm3(float x, float y, float z, uint32_t seed, int octaves) {
90 float sum = 0.f;
91 float amp = 0.5f;
92 float freq = 1.f;
93 float norm = 0.f;
94 for (int i = 0; i < octaves; ++i) {
95 sum += valueNoise3(x * freq, y * freq, z * freq, seed + uint32_t(i) * 1013u) * amp;
96 norm += amp;
97 amp *= 0.5f;
98 freq *= 2.f;
99 }
100 return norm > 0.f ? sum / norm : 0.f;
101}
102
103struct Vec3 {
104 float x = 0.f, y = 0.f, z = 0.f;
105};
106
107Vec3 add(Vec3 a, Vec3 b) { return {a.x + b.x, a.y + b.y, a.z + b.z}; }
108Vec3 sub(Vec3 a, Vec3 b) { return {a.x - b.x, a.y - b.y, a.z - b.z}; }
109Vec3 mul(Vec3 a, float s) { return {a.x * s, a.y * s, a.z * s}; }
110float dot(Vec3 a, Vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
111Vec3 cross(Vec3 a, Vec3 b) {
112 return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
113}
114Vec3 normalized(Vec3 v) {
115 const float len = std::sqrt(dot(v, v));
116 return len > 1e-8f ? mul(v, 1.f / len) : Vec3{0.f, 1.f, 0.f};
117}
118
119struct Triangle {
120 uint32_t a, b, c;
121};
122
123uint64_t edgeKey(uint32_t a, uint32_t b) {
124 if (a > b) std::swap(a, b);
125 return (uint64_t(a) << 32u) | uint64_t(b);
126}
127
128uint32_t midpoint(uint32_t a, uint32_t b, std::vector<Vec3> &vertices,
129 std::map<uint64_t, uint32_t> &cache) {
130 const uint64_t key = edgeKey(a, b);
131 const auto it = cache.find(key);
132 if (it != cache.end()) return it->second;
133 const uint32_t index = uint32_t(vertices.size());
134 vertices.push_back(normalized(add(vertices[a], vertices[b])));
135 cache.emplace(key, index);
136 return index;
137}
138
139void addPlanetVertex(MeshBuild &out, Vec3 p, float radius) {
140 p = normalized(p);
141 constexpr float kPi = 3.14159265358979323846f;
142 const float u = std::atan2(p.z, p.x) / (2.f * kPi) + 0.5f;
143 const float v = std::asin(std::clamp(p.y, -1.f, 1.f)) / kPi + 0.5f;
144 out.addVertex(p.x * radius, p.y * radius, p.z * radius, p.x, p.y, p.z, u, v);
145}
146
147} // namespace
148
149bool generateHexPlanetMesh(const Params &params, MeshBuild &out, std::string &error) {
150 const int subdivisions = params.getInt("subdivisions", 2);
151 const float radius = params.getFloat("radius", 1.f);
152 const float inset = params.getFloat("tileInset", 0.06f);
153 if (subdivisions < 0 || subdivisions > 7) {
154 error = "mesh.hexplanet: subdivisions must be in [0, 7]";
155 return false;
156 }
157 if (!(radius > 0.f)) {
158 error = "mesh.hexplanet: radius must be positive";
159 return false;
160 }
161 if (inset < 0.f || inset >= 0.5f) {
162 error = "mesh.hexplanet: tileInset must be in [0, 0.5)";
163 return false;
164 }
165
166 const float phi = (1.f + std::sqrt(5.f)) * 0.5f;
167 std::vector<Vec3> vertices = {
168 {-1, phi, 0}, {1, phi, 0}, {-1, -phi, 0}, {1, -phi, 0},
169 {0, -1, phi}, {0, 1, phi}, {0, -1, -phi}, {0, 1, -phi},
170 {phi, 0, -1}, {phi, 0, 1}, {-phi, 0, -1}, {-phi, 0, 1},
171 };
172 for (Vec3 &v : vertices) v = normalized(v);
173 std::vector<Triangle> faces = {
174 {0, 11, 5}, {0, 5, 1}, {0, 1, 7}, {0, 7, 10}, {0, 10, 11},
175 {1, 5, 9}, {5, 11, 4}, {11, 10, 2}, {10, 7, 6}, {7, 1, 8},
176 {3, 9, 4}, {3, 4, 2}, {3, 2, 6}, {3, 6, 8}, {3, 8, 9},
177 {4, 9, 5}, {2, 4, 11}, {6, 2, 10}, {8, 6, 7}, {9, 8, 1},
178 };
179 for (int level = 0; level < subdivisions; ++level) {
180 std::map<uint64_t, uint32_t> cache;
181 std::vector<Triangle> next;
182 next.reserve(faces.size() * 4u);
183 for (const Triangle &f : faces) {
184 const uint32_t ab = midpoint(f.a, f.b, vertices, cache);
185 const uint32_t bc = midpoint(f.b, f.c, vertices, cache);
186 const uint32_t ca = midpoint(f.c, f.a, vertices, cache);
187 next.insert(next.end(), {{f.a, ab, ca}, {f.b, bc, ab}, {f.c, ca, bc}, {ab, bc, ca}});
188 }
189 faces.swap(next);
190 }
191
192 std::vector<std::vector<uint32_t>> incident(vertices.size());
193 std::vector<Vec3> faceCenters;
194 faceCenters.reserve(faces.size());
195 for (uint32_t i = 0; i < faces.size(); ++i) {
196 const Triangle &f = faces[i];
197 faceCenters.push_back(normalized(add(add(vertices[f.a], vertices[f.b]), vertices[f.c])));
198 incident[f.a].push_back(i);
199 incident[f.b].push_back(i);
200 incident[f.c].push_back(i);
201 }
202
203 out.clear();
204 int pentagons = 0, hexagons = 0;
205 for (uint32_t cell = 0; cell < vertices.size(); ++cell) {
206 const Vec3 center = vertices[cell];
207 const Vec3 reference = std::fabs(center.y) < 0.9f ? normalized(cross({0, 1, 0}, center))
208 : normalized(cross({1, 0, 0}, center));
209 const Vec3 tangent = cross(center, reference);
210 auto &ring = incident[cell];
211 std::sort(ring.begin(), ring.end(), [&](uint32_t lhs, uint32_t rhs) {
212 const Vec3 a = sub(faceCenters[lhs], mul(center, dot(faceCenters[lhs], center)));
213 const Vec3 b = sub(faceCenters[rhs], mul(center, dot(faceCenters[rhs], center)));
214 return std::atan2(dot(a, tangent), dot(a, reference)) <
215 std::atan2(dot(b, tangent), dot(b, reference));
216 });
217 if (ring.size() == 5) ++pentagons;
218 else if (ring.size() == 6) ++hexagons;
219
220 const uint32_t base = uint32_t(out.getVertexCount());
221 addPlanetVertex(out, center, radius);
222 for (uint32_t faceIndex : ring) {
223 // Pull dual corners toward this cell's center. Re-normalizing keeps every tile spherical.
224 addPlanetVertex(out, normalized(add(mul(faceCenters[faceIndex], 1.f - inset),
225 mul(center, inset))), radius);
226 }
227 for (uint32_t i = 0; i < ring.size(); ++i) {
228 out.addTriangle(base, base + 1u + i, base + 1u + (i + 1u) % uint32_t(ring.size()));
229 }
230 }
231 out.setMeta("algorithm", "mesh.hexplanet");
232 out.setMeta("cells", std::to_string(vertices.size()));
233 out.setMeta("pentagons", std::to_string(pentagons));
234 out.setMeta("hexagons", std::to_string(hexagons));
235 out.setMeta("subdivisions", std::to_string(subdivisions));
236 return true;
237}
238
239bool marchingCubes(const float *density, int nx, int ny, int nz, float isolevel, MeshBuild &out,
240 std::string *error) {
241 if (!density) {
242 if (error) *error = "marchingCubes: null density";
243 return false;
244 }
245 if (nx < 2 || ny < 2 || nz < 2) {
246 if (error) *error = "marchingCubes: volume must be at least 2x2x2";
247 return false;
248 }
249
250 out.clear();
251 out.reserve((nx * ny * nz) / 2, (nx * ny * nz) * 3);
252
253 auto at = [&](int x, int y, int z) -> float {
254 return density[size_t(x) + size_t(y) * size_t(nx) + size_t(z) * size_t(nx) * size_t(ny)];
255 };
256
257 // World-space mapping: unit cube centered at origin spanning [-0.5, 0.5]^3.
258 const float sx = 1.f / float(nx - 1);
259 const float sy = 1.f / float(ny - 1);
260 const float sz = 1.f / float(nz - 1);
261
262 for (int z = 0; z < nz - 1; ++z) {
263 for (int y = 0; y < ny - 1; ++y) {
264 for (int x = 0; x < nx - 1; ++x) {
265 float val[8];
266 int cubeIndex = 0;
267 for (int i = 0; i < 8; ++i) {
268 const int cx = x + int(kCornerOffset[i][0]);
269 const int cy = y + int(kCornerOffset[i][1]);
270 const int cz = z + int(kCornerOffset[i][2]);
271 val[i] = at(cx, cy, cz);
272 if (val[i] < isolevel) cubeIndex |= (1 << i);
273 }
274 const int edges = kEdgeTable[cubeIndex];
275 if (edges == 0) continue;
276
277 float vertList[12][3];
278 for (int e = 0; e < 12; ++e) {
279 if (!(edges & (1 << e))) continue;
280 const int a = kEdgeCorners[e][0];
281 const int b = kEdgeCorners[e][1];
282 const float va = val[a];
283 const float vb = val[b];
284 float t = (isolevel - va) / (vb - va + 1e-12f);
285 t = std::clamp(t, 0.f, 1.f);
286 const float px =
287 (float(x) + lerp(kCornerOffset[a][0], kCornerOffset[b][0], t)) * sx - 0.5f;
288 const float py =
289 (float(y) + lerp(kCornerOffset[a][1], kCornerOffset[b][1], t)) * sy - 0.5f;
290 const float pz =
291 (float(z) + lerp(kCornerOffset[a][2], kCornerOffset[b][2], t)) * sz - 0.5f;
292 vertList[e][0] = px;
293 vertList[e][1] = py;
294 vertList[e][2] = pz;
295 }
296
297 for (int i = 0; kTriTable[cubeIndex][i] != -1; i += 3) {
298 const int e0 = kTriTable[cubeIndex][i];
299 const int e1 = kTriTable[cubeIndex][i + 1];
300 const int e2 = kTriTable[cubeIndex][i + 2];
301 const float *p0 = vertList[e0];
302 const float *p1 = vertList[e1];
303 const float *p2 = vertList[e2];
304
305 float ax = p1[0] - p0[0], ay = p1[1] - p0[1], az = p1[2] - p0[2];
306 float bx = p2[0] - p0[0], by = p2[1] - p0[1], bz = p2[2] - p0[2];
307 float nxn = ay * bz - az * by;
308 float nyn = az * bx - ax * bz;
309 float nzn = ax * by - ay * bx;
310 normalize3(nxn, nyn, nzn);
311
312 // Flip so normals point toward empty (lower density / outside).
313 // With cubeIndex bits for val < isolevel, winding already tends outward.
314 const uint32_t base = uint32_t(out.getVertexCount());
315 const float u0 = p0[0] + 0.5f, v0 = p0[1] + 0.5f;
316 const float u1 = p1[0] + 0.5f, v1 = p1[1] + 0.5f;
317 const float u2 = p2[0] + 0.5f, v2 = p2[1] + 0.5f;
318 out.addVertex(p0[0], p0[1], p0[2], nxn, nyn, nzn, u0, v0);
319 out.addVertex(p1[0], p1[1], p1[2], nxn, nyn, nzn, u1, v1);
320 out.addVertex(p2[0], p2[1], p2[2], nxn, nyn, nzn, u2, v2);
321 out.addTriangle(base, base + 1, base + 2);
322 }
323 }
324 }
325 }
326
327 out.setMeta("algorithm", "mesh.marchingcubes");
328 return true;
329}
330
331bool fillDensityField(const Params &params, std::vector<float> &density, int &nx, int &ny, int &nz,
332 std::string &error) {
333 const int res = params.getInt("resolution",
334 params.getWidth() > 0 ? params.getWidth() : 24);
335 nx = params.getInt("nx", res);
336 ny = params.getInt("ny", params.getHeight() > 0 ? params.getHeight() : res);
337 nz = params.getInt("nz", params.getInt("depth", res));
338 if (nx < 2 || ny < 2 || nz < 2) {
339 error = "mesh.marchingcubes: resolution must be at least 2 in each axis";
340 return false;
341 }
342 if (nx > 128 || ny > 128 || nz > 128) {
343 error = "mesh.marchingcubes: resolution capped at 128 per axis";
344 return false;
345 }
346
347 const std::string field = params.getString("field", "sphere");
348 const float scale = params.getFloat("scale", 1.f);
349 const int octaves = std::max(1, params.getInt("octaves", 3));
350 const uint32_t seed = params.getSeed();
351
352 density.assign(size_t(nx) * size_t(ny) * size_t(nz), 0.f);
353 for (int z = 0; z < nz; ++z) {
354 for (int y = 0; y < ny; ++y) {
355 for (int x = 0; x < nx; ++x) {
356 const float px = (float(x) / float(nx - 1) - 0.5f) * 2.f;
357 const float py = (float(y) / float(ny - 1) - 0.5f) * 2.f;
358 const float pz = (float(z) / float(nz - 1) - 0.5f) * 2.f;
359 float d = 0.f;
360 if (field == "sphere") {
361 const float r = params.getFloat("radius", 0.7f);
362 d = r - std::sqrt(px * px + py * py + pz * pz);
363 } else if (field == "rock") {
364 // An ellipsoid SDF whose radius is displaced by low-frequency strata and
365 // higher-frequency erosion. Quantising the direction before sampling the
366 // strata creates broad, natural fracture planes instead of a noisy sphere.
367 const float radius = params.getFloat("radius", 0.68f);
368 const float flattening =
369 std::clamp(params.getFloat("flattening", 0.22f), 0.f, 0.7f);
370 const float angularity =
371 std::clamp(params.getFloat("angularity", 0.35f), 0.f, 1.f);
372 const float erosion =
373 std::clamp(params.getFloat("erosion", 0.18f), 0.f, 0.45f);
374 const float detailScale = std::max(0.25f, scale);
375 const float sy = std::max(0.3f, 1.f - flattening);
376 const float ex = px;
377 const float ey = py / sy;
378 const float ez = pz;
379 const float len = std::sqrt(ex * ex + ey * ey + ez * ez);
380 const float invLen = len > 1e-5f ? 1.f / len : 0.f;
381 const float steps = 3.f + angularity * 9.f;
382 const float qx = std::round(ex * invLen * steps) / steps;
383 const float qy = std::round(ey * invLen * steps) / steps;
384 const float qz = std::round(ez * invLen * steps) / steps;
385 const float strata = fbm3((qx + 2.3f) * detailScale,
386 (qy + 4.7f) * detailScale,
387 (qz + 8.1f) * detailScale, seed, octaves);
388 const float pits = fbm3((px + 7.2f) * detailScale * 2.7f,
389 (py + 1.9f) * detailScale * 2.7f,
390 (pz + 5.4f) * detailScale * 2.7f,
391 seed + 7919u, std::max(2, octaves - 1));
392 const float displacement = strata * (0.08f + angularity * 0.16f) -
393 std::max(0.f, pits) * erosion;
394 d = radius + displacement - len;
395 } else if (field == "torus") {
396 const float R = params.getFloat("majorRadius", 0.55f);
397 const float r = params.getFloat("minorRadius", 0.22f);
398 const float q = std::sqrt(px * px + pz * pz) - R;
399 d = r - std::sqrt(q * q + py * py);
400 } else if (field == "terrain") {
401 const float h =
402 fbm3(px * scale + 3.1f, 0.f, pz * scale + 1.7f, seed, octaves) * 0.45f;
403 d = h - py;
404 } else if (field == "noise") {
405 const float n =
406 fbm3(px * scale + 2.f, py * scale + 5.f, pz * scale + 9.f, seed, octaves);
407 d = n - params.getFloat("threshold", 0.05f);
408 } else {
409 error = "mesh.marchingcubes: unknown field '" + field +
410 "' (use sphere|rock|torus|noise|terrain)";
411 return false;
412 }
413 // Soft boundary falloff so surfaces close.
414 const float margin = 0.92f;
415 const float bx = std::max(0.f, std::fabs(px) - margin);
416 const float by = std::max(0.f, std::fabs(py) - margin);
417 const float bz = std::max(0.f, std::fabs(pz) - margin);
418 d -= (bx * bx + by * by + bz * bz) * 4.f;
419 density[size_t(x) + size_t(y) * size_t(nx) + size_t(z) * size_t(nx) * size_t(ny)] =
420 d;
421 }
422 }
423 }
424 return true;
425}
426
427bool generateMarchingCubesMesh(const Params &params, MeshBuild &out, std::string &error) {
428 std::vector<float> density;
429 int nx = 0, ny = 0, nz = 0;
430 if (!fillDensityField(params, density, nx, ny, nz, error)) return false;
431 const float isolevel = params.getFloat("isolevel", 0.f);
432 if (!marchingCubes(density.data(), nx, ny, nz, isolevel, out, &error)) return false;
433 out.setMeta("field", params.getString("field", "sphere"));
434 if (out.empty()) {
435 error = "mesh.marchingcubes: empty mesh (adjust field/isolevel/resolution)";
436 return false;
437 }
438 return true;
439}
440
441MeshRecipeRegistry &MeshRecipeRegistry::instance() {
442 static MeshRecipeRegistry reg;
443 return reg;
444}
445
446void MeshRecipeRegistry::registerRecipe(const std::string &id, MeshRecipeFn fn) {
447 recipes_[id] = std::move(fn);
448}
449
450bool MeshRecipeRegistry::has(const std::string &id) const {
451 return recipes_.find(id) != recipes_.end();
452}
453
454bool MeshRecipeRegistry::generate(const std::string &id, const Params &params, MeshBuild &out,
455 std::string &error) const {
456 auto it = recipes_.find(id);
457 if (it == recipes_.end()) {
458 error = "unknown mesh recipe '" + id + "'";
459 return false;
460 }
461 return it->second(params, out, error);
462}
463
464std::vector<std::string> MeshRecipeRegistry::list() const {
465 std::vector<std::string> ids;
466 ids.reserve(recipes_.size());
467 for (const auto &kv : recipes_) ids.push_back(kv.first);
468 std::sort(ids.begin(), ids.end());
469 return ids;
470}
471
472void MeshRecipeRegistry::registerBuiltins() {
473 if (builtinsRegistered_) return;
474 registerRecipe("mesh.marchingcubes", generateMarchingCubesMesh);
475 registerRecipe("mesh.rock", generateRockMesh);
476 registerRecipe("mesh.hexplanet", generateHexPlanetMesh);
477 registerRecipe("mesh.tree", generateTreeMesh);
478 registerRecipe("mesh.bush", generateBushMesh);
479 registerRecipe("mesh.skyscraper", generateSkyscraperMesh);
481 builtinsRegistered_ = true;
482}
483
484} // namespace eve::procgen
float cx
Definition CardTypes.cpp:31
float cy
Definition CardTypes.cpp:32
uint32_t seed
std::string id
int y
Definition Grass.cpp:135
int z
Definition Grass.cpp:135
float u
Definition Grass.cpp:234
int x
Definition Grass.cpp:135
glm::vec3 n
Definition Grass.cpp:64
int h
int w
std::vector< Colorf > px
std::string error
uint32_t a
uint32_t b
float y
uint32_t c
float x
float z
float f
glm::vec4 p[6]
SettlementPipeline::Stage fn
int d
int v
int margin
float scale
Definition TreeMesh.cpp:122
uint32_t s
Definition Weather.cpp:28
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
Definition MeshBuild.h:14
void addVertex(float px, float py, float pz, float nx, float ny, float nz, float u, float v)
Definition MeshBuild.cpp:22
void addTriangle(uint32_t i0, uint32_t i1, uint32_t i2)
Definition MeshBuild.cpp:34
void reserve(int vertexCount, int indexCount)
Definition MeshBuild.cpp:13
void setMeta(const std::string &key, const std::string &value)
Definition MeshBuild.cpp:81
int getVertexCount() const
Definition MeshBuild.cpp:40
Generation parameters. Algorithm-specific keys live in values as strings (no overloads; typed setters...
Definition Params.h:13
uint32_t getSeed() const
Definition Params.cpp:8
int getHeight() const
Definition Params.cpp:15
int getWidth() const
Definition Params.cpp:14
float getFloat(const std::string &key, float defaultValue) const
Definition Params.cpp:32
std::string getString(const std::string &key, const std::string &defaultValue) const
Definition Params.cpp:41
int getInt(const std::string &key, int defaultValue) const
Definition Params.cpp:23
bool fillDensityField(const Params &params, std::vector< float > &density, int &nx, int &ny, int &nz, std::string &error)
Fill a density volume from a named field recipe (sphere / noise / terrain / torus).
bool generateHexPlanetMesh(const Params &params, MeshBuild &out, std::string &error)
Build the dual of a subdivided icosahedron. The result is a closed planet made of hexagonal cells plu...
std::function< bool(const Params &params, MeshBuild &out, std::string &error)> MeshRecipeFn
bool marchingCubes(const float *density, int nx, int ny, int nz, float isolevel, MeshBuild &out, std::string *error)
Classic Marching Cubes (Lorensen & Cline) over a regular scalar volume. Density >= isolevel is treate...
bool generateBushMesh(const Params &params, MeshBuild &out, std::string &error)
Build a deterministic procedural small bush. Registered as the mesh.bush recipe.
Definition BushMesh.cpp:127
void registerLinearStructureRecipes(MeshRecipeRegistry &registry)
Register all built-in linear structure mesh recipes into a registry.
bool generateTreeMesh(const Params &params, MeshBuild &out, std::string &error)
Build a deterministic procedural tree. Registered as the mesh.tree recipe.
Definition TreeMesh.cpp:369
bool generateMarchingCubesMesh(const Params &params, MeshBuild &out, std::string &error)
Build mesh from Params (field + resolution + isolevel).
bool generateRockMesh(const Params &params, MeshBuild &out, std::string &error)
Build a shared-vertex, deformed icosphere rock for economical game props.
Definition RockMesh.cpp:165
bool generateSkyscraperMesh(const Params &params, MeshBuild &out, std::string &error)
Build a deterministic procedural skyscraper. Registered as the mesh.skyscraper recipe.