载入中...
搜索中...
未找到
TerrainMask.cpp
浏览该文件的文档.
2#include <array>
3#include <cstdint>
4#include <numbers>
8
9namespace eve::procgen {
10using namespace raster_detail;
11
12Result<int> smoothTerrainMask(Heightmap& target, const Heightmap& input, float verticality, float blurRadius) {
13 if (!validRaster(target) || !validRaster(input) || target.getWidth() != input.getWidth() ||
14 target.getHeight() != input.getHeight() || !std::isfinite(verticality) || std::abs(verticality) > 1 ||
15 !std::isfinite(blurRadius) || blurRadius < 0)
16 return invalid("terrain.smoothMask: matching finite rasters, verticality [-1,1] and radius >= 0 required");
17 Heightmap candidate = input;
18 Heightmap fullMask(input.getWidth(), input.getHeight());
19 std::fill(fullMask.data().begin(), fullMask.data().end(), 1.0F);
21 settings.radius = blurRadius;
22 settings.verticality = verticality;
23 settings.strength = 1;
24 auto result = applyTerrainSmooth(candidate, fullMask, settings);
25 if (!result.ok()) return result;
26 return publish(target, std::move(candidate.data()));
27}
28
30 int contextWidth, int contextHeight, const TerrainSampleGrid& h) {
31 auto positive = [](double v) { return std::isfinite(v) && v > 0; };
32 if (contextWidth <= 0 || contextHeight <= 0 || h.width <= 0 || h.height <= 0 || !positive(w.spacingX) ||
33 !positive(w.spacingZ) || !positive(h.spacingX) || !positive(h.spacingZ) || !positive(w.width) ||
34 !positive(w.depth) || !std::isfinite(target.strength))
35 return invalid("terrain.paintContext: positive dimensions, spacing, footprint and finite strength required");
36 for (double v : {w.originX, w.originZ, w.centerX, w.centerZ, w.rotation, h.originX, h.originZ})
37 if (!std::isfinite(v)) return invalid("terrain.paintContext: finite world coordinates required");
38 const double spanX = contextWidth * w.spacingX, spanZ = contextHeight * w.spacingZ;
39 const double heightSpanX = h.width * h.spacingX, heightSpanZ = h.height * h.spacingZ;
40 if (!std::isfinite(spanX) || !std::isfinite(spanZ) || !std::isfinite(heightSpanX) || !std::isfinite(heightSpanZ))
41 return invalid("terrain.paintContext: world spans exceed finite double range");
42 const double c = std::cos(w.rotation), s = std::sin(w.rotation);
43 const double bx = (w.originX - w.centerX) - 0.5 * w.spacingX, bz = (w.originZ - w.centerZ) - 0.5 * w.spacingZ;
44 const double values[] = {spanX / heightSpanX,
45 0,
46 0,
47 spanZ / heightSpanZ,
48 ((w.originX - h.originX) - 0.5 * w.spacingX + 0.5 * h.spacingX) / heightSpanX,
49 ((w.originZ - h.originZ) - 0.5 * w.spacingZ + 0.5 * h.spacingZ) / heightSpanZ,
50 c * spanX / w.width,
51 s * spanZ / w.width,
52 -s * spanX / w.depth,
53 c * spanZ / w.depth,
54 0.5 + (c * bx + s * bz) / w.width,
55 0.5 + (-s * bx + c * bz) / w.depth};
56 if (!std::all_of(std::begin(values), std::end(values), [](double v) { return isRepresentable(v); }))
57 return invalid("terrain.paintContext: UV coefficients exceed finite float range");
59 float* fields[] = {&candidate.heightXX, &candidate.heightXZ, &candidate.heightZX,
60 &candidate.heightZZ, &candidate.heightOffsetX, &candidate.heightOffsetZ,
61 &candidate.brushXX, &candidate.brushXZ, &candidate.brushZX,
62 &candidate.brushZZ, &candidate.brushOffsetX, &candidate.brushOffsetZ};
63 int changed = 0;
64 for (size_t i = 0; i < std::size(values); ++i) {
65 changed += *fields[i] != float(values[i]);
66 *fields[i] = float(values[i]);
67 }
68 target = candidate;
69 return Result<int>::success(changed);
70}
71
72
75 TerrainMaskBlend mode) {
76 if (!validRaster(target) || !validRaster(input) || !validRaster(heights) || !validRaster(curve) ||
77 target.getWidth() != input.getWidth() || target.getHeight() != input.getHeight() || curve.getHeight() != 1 ||
78 !std::isfinite(s.featureSize) || s.featureSize <= 0 || double(s.featureSize) > UINT32_MAX ||
79 !std::isfinite(s.concavity) || mode < TerrainMaskBlend::Multiply || mode > TerrainMaskBlend::Subtract)
80 return invalid("terrain.concavity: finite compatible rasters and valid feature controls required");
81 const double factor = double(heights.getWidth()) / input.getWidth();
82 if ((input.getHeight() - 1) * factor > UINT32_MAX)
83 return invalid("terrain.concavity: mapped coordinates exceed uint32");
84 const uint32_t width = heights.getWidth(), height = heights.getHeight(), offset = uint32_t(s.featureSize);
85 auto sample = [&](uint32_t x, uint32_t z) -> double {
86 return x < width && z < height ? heights.height(int(x), int(z)) : 0;
87 };
88 auto gradient = [&](uint32_t x, uint32_t z) {
89 const uint32_t r = x - offset, l = std::min(width, uint32_t(x + offset));
90 const uint32_t u = z - offset, d = std::min(height, uint32_t(z + offset));
91 const double dx =
92 (sample(r, u) + 2 * sample(r, z) + sample(r, d) - sample(l, u) - 2 * sample(l, z) - sample(l, d)) / 8;
93 const double dz =
94 (sample(l, d) + 2 * sample(x, d) + sample(r, d) - sample(l, u) - 2 * sample(x, u) - sample(r, u)) / 8;
95 const double length = std::hypot(dx, dz);
96 return length == 0 ? std::array<double, 2>{0, 0} : std::array<double, 2>{dx / length, dz / length};
97 };
98 std::vector<float> output(target.data().size());
99 for (int z = 0; z < input.getHeight(); ++z)
100 for (int x = 0; x < input.getWidth(); ++x) {
101 const uint32_t hx = uint32_t(x * factor), hz = uint32_t(z * factor);
102 const uint32_t r = hx - offset, l = std::min(width, uint32_t(hx + offset));
103 const uint32_t u = hz - offset, d = std::min(height, uint32_t(hz + offset));
104 const auto ru = gradient(r, u), rc = gradient(r, hz), rd = gradient(r, d);
105 const auto lu = gradient(l, u), lc = gradient(l, hz), ld = gradient(l, d);
106 const auto cd = gradient(hx, d), cu = gradient(hx, u);
107 const double dx = (ru[0] + 2 * rc[0] + rd[0] - lu[0] - 2 * lc[0] - ld[0]) / 8;
108 const double dz = (ld[1] + 2 * cd[1] + rd[1] - lu[1] - 2 * cu[1] - ru[1]) / 8;
109 const uint32_t border = std::min({uint32_t(width - hx), hx, uint32_t(height - hz), hz});
110 double fade = std::clamp((double(border) - 2 * s.featureSize) / (2 * s.featureSize), 0.0, 1.0);
111 fade = fade * fade * (3 - 2 * fade);
112 const double value =
113 std::clamp(2 * fade * std::clamp(double(s.concavity) * (dx + dz) / 2, 0.0, 1.0), 0.0, 1.0);
114 const double filter = curve.height(int(value * (curve.getWidth() - 1)), 0);
115 const size_t i = size_t(z) * input.getWidth() + x;
116 const double old = input.data()[i];
117 double result = old;
118 switch (mode) {
119 case TerrainMaskBlend::Multiply: result = old * filter; break;
120 case TerrainMaskBlend::Maximum: result = std::max(old, filter); break;
121 case TerrainMaskBlend::Minimum: result = std::min(old, filter); break;
122 case TerrainMaskBlend::Add: result = old + filter; break;
123 case TerrainMaskBlend::Subtract: result = old - filter; break;
124 }
125 if (!isRepresentable(result)) return invalid("terrain.concavity: result exceeds finite float range");
126 output[i] = float(result);
127 }
128 return publish(target, std::move(output));
129}
130
131// Radial blur design attribution from the reference shader:
132// MIT License, Copyright (c) 2018 @XorDev
133// Permission is hereby granted, free of charge, to any person obtaining a copy
134// of this software and associated documentation files (the "Software"), to deal
135// in the Software without restriction, including without limitation the rights
136// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
137// copies of the Software, and to permit persons to whom the Software is
138// furnished to do so, subject to the following conditions:
139// The above copyright notice and this permission notice shall be included in all
140// copies or substantial portions of the Software.
141// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
142// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
143// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
144// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
145// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
146// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
149 TerrainMaskBlend mode) {
150 if (!validRaster(target) || !validRaster(input) || !validRaster(heights) || !validRaster(curve) ||
151 curve.getHeight() != 1 || target.getWidth() != input.getWidth() || target.getHeight() != input.getHeight() ||
152 !std::isfinite(s.radius) || s.radius < 0 || !std::isfinite(s.worldUnits) || !std::isfinite(s.intensity) ||
153 s.intensity <= 0 || s.steps <= 0 || s.directions <= 0 ||
154 s.steps > (std::numeric_limits<int>::max() - 1) / s.directions || mode < TerrainMaskBlend::Multiply ||
156 return invalid("terrain.curvature: finite compatible rasters, curve and valid radial controls required");
157 std::vector<float> output(target.data().size());
158 for (int z = 0; z < target.getHeight(); ++z)
159 for (int x = 0; x < target.getWidth(); ++x) {
160 const double u = (x + 0.5) / target.getWidth(), v = (z + 0.5) / target.getHeight();
161 const double origin = textureSample(heights, u, v);
162 double sum = origin;
163 for (int direction = 0; direction < s.directions; ++direction) {
164 const double angle = 2 * std::numbers::pi * direction / s.directions;
165 for (int step = 1; step <= s.steps; ++step) {
166 const double radius = double(s.radius) * step / s.steps;
167 sum += textureSample(heights, u + std::cos(angle) * radius, v + std::sin(angle) * radius);
168 }
169 }
170 const double base = (origin - sum / (double(s.steps) * s.directions + 1)) * s.worldUnits;
171 if (base < 0 && std::floor(s.intensity) != s.intensity)
172 return invalid("terrain.curvature: negative base with fractional intensity has no real result");
173 const double powered = std::pow(base, double(s.intensity));
174 if (std::isnan(powered)) return invalid("terrain.curvature: undefined power result");
175 const double filter = curveSample(curve, std::clamp(std::abs(powered), 0.0, 1.0));
176 const size_t i = size_t(z) * target.getWidth() + x;
177 const double previous = input.data()[i];
178 double result = previous;
179 switch (mode) {
180 case TerrainMaskBlend::Multiply: result = previous * filter; break;
181 case TerrainMaskBlend::Maximum: result = std::max(previous, filter); break;
182 case TerrainMaskBlend::Minimum: result = filter < 1 - previous ? filter : previous; break;
183 case TerrainMaskBlend::Add: result = previous + filter; break;
184 case TerrainMaskBlend::Subtract: result = previous - filter; break;
185 }
186 if (!isRepresentable(result)) return invalid("terrain.curvature: result exceeds finite float range");
187 output[i] = float(result);
188 }
189 return publish(target, std::move(output));
190}
191
193 const Heightmap& brush, const TerrainBrushBlendSettings& spatial,
194 const Heightmap& curve, TerrainStrengthMode mode, float strength,
195 bool userInvert) {
196 Heightmap candidate = target;
197 auto blended = blendTerrainBrush(candidate, oldHeights, erosion, brush, spatial);
198 if (!blended.ok()) return blended;
199 auto filtered = applyTerrainStrength(candidate, candidate, curve, mode, strength, !userInvert);
200 if (!filtered.ok()) return filtered;
201 return publish(target, std::move(candidate.data()));
202}
204 const double radius = std::abs(double(distance));
206 target.getWidth() != source.getWidth() || target.getHeight() != source.getHeight() ||
207 !std::isfinite(distance) || 2 * radius * source.getWidth() > std::numeric_limits<int>::max())
208 return invalid(
209 "terrain.growShrink: matching finite rasters, one-row curve and indexable finite radius required");
210 const int width = source.getWidth(), height = source.getHeight();
211 const long long last = static_cast<long long>(std::floor(2 * radius * width));
212 std::vector<float> output(target.data().size());
213 for (int z = 0; z < height; ++z)
214 for (int x = 0; x < width; ++x) {
215 const double u = (x + 0.5) / width, v = (z + 0.5) / height;
216 double value = source.height(x, z);
217 if (radius > 0) {
218 // Skip definitely out-of-bounds indices, retaining one boundary candidate for rounding.
219 const auto firstX = std::max(0LL, static_cast<long long>(std::floor((radius - u) * width)) - 1);
220 const auto lastX = std::min(last, static_cast<long long>(std::ceil((radius + 1 - u) * width)) + 1);
221 const auto firstZ = std::max(0LL, static_cast<long long>(std::floor((radius - v) * width)) - 1);
222 const auto lastZ = std::min(last, static_cast<long long>(std::ceil((radius + 1 - v) * width)) + 1);
223 for (auto ix = firstX; ix <= lastX; ++ix)
224 for (auto iz = firstZ; iz <= lastZ; ++iz) {
225 const double dx = -radius + double(ix) / width, dz = -radius + double(iz) / width;
226 if (u + dx < 0 || u + dx > 1 || v + dz < 0 || v + dz > 1) continue;
227 double weight = std::clamp(std::hypot(dx, dz) / std::hypot(radius, radius), 0.0, 1.0);
228 weight = weight * weight * (3 - 2 * weight);
229 const double candidate = std::lerp(textureSample(source, u + dx, v + dz), value, weight);
230 value = distance > 0 ? std::max(value, candidate) : std::min(value, candidate);
231 }
232 }
233 const double transformed = curveSample(curve, value);
234 if (!isRepresentable(transformed)) return invalid("terrain.growShrink: result exceeds finite float range");
235 output[size_t(z) * width + x] = float(transformed);
236 }
237 return publish(target, std::move(output));
238}
240 TerrainStrengthMode mode, float strength, bool invert) {
242 target.getWidth() != source.getWidth() || target.getHeight() != source.getHeight() ||
243 !std::isfinite(strength) || strength < 0 || strength > 1 || mode < TerrainStrengthMode::Replace ||
245 return invalid(
246 "terrain.strength: matching finite rasters, one-row curve, valid mode and unit strength required");
247 std::vector<float> output(target.data().size());
248 for (size_t i = 0; i < output.size(); ++i) {
249 const double old = source.data()[i];
250 double value = curveSample(curve, old);
251 if (invert) value = 1 - value;
252 switch (mode) {
254 case TerrainStrengthMode::Maximum: value = std::max(old, value); break;
255 case TerrainStrengthMode::Minimum: value = std::min(old, value); break;
256 case TerrainStrengthMode::Add: value = old + value; break;
257 case TerrainStrengthMode::Subtract: value = old - value; break;
258 }
259 value = std::lerp(old, value, double(strength));
260 if (!isRepresentable(value)) return invalid("terrain.strength: output exceeds finite float range");
261 output[i] = float(value);
262 }
263 return publish(target, std::move(output));
264}
265Result<int> blendTerrainBrush(Heightmap& target, const Heightmap& oldHeights, const Heightmap& newHeights,
266 const Heightmap& brush, const TerrainBrushBlendSettings& s) {
267 const float coefficients[] = {s.heightXX, s.heightXZ, s.heightZX, s.heightZZ, s.heightOffsetX,
268 s.heightOffsetZ, s.brushXX, s.brushXZ, s.brushZX, s.brushZZ,
269 s.brushOffsetX, s.brushOffsetZ, s.strength};
270 if (!validRaster(target) || !validRaster(oldHeights) || !validRaster(newHeights) || !validRaster(brush) ||
271 !std::all_of(std::begin(coefficients), std::end(coefficients), [](float v) { return std::isfinite(v); }))
272 return invalid("terrain.brush: finite rasters and transform coefficients required");
273 std::vector<float> output(target.data().size());
274 for (int z = 0; z < target.getHeight(); ++z)
275 for (int x = 0; x < target.getWidth(); ++x) {
276 const double u = (x + 0.5) / target.getWidth(), v = (z + 0.5) / target.getHeight();
277 const double hu = s.heightXX * u + s.heightXZ * v + s.heightOffsetX,
278 hv = s.heightZX * u + s.heightZZ * v + s.heightOffsetZ;
279 const double bu = s.brushXX * u + s.brushXZ * v + s.brushOffsetX,
280 bv = s.brushZX * u + s.brushZZ * v + s.brushOffsetZ;
281 double result = textureSample(oldHeights, hu, hv);
282 if (bu >= 0 && bu <= 1 && bv >= 0 && bv <= 1)
283 result = std::lerp(result, textureSample(newHeights, hu, hv),
284 double(s.strength) * textureSample(brush, bu, bv));
285 if (!isRepresentable(result)) return invalid("terrain.brush: result exceeds finite float range");
286 output[size_t(z) * target.getWidth() + x] = float(result);
287 }
288 return publish(target, std::move(output));
289}
290namespace {
291bool sameShape(const Heightmap& a, const Heightmap& b) {
292 return validRaster(a) && validRaster(b) && a.getWidth() == b.getWidth() && a.getHeight() == b.getHeight();
293}
294bool validCurve(const Heightmap& curve) { return validRaster(curve) && curve.getHeight() == 1; }
295double smooth(double value) {
296 value = std::clamp(value, 0.0, 1.0);
297 return value * value * (3 - 2 * value);
298}
299double wrap(double value) {
300 // Match the source shader's boundary convention: zero/negative integers become 1.
301 return value > 0 ? std::fmod(value, 1.0) : 1.0 + std::fmod(value, 1.0);
302}
303} // namespace
304
306 if (!sameShape(target, source) || !validCurve(curve))
307 return invalid("terrain.mask.transform: matching finite rasters and a one-row curve required");
308 std::vector<float> result(target.data().size());
309 for (size_t i = 0; i < result.size(); ++i) result[i] = float(curveSample(curve, source.data()[i]));
310 return publish(target, std::move(result));
311}
312
314 const Heightmap& filterCurve, const Heightmap& strengthCurve) {
315 if (!sameShape(target, source) || !validCurve(filterCurve) || !validCurve(strengthCurve) ||
316 !std::isfinite(minimum) || !std::isfinite(maximum) || minimum >= maximum)
317 return invalid(
318 "terrain.mask.range: matching finite rasters, one-row curves and increasing finite range required");
319 std::vector<float> result(target.data().size());
320 for (size_t i = 0; i < result.size(); ++i) {
321 const double t = smooth((double(source.data()[i]) - minimum) / (double(maximum) - minimum));
322 result[i] = float(curveSample(strengthCurve, curveSample(filterCurve, t)));
323 }
324 return publish(target, std::move(result));
325}
326
328 float heightScale) {
329 if (!sameShape(target, heights) || !std::isfinite(spacingX) || !std::isfinite(spacingZ) ||
330 !std::isfinite(heightScale) || spacingX <= 0 || spacingZ <= 0)
331 return invalid(
332 "terrain.mask.slope: matching finite rasters, positive spacing and finite height scale required");
333 std::vector<float> result(target.data().size());
334 for (int y = 0; y < heights.getHeight(); ++y) {
335 for (int x = 0; x < heights.getWidth(); ++x) {
336 const int left = std::max(0, x - 1), right = std::min(x + 1, heights.getWidth() - 1);
337 const int top = std::max(0, y - 1), bottom = std::min(y + 1, heights.getHeight() - 1);
338 double dx = 0, dz = 0;
339 if (right > left)
340 dx = (double(heights.height(right, y)) - heights.height(left, y)) * heightScale /
341 (double(right - left) * spacingX);
342 if (bottom > top)
343 dz = (double(heights.height(x, bottom)) - heights.height(x, top)) * heightScale /
344 (double(bottom - top) * spacingZ);
345 result[size_t(y) * heights.getWidth() + x] = float(1 - 1 / std::hypot(1.0, dx, dz));
346 }
347 }
348 return publish(target, std::move(result));
349}
350
352 const Heightmap& filterCurve, const Heightmap& strengthCurve) {
353 if (!validRaster(target) || !validCurve(filterCurve) || !validCurve(strengthCurve) ||
354 s.axis < TerrainDistanceAxis::Circle || s.axis > TerrainDistanceAxis::RoundedSquare || s.scaleX == 0 ||
355 s.scaleZ == 0 || s.roundness <= 0)
356 return invalid("terrain.mask.distance: valid raster, curves, axis, scales and roundness required");
357 for (float value : {s.offsetX, s.offsetZ, s.scaleX, s.scaleZ, s.rotation, s.roundness})
358 if (!std::isfinite(value)) return invalid("terrain.mask.distance: finite settings required");
359 const double c = std::cos(double(s.rotation)), sn = std::sin(double(s.rotation));
360 std::vector<float> result(target.data().size());
361 for (int y = 0; y < target.getHeight(); ++y) {
362 for (int x = 0; x < target.getWidth(); ++x) {
363 const double px = ((x + 0.5) / target.getWidth() - 0.5 - s.offsetX) / s.scaleX;
364 const double pz = ((y + 0.5) / target.getHeight() - 0.5 - s.offsetZ) / s.scaleZ;
365 double u = c * px + sn * pz + 0.5, v = -sn * px + c * pz + 0.5;
366 if (s.tiling) {
367 u = wrap(u);
368 v = wrap(v);
369 }
370 double parameter = 0, filtered = 0;
371 if (s.axis != TerrainDistanceAxis::RoundedSquare || (u >= 0 && u <= 1 && v >= 0 && v <= 1)) {
372 switch (s.axis) {
373 case TerrainDistanceAxis::Circle: parameter = smooth(2 * std::hypot(u - 0.5, v - 0.5)); break;
374 case TerrainDistanceAxis::X: parameter = u; break;
375 case TerrainDistanceAxis::Z: parameter = v; break;
377 parameter = 1 - 2 * std::pow(u * (1 - v) * v * (1 - u), double(s.roundness));
378 break;
379 }
380 filtered = curveSample(filterCurve, parameter);
381 }
382 result[size_t(y) * target.getWidth() + x] = float(curveSample(strengthCurve, filtered));
383 }
384 }
385 return publish(target, std::move(result));
386}
387} // namespace eve::procgen
LogicalId target
double value
SQInteger top
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
std::string output
eve::action::ActionSpatialBinding spatial
const std::string & s
int bz
Definition CaveMesh.cpp:114
int bx
Definition CaveMesh.cpp:114
float length
Definition CaveMesh.cpp:94
float pz
std::map< std::string, Var > values
EvpackChunkInput input
Definition Evpack.cpp:170
float maximum[3]
float minimum[3]
float u
Definition Grass.cpp:233
double r
bool border
float v
HexVec3 left
HexVec3 right
std::int32_t c
int h
std::vector< Colorf > px
std::uint32_t height
std::uint32_t width
size_t offset
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float distance
graphics::Canvas * previous
float radius
std::vector< std::string > fields
Definition PlayHost.cpp:111
float d
float t
V3 origin
Definition RoadBake.cpp:138
RoadLaneDirection direction
std::string filter
float dz
float dx
Heightmap curve
TerrainThermalSettings settings
double spacingZ
double spacingX
float step
Definition TreeMesh.cpp:314
const UnitySourceAsset & source
float bottom
float angle
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
float height(int x, int y) const
Height.
Definition Heightmap.cpp:28
int getWidth() const
Returns the width.
Definition Heightmap.cpp:16
double textureSample(const Heightmap &map, double u, double v)
Texture sample.
double curveSample(const Heightmap &curve, double value)
Curve sample.
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.
TerrainStrengthMode
StrengthTransform pass order; source pass 0 named Multiply actually replaces with the curve value.
Definition TerrainMask.h:82
Result< int > generateTerrainDistanceMask(Heightmap &target, const TerrainDistanceMaskSettings &s, const Heightmap &filterCurve, const Heightmap &strengthCurve)
Generate a transformed distance mask using circle, X, Z or rounded-square coordinates.
Result< int > transformTerrainMask(Heightmap &target, const Heightmap &source, const Heightmap &curve)
Transform scalar input through a clamped bilinear one-row curve texture.
Result< int > generateTerrainRangeMask(Heightmap &target, const Heightmap &source, float minimum, float maximum, const Heightmap &filterCurve, const Heightmap &strengthCurve)
Generate height/range fitness with smoothstep followed by filter and strength curves.
Result< int > generateTerrainCurvatureMask(Heightmap &target, const Heightmap &input, const Heightmap &heights, const Heightmap &curve, const TerrainCurvatureSettings &s, TerrainMaskBlend mode)
Radially blur heights, derive source curvature, transform its curve and combine with an input mask.
Result< int > configureTerrainBrushWorld(TerrainBrushBlendSettings &target, const TerrainStampSettings &w, int contextWidth, int contextHeight, const TerrainSampleGrid &h)
Configure paint-context UV matrices from world sample centers and a rotated stamp footprint.
Result< int > blendTerrainBrush(Heightmap &target, const Heightmap &oldHeights, const Heightmap &newHeights, const Heightmap &brush, const TerrainBrushBlendSettings &s)
Blend an erosion scalar output with old heights through transformed brush UVs.
TerrainMaskBlend
Ordered scalar-mask arithmetic; intermediate values are not clamped.
Definition TerrainStamp.h:9
Result< int > smoothTerrainMask(Heightmap &target, const Heightmap &input, float verticality, float blurRadius)
Apply Pcg's two-pass Smooth ImageMask operation to a scalar mask.
Result< int > applyTerrainSmooth(Heightmap &target, const Heightmap &mask, const TerrainSmoothSettings &settings)
Apply Pcg's horizontal then vertical seven-pair weighted smooth passes.
Result< int > applyTerrainStrength(Heightmap &target, const Heightmap &source, const Heightmap &curve, TerrainStrengthMode mode, float strength, bool invert)
Apply the reference strength curve, inversion, mode and final interpolation.
Result< int > deriveTerrainSlope(Heightmap &target, const Heightmap &heights, float spacingX, float spacingZ, float heightScale)
Derive the shader's slope metric 1-normal.y from world-scaled height gradients.
Result< int > generateTerrainErosionMask(Heightmap &target, const Heightmap &oldHeights, const Heightmap &erosion, const Heightmap &brush, const TerrainBrushBlendSettings &spatial, const Heightmap &curve, TerrainStrengthMode mode, float strength, bool userInvert)
Compose spatial erosion-output blending and the source's inverted strength filter atomically.
Result< int > generateTerrainConcavityMask(Heightmap &target, const Heightmap &input, const Heightmap &heights, const Heightmap &curve, const TerrainConcavitySettings &s, TerrainMaskBlend mode)
Generate source normalized-gradient concavity with border attenuation and indexed curve lookup.
Result< int > growShrinkTerrainMask(Heightmap &target, const Heightmap &source, const Heightmap &curve, float distance)
Ordered radial Grow/Shrink followed by the reference strength curve.
Paint-context affine UV mappings, evaluated at destination pixel centers.
Concavity source controls; featureSize is in height texels, concavity is signed.
Definition TerrainMask.h:11
Radial curvature controls; radius is UV distance, worldUnits scales signed height-minus-blur.
Definition TerrainMask.h:37
Value-only UV transform; distances are relative to the target raster, rotation in radians.
World X/Z positions of height sample centers, with positive spacing and dimensions.
Two-pass smoothing controls; radius in texels, verticality in [-1,1], strength in [0,...
Value configuration for a rectangular stamp in world X/Z coordinates.