9 using namespace raster_detail;
11 !std::isfinite(
settings.maximumThreshold) ||
settings.minimumThreshold < 0 ||
14 return invalid(
"terrain.legacyDistributedErosion: finite thresholds in [0,1], minimum below maximum, "
15 "nonnegative iterations and finite terrain required");
20 std::vector<double> diff(
current.size(), 0);
21 const auto at = [
width](
int x,
int z) {
return size_t(
z) * size_t(
width) + size_t(
x); };
24 for (
int z = 1;
z <
depth - 1; ++
z) {
26 const std::array<size_t, 4> neighbor = {
at(
x,
z + 1),
at(
x - 1,
z),
at(
x + 1,
z),
at(
x,
z - 1)};
27 std::array<double, 4> drop{};
28 double maximum = -std::numeric_limits<float>::max(), total = 0;
29 for (
int i = 0; i < 4; ++i) {
36 if (maximum < settings.minimumThreshold || maximum >
settings.maximumThreshold)
continue;
37 const double movement =
maximum * 0.5, factor = movement / total;
39 for (
int i = 0; i < 4; ++i)
40 if (drop[i] > 0) diff[neighbor[i]] += factor * drop[i];
42 for (
size_t i = 0; i <
current.size(); ++i) {
45 return invalid(
"terrain.legacyDistributedErosion: output exceeds finite float range");
54double pcgNormalizedSample(
const Heightmap& map,
double u,
double v) {
55 const double x =
u * map.getWidth(),
z =
v * map.getHeight();
56 const int x0 = std::min(
int(
x), map.getWidth() - 1), z0 = std::min(
int(
z), map.getHeight() - 1);
57 const int x1 = std::min(x0 + 1, map.getWidth() - 1), z1 = std::min(z0 + 1, map.getHeight() - 1);
58 const double tx =
x - x0, tz =
z - z0;
59 return (1 - tx) * (1 - tz) * map.height(x0, z0) + (1 - tx) * tz * map.height(x0, z1) +
60 tx * (1 - tz) * map.height(x1, z0) + tx * tz * map.height(x1, z1);
66 using namespace raster_detail;
69 !std::all_of(hardness.
data().begin(), hardness.
data().end(),
unit) ||
70 !std::isfinite(
settings.talusMinimum) || !std::isfinite(
settings.talusMaximum) ||
73 return invalid(
"terrain.legacySteepestErosion: finite terrain, normalized hardness/talus bounds and "
74 "nonnegative iterations required");
78 auto result = heights.
data();
80 const auto at = [
width](
int x,
int z) {
return size_t(
z) * size_t(
width) + size_t(
x); };
83 for (
int z = 1;
z <
depth - 1; ++
z) {
85 const std::array<size_t, 4> neighbor = {
at(
x,
z + 1),
at(
x - 1,
z),
at(
x + 1,
z),
at(
x,
z - 1)};
88 for (
int i = 0; i < 4; ++i) {
89 const double drop = double(result[
source]) - result[neighbor[i]];
92 if (maximum < settings.talusMinimum || maximum >
settings.talusMaximum || selected < 0)
continue;
93 const double resistance = pcgNormalizedSample(hardnessInput,
double(
x) /
width,
double(
z) /
depth);
94 const double movement =
maximum * (1 - resistance) * 0.5;
95 const double sourceValue = double(result[
source]) - movement;
96 const double targetValue = double(result[neighbor[selected]]) + movement;
98 return invalid(
"terrain.legacySteepestErosion: output exceeds finite float range");
99 result[
source] = float(sourceValue);
100 result[neighbor[selected]] = float(targetValue);
102 return publish(heights, std::move(result));
TerrainThermalSettings settings
const UnitySourceAsset & source
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
In-memory terrain heightmap: a dense float grid (row-major, index = y * width + x) materialized from ...
int getHeight() const
Returns the height.
const std::vector< float > & data() const
Data.
int getWidth() const
Returns the width.
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.
EVENGINE_API_DOMAINS Result< int > applyTerrainLegacyDistributedErosion(Heightmap &heights, const TerrainLegacyDistributedErosionSettings &settings)
Apply Pcg HeightMap.Erode's synchronous four-neighbor material redistribution.
EVENGINE_API_DOMAINS Result< int > applyTerrainLegacySteepestErosion(Heightmap &heights, const Heightmap &hardness, const TerrainLegacySteepestErosionSettings &settings)
Apply Pcg HeightMap.ErodeThermal's ordered steepest-cardinal transport with hardness.
Thresholds and iteration count for Pcg HeightMap.Erode's synchronous cardinal transport.
Thresholds and iteration count for Pcg HeightMap.ErodeThermal's in-place steepest transport.