载入中...
搜索中...
未找到
TerrainSampler.cpp
浏览该文件的文档.
2
3#include "procgen/Semantic.h"
4
5#include <algorithm>
6#include <cmath>
7
8namespace eve::procgen {
9
10void TerrainSampler::setSeed(uint32_t seed) { field_.seed = seed; }
11uint32_t TerrainSampler::getSeed() const { return field_.seed; }
12void TerrainSampler::setScale(float scale) { frequency_ = std::max(0.0001f, scale); }
13float TerrainSampler::getScale() const { return frequency_; }
14void TerrainSampler::setFrequency(float frequency) { setScale(frequency); }
15float TerrainSampler::getFrequency() const { return frequency_; }
16void TerrainSampler::setWavelength(float wavelength) {
17 setFrequency(1.f / std::max(0.001f, wavelength));
18}
19float TerrainSampler::getWavelength() const { return 1.f / frequency_; }
20void TerrainSampler::setOctaves(int octaves) { octaves_ = std::max(1, octaves); }
21int TerrainSampler::getOctaves() const { return octaves_; }
22void TerrainSampler::setLacunarity(float lacunarity) { lacunarity_ = std::max(0.1f, lacunarity); }
23float TerrainSampler::getLacunarity() const { return lacunarity_; }
24void TerrainSampler::setGain(float gain) { gain_ = std::max(0.01f, gain); }
25float TerrainSampler::getGain() const { return gain_; }
26void TerrainSampler::setRidge(float ridge) { ridge_ = std::clamp(ridge, 0.f, 1.f); }
27float TerrainSampler::getRidge() const { return ridge_; }
28void TerrainSampler::setWarp(float warp) { warp_ = std::max(0.f, warp); }
29float TerrainSampler::getWarp() const { return warp_; }
30void TerrainSampler::setExponent(float exponent) { exponent_ = std::max(0.1f, exponent); }
31float TerrainSampler::getExponent() const { return exponent_; }
32void TerrainSampler::setContinent(float continent) { continent_ = std::clamp(continent, 0.f, 1.f); }
33float TerrainSampler::getContinent() const { return continent_; }
34void TerrainSampler::setIsland(float island) { island_ = std::clamp(island, 0.f, 1.f); }
35float TerrainSampler::getIsland() const { return island_; }
36void TerrainSampler::setCoastSoftness(float softness) { coastSoft_ = std::clamp(softness, 0.02f, 0.4f); }
37float TerrainSampler::getCoastSoftness() const { return coastSoft_; }
39 worldW_ = width > 0 ? width : 0;
40 worldH_ = height > 0 ? height : 0;
41}
42int TerrainSampler::getWorldWidth() const { return worldW_; }
43int TerrainSampler::getWorldHeight() const { return worldH_; }
44void TerrainSampler::setBase(float base) { base_ = base; }
45float TerrainSampler::getBase() const { return base_; }
46void TerrainSampler::setAmplitude(float amplitude) { amplitude_ = std::max(0.f, amplitude); }
47float TerrainSampler::getAmplitude() const { return amplitude_; }
48void TerrainSampler::setClamp(bool enabled, float minHeight, float maxHeight) {
49 clamp_ = enabled;
50 clampMin_ = std::min(minHeight, maxHeight);
51 clampMax_ = std::max(minHeight, maxHeight);
52}
53bool TerrainSampler::isClamped() const { return clamp_; }
54float TerrainSampler::getClampMin() const { return clampMin_; }
55float TerrainSampler::getClampMax() const { return clampMax_; }
56
57namespace {
58float smoother(float edge0, float edge1, float x) {
59 if (edge1 <= edge0) return x < edge0 ? 0.f : 1.f;
60 const float t = std::clamp((x - edge0) / (edge1 - edge0), 0.f, 1.f);
61 return t * t * (3.f - 2.f * t);
62}
63} // namespace
64
65float TerrainSampler::sample(float x, float y) const {
66 const float mx = x * frequency_;
67 const float my = y * frequency_;
68
69 // Continent: 3 low/mid octaves (~100 / 50 / 25 px bays on a 512 map).
70 // Warp at the same scale so the silhouette twists without pixel breakup.
71 float cx = mx * 0.36f;
72 float cy = my * 0.36f;
73 const float maskWarp = 0.70f;
74 cx += (field_.perlinNoise(mx * 0.18f + 2.4f, my * 0.18f + 9.1f) - 0.5f) * maskWarp;
75 cy += (field_.perlinNoise(mx * 0.18f + 7.6f, my * 0.18f + 1.3f) - 0.5f) * maskWarp;
76 float macro = field_.fbmPerlin(cx, cy, 3, 2.f, 0.42f);
77
78 if (island_ > 0.f && worldW_ > 0 && worldH_ > 0) {
79 const float nx = x / float(worldW_) * 2.f - 1.f;
80 const float ny = y / float(worldH_) * 2.f - 1.f;
81 const float dSq = 1.f - (1.f - nx * nx) * (1.f - ny * ny);
82 const float dEu = std::sqrt(std::min(1.f, nx * nx + ny * ny));
83 const float d = std::clamp(dSq * 0.5f + dEu * 0.5f, 0.f, 1.f);
84 // Multiply, don't lerp: corners sink, the noise silhouette stays.
85 macro *= 1.f - island_ * std::pow(d, 1.25f);
86 }
87
88 // Medium-scale shore wiggle (~70 px). Amplitude stays below the smoothstep
89 // width so it cannot punch isolated pixels through the waterline.
90 const float wiggle =
91 (field_.perlinNoise(mx * 0.42f + 11.f, my * 0.42f + 4.f) - 0.5f) * coastSoft_ * 0.55f;
92 const float sea = 0.54f - continent_ * 0.14f + wiggle;
93 const float land = smoother(sea - coastSoft_ * 0.45f, sea + coastSoft_ * 0.55f, macro);
94 const float inland = land * land;
95
96 float dx = mx;
97 float dy = my;
98 if (warp_ > 0.f) {
99 dx += (field_.fbmPerlin(mx + 19.1f, my + 7.3f, 2) - 0.5f) * warp_;
100 dy += (field_.fbmPerlin(mx + 5.2f, my + 31.7f, 2) - 0.5f) * warp_;
101 }
102
103 float detail = field_.fbmPerlin(dx, dy, octaves_, lacunarity_, gain_);
104 if (ridge_ > 0.f) {
105 const float mountains =
106 field_.ridgedPerlin(dx * 1.4f, dy * 1.4f, octaves_, lacunarity_, gain_);
107 detail = detail + (mountains - detail) * ridge_;
108 }
109 detail = std::pow(std::clamp(detail, 0.f, 1.f), exponent_);
110
111 // land=0 stays below waterMax (0.25). Detail cannot open holes in the sea.
112 const float shelf = 0.06f + land * 0.32f;
113 const float e = shelf + inland * detail * 0.62f;
114
115 float h = base_ + e * amplitude_;
116 if (clamp_) h = std::clamp(h, clampMin_, clampMax_);
117 return h;
118}
119
120float TerrainSampler::sampleTile(int tileX, int tileY) const {
121 return sample(float(tileX) + 0.5f, float(tileY) + 0.5f);
122}
123
124std::function<float(float, float)> TerrainSampler::asFunction() const {
125 return [self = *this](float x, float y) { return self.sample(x, y); };
126}
127
130 s.setSeed(params.getSeed());
131 s.setWorldSize(params.getWidth(), params.getHeight());
132
133 const float wavelength = params.getFloat("wavelength", 0.f);
134 if (wavelength > 0.f) {
135 s.setWavelength(wavelength);
136 } else if (params.has("frequency")) {
137 s.setFrequency(params.getFloat("frequency", 1.f / 32.f));
138 } else {
139 s.setScale(params.getFloat("scale", 1.f / 32.f));
140 }
141
142 s.setOctaves(params.getInt("octaves", 5));
143 s.setLacunarity(params.getFloat("lacunarity", 2.f));
144 s.setGain(params.getFloat("gain", 0.5f));
145 s.setRidge(params.getFloat("ridge", 0.35f));
146 s.setWarp(params.getFloat("warp", 0.35f));
147 s.setExponent(params.getFloat("exponent", 2.f));
148 s.setContinent(params.getFloat("continent", 0.55f));
149 s.setIsland(params.getFloat("island", 0.38f));
150 s.setCoastSoftness(params.getFloat("coast", 0.12f));
151 s.setBase(params.getFloat("base", 0.f));
152 s.setAmplitude(params.getFloat("amplitude", 1.f));
153 const float lo = params.getFloat("heightMin", 0.f);
154 const float hi = params.getFloat("heightMax", 1.f);
155 s.setClamp(params.getInt("clamp", 1) != 0, lo, hi);
156 return s;
157}
158
159uint32_t TerrainBands::semanticAt(float height) const {
160 if (height < waterMax) return Semantic::Water;
161 if (height < sandMax) return Semantic::Sand;
162 if (height < grassMax) return Semantic::Grass;
163 if (height < dirtMax) return Semantic::Dirt;
164 if (height < stoneMax) return Semantic::Stone;
165 return Semantic::Snow;
166}
167
169 TerrainBands bands;
170 bands.waterMax = std::clamp(params.getFloat("waterMax", 0.25f), 0.f, 1.f);
171 bands.sandMax = std::clamp(params.getFloat("sandMax", 0.35f), 0.f, 1.f);
172 bands.grassMax = std::clamp(params.getFloat("grassMax", 0.65f), 0.f, 1.f);
173 bands.dirtMax = std::clamp(params.getFloat("dirtMax", 0.80f), 0.f, 1.f);
174 bands.stoneMax = std::clamp(params.getFloat("stoneMax", 0.92f), 0.f, 1.f);
175 return bands;
176}
177
178} // namespace eve::procgen
float cx
Definition CardTypes.cpp:31
float cy
Definition CardTypes.cpp:32
uint32_t seed
int y
Definition Grass.cpp:135
float height
Definition Grass.cpp:235
int x
Definition Grass.cpp:135
int h
int width
bool enabled
int d
float scale
Definition TreeMesh.cpp:122
uint32_t s
Definition Weather.cpp:28
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
bool has(const std::string &key) const
Definition Params.cpp:21
float getFloat(const std::string &key, float defaultValue) const
Definition Params.cpp:32
int getInt(const std::string &key, int defaultValue) const
Definition Params.cpp:23
Deterministic terrain height sampling (Red Blob Games / Perlin fBm recipe).
float sample(float x, float y) const
float sampleTile(int tileX, int tileY) const
Height at the center of map tile (tileX, tileY).
void setLacunarity(float lacunarity)
void setFrequency(float frequency)
void setContinent(float continent)
void setClamp(bool enabled, float minHeight, float maxHeight)
void setAmplitude(float amplitude)
void setScale(float scale)
Cycles per world unit (alias of frequency). Default 1/32.
void setCoastSoftness(float softness)
Shore width of the land mask smoothstep. Smaller = cleaner, harder coast.
void setWorldSize(int width, int height)
void setExponent(float exponent)
static TerrainSampler fromParams(const Params &params)
std::function< float(float, float)> asFunction() const
void setWavelength(float wavelength)
Distance per large oscillation. Sets frequency = 1/wavelength.
constexpr uint32_t Grass
Definition Semantic.h:16
constexpr uint32_t Stone
Definition Semantic.h:18
constexpr uint32_t Sand
Definition Semantic.h:15
constexpr uint32_t Dirt
Definition Semantic.h:17
constexpr uint32_t Water
Definition Semantic.h:14
constexpr uint32_t Snow
Definition Semantic.h:19
float fbmPerlin(float x, float y, int octaves=4, float lacunarity=2.f, float gain=0.5f) const
float perlinNoise(float x, float y) const
Classic 2D Perlin gradient noise, remapped to [0, 1].
float ridgedPerlin(float x, float y, int octaves=4, float lacunarity=2.f, float gain=0.5f) const
uint32_t semanticAt(float height) const
static TerrainBands fromParams(const Params &params)