载入中...
搜索中...
未找到
HexSphereGenerator.cpp
浏览该文件的文档.
2
3#include "common/Diagnostic.h"
4#include "hexmap/HexCell.h"
5#include "hexmap/HexMetrics.h"
6
7#include <algorithm>
8#include <cmath>
9#include <cstdint>
10#include <string>
11#include <vector>
12
13namespace eve::hexmap {
14namespace {
15
16// --- 3D value noise ---------------------------------------------------------
17//
18// `HexNoise` is a planar field sampled in XZ. Sampling it with a cell's direction
19// would degenerate at the poles - the direction's XZ projection goes to zero there -
20// and would seam where the longitude wraps, so the spherical generator carries its
21// own lattice. It is the spherical analogue of the planar field, not a second
22// source of truth for it: nothing else in the module samples it.
23
25[[nodiscard]] std::uint32_t hashLattice(int x, int y, int z, std::uint32_t seed) noexcept {
26 std::uint32_t h = seed ^ 0x9e3779b9u;
27 h ^= static_cast<std::uint32_t>(x) * 0x8da6b343u;
28 h ^= static_cast<std::uint32_t>(y) * 0xd8163841u;
29 h ^= static_cast<std::uint32_t>(z) * 0xcb1ab31fu;
30 h ^= h >> 15;
31 h *= 0x2c1b3c6du;
32 h ^= h >> 12;
33 h *= 0x297a2d39u;
34 h ^= h >> 15;
35 return h;
36}
37
39[[nodiscard]] float latticeValue(int x, int y, int z, std::uint32_t seed) noexcept {
40 return static_cast<float>(hashLattice(x, y, z, seed) & 0xffffffu) / 8388608.0f - 1.f;
41}
42
44[[nodiscard]] float fade(float t) noexcept { return t * t * t * (t * (t * 6.f - 15.f) + 10.f); }
45
46[[nodiscard]] float valueNoise3(HexVec3 point, std::uint32_t seed) noexcept {
47 const float fx = std::floor(point.x);
48 const float fy = std::floor(point.y);
49 const float fz = std::floor(point.z);
50 const int x0 = static_cast<int>(fx);
51 const int y0 = static_cast<int>(fy);
52 const int z0 = static_cast<int>(fz);
53 const float tx = fade(point.x - fx);
54 const float ty = fade(point.y - fy);
55 const float tz = fade(point.z - fz);
56
57 const float c000 = latticeValue(x0, y0, z0, seed);
58 const float c100 = latticeValue(x0 + 1, y0, z0, seed);
59 const float c010 = latticeValue(x0, y0 + 1, z0, seed);
60 const float c110 = latticeValue(x0 + 1, y0 + 1, z0, seed);
61 const float c001 = latticeValue(x0, y0, z0 + 1, seed);
62 const float c101 = latticeValue(x0 + 1, y0, z0 + 1, seed);
63 const float c011 = latticeValue(x0, y0 + 1, z0 + 1, seed);
64 const float c111 = latticeValue(x0 + 1, y0 + 1, z0 + 1, seed);
65
66 const auto blend = [](float a, float b, float t) { return a + (b - a) * t; };
67 const float x00 = blend(c000, c100, tx);
68 const float x10 = blend(c010, c110, tx);
69 const float x01 = blend(c001, c101, tx);
70 const float x11 = blend(c011, c111, tx);
71 return blend(blend(x00, x10, ty), blend(x01, x11, ty), tz);
72}
73
75[[nodiscard]] float fbm3(HexVec3 point, std::int32_t octaves, std::uint32_t seed) noexcept {
76 float total = 0.f;
77 float amplitude = 1.f;
78 float sum = 0.f;
79 HexVec3 cursor = point;
80 for (std::int32_t octave = 0; octave < octaves; ++octave) {
81 total += valueNoise3(cursor, seed + static_cast<std::uint32_t>(octave) * 7919u) * amplitude;
82 sum += amplitude;
83 amplitude *= 0.5f;
84 cursor = HexVec3{cursor.x * 2.03f + 13.1f, cursor.y * 2.03f - 7.7f, cursor.z * 2.03f + 3.3f};
85 }
86 if (!(sum > 0.f)) return 0.5f;
87 return std::clamp(total / sum * 0.5f + 0.5f, 0.f, 1.f);
88}
89
91[[nodiscard]] float quantile(const std::vector<float>& sorted, float fraction) noexcept {
92 if (sorted.empty()) return 0.f;
93 const float position = std::clamp(fraction, 0.f, 1.f) * static_cast<float>(sorted.size() - 1);
94 return sorted[static_cast<std::size_t>(std::lround(position))];
95}
96
106[[nodiscard]] std::int32_t terrainFor(std::int32_t elevation, float moisture, float latitude,
107 std::int32_t waterLevel) noexcept {
108 const float absLatitude = std::fabs(latitude);
109 if (absLatitude > 0.80f) return static_cast<std::int32_t>(HexTerrainType::Snow);
110 if (elevation >= 7) return static_cast<std::int32_t>(HexTerrainType::Snow);
111 if (elevation >= 5) return static_cast<std::int32_t>(HexTerrainType::Stone);
112 if (absLatitude > 0.72f && elevation >= 2) return static_cast<std::int32_t>(HexTerrainType::Snow);
113 // Flooded floor: dark, and mostly hidden by the ocean surface.
114 if (elevation <= waterLevel) return static_cast<std::int32_t>(HexTerrainType::Mud);
115 if (elevation == waterLevel + 1) return static_cast<std::int32_t>(HexTerrainType::Sand);
116 if (elevation >= 4) return static_cast<std::int32_t>(HexTerrainType::Stone);
117 if (moisture < 0.36f && absLatitude < 0.45f) return static_cast<std::int32_t>(HexTerrainType::Sand);
118 if (moisture > 0.62f) return static_cast<std::int32_t>(HexTerrainType::Mud);
119 return static_cast<std::int32_t>(HexTerrainType::Grass);
120}
121
122} // namespace
123
125 if (map.empty()) {
127 "cannot generate into an empty hex sphere map",
128 "hexmap.sphere.generate"));
129 }
130
131 const std::int32_t cellCount = map.cellCount();
132 const float frequency = std::max(0.05f, settings.frequency);
133 const std::int32_t octaves = std::clamp(settings.octaves, 1, 8);
134
135 std::vector<float> continents(static_cast<std::size_t>(cellCount));
136 std::vector<float> moisture(static_cast<std::size_t>(cellCount));
137 for (HexSphereCell cell = 0; cell < cellCount; ++cell) {
138 const HexVec3 direction = map.direction(cell);
139 const HexVec3 coarse{direction.x * frequency, direction.y * frequency, direction.z * frequency};
140 const HexVec3 damp{direction.x * frequency * 2.7f + 41.3f, direction.y * frequency * 2.7f - 17.9f,
141 direction.z * frequency * 2.7f + 5.1f};
142 continents[static_cast<std::size_t>(cell)] = fbm3(coarse, octaves, settings.seed);
143 moisture[static_cast<std::size_t>(cell)] = fbm3(damp, 3, settings.seed ^ 0x5bf03635u);
144 }
145
146 // The sea level is chosen from the field itself rather than assumed, so
147 // `landPercentage` is what decides how much of the planet is dry - the noise's own
148 // distribution stays whatever the octave count made it.
149 std::vector<float> sorted = continents;
150 std::sort(sorted.begin(), sorted.end());
151 const float landFraction = std::clamp(static_cast<float>(settings.landPercentage) / 100.f, 0.f, 1.f);
152 const float seaLevel = quantile(sorted, 1.f - landFraction);
153
154 float highest = 0.f;
155 float lowest = 0.f;
156 for (const float value : continents) {
157 highest = std::max(highest, value - seaLevel);
158 lowest = std::max(lowest, seaLevel - value);
159 }
160 highest = std::max(highest, 1e-4f);
161 lowest = std::max(lowest, 1e-4f);
162
163 const std::int32_t waterLevel = std::clamp(settings.waterLevel, HexMetrics::kMinElevation, HexMetrics::kMaxElevation);
164 const float landSpan = static_cast<float>(HexMetrics::kMaxElevation - waterLevel - 1);
165 const float oceanSpan = static_cast<float>(waterLevel - HexMetrics::kMinElevation);
166
167 for (HexSphereCell cell = 0; cell < cellCount; ++cell) {
168 const float above = continents[static_cast<std::size_t>(cell)] - seaLevel;
169 const float unit = above / (above >= 0.f ? highest : lowest);
170
171 std::int32_t elevation = 0;
172 if (above >= 0.f) {
173 elevation = waterLevel + 1 + static_cast<std::int32_t>(std::lround(unit * landSpan));
174 } else {
175 elevation = waterLevel + static_cast<std::int32_t>(std::lround(unit * oceanSpan));
176 }
178
179 const float latitude = map.direction(cell).y;
180 map.setElevation(cell, elevation).ignore("generator clamps every elevation itself");
181 map.setTerrainType(cell, terrainFor(elevation, moisture[static_cast<std::size_t>(cell)], latitude, waterLevel))
182 .ignore("generator sets every terrain type itself");
184 .ignore("generator sets every water level itself");
185 }
186
187 map.markAllDirty();
188 return Result<void>::success();
189}
190
191} // namespace eve::hexmap
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
Stable, structured diagnostics shared by engine modules.
Packed per-cell state records of the hex map.
float moisture
std::int32_t waterLevel
Pointy-top hex metrics, directions and vertex helpers.
Procedural terrain generator for a spherical hex map.
float elevation
float blend
int h
std::array< float, 3 > position
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
std::uint32_t seed
Definition PointSet.cpp:807
float t
RoadLaneDirection direction
Cell cell
TacticalUnit * unit
std::size_t cursor
TerrainThermalSettings settings
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
void ignore(std::string_view reason={}) const noexcept
Explicitly discard this result after documenting the reason.
Definition Result.h:537
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
static constexpr int kMinElevation
Minimum editable elevation.
Definition HexMetrics.h:160
static constexpr int kMaxElevation
Maximum editable elevation.
Definition HexMetrics.h:162
An editable hex map wrapped onto a sphere.
Result< void > setWaterLevel(HexSphereCell c, std::int32_t waterLevel)
Sets a cell's water level, clamped to [0, kMaxElevation].
void markAllDirty() noexcept
Marks every cell dirty.
bool empty() const noexcept
Whether the map holds any cell.
Result< void > setElevation(HexSphereCell c, std::int32_t elevation)
Sets a cell's elevation, clamped to the editable range, and refreshes dependents.
Result< void > setTerrainType(HexSphereCell c, std::int32_t terrainType)
Sets a cell's terrain palette index, clamped to the palette range.
std::int32_t cellCount() const noexcept
Number of cells (10 * f^2 + 2).
HexVec3 direction(HexSphereCell cell) const noexcept
Unit direction from the sphere centre to the centre of cell.
Result< void > generateSphereMap(HexSphereMap &map, const HexSphereGeneratorSettings &settings)
Regenerates every cell of map procedurally.
std::int32_t HexSphereCell
Dense identifier of one cell of a spherical hex topology.
Tunables of the spherical generator.
Minimal 3-component float vector used by the hex mesh builders.
Definition HexMetrics.h:18