41uint32_t
hash(uint32_t
x) {
49float valueNoise(
float x,
float y, uint32_t
seed) {
50 const int ix = int(std::floor(
x));
51 const int iy = int(std::floor(
y));
52 const float fx =
x - float(ix);
53 const float fy =
y - float(iy);
54 const float sx = fx * fx * (3.f - 2.f * fx);
55 const float sy = fy * fy * (3.f - 2.f * fy);
57 const uint32_t
h =
hash(uint32_t(
px) * 0x9e3779b9u ^ uint32_t(
py) * 0x85ebca6bu ^
seed);
58 return float(
h & 0xffffu) / 32767.5f - 1.f;
61 const float b = std::lerp(
sample(ix, iy + 1),
sample(ix + 1, iy + 1),
sx);
62 return std::lerp(
a,
b,
sy);
65float fbm(
float x,
float y, uint32_t
seed) {
66 float value = 0.f, amplitude = 0.55f, frequency = 1.f, norm = 0.f;
67 for (
int octave = 0; octave < 5; ++octave) {
68 value += valueNoise(
x * frequency,
y * frequency,
seed + uint32_t(octave) * 1013u) * amplitude;
80std::array<int, 6> neighbours(
int q,
int r,
int width,
int height) {
81 static constexpr int dq[6] = {1, 0, -1, -1, 0, 1};
82 const int parity =
q & 1;
83 const int dr[6] = {parity, 1, parity, parity - 1, -1, parity - 1};
84 std::array<int, 6> result{};
85 for (
int i = 0; i < 6; ++i) result[i] = indexOf(
q + dq[i],
r + dr[i],
width,
height);
89void addRiverArm(MeshBuild& out,
float cx,
float y,
float cz,
float radius,
int edge,
90 float coverage, uint32_t shapeSeed) {
91 const float angle = 1.0471975512f * (float(
edge) + 0.5f);
94 const float ez = cz +
dz *
radius * 0.94f;
95 const float bend = (float(
hash(shapeSeed ^ uint32_t(
edge) * 0x9e3779b9u) & 0xffffu) /
96 65535.f * 2.f - 1.f) *
radius * 0.30f;
98 const float mz = cz +
dz *
radius * 0.48f +
dx * bend;
99 const float u = float(
River) + 0.04f;
100 const float v = float(
River) + std::clamp(coverage, 0.f, 0.999f);
101 const uint32_t base = uint32_t(out.getVertexCount());
102 constexpr int curveSegments = 6;
103 for (
int segment = 0; segment <= curveSegments; ++segment) {
104 const float t = float(segment) / float(curveSegments);
105 const float omt = 1.f -
t;
106 const float x = omt * omt *
cx + 2.f * omt *
t * mx +
t *
t * ex;
107 const float z = omt * omt * cz + 2.f * omt *
t * mz +
t *
t * ez;
108 float tx = 2.f * omt * (mx -
cx) + 2.f *
t * (ex - mx);
109 float tz = 2.f * omt * (mz - cz) + 2.f *
t * (ez - mz);
110 const float invLength = 1.f / std::max(0.0001f, std::sqrt(tx * tx + tz * tz));
114 (0.82f + 0.18f * std::sin(
t * 3.1415926536f));
115 const float px = -tz,
pz = tx;
117 0.f, 1.f, 0.f,
u,
v);
119 0.f, 1.f, 0.f,
u,
v);
121 for (
int segment = 0; segment < curveSegments; ++segment) {
122 const uint32_t
a = base + uint32_t(segment * 2);
123 out.addTriangle(
a,
a + 1u,
a + 3u);
124 out.addTriangle(
a,
a + 3u,
a + 2u);
128void addRiverJunction(MeshBuild& out,
float cx,
float y,
float cz,
float radius,
130 constexpr int segments = 12;
131 const float junctionRadius =
radius * (0.105f + coverage * 0.045f);
132 const float u = float(
River) + 0.04f;
133 const float v = float(
River) + std::clamp(coverage, 0.f, 0.999f);
134 const uint32_t
center = uint32_t(out.getVertexCount());
135 out.addVertex(
cx,
y, cz, 0.f, 1.f, 0.f,
u,
v);
136 for (
int k = 0; k < segments; ++k) {
137 const float angle = 6.2831853072f * float(k) / float(segments);
138 out.addVertex(
cx + std::cos(
angle) * junctionRadius,
y,
139 cz + std::sin(
angle) * junctionRadius, 0.f, 1.f, 0.f,
u,
v);
141 for (
int k = 0; k < segments; ++k)
143 center + 1u + uint32_t((k + 1) % segments));
146void addShoreBand(MeshBuild& out,
float cx,
float y,
float cz,
float radius,
int edge) {
147 const float a0 = 1.0471975512f * float(
edge);
148 const float a1 = 1.0471975512f * float((
edge + 1) % 6);
149 constexpr float innerScale = 0.89f;
150 const float u = float(Coast) + 0.92f;
151 const float v = float(
Ocean) + 0.15f;
152 const uint32_t base = uint32_t(out.getVertexCount());
153 out.addVertex(
cx + std::cos(a0) *
radius,
y, cz + std::sin(a0) *
radius,
154 0.f, 1.f, 0.f,
u,
v);
155 out.addVertex(
cx + std::cos(a1) *
radius,
y, cz + std::sin(a1) *
radius,
156 0.f, 1.f, 0.f,
u,
v);
157 out.addVertex(
cx + std::cos(a1) *
radius * innerScale,
y,
158 cz + std::sin(a1) *
radius * innerScale, 0.f, 1.f, 0.f,
u,
v);
159 out.addVertex(
cx + std::cos(a0) *
radius * innerScale,
y,
160 cz + std::sin(a0) *
radius * innerScale, 0.f, 1.f, 0.f,
u,
v);
161 out.addTriangle(base, base + 1u, base + 2u);
162 out.addTriangle(base, base + 2u, base + 3u);
165void classify(Cell&
c,
float sea) {
166 if (
c.elevation < sea - 0.22f)
c.biome = DeepOcean;
167 else if (
c.temperature < 0.16f)
c.biome =
Ice;
168 else if (
c.elevation < sea - 0.045f)
c.biome =
Ocean;
169 else if (
c.elevation < sea + 0.025f)
c.biome = Coast;
170 else if (
c.elevation > sea + 0.55f)
c.biome = Mountain;
171 else if (
c.elevation > sea + 0.35f)
c.biome = Hills;
172 else if (
c.moisture > 0.68f &&
c.elevation < sea + 0.16f)
c.biome = Swamp;
173 else if (
c.moisture > 0.64f &&
c.temperature > 0.55f)
c.biome =
Rainforest;
174 else if (
c.moisture > 0.56f)
c.biome =
Forest;
176 c.secondary =
c.biome;
179float surfaceHeight(
const Cell&
c,
float sea,
float heightScale) {
180 float y = (
c.elevation - sea) * heightScale;
181 if (
c.lake)
return y - 0.035f * heightScale;
182 if (
c.biome == DeepOcean ||
c.biome ==
Ocean)
183 return std::min(
y, -0.08f * heightScale);
185 const float land = std::max(0.f,
c.elevation - sea);
186 if (
c.biome == Hills)
y += (0.08f + land * 0.16f) * heightScale;
187 if (
c.biome == Mountain)
y += (0.22f + land * 0.38f) * heightScale;
188 if (
c.biome == Ice && land > 0.28f)
y += land * 0.22f * heightScale;
189 if (
c.river > 0.f)
y -= (0.045f + 0.035f *
c.river) * heightScale;
193void addCone(MeshBuild& out,
float cx,
float baseY,
float cz,
float radius,
float height,
194 int biome,
int segments = 6, uint32_t shapeSeed = 0) {
195 const uint32_t base = uint32_t(out.getVertexCount());
197 const float u = float(
biome) + 0.92f;
198 const float v = float(
biome);
199 for (
int k = 0; k < segments; ++k) {
200 const float a = 6.2831853072f * float(k) / float(segments);
201 const float nx = std::cos(
a),
nz = std::sin(
a);
202 const float radial = shapeSeed == 0 ? 1.f :
203 0.78f + float(
hash(shapeSeed + uint32_t(k) * 0x85ebca6bu) & 0xffffu) /
208 const float apexX = shapeSeed == 0 ?
cx :
cx +
209 (float(
hash(shapeSeed ^ 0x41c64e6du) & 0xffffu) / 65535.f - 0.5f) *
radius * 0.34f;
210 const float apexZ = shapeSeed == 0 ? cz : cz +
211 (float(
hash(shapeSeed ^ 0xc2b2ae35u) & 0xffffu) / 65535.f - 0.5f) *
radius * 0.34f;
212 if (shapeSeed != 0) {
213 const uint32_t shoulder = uint32_t(out.getVertexCount());
214 for (
int k = 0; k < segments; ++k) {
215 const float a = 6.2831853072f * (float(k) + 0.16f) /
float(segments);
216 const float nx = std::cos(
a),
nz = std::sin(
a);
217 const float radial = 0.43f +
218 float(
hash(shapeSeed ^ 0xa511e9b3u ^ uint32_t(k) * 0x27d4eb2du) & 0xffffu) /
220 out.addVertex(std::lerp(
cx, apexX, 0.34f) +
nx *
radius * radial,
221 baseY +
height * (0.43f + radial * 0.16f),
222 std::lerp(cz, apexZ, 0.34f) +
nz *
radius * radial,
223 nx * 0.88f, 0.34f,
nz * 0.88f,
u,
v);
225 const uint32_t apex = uint32_t(out.getVertexCount());
226 out.addVertex(apexX, baseY +
height, apexZ, 0.f, 1.f, 0.f,
u,
v);
227 for (
int k = 0; k < segments; ++k) {
228 const uint32_t
next = uint32_t((k + 1) % segments);
229 const uint32_t lower = base + uint32_t(k);
230 const uint32_t lowerNext = base +
next;
231 const uint32_t upper = shoulder + uint32_t(k);
232 const uint32_t upperNext = shoulder +
next;
233 out.addTriangle(lower, lowerNext, upperNext);
234 out.addTriangle(lower, upperNext, upper);
235 out.addTriangle(upper, upperNext, apex);
239 const uint32_t apex = uint32_t(out.getVertexCount());
240 out.addVertex(apexX, baseY +
height, apexZ, 0.f, 1.f, 0.f,
u,
v);
241 for (
int k = 0; k < segments; ++k)
242 out.addTriangle(base + uint32_t(k), base + uint32_t((k + 1) % segments), apex);
245void addTrunk(MeshBuild& out,
float cx,
float y,
float cz,
float radius,
float height) {
246 constexpr int segments = 5;
247 const uint32_t base = uint32_t(out.getVertexCount());
248 const float u = float(Cliff) + 0.12f;
250 for (
int level = 0;
level < 2; ++
level)
for (
int k = 0; k < segments; ++k) {
251 const float a = 6.2831853072f * float(k) / float(segments);
252 const float nx = std::cos(
a),
nz = std::sin(
a);
256 for (
int k = 0; k < segments; ++k) {
257 const uint32_t
next = uint32_t((k + 1) % segments);
258 out.addTriangle(base + uint32_t(k), base + next, base + uint32_t(segments + k));
259 out.addTriangle(base + next, base + uint32_t(segments) + next,
260 base + uint32_t(segments + k));
264void addCrownBlob(MeshBuild& out,
float cx,
float y,
float cz,
float radius,
float height,
265 int biome,
int segments = 7) {
266 const float u = float(
biome) + 0.92f;
267 const float v = float(
biome);
268 const uint32_t
bottom = uint32_t(out.getVertexCount());
269 out.addVertex(
cx,
y, cz, 0.f, -1.f, 0.f,
u,
v);
270 const uint32_t ring = uint32_t(out.getVertexCount());
271 for (
int k = 0; k < segments; ++k) {
272 const float a = 6.2831853072f * float(k) / float(segments);
273 const float nx = std::cos(
a),
nz = std::sin(
a);
277 const uint32_t
top = uint32_t(out.getVertexCount());
278 out.addVertex(
cx,
y +
height, cz, 0.f, 1.f, 0.f,
u,
v);
279 for (
int k = 0; k < segments; ++k) {
280 const uint32_t
a = ring + uint32_t(k);
281 const uint32_t
b = ring + uint32_t((k + 1) % segments);
283 out.addTriangle(
a,
b,
top);
287void addTree(MeshBuild& out,
float x,
float y,
float z,
float scale,
int biome,
306void addTop(MeshBuild& out,
float cx,
float cz,
float y,
float radius,
const Cell&
c) {
307 const uint32_t
center = uint32_t(out.getVertexCount());
308 const float u = float(
c.biome) + std::clamp(
c.blend, 0.f, 0.999f);
309 const float v = float(
c.secondary) + std::clamp(
c.river, 0.f, 0.999f);
310 out.addVertex(
cx,
y, cz, 0.f, 1.f, 0.f,
u,
v);
311 for (
int k = 0; k < 6; ++k) {
312 const float a = 1.0471975512f * float(k);
314 0.f, 1.f, 0.f,
u,
v);
316 for (
int k = 0; k < 6; ++k)
317 out.addTriangle(
center,
center + 1u + uint32_t(k),
center + 1u + uint32_t((k + 1) % 6));
321 const Cell&
c, uint32_t shapeSeed) {
322 const float a0 = 1.0471975512f * float(
edge);
323 const float a1 = 1.0471975512f * float((
edge + 1) % 6);
324 const float x0 =
cx + std::cos(a0) *
radius, z0 = cz + std::sin(a0) *
radius;
325 const float x1 =
cx + std::cos(a1) *
radius, z1 = cz + std::sin(a1) *
radius;
326 const float nx = std::sin((a0 + a1) * 0.5f),
nz = -std::cos((a0 + a1) * 0.5f);
327 const float relief = std::abs(
top -
bottom);
328 const bool rockFace = relief >
radius * 0.42f;
329 const float u = rockFace ? float(Cliff) + std::min(0.999f, relief)
331 const float v = rockFace ? float(
c.biome)
333 constexpr int wallSegments = 5;
334 std::array<float, wallSegments + 1> topX{}, topY{}, topZ{}, bottomX{}, bottomY{}, bottomZ{};
335 for (
int segment = 0; segment <= wallSegments; ++segment) {
336 const float t = float(segment) / float(wallSegments);
337 const float straightX = std::lerp(x0, x1,
t);
338 const float straightZ = std::lerp(z0, z1,
t);
339 const uint32_t
h =
hash(shapeSeed + uint32_t(segment) * 0x9e3779b9u);
340 const float endMask = segment == 0 || segment == wallSegments ? 0.f : 1.f;
341 const float lipScale = rockFace ? 0.26f : 0.12f;
342 const float footScale = rockFace ? 0.34f : 0.16f;
343 const float lipOffset = (float(
h & 0xffffu) / 65535.f - 0.42f) *
344 radius * lipScale * endMask;
345 const float footOffset = (float((
h >> 16) & 0xffffu) / 65535.f - 0.5f) *
346 radius * footScale * endMask;
347 topX[size_t(segment)] = straightX +
nx * lipOffset;
348 topY[size_t(segment)] =
top + (rockFace ?
349 (float(
hash(
h ^ 0x165667b1u) & 0xffffu) / 65535.f - 0.38f) *
350 radius * 0.22f * endMask : 0.f);
351 topZ[size_t(segment)] = straightZ +
nz * lipOffset;
352 bottomX[size_t(segment)] = straightX +
nx * footOffset;
353 bottomY[size_t(segment)] =
bottom + (rockFace ?
354 (float(
hash(
h ^ 0xd3a2646cu) & 0xffffu) / 65535.f) *
355 std::min(relief * 0.16f,
radius * 0.16f) * endMask : 0.f);
356 bottomZ[size_t(segment)] = straightZ +
nz * footOffset;
358 for (
int segment = 0; segment < wallSegments; ++segment) {
359 const uint32_t
h =
hash(shapeSeed ^ uint32_t(segment) * 0x85ebca6bu);
360 const float shadeTurn = (float(
h & 0xffffu) / 65535.f - 0.5f) * 0.34f;
361 const float snx =
nx + std::cos(a1) * shadeTurn;
362 const float snz =
nz + std::sin(a1) * shadeTurn;
363 const uint32_t base = uint32_t(out.getVertexCount());
364 out.addVertex(topX[
size_t(segment)], topY[
size_t(segment)], topZ[
size_t(segment)], snx, 0.f, snz,
u,
v);
365 out.addVertex(topX[
size_t(segment + 1)], topY[
size_t(segment + 1)], topZ[
size_t(segment + 1)], snx, 0.f, snz,
u,
v);
366 out.addVertex(bottomX[
size_t(segment + 1)], bottomY[
size_t(segment + 1)], bottomZ[
size_t(segment + 1)], snx, 0.f, snz,
u,
v);
367 out.addVertex(bottomX[
size_t(segment)], bottomY[
size_t(segment)], bottomZ[
size_t(segment)], snx, 0.f, snz,
u,
v);
368 out.addTriangle(base, base + 1u, base + 2u);
369 out.addTriangle(base, base + 2u, base + 3u);
378 const int riverCount =
params.getInt(
"riverCount", 8);
379 const uint32_t
seed = uint32_t(
params.getInt(
"seed", 1));
381 const float sea =
params.getFloat(
"seaLevel", 0.43f);
382 const float heightScale =
params.getFloat(
"heightScale", 4.f);
383 const bool decorations =
params.getBool(
"decorations",
true);
384 const float vegetationDensity =
params.getFloat(
"vegetationDensity", 1.f);
385 if (
width < 2 || height < 2 || width > 256 ||
height > 256) {
386 error =
"mesh.hexterrain: width and height must be in [2, 256]";
389 if (!(
radius > 0.f) || !(heightScale > 0.f) || sea < 0.f || sea > 1.f || riverCount < 0 ||
390 vegetationDensity < 0.f || vegetationDensity > 2.f) {
391 error =
"mesh.hexterrain: invalid radius, heightScale, seaLevel or riverCount";
398 const float nx = (float(
q) / float(
width - 1)) * 2.f - 1.f;
399 const float ny = (float(
r) / float(
height - 1)) * 2.f - 1.f;
400 const float continental = 1.f - std::pow(std::min(1.f, std::sqrt(
nx *
nx +
ny *
ny)), 1.7f);
401 c.elevation = std::clamp(0.12f + continental * 0.78f + fbm(
q * 0.085f,
r * 0.085f,
seed) * 0.22f, 0.f, 1.f);
402 c.moisture = std::clamp(0.52f + fbm(
q * 0.11f,
r * 0.11f,
seed ^ 0x51f15e5du) * 0.42f, 0.f, 1.f);
403 const float latitude = std::abs(
ny);
404 c.temperature = std::clamp(1.f - latitude * 0.92f - std::max(0.f,
c.elevation - sea) * 0.48f +
405 fbm(
q * 0.06f,
r * 0.06f,
seed ^ 0xa53a9d1bu) * 0.1f, 0.f, 1.f);
410 std::vector<int> riverSources;
412 std::vector<uint8_t> visited(
size_t(
width *
height), 0);
415 float bestSourceScore = -std::numeric_limits<float>::max();
416 for (
int attempt = 0; attempt <
width *
height; ++attempt) {
417 const int candidate = int(rng() % uint32_t(
width *
height));
420 const int candidateQ = candidate %
width;
421 const int candidateR = candidate /
width;
422 for (
int source : riverSources) {
425 sourceDistance = std::min(sourceDistance, std::sqrt(
float(dq * dq + dr * dr)));
427 const float sourceScore =
cells[size_t(candidate)].elevation + sourceDistance * 0.014f;
428 if (sourceScore > bestSourceScore) {
429 bestSourceScore = sourceScore;
436 if (
c.elevation <= sea + 0.02f)
break;
438 c.river = std::max(
c.river, std::min(0.95f, 0.35f +
float(
step) * 0.025f));
442 float bestScore = std::numeric_limits<float>::max();
446 const int n = ns[size_t(
edge)];
447 if (
n >= 0 && !visited[
size_t(
n)]) {
449 const float routeNoise = float(
hash(
seed ^ uint32_t(
current) * 0x9e3779b9u ^
450 uint32_t(
edge) * 0x85ebca6bu) & 0xffffu) /
452 score += (routeNoise - 0.5f) * 0.055f;
454 const int delta = (
edge - lastEdge + 6) % 6;
455 const int turnSign = ((
step / 3 +
river) & 1) == 0 ? 1 : -1;
456 const int preferredEdge = (lastEdge + turnSign + 6) % 6;
458 if (delta == 3)
score += 0.30f;
459 if (
edge == preferredEdge)
score -= 0.060f;
462 if (
score >= bestScore)
continue;
469 if (
cells[
size_t(next)].elevation >=
c.elevation + 0.075f &&
step >= 5 &&
470 c.elevation > sea + 0.10f) {
474 c.river = std::max(
c.river, 0.72f);
478 cells[size_t(next)].elevation = std::max(sea + 0.01f,
c.elevation - 0.018f);
479 c.riverEdges |= uint8_t(1u << uint32_t(nextEdge));
480 cells[size_t(next)].riverEdges |= uint8_t(1u << uint32_t((nextEdge + 3) % 6));
489 float bestDifference = 0.f;
490 for (
int n : ns)
if (
n >= 0 &&
cells[
size_t(
n)].biome !=
c.biome) {
491 const float difference = std::abs(
c.elevation -
cells[
size_t(
n)].elevation);
492 if (difference >= bestDifference) {
493 bestDifference = difference;
494 c.secondary =
cells[size_t(
n)].biome;
495 c.blend = std::clamp(0.18f + (1.f - difference) * 0.32f, 0.f, 0.49f);
498 if (
c.river > 0.f)
c.secondary =
River;
503 std::array<int, 12> biomeCounts{};
504 int cliffEdgeCount = 0;
505 int riverCellCount = 0;
506 int lakeCellCount = 0;
507 const float xStep =
radius * 1.5f, zStep =
radius * 1.7320508076f;
511 const float cx = float(
q) * xStep;
512 const float cz = (float(
r) + float(
q & 1) * 0.5f) * zStep;
513 const float y = surfaceHeight(
c, sea, heightScale);
515 ++biomeCounts[size_t(std::clamp(
c.biome, 0, 11))];
516 if (
c.riverEdges != 0) ++riverCellCount;
517 if (
c.lake) ++lakeCellCount;
519 if (
c.riverEdges != 0) {
522 if ((
c.riverEdges & uint8_t(1u << uint32_t(
edge))) != 0)
524 const int neighbourIndex = ns[size_t(
edge)];
525 const int safeNeighbour = neighbourIndex >= 0 ? neighbourIndex :
idx;
526 const uint32_t lo = uint32_t(std::min(
idx, safeNeighbour));
527 const uint32_t hi = uint32_t(std::max(
idx, safeNeighbour));
529 hash(
seed ^ lo * 0x27d4eb2du ^ hi * 0x85ebca6bu));
533 float neighbourY = -sea * heightScale;
534 if (ns[
size_t(
edge)] >= 0) {
535 const Cell& nc =
cells[size_t(ns[
size_t(
edge)])];
536 neighbourY = surfaceHeight(nc, sea, heightScale);
538 if (
y > neighbourY + 0.025f) {
544 if (
c.biome == Coast && ns[
size_t(
edge)] >= 0 &&
550 const uint32_t cellSeed =
hash(
seed ^ uint32_t(
idx) * 0x9e3779b9u);
552 const int count = int(std::round(
float(baseCount) * vegetationDensity));
553 for (
int tree = 0; tree <
count; ++tree) {
554 const uint32_t
h =
hash(cellSeed + uint32_t(tree) * 0x85ebca6bu);
555 const float a = float(
h & 0xffffu) / 65535.f * 6.2831853072f;
556 const float d = (0.16f + float((
h >> 16) & 0xffu) / 255.f * 0.36f) *
radius;
557 const float variation = 0.82f + float((
h >> 24) & 0xffu) / 255.f * 0.38f;
558 const float scale =
radius * variation * (
c.biome == Swamp ? 0.75f : 1.25f);
559 const int treeBiome =
c.biome == Swamp ?
Forest :
c.biome;
560 const bool broadleaf =
c.biome !=
Forest || ((
h >> 12) & 1u) != 0;
561 addTree(out,
cx + std::cos(
a) *
d,
y, cz + std::sin(
a) *
d,
scale,
562 treeBiome, broadleaf);
564 }
else if (decorations &&
c.biome == Mountain) {
565 const uint32_t mountainSeed =
hash(
seed ^ uint32_t(
idx) * 0x27d4eb2du);
566 const float mainHeight =
radius *
567 (1.20f + float(mountainSeed & 0xffu) / 255.f * 0.58f);
568 const float mainRadius =
radius *
569 (0.48f + float((mountainSeed >> 8) & 0xffu) / 255.f * 0.12f);
570 const float angle = float((mountainSeed >> 16) & 0xffffu) / 65535.f *
573 cz - std::sin(
angle) *
radius * 0.08f, mainRadius, mainHeight,
574 Mountain, 8, mountainSeed);
577 mainHeight * 0.64f, Mountain, 7, mountainSeed ^ 0x68bc21ebu);
578 if (((mountainSeed >> 30) & 1u) != 0)
582 radius * 0.24f, mainHeight * 0.48f, Mountain, 6,
583 mountainSeed ^ 0x02e5be93u);
586 out.
setMeta(
"algorithm",
"mesh.hexterrain");
587 out.
setMeta(
"uvEncoding",
"u=primaryBiome+blend,v=secondaryBiome+riverCoverage");
588 out.
setMeta(
"biomeCount",
"12");
589 out.
setMeta(
"shoreGeometry",
"edge-bands");
590 out.
setMeta(
"hydrology",
"drainage-rivers+basin-lakes+confluences");
591 out.
setMeta(
"riverGeometry",
"seeded-quadratic-ribbons");
592 out.
setMeta(
"cliffGeometry",
"seeded-segmented-rock-walls");
593 out.
setMeta(
"mountainGeometry",
"seeded-offset-three-ring-peaks");
594 static constexpr const char* biomeNames[10] = {
595 "deepOcean",
"ocean",
"coast",
"grassland",
"hills",
596 "mountain",
"forest",
"swamp",
"rainforest",
"ice",
600 std::to_string(biomeCounts[
size_t(
biome)]));
601 out.
setMeta(
"cells.river", std::to_string(riverCellCount));
602 out.
setMeta(
"cells.lake", std::to_string(lakeCellCount));
603 out.
setMeta(
"edges.cliff", std::to_string(cliffEdgeCount));
std::array< std::uint8_t, 32 > hash
std::array< double, 10 > q
std::array< float, 3 > scale
std::array< PixelCell, kPixelChunkSize *kPixelChunkSize > cells
const UnitySourceAsset & source
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
void reserve(int vertexCount, int indexCount)
Reserve.
void setMeta(const std::string &key, const std::string &value)
Sets the meta.
Owning, typed generation parameters.
std::vector< ParamSpec > params
@ Cell
A cell was removed; the out-parameter holds it.
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
double sample(const Heightmap &map, double u, double v)
Sample.
bool generateHexTerrainMesh(const Params ¶ms, MeshBuild &out, std::string &error)
Build a deterministic, flat-top hexagonal continent mesh.