载入中...
搜索中...
未找到
TerrainTerraceRemover.cpp
浏览该文件的文档.
4
5#include <array>
6
7namespace eve::procgen {
8namespace {
9bool valid(const TerrainTerraceRemovalSettings& s) {
10 const std::array<float, 7> values = {s.perlinScale, s.perlinStrength, s.slopeTerraceThreshold,
11 s.flatThreshold, s.verticalGradientThreshold,
12 s.minimumTerraceThreshold, s.maximumTerraceThreshold};
13 return std::all_of(values.begin(), values.end(), [](float value) { return std::isfinite(value) && value >= 0; }) &&
14 s.minimumTerraceThreshold <= s.maximumTerraceThreshold && s.noiseSeed >= 0;
15}
16bool matching(const Heightmap& a, const Heightmap& b) {
17 return raster_detail::validRaster(a) && raster_detail::validRaster(b) && a.getWidth() == b.getWidth() &&
18 a.getHeight() == b.getHeight();
19}
20}
21
24 using namespace raster_detail;
25 if (&target == &source || !matching(target, source) || !valid(s))
26 return invalid("terrain.terraceRemoval.analyze: distinct matching finite rasters and valid thresholds required");
27 const int width = source.getWidth(), height = source.getHeight();
28 std::vector<float> output(size_t(width) * height, float(TerrainTerraceClass::Black));
29 auto at = [width](int x, int y) { return size_t(y) * width + x; };
30 for (int y = 1; y < height - 1; ++y)
31 for (int x = 1; x < width - 1; ++x) {
32 const float current = source.data()[at(x, y)];
33 const float gradientX = std::abs(current - source.data()[at(x - 1, y)]) +
34 std::abs(current - source.data()[at(x + 1, y)]);
35 const float gradientY = std::abs(current - source.data()[at(x, y - 1)]) +
36 std::abs(current - source.data()[at(x, y + 1)]);
37 const float gradient = gradientX + gradientY;
38 const float vertical = std::abs(source.data()[at(x, y - 1)] - source.data()[at(x, y + 1)]);
39 bool slope = false;
40 if (gradient < s.slopeTerraceThreshold && vertical < s.verticalGradientThreshold)
41 slope = std::abs(source.data()[at(x - 1, y)] - current) < s.slopeTerraceThreshold &&
42 std::abs(source.data()[at(x + 1, y)] - current) < s.slopeTerraceThreshold &&
43 std::abs(source.data()[at(x, y - 1)] - current) < s.slopeTerraceThreshold &&
44 std::abs(source.data()[at(x, y + 1)] - current) < s.slopeTerraceThreshold;
45 const bool flat = gradient < s.flatThreshold;
46 const bool mountain = vertical > s.verticalGradientThreshold;
47 const bool terrace = vertical >= s.minimumTerraceThreshold && vertical <= s.maximumTerraceThreshold;
48 output[at(x, y)] = float(terrace ? TerrainTerraceClass::Green
50 : mountain ? TerrainTerraceClass::Red
53 }
54 return publish(target, std::move(output));
55}
56
59 using namespace raster_detail;
60 if (&target == &classes || !matching(target, source) || !matching(source, classes) || !valid(s))
61 return invalid("terrain.terraceRemoval.apply: matching finite rasters, distinct class output and valid settings required");
62 const int width = source.getWidth(), height = source.getHeight();
63 const auto original = source.data();
64 auto index = [width](int x, int y) { return size_t(y) * width + x; };
65 std::vector<unsigned char> mask(original.size(), 0);
66 for (int y = 1; y < height - 1; ++y)
67 for (int x = 1; x < width - 1; ++x) {
68 const float category = classes.data()[index(x, y)];
69 if ((s.excludeRed && category == float(TerrainTerraceClass::Red)) ||
70 (s.excludeBlack && category == float(TerrainTerraceClass::Black)))
71 continue;
72 const float current = original[index(x, y)];
73 mask[index(x, y)] = std::abs(current - original[index(x + 1, y)]) < 0.01F &&
74 std::abs(current - original[index(x, y + 1)]) < 0.01F;
75 }
76 auto smooth = original;
77 for (int y = 1; y < height - 1; ++y)
78 for (int x = 1; x < width - 1; ++x)
79 if (mask[index(x, y)]) {
80 double sum = 0;
81 for (int dy = -1; dy <= 1; ++dy)
82 for (int dx = -1; dx <= 1; ++dx) sum += original[index(x + dx, y + dy)];
83 smooth[index(x, y)] = float(sum / 9.0);
84 }
85 auto noisy = smooth;
86 NoiseField noise{static_cast<unsigned int>(s.noiseSeed)};
87 const float workflowScale = s.terrainWorkflow ? 0.00042F / 0.0035F : 1.F;
88 for (int y = 1; y < height - 1; ++y)
89 for (int x = 1; x < width - 1; ++x)
90 if (mask[index(x, y)]) {
91 const float dx = std::abs(smooth[index(x + 1, y)] - smooth[index(x - 1, y)]);
92 const float dy = std::abs(smooth[index(x, y + 1)] - smooth[index(x, y - 1)]);
93 const float gradient = std::sqrt(dx * dx + dy * dy);
94 const float strength = s.perlinStrength * workflowScale * (1.F - std::clamp(gradient * 10.F, 0.F, 1.F));
95 const double value = double(smooth[index(x, y)]) + noise.perlinNoise(x * s.perlinScale, y * s.perlinScale) * strength;
96 if (!isRepresentable(value)) return invalid("terrain.terraceRemoval.apply: noise result exceeds float range");
97 noisy[index(x, y)] = float(value);
98 }
99 auto directional = noisy;
100 constexpr int weights[3][3] = {{1, 2, 1}, {2, 4, 2}, {1, 2, 1}};
101 for (int y = 1; y < height - 1; ++y)
102 for (int x = 1; x < width - 1; ++x) {
103 double sum = 0;
104 for (int dy = -1; dy <= 1; ++dy)
105 for (int dx = -1; dx <= 1; ++dx) sum += noisy[index(x + dx, y + dy)] * weights[dy + 1][dx + 1];
106 directional[index(x, y)] = float(sum / 16.0);
107 }
108 std::array<double, 25> kernel{};
109 double kernelSum = 0;
110 for (int ky = -2; ky <= 2; ++ky)
111 for (int kx = -2; kx <= 2; ++kx) {
112 const double weight = std::exp(-(kx * kx + ky * ky) / 2.0);
113 kernel[size_t(ky + 2) * 5 + kx + 2] = weight; kernelSum += weight;
114 }
115 auto output = directional;
116 for (int y = 2; y < height - 2; ++y)
117 for (int x = 2; x < width - 2; ++x) {
118 double sum = 0;
119 for (int ky = -2; ky <= 2; ++ky)
120 for (int kx = -2; kx <= 2; ++kx)
121 sum += directional[index(x + kx, y + ky)] * kernel[size_t(ky + 2) * 5 + kx + 2];
122 const double value = sum / kernelSum;
123 if (!isRepresentable(value)) return invalid("terrain.terraceRemoval.apply: filtered result exceeds float range");
124 output[index(x, y)] = float(value);
125 }
126 return publish(target, std::move(output));
127}
128}
LogicalId target
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
std::string output
const std::string & s
int mask
std::map< std::string, Var > values
std::uint32_t height
std::uint32_t width
bool valid
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
double current
float dy
float dx
float weights[3]
const char * category
uint32_t index
const UnitySourceAsset & source
std::size_t at
Move-only operation result carrying either a value or Status.
Definition Result.h:155
In-memory terrain heightmap: a dense float grid (row-major, index = y * width + x) materialized from ...
Definition Heightmap.h:21
const std::vector< float > & data() const
Data.
Definition Heightmap.h:48
Result< int > publish(Heightmap &target, std::vector< float > candidate)
Publish.
bool validRaster(const Heightmap &map)
Valid raster.
Result< int > invalid(std::string message)
Invalid.
bool isRepresentable(double value)
True when representable.
Result< int > analyzeTerrainTerraces(Heightmap &target, const Heightmap &source, const TerrainTerraceRemovalSettings &s)
Classify a heightmap into Pcg's black/red/green/blue important-detail categories.
Result< int > removeTerrainTerraces(Heightmap &target, const Heightmap &source, const Heightmap &classes, const TerrainTerraceRemovalSettings &s)
Apply Pcg's adaptive smoothing, gradient noise, 3x3 filter, and fixed 5x5 Gaussian blur.
Deterministic value-noise helpers for pixel textures. When periodX/periodY > 0, lattice wraps for sea...
Definition NoiseField.h:13
Portable controls for Pcg HeightmapTerraceRemover's analysis and processing pipeline.