载入中...
搜索中...
未找到
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 y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
const std::string & s
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
int island
float nx
float ny
float warp
int h
std::uint32_t height
std::uint32_t width
std::array< float, 3 > scale
std::uint32_t seed
Definition PointSet.cpp:807
float d
int detail
float t
float dy
float dx
Owning, typed generation parameters.
Definition Params.h:27
Deterministic terrain height sampling (Red Blob Games / Perlin fBm recipe).
float getContinent() const
Returns the continent.
void setOctaves(int octaves)
Sets the octaves.
float getGain() const
Returns the gain.
void setSeed(uint32_t seed)
Sets the seed.
float sample(float x, float y) const
Sample.
float sampleTile(int tileX, int tileY) const
Height at the center of map tile (tileX, tileY).
float getClampMin() const
Returns the clamp min.
void setRidge(float ridge)
Sets the ridge.
void setLacunarity(float lacunarity)
Sets the lacunarity.
void setIsland(float island)
Sets the island.
float getClampMax() const
Returns the clamp max.
void setFrequency(float frequency)
Sets the frequency.
float getFrequency() const
Returns the frequency.
float getRidge() const
Returns the ridge.
uint32_t getSeed() const
Returns the seed.
float getExponent() const
Returns the exponent.
void setGain(float gain)
Sets the gain.
void setWarp(float warp)
Sets the warp.
float getScale() const
Returns the scale.
float getCoastSoftness() const
Returns the coast softness.
bool isClamped() const
True when clamped.
void setContinent(float continent)
Sets the continent.
void setClamp(bool enabled, float minHeight, float maxHeight)
Sets the clamp.
int getWorldWidth() const
Returns the world width.
float getWarp() const
Returns the warp.
void setAmplitude(float amplitude)
Sets the amplitude.
float getBase() const
Returns the base.
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.
int getWorldHeight() const
Returns the world height.
float getLacunarity() const
Returns the lacunarity.
void setBase(float base)
Sets the base.
void setWorldSize(int width, int height)
Sets the world size.
void setExponent(float exponent)
Sets the exponent.
static TerrainSampler fromParams(const Params &params)
From params.
float getAmplitude() const
Returns the amplitude.
float getWavelength() const
Returns the wavelength.
std::function< float(float, float)> asFunction() const
Float.
void setWavelength(float wavelength)
Distance per large oscillation. Sets frequency = 1/wavelength.
int getOctaves() const
Returns the octaves.
float getIsland() const
Returns the island.
std::vector< ParamSpec > params
constexpr uint32_t Grass
Definition Semantic.h:18
constexpr uint32_t Stone
Definition Semantic.h:20
constexpr uint32_t Sand
Definition Semantic.h:17
constexpr uint32_t Dirt
Definition Semantic.h:19
constexpr uint32_t Water
Definition Semantic.h:16
constexpr uint32_t Snow
Definition Semantic.h:21
bool enabled
float fbmPerlin(float x, float y, int octaves=4, float lacunarity=2.f, float gain=0.5f) const
Fbm perlin.
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
Ridged perlin.
TerrainBands public API.
uint32_t semanticAt(float height) const
Semantic at.
static TerrainBands fromParams(const Params &params)
From params.