载入中...
搜索中...
未找到
TerrainLegacyHydraulic.cpp
浏览该文件的文档.
3
4#include <array>
5
6namespace eve::procgen {
9 using namespace raster_detail;
10 const auto sameShape = [&](const Heightmap& map) {
11 return validRaster(map) && map.getWidth() == heights.getWidth() && map.getHeight() == heights.getHeight();
12 };
13 const auto unit = [](float value) { return value >= 0 && value <= 1; };
14 if (&heights == &sediment || !validRaster(heights) || !sameShape(sediment) || !sameShape(hardness) ||
15 !sameShape(rain) || settings.iterations < 0 || settings.rainFrequency <= 0 ||
16 !std::isfinite(settings.sedimentDissolveRate) || !unit(settings.sedimentDissolveRate) ||
17 !std::all_of(heights.data().begin(), heights.data().end(), unit) ||
18 !std::all_of(hardness.data().begin(), hardness.data().end(), unit) ||
19 std::any_of(rain.data().begin(), rain.data().end(), [](float value) { return value < 0; }))
20 return invalid("terrain.legacyHydraulic: distinct matching rasters, normalized heights/hardness, "
21 "nonnegative rain/iterations, positive rain frequency and dissolve rate in [0,1] required");
22 if (settings.iterations == 0) return Result<int>::success(0);
23
24 constexpr double time = 0.2;
25 const int width = heights.getWidth(), depth = heights.getHeight();
26 const size_t count = heights.data().size();
27 auto terrain = heights.data(), sedimentResult = sediment.data();
28 const auto hardnessInput = hardness.data(), rainInput = rain.data();
29 std::vector<double> water(count, 0), sedimentDiff(count, 0);
30 std::vector<std::array<double, 4>> outflow(count);
31 const auto index = [width](int x, int z) { return size_t(z) * size_t(width) + size_t(x); };
32
33 for (int iteration = 0; iteration < settings.iterations; ++iteration) {
34 if (iteration % settings.rainFrequency == 0)
35 for (size_t i = 0; i < count; ++i) {
36 water[i] += rainInput[i];
37 if (!std::isfinite(water[i])) return invalid("terrain.legacyHydraulic: water exceeds finite range");
38 }
39
40 for (int z = 0; z < depth; ++z)
41 for (int x = 0; x < width; ++x) {
42 const size_t i = index(x, z);
43 const std::array<size_t, 4> neighbor = {
44 index(std::max(x - 1, 0), z), index(std::min(x + 1, width - 1), z),
45 index(x, std::max(z - 1, 0)), index(x, std::min(z + 1, depth - 1))};
46 double sum = 0;
47 for (int direction = 0; direction < 4; ++direction) {
48 const double difference = water[i] + terrain[i] - water[neighbor[direction]] - terrain[neighbor[direction]];
49 outflow[i][direction] = std::max(0.0, outflow[i][direction] + difference);
50 sum += outflow[i][direction];
51 }
52 if (sum > 0) {
53 const double scale = std::clamp(water[i] / (sum * time), 0.0, 1.0);
54 for (double& flow : outflow[i]) flow *= scale;
55 } else {
56 outflow[i].fill(0);
57 }
58 }
59
60 auto nextWater = water;
61 for (int z = 0; z < depth; ++z)
62 for (int x = 0; x < width; ++x) {
63 const size_t i = index(x, z);
64 const double flowOut = outflow[i][0] + outflow[i][1] + outflow[i][2] + outflow[i][3];
65 double flowIn = 0;
66 if (x > 0) flowIn += outflow[index(x - 1, z)][1];
67 if (x + 1 < width) flowIn += outflow[index(x + 1, z)][0];
68 if (z > 0) flowIn += outflow[index(x, z - 1)][3];
69 if (z + 1 < depth) flowIn += outflow[index(x, z + 1)][2];
70 nextWater[i] = std::max(0.0, water[i] + (flowIn - flowOut) * time);
71 if (!std::isfinite(nextWater[i])) return invalid("terrain.legacyHydraulic: water exceeds finite range");
72 }
73 water.swap(nextWater);
74
75 for (int z = 0; z < depth; ++z)
76 for (int x = 0; x < width; ++x) {
77 const size_t i = index(x, z);
78 double totalDrop = 0;
79 for (int nz = std::max(z - 1, 0); nz <= std::min(z + 1, depth - 1); ++nz)
80 for (int nx = std::max(x - 1, 0); nx <= std::min(x + 1, width - 1); ++nx)
81 totalDrop += std::max(0.0, double(terrain[i]) - terrain[index(nx, nz)]);
82 if (totalDrop == 0) continue;
83 const double moved = water[i] * totalDrop * settings.sedimentDissolveRate * (1.0 - hardnessInput[i]);
84 sedimentDiff[i] -= moved;
85 for (int nz = std::max(z - 1, 0); nz <= std::min(z + 1, depth - 1); ++nz)
86 for (int nx = std::max(x - 1, 0); nx <= std::min(x + 1, width - 1); ++nx) {
87 const size_t n = index(nx, nz);
88 const double drop = double(terrain[i]) - terrain[n];
89 if (drop > 0) sedimentDiff[n] += moved * drop / totalDrop;
90 }
91 }
92
93 for (size_t i = 0; i < count; ++i) {
94 const double nextSediment = double(sedimentResult[i]) + sedimentDiff[i];
95 if (!isRepresentable(nextSediment)) return invalid("terrain.legacyHydraulic: sediment exceeds finite float range");
96 sedimentResult[i] = float(nextSediment);
97 terrain[i] = float(std::clamp(double(terrain[i]) + sedimentDiff[i], 0.0, 1.0));
98 water[i] = std::clamp(water[i] - 1.0 / settings.rainFrequency, 0.0, 1.0);
99 sedimentDiff[i] = 0;
100 }
101 }
102 int changed = 0;
103 for (size_t i = 0; i < count; ++i)
104 changed += terrain[i] != heights.data()[i] || sedimentResult[i] != sediment.data()[i];
105 heights.data().swap(terrain);
106 sediment.data().swap(sedimentResult);
107 return Result<int>::success(changed);
108}
109} // namespace eve::procgen
double value
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
std::string terrain
float nx
float nz
glm::vec3 n
Definition Grass.cpp:63
std::uint32_t width
std::array< float, 3 > scale
RoadLaneDirection direction
std::uint32_t count
TacticalUnit * unit
TerrainWaterField water
TerrainThermalSettings settings
Heightmap sediment
uint32_t index
std::uint32_t depth
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
In-memory terrain heightmap: a dense float grid (row-major, index = y * width + x) materialized from ...
Definition Heightmap.h:21
int getHeight() const
Returns the height.
Definition Heightmap.cpp:17
const std::vector< float > & data() const
Data.
Definition Heightmap.h:48
int getWidth() const
Returns the width.
Definition Heightmap.cpp:16
bool validRaster(const Heightmap &map)
Valid raster.
Result< int > invalid(std::string message)
Invalid.
bool isRepresentable(double value)
True when representable.
EVENGINE_API_DOMAINS Result< int > applyTerrainLegacyHydraulic(Heightmap &heights, Heightmap &sediment, const Heightmap &hardness, const Heightmap &rain, const TerrainLegacyHydraulicSettings &settings)
Apply Pcg HeightMap.ErodeHydraulic's rain, four-way water and sediment loop atomically.
Controls Pcg HeightMap.ErodeHydraulic's legacy CPU erosion loop.