载入中...
搜索中...
未找到
TerrainStamp.cpp
浏览该文件的文档.
2
5
6#include <algorithm>
7#include <cmath>
8#include <limits>
9#include <string>
10#include <vector>
11
12namespace eve::procgen {
13using namespace raster_detail;
14
16 bool invert) {
17 if (!validRaster(target) || !validRaster(source) || target.getWidth() != source.getWidth() ||
18 target.getHeight() != source.getHeight())
19 return invalid("terrain.mask: finite nonempty rasters with matching dimensions required");
20 if (!std::isfinite(strength) || strength < 0 || strength > 1 || mode < TerrainMaskBlend::Multiply ||
22 return invalid("terrain.mask: invalid blend mode or strength");
23 std::vector<float> candidate(target.data().size());
24 for (size_t i = 0; i < candidate.size(); ++i) {
25 const double a = target.data()[i], b = invert ? 1.0 - source.data()[i] : source.data()[i];
26 double value = a;
27 switch (mode) {
28 case TerrainMaskBlend::Multiply: value = a * b; break;
29 case TerrainMaskBlend::Maximum: value = std::max(a, b); break;
30 case TerrainMaskBlend::Minimum: value = std::min(a, b); break;
31 case TerrainMaskBlend::Add: value = a + b; break;
32 case TerrainMaskBlend::Subtract: value = a - b; break;
33 }
34 value = std::lerp(a, value, double(strength));
35 if (!isRepresentable(value)) return invalid("terrain.mask: result exceeds finite height range");
36 candidate[i] = float(value);
37 }
38 return publish(target, std::move(candidate));
39}
40
42 const Heightmap& localMask, const Heightmap& globalMask) {
43 if (!validRaster(target) || !validRaster(stamp) || !validRaster(localMask) || !validRaster(globalMask))
44 return invalid("terrain.stamp: finite nonempty rasters required");
45 for (double v : {s.originX, s.originZ, s.spacingX, s.spacingZ, s.centerX, s.centerZ, s.width, s.depth, s.rotation,
46 double(s.amplitude), double(s.baseHeight), double(s.blendStrength)})
47 if (!std::isfinite(v)) return invalid("terrain.stamp: settings must be finite");
48 if (s.spacingX <= 0 || s.spacingZ <= 0 || s.width <= 0 || s.depth <= 0 || s.blendStrength < 0 ||
49 s.blendStrength > 1 || s.operation < TerrainStampOperation::Raise ||
51 return invalid("terrain.stamp: invalid operation, extent, spacing or blend strength");
52
53 std::vector<float> candidate = target.data();
54 const double c = std::cos(s.rotation), sn = std::sin(s.rotation);
55 for (int y = 0; y < target.getHeight(); ++y) {
56 for (int x = 0; x < target.getWidth(); ++x) {
57 const double dx = (s.originX + x * s.spacingX) - s.centerX;
58 const double dz = (s.originZ + y * s.spacingZ) - s.centerZ;
59 double u = (c * dx + sn * dz) / s.width + 0.5;
60 double v = (-sn * dx + c * dz) / s.depth + 0.5;
61 if (!std::isfinite(u) || !std::isfinite(v))
62 return invalid("terrain.stamp: world-to-stamp coordinates overflow");
63 // A quarter-turn must not lose an edge sample to trigonometric roundoff.
64 constexpr double edgeTolerance = 8 * std::numeric_limits<double>::epsilon();
65 if (u < -edgeTolerance || u > 1 + edgeTolerance || v < -edgeTolerance || v > 1 + edgeTolerance) continue;
66 u = std::clamp(u, 0.0, 1.0);
67 v = std::clamp(v, 0.0, 1.0);
68 const double weight = std::clamp(sample(globalMask, u, v), 0.0, 1.0);
69 if (weight == 0) continue;
70 const double old = target.height(x, y);
71 const double level = s.baseHeight + s.amplitude * sample(stamp, u, v) * sample(localMask, u, v);
72 double next = old;
73 switch (s.operation) {
74 case TerrainStampOperation::Raise: next = std::max(old, level); break;
75 case TerrainStampOperation::Lower: next = std::min(old, level); break;
76 case TerrainStampOperation::Set: next = level; break;
77 case TerrainStampOperation::Blend: next = std::lerp(old, level, double(s.blendStrength)); break;
78 case TerrainStampOperation::Add: next = old + level; break;
79 case TerrainStampOperation::Subtract: next = old - level; break;
80 }
81 next = std::lerp(old, next, weight);
82 if (!isRepresentable(next)) return invalid("terrain.stamp: result exceeds finite height range");
83 candidate[size_t(y) * size_t(target.getWidth()) + size_t(x)] = float(next);
84 }
85 }
86 return publish(target, std::move(candidate));
87}
88} // namespace eve::procgen
LogicalId target
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
const std::string & s
float u
Definition Grass.cpp:233
float v
std::int32_t c
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
int level
float dz
float dx
Heightmap stamp
const UnitySourceAsset & source
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
double sample(const Heightmap &map, double u, double v)
Sample.
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 > blendTerrainMask(Heightmap &target, const Heightmap &source, TerrainMaskBlend mode, float strength, bool invert)
Blend one scalar raster into an existing mask stack, in call order.
TerrainMaskBlend
Ordered scalar-mask arithmetic; intermediate values are not clamped.
Definition TerrainStamp.h:9
Result< int > applyTerrainStamp(Heightmap &target, const Heightmap &stamp, const TerrainStampSettings &s, const Heightmap &localMask, const Heightmap &globalMask)
Apply a rotated, bilinearly sampled stamp; publish only after all checks succeed.
Value configuration for a rectangular stamp in world X/Z coordinates.