载入中...
搜索中...
未找到
TerrainDetailPlacement.cpp
浏览该文件的文档.
2#include <algorithm>
3#include <cmath>
4#include <limits>
5#include <type_traits>
6#include "procgen/PointSet.h"
9namespace eve::procgen {
10namespace {
11uint64_t mix(uint64_t x) {
12 x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
13 x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
14 return x ^ (x >> 31);
15}
16double unit(uint64_t identity, uint64_t stream, uint32_t seed) {
17 return double(mix(identity ^ stream ^ (uint64_t(seed) << 32)) >> 40) / 16777216.0;
18}
19double smooth(double value) { return value * value * (3.0 - 2.0 * value); }
20double lattice(int64_t x, int64_t z, int32_t seed) {
21 const uint64_t key = uint64_t(x) * UINT64_C(0x9e3779b185ebca87) ^ uint64_t(z) * UINT64_C(0xc2b2ae3d27d4eb4f) ^
22 uint64_t(uint32_t(seed));
23 return double(mix(key) >> 40) / 16777216.0;
24}
25double detailColorNoise(double u, double v, float spread, int32_t seed) {
26 if (spread == 0) return 0.5;
27 const double frequency = 1.0 + 31.0 * double(spread);
28 const double x = u * frequency, z = v * frequency;
29 const int64_t x0 = int64_t(std::floor(x)), z0 = int64_t(std::floor(z));
30 const double tx = smooth(x - x0), tz = smooth(z - z0);
31 const double a = std::lerp(lattice(x0, z0, seed), lattice(x0 + 1, z0, seed), tx);
32 const double b = std::lerp(lattice(x0, z0 + 1, seed), lattice(x0 + 1, z0 + 1, seed), tx);
33 return std::lerp(a, b, tz);
34}
35bool normalized(float value) { return std::isfinite(value) && value >= 0 && value <= 1; }
36} // namespace
39 using namespace raster_detail;
40 if (layer.getWidth() <= 0 || layer.getHeight() <= 0 || !validRaster(heights) || !std::isfinite(s.originX) ||
41 !std::isfinite(s.originZ) || !std::isfinite(s.width) || s.width <= 0 || !std::isfinite(s.depth) ||
42 s.depth <= 0 || !std::isfinite(s.heightScale) || !std::isfinite(s.minimumScale) || s.minimumScale <= 0 ||
43 !std::isfinite(s.maximumScale) || s.maximumScale < s.minimumScale || !std::isfinite(s.minimumWidth) ||
44 s.minimumWidth <= 0 || !std::isfinite(s.maximumWidth) || s.maximumWidth < s.minimumWidth ||
45 !std::isfinite(s.minimumHeight) || s.minimumHeight <= 0 || !std::isfinite(s.maximumHeight) ||
46 s.maximumHeight < s.minimumHeight || !normalized(s.healthyR) || !normalized(s.healthyG) ||
47 !normalized(s.healthyB) || !normalized(s.healthyA) || !normalized(s.dryR) || !normalized(s.dryG) ||
48 !normalized(s.dryB) || !normalized(s.dryA) || !normalized(s.noiseSpread) || s.namespaceId == 0 ||
49 s.asset.empty() || s.maxPoints < 0)
50 return invalid("terrain.detailPoints: valid layer, heights, domain, resource and budget required");
51 int total = 0;
52 for (int z = 0; z < layer.getHeight(); ++z)
53 for (int x = 0; x < layer.getWidth(); ++x) {
54 auto count = layer.sampleResource(s.densityNamespaceId, x, z);
55 if (!count.ok()) return Result<int>::failure(count.status());
56 if (count.value() > s.maxPoints - total) return invalid("terrain.detailPoints: point budget exceeded");
57 total += count.value();
58 }
59 PointSet next;
60 next.reserve(size_t(total));
61 for (int z = 0; z < layer.getHeight(); ++z)
62 for (int x = 0; x < layer.getWidth(); ++x) {
63 auto count = layer.sampleResource(s.densityNamespaceId, x, z);
64 if (!count.ok()) return Result<int>::failure(count.status());
65 const auto cell = uint64_t(z) * uint64_t(layer.getWidth()) + uint64_t(x);
66 for (int ordinal = 0; ordinal < count.value(); ++ordinal) {
68 p.id = mix(mix(s.namespaceId + UINT64_C(0x9e3779b97f4a7c15)) ^ ((cell << 32) | uint64_t(ordinal + 1)));
69 if (p.id == 0) return invalid("terrain.detailPoints: reserved zero identity; choose another namespace");
70 const double u = (x + unit(p.id, UINT64_C(0x706f736974696f58), s.seed)) / layer.getWidth();
71 const double v = (z + unit(p.id, UINT64_C(0x706f736974696f5a), s.seed)) / layer.getHeight();
72 const double px = double(s.originX) + u * s.width, pz = double(s.originZ) + v * s.depth;
73 const double py = sample(heights, u, v) * double(s.heightScale);
75 return invalid("terrain.detailPoints: unrepresentable world position");
76 p.x = float(px);
77 p.y = float(py);
78 p.z = float(pz);
79 p.yaw = float(360 * unit(p.id, UINT64_C(0x726f746174696f6e), s.seed));
80 p.scaleX = p.scaleY = p.scaleZ =
81 float(double(s.minimumScale) +
82 (double(s.maximumScale) - s.minimumScale) * unit(p.id, UINT64_C(0x7363616c65), s.seed));
83 const double horizontal =
84 double(p.scaleX) * (double(s.minimumWidth) + (double(s.maximumWidth) - s.minimumWidth) *
85 unit(p.id, UINT64_C(0x7769647468), s.seed));
86 const double vertical =
87 double(p.scaleY) * (double(s.minimumHeight) + (double(s.maximumHeight) - s.minimumHeight) *
88 unit(p.id, UINT64_C(0x686569676874), s.seed));
89 if (!isRepresentable(horizontal) || !isRepresentable(vertical) || float(horizontal) <= 0 ||
90 float(vertical) <= 0)
91 return invalid("terrain.detailPoints: unrepresentable instance dimensions");
92 p.scaleX = p.scaleZ = float(horizontal);
93 p.scaleY = float(vertical);
94 const double colorMix = detailColorNoise(u, v, s.noiseSpread, s.noiseSeed);
95 p.colorR = float(std::lerp(double(s.dryR), double(s.healthyR), colorMix));
96 p.colorG = float(std::lerp(double(s.dryG), double(s.healthyG), colorMix));
97 p.colorB = float(std::lerp(double(s.dryB), double(s.healthyB), colorMix));
98 p.colorA = float(std::lerp(double(s.dryA), double(s.healthyA), colorMix));
99 p.seed = uint32_t(mix(p.id ^ s.seed));
100 const int row = next.appendPoint(p);
101 auto asset = next.trySetStringAttribute(row, "asset", s.asset);
102 if (!asset.ok()) return Result<int>::failure(asset.status());
103 auto source = next.trySetStringAttribute(row, "spawnNamespace", std::to_string(s.namespaceId));
104 if (!source.ok()) return Result<int>::failure(source.status());
105 auto cellAttribute = next.trySetIntAttribute(row, "detailCell", int64_t(cell));
106 if (!cellAttribute.ok()) return Result<int>::failure(cellAttribute.status());
107 auto ordinalAttribute = next.trySetIntAttribute(row, "detailOrdinal", ordinal);
108 if (!ordinalAttribute.ok()) return Result<int>::failure(ordinalAttribute.status());
109 }
110 }
111 static_assert(std::is_nothrow_move_assignable_v<PointSet>);
112 output = std::move(next);
113 return Result<int>::success(total);
114}
115} // namespace eve::procgen
double value
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
std::string output
const std::string & s
float py
float pz
glm::vec4 p[6]
std::uint32_t key
float u
Definition Grass.cpp:233
float v
std::vector< Colorf > px
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
TileLayer * layer
std::uint32_t seed
Definition PointSet.cpp:807
std::uint32_t count
Cell cell
TacticalUnit * unit
const UnitySourceAsset & source
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
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
In-memory terrain heightmap: a dense float grid (row-major, index = y * width + x) materialized from ...
Definition Heightmap.h:21
Script-friendly collection of attributed 3D samples.
Definition PointSet.h:59
Owned integer terrain-detail layer, independent of graphics and resource registries....
double sample(const Heightmap &map, double u, double v)
Sample.
bool validRaster(const Heightmap &map)
Valid raster.
Result< int > invalid(std::string message)
Invalid.
bool isRepresentable(double value)
True when representable.
Result< int > exportTerrainDetailPoints(PointSet &output, const TerrainDetailLayer &layer, const Heightmap &heights, const TerrainDetailPlacementSettings &s)
Expand integer detail counts into the existing attributed PointSet, atomically.
One deterministic sample used by script-first procedural pipelines.
Definition PointSet.h:17
std::uint64_t id
Stable non-zero identity; zero marks legacy or not-yet-assigned data.
Definition PointSet.h:19
Explicit native placement domain and resource for a terrain detail layer.