载入中...
搜索中...
未找到
TerrainStreaming.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cmath>
5#include <tuple>
6#include <vector>
7
8namespace eve::procgen {
9
10uint64_t TerrainStreamingCache::key(int chunkX, int chunkY) {
11 return uint64_t(uint32_t(chunkX)) | (uint64_t(uint32_t(chunkY)) << 32);
12}
13
14bool TerrainStreamingCache::open(const uint8_t *data, size_t size, std::string *error) {
15 TerrainAsset next;
16 if (!next.open(data, size, error)) return false;
17 asset_ = std::move(next);
18 resident_.clear();
19 return true;
20}
21
23 std::shared_ptr<const ITerrainArchiveSource> source) {
24 TerrainAsset next;
25 auto opened = next.openSource(std::move(source));
26 if (!opened) return opened;
27 asset_ = std::move(next);
28 resident_.clear();
29 return Result<void>::success();
30}
31
32void TerrainStreamingCache::clear() { resident_.clear(); }
33
35 int maxLoads, std::string *error) {
37 if (radius < 0 || asset_.getChunkSize() <= 0) { stats.resident = getResidentCount(); return stats; }
38 const int chunkSize = asset_.getChunkSize();
39 const int centerX = worldX >= 0 ? worldX / chunkSize : -((-worldX - 1) / chunkSize) - 1;
40 const int centerY = worldY >= 0 ? worldY / chunkSize : -((-worldY - 1) / chunkSize) - 1;
41 const int64_t radiusSq = int64_t(radius) * radius;
42
43 for (auto it = resident_.begin(); it != resident_.end();) {
44 const int cx = int32_t(uint32_t(it->first));
45 const int cy = int32_t(uint32_t(it->first >> 32));
46 const int64_t dx = int64_t(cx) - centerX, dy = int64_t(cy) - centerY;
47 if (dx * dx + dy * dy > radiusSq) { it = resident_.erase(it); ++stats.evicted; }
48 else ++it;
49 }
50
51 struct Request { int x, y; int64_t distanceSq; };
52 std::vector<Request> requests;
53 for (int dy = -radius; dy <= radius; ++dy) {
54 for (int dx = -radius; dx <= radius; ++dx) {
55 const int64_t distanceSq = int64_t(dx) * dx + int64_t(dy) * dy;
56 if (distanceSq > radiusSq) continue;
57 const int chunkX = centerX + dx, chunkY = centerY + dy;
58 if (asset_.findChunk(chunkX, chunkY) && !getChunk(chunkX, chunkY))
59 requests.push_back({chunkX, chunkY, distanceSq});
60 }
61 }
62 std::sort(requests.begin(), requests.end(), [](const Request &a, const Request &b) {
63 return std::tie(a.distanceSq, a.y, a.x) < std::tie(b.distanceSq, b.y, b.x);
64 });
65 const size_t loadCount = maxLoads > 0 ? std::min(requests.size(), size_t(maxLoads)) : requests.size();
66 for (size_t i = 0; i < loadCount; ++i) {
67 TerrainChunkData chunk;
68 std::string chunkError;
69 if (!asset_.loadChunk(requests[i].x, requests[i].y, chunk, &chunkError)) {
70 ++stats.failed;
71 if (error && error->empty()) *error = chunkError;
72 continue;
73 }
74 resident_.emplace(key(requests[i].x, requests[i].y), std::move(chunk));
75 ++stats.loaded;
76 }
77 stats.pending = int(requests.size() - loadCount);
78 stats.resident = getResidentCount();
79 return stats;
80}
81
82const TerrainChunkData *TerrainStreamingCache::getChunk(int chunkX, int chunkY) const {
83 const auto it = resident_.find(key(chunkX, chunkY));
84 return it == resident_.end() ? nullptr : &it->second;
85}
86
87bool TerrainStreamingCache::sampleCell(int worldX, int worldY, TerrainSample &out) const {
88 if (worldX < 0 || worldY < 0 || worldX >= asset_.getWidth() || worldY >= asset_.getHeight()) return false;
89 const int cs = asset_.getChunkSize(), cx = worldX / cs, cy = worldY / cs;
90 const TerrainChunkData *chunk = getChunk(cx, cy);
91 if (!chunk) return false;
92 const int lx = worldX - cx * cs, ly = worldY - cy * cs;
93 if (lx >= chunk->width || ly >= chunk->height) return false;
94 const size_t i = size_t(ly) * size_t(chunk->width) + size_t(lx);
95 out.height = chunk->heights.data()[i]; out.flowAccumulation = chunk->flowAccumulation[i];
96 out.flowDirection = int(chunk->flowDirection[i]);
97 out.flowVectorX = chunk->flowVectorX[i]; out.flowVectorY = chunk->flowVectorY[i];
98 out.streamOrder = int(chunk->streamOrder[i]);
99 out.lakeDepth = chunk->lakeDepth[i]; out.lake = out.lakeDepth > 0.001f;
100 out.temperature = chunk->temperature[i]; out.moisture = chunk->moisture[i];
101 out.river = chunk->rivers[i] != 0; out.biome = chunk->biomes[i];
102 return true;
103}
104
105bool TerrainStreamingCache::getReceiver(int worldX, int worldY, int &receiverX,
106 int &receiverY) const {
107 static constexpr int dx[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
108 static constexpr int dy[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
110 if (!sampleCell(worldX, worldY, source) || source.flowDirection < 0 ||
111 source.flowDirection >= 8) return false;
112 receiverX = worldX + dx[source.flowDirection];
113 receiverY = worldY + dy[source.flowDirection];
114 TerrainSample receiver;
115 return sampleCell(receiverX, receiverY, receiver);
116}
117
118bool TerrainStreamingCache::traceFlow(int worldX, int worldY, int maxSteps,
119 std::vector<std::pair<int, int>> &out) const {
120 out.clear();
122 if (maxSteps <= 0 || !sampleCell(worldX, worldY, start)) return false;
123 out.emplace_back(worldX, worldY);
124 for (int step = 0; step < maxSteps; ++step) {
126 if (!sampleCell(worldX, worldY, current)) return false;
127 if (current.flowDirection < 0) return true;
128 int nextX = 0, nextY = 0;
129 if (!getReceiver(worldX, worldY, nextX, nextY)) return false;
130 if (std::find(out.begin(), out.end(), std::pair<int, int>{nextX, nextY}) != out.end())
131 return false;
132 out.emplace_back(nextX, nextY);
133 worldX = nextX; worldY = nextY;
134 }
136 return sampleCell(worldX, worldY, current) && current.flowDirection < 0;
137}
138
139bool TerrainStreamingCache::buildWindow(int originX, int originY, int width, int height,
140 TerrainStreamingWindow &out) const {
141 if (width <= 0 || height <= 0 || originX < 0 || originY < 0 ||
142 int64_t(originX) + width > asset_.getWidth() ||
143 int64_t(originY) + height > asset_.getHeight()) return false;
145 next.originX = originX; next.originY = originY;
146 next.heights = Heightmap(width, height);
147 next.hydrology.width = width; next.hydrology.height = height;
148 next.climate.width = width; next.climate.height = height;
149 const size_t count = size_t(width) * size_t(height);
150 next.hydrology.flowDirection.resize(count);
151 next.hydrology.flowVectorX.resize(count); next.hydrology.flowVectorY.resize(count);
152 next.hydrology.flowAccumulation.resize(count); next.hydrology.lakeDepth.resize(count);
153 next.hydrology.rivers.resize(count); next.hydrology.streamOrder.resize(count);
154 next.climate.temperature.resize(count); next.climate.moisture.resize(count);
155 next.climate.biomes.resize(count);
156 for (int y = 0; y < height; ++y) for (int x = 0; x < width; ++x) {
158 if (!sampleCell(originX + x, originY + y, sample)) return false;
159 const size_t i = size_t(y) * size_t(width) + size_t(x);
160 next.heights.setHeight(x, y, sample.height);
161 next.hydrology.flowDirection[i] = int8_t(sample.flowDirection);
162 next.hydrology.flowVectorX[i] = sample.flowVectorX;
163 next.hydrology.flowVectorY[i] = sample.flowVectorY;
164 next.hydrology.flowAccumulation[i] = sample.flowAccumulation;
165 next.hydrology.lakeDepth[i] = sample.lakeDepth;
166 next.hydrology.rivers[i] = uint8_t(sample.river);
167 next.hydrology.streamOrder[i] = uint8_t(sample.streamOrder);
168 next.climate.temperature[i] = sample.temperature;
169 next.climate.moisture[i] = sample.moisture;
170 next.climate.biomes[i] = sample.biome;
171 }
172 out = std::move(next);
173 return true;
174}
175
176bool TerrainStreamingCache::sampleHeight(float worldX, float worldY, float &out) const {
177 if (!std::isfinite(worldX) || !std::isfinite(worldY) || worldX < 0.f || worldY < 0.f ||
178 worldX > float(asset_.getWidth() - 1) || worldY > float(asset_.getHeight() - 1)) return false;
179 const int x0 = int(std::floor(worldX)), y0 = int(std::floor(worldY));
180 const int x1 = std::min(x0 + 1, asset_.getWidth() - 1), y1 = std::min(y0 + 1, asset_.getHeight() - 1);
181 TerrainSample a, b, c, d;
182 if (!sampleCell(x0, y0, a) || !sampleCell(x1, y0, b) || !sampleCell(x0, y1, c) || !sampleCell(x1, y1, d)) return false;
183 const float tx = worldX - float(x0), ty = worldY - float(y0);
184 const float top = a.height + (b.height - a.height) * tx;
185 const float bottom = c.height + (d.height - c.height) * tx;
186 out = top + (bottom - top) * ty;
187 return true;
188}
189
190} // namespace eve::procgen
Duration start
SQInteger top
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
std::uint32_t key
std::int32_t c
std::uint32_t height
std::uint32_t width
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
OnnxTransferStats stats
Definition OnnxGpgpu.cpp:56
std::string error
Definition Package.cpp:60
float radius
float d
double current
float dy
float dx
std::uint32_t count
float step
Definition TreeMesh.cpp:314
float size
Definition TreeMesh.cpp:156
const UnitySourceAsset & source
float bottom
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
const std::vector< float > & data() const
Data.
Definition Heightmap.h:48
Versioned, random-access terrain archive.
const TerrainChunkEntry * findChunk(int chunkX, int chunkY) const noexcept
Find one chunk directory entry without scanning the full directory.
bool loadChunk(int chunkX, int chunkY, TerrainChunkData &out, std::string *error=nullptr) const
Compatibility operation that loads and verifies one chunk.
int getChunkSize() const
Returns the chunk size.
int getHeight() const
Returns the height.
int getWidth() const
Returns the width.
const TerrainChunkData * getChunk(int chunkX, int chunkY) const
Borrow a resident chunk. @lifetime Pointer remains valid until cache mutation.
bool getReceiver(int worldX, int worldY, int &receiverX, int &receiverY) const
Resolve a cell's D8 receiver in global coordinates, including an adjacent chunk.
int getResidentCount() const
Returns the resident count.
bool buildWindow(int originX, int originY, int width, int height, TerrainStreamingWindow &out) const
Compatibility operation that assembles a dense hydrology window.
bool open(const uint8_t *data, size_t size, std::string *error=nullptr)
Compatibility operation that opens EVTR and clears decoded chunks.
bool traceFlow(int worldX, int worldY, int maxSteps, std::vector< std::pair< int, int > > &out) const
Compatibility operation that follows D8 receivers across chunks.
bool sampleHeight(float worldX, float worldY, float &out) const
Compatibility operation for cross-chunk bilinear height sampling.
Result< void > openSource(std::shared_ptr< const ITerrainArchiveSource > source)
Open EVTR through a retained random-access source without loading chunk payloads.
TerrainStreamStats streamAround(int worldX, int worldY, int radiusChunks, int maxLoads=0, std::string *error=nullptr)
Stream chunks around a world-space sample coordinate.
bool sampleCell(int worldX, int worldY, TerrainSample &out) const
Compatibility operation that reads a resident integer world cell.
double sample(const Heightmap &map, double u, double v)
Sample.
Decoded terrain chunk containing all baked runtime layers.
std::vector< float > flowAccumulation
std::vector< float > moisture
std::vector< float > lakeDepth
std::vector< float > flowVectorX
std::vector< uint8_t > rivers
std::vector< Biome > biomes
std::vector< uint8_t > streamOrder
std::vector< int8_t > flowDirection
std::vector< float > flowVectorY
std::vector< float > temperature
TerrainSample public API.
TerrainStreamStats public API.
A rectangular, globally addressed terrain window assembled from resident chunks.