载入中...
搜索中...
未找到
TerrainAsset.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <array>
5#include <cmath>
6#include <cstring>
7#include <limits>
8
9namespace eve::procgen {
10namespace {
11constexpr uint16_t kVersion = 5;
12constexpr size_t kHeaderSize = 44;
13constexpr size_t kEntrySize = 36;
14constexpr size_t kBytesPerCell = 12;
15size_t bytesPerCell(uint16_t version) {
16 return version == 1 ? 7u : (version == 2 ? 8u :
17 (version == 3 ? 9u : (version == 4 ? 11u : 12u)));
18}
19
20void fail(std::string *error, const char *message) { if (error) *error = message; }
21void putU16(std::vector<uint8_t> &out, uint16_t v) { out.push_back(uint8_t(v)); out.push_back(uint8_t(v >> 8)); }
22void putU32(std::vector<uint8_t> &out, uint32_t v) {
23 for (int i = 0; i < 4; ++i) out.push_back(uint8_t(v >> (i * 8)));
24}
25void putI32(std::vector<uint8_t> &out, int32_t v) { putU32(out, uint32_t(v)); }
26void putU64(std::vector<uint8_t> &out, uint64_t v) {
27 for (int i = 0; i < 8; ++i) out.push_back(uint8_t(v >> (i * 8)));
28}
29void putFloat(std::vector<uint8_t> &out, float v) { uint32_t bits; std::memcpy(&bits, &v, 4); putU32(out, bits); }
30
31bool getU16(const uint8_t *&p, const uint8_t *end, uint16_t &v) {
32 if (end - p < 2) return false;
33 v = uint16_t(p[0]) | uint16_t(p[1] << 8); p += 2; return true;
34}
35bool getU32(const uint8_t *&p, const uint8_t *end, uint32_t &v) {
36 if (end - p < 4) return false;
37 v = uint32_t(p[0]) | uint32_t(p[1]) << 8 | uint32_t(p[2]) << 16 | uint32_t(p[3]) << 24; p += 4; return true;
38}
39bool getI32(const uint8_t *&p, const uint8_t *end, int32_t &v) { uint32_t u; if (!getU32(p, end, u)) return false; v = int32_t(u); return true; }
40bool getU64(const uint8_t *&p, const uint8_t *end, uint64_t &v) {
41 if (end - p < 8) return false;
42 v = 0; for (int i = 0; i < 8; ++i) v |= uint64_t(p[i]) << (i * 8); p += 8; return true;
43}
44bool getFloat(const uint8_t *&p, const uint8_t *end, float &v) { uint32_t bits; if (!getU32(p, end, bits)) return false; std::memcpy(&v, &bits, 4); return true; }
45
46uint32_t checksum(const std::vector<uint8_t> &data) {
47 uint32_t value = 2166136261u;
48 for (uint8_t byte : data) { value ^= byte; value *= 16777619u; }
49 return value;
50}
51
52std::vector<uint8_t> packBits(const std::vector<uint8_t> &src) {
53 std::vector<uint8_t> out; out.reserve(src.size()); size_t i = 0;
54 while (i < src.size()) {
55 size_t run = 1;
56 while (i + run < src.size() && src[i + run] == src[i] && run < 128) ++run;
57 if (run >= 3) { out.push_back(uint8_t(0x80u | uint8_t(run - 1))); out.push_back(src[i]); i += run; continue; }
58 const size_t begin = i; i += run;
59 while (i < src.size() && i - begin < 128) {
60 size_t nextRun = 1;
61 while (i + nextRun < src.size() && src[i + nextRun] == src[i] && nextRun < 3) ++nextRun;
62 if (nextRun >= 3) break;
63 i += std::min(nextRun, size_t(128) - (i - begin));
64 }
65 out.push_back(uint8_t(i - begin - 1)); out.insert(out.end(), src.begin() + ptrdiff_t(begin), src.begin() + ptrdiff_t(i));
66 }
67 return out;
68}
69
70bool unpackBits(const uint8_t *data, size_t size, size_t rawSize, std::vector<uint8_t> &out) {
71 out.clear(); out.reserve(rawSize); size_t i = 0;
72 while (i < size && out.size() < rawSize) {
73 const uint8_t token = data[i++]; const size_t count = size_t(token & 0x7fu) + 1;
74 if (count > rawSize - out.size()) return false;
75 if (token & 0x80u) { if (i >= size) return false; out.insert(out.end(), count, data[i++]); }
76 else { if (count > size - i) return false; out.insert(out.end(), data + i, data + i + count); i += count; }
77 }
78 return i == size && out.size() == rawSize;
79}
80
81uint8_t quantize8(float v) { return uint8_t(std::lround(std::clamp(v, 0.f, 1.f) * 255.f)); }
82uint16_t quantize16(float v) { return uint16_t(std::lround(std::clamp(v, 0.f, 1.f) * 65535.f)); }
83} // namespace
84
86 std::uint64_t offset, std::size_t length) const {
87 if (offset > bytes_.size() || length > bytes_.size() - static_cast<std::size_t>(offset))
89 DiagnosticCode::InvalidArgument, "terrain archive read is out of bounds", {}, {},
90 "procgen.terrain-source"));
91 const auto begin = bytes_.begin() + static_cast<std::ptrdiff_t>(offset);
92 return Result<std::vector<std::uint8_t>>::success(
93 std::vector<std::uint8_t>(begin, begin + static_cast<std::ptrdiff_t>(length)));
94}
95
96bool TerrainAsset::bake(const Heightmap &hm, const HydrologyMap &hydro, const ClimateMap &climate,
97 int chunkSize, std::vector<uint8_t> &out, std::string *error) {
98 const int w = hm.getWidth(), h = hm.getHeight(); const size_t cells = size_t(w) * size_t(h);
99 if (w <= 0 || h <= 0 || chunkSize <= 0 || chunkSize > 65535) { fail(error, "terrain asset: invalid dimensions"); return false; }
100 if (hydro.width != w || hydro.height != h || climate.width != w || climate.height != h ||
101 hydro.flowAccumulation.size() != cells || hydro.rivers.size() != cells ||
102 (!hydro.flowDirection.empty() && hydro.flowDirection.size() != cells) ||
103 (!hydro.flowVectorX.empty() && hydro.flowVectorX.size() != cells) ||
104 (!hydro.flowVectorY.empty() && hydro.flowVectorY.size() != cells) ||
105 (hydro.flowVectorX.empty() != hydro.flowVectorY.empty()) ||
106 (!hydro.streamOrder.empty() && hydro.streamOrder.size() != cells) ||
107 (!hydro.lakeDepth.empty() && hydro.lakeDepth.size() != cells) ||
108 climate.temperature.size() != cells || climate.moisture.size() != cells || climate.biomes.size() != cells) {
109 fail(error, "terrain asset: layer dimensions do not match heightmap"); return false;
110 }
111 const auto [minIt, maxIt] = std::minmax_element(hm.data().begin(), hm.data().end());
112 const float minHeight = *minIt, maxHeight = *maxIt;
113 const float maxFlow = std::max(1.f, *std::max_element(hydro.flowAccumulation.begin(), hydro.flowAccumulation.end()));
114 struct Block { TerrainChunkEntry entry; std::vector<uint8_t> bytes; };
115 std::vector<Block> blocks;
116 for (int cy = 0; cy * chunkSize < h; ++cy) for (int cx = 0; cx * chunkSize < w; ++cx) {
117 Block block; block.entry.chunkX = cx; block.entry.chunkY = cy;
118 block.entry.width = std::min(chunkSize, w - cx * chunkSize);
119 block.entry.height = std::min(chunkSize, h - cy * chunkSize);
120 std::vector<uint8_t> raw; raw.reserve(size_t(block.entry.width) * size_t(block.entry.height) * kBytesPerCell);
121 for (int ly = 0; ly < block.entry.height; ++ly) for (int lx = 0; lx < block.entry.width; ++lx) {
122 const size_t i = size_t(cy * chunkSize + ly) * size_t(w) + size_t(cx * chunkSize + lx);
123 const float normalizedHeight = maxHeight > minHeight ? (hm.data()[i] - minHeight) / (maxHeight - minHeight) : 0.f;
124 const uint16_t qh = quantize16(normalizedHeight); putU16(raw, qh);
125 raw.push_back(quantize8(hydro.flowAccumulation[i] / maxFlow));
126 const int direction = hydro.flowDirection.empty() ? -1 : int(hydro.flowDirection[i]);
127 if (direction < -1 || direction > 7) {
128 fail(error, "terrain asset: invalid flow direction"); return false;
129 }
130 raw.push_back(uint8_t(direction + 1));
131 float flowX = 0.f, flowY = 0.f;
132 if (!hydro.flowVectorX.empty()) {
133 flowX = hydro.flowVectorX[i]; flowY = hydro.flowVectorY[i];
134 } else if (direction >= 0) {
135 static constexpr std::array<float, 8> vectorX{
136 -0.70710678f, 0.f, 0.70710678f, -1.f, 1.f,
137 -0.70710678f, 0.f, 0.70710678f};
138 static constexpr std::array<float, 8> vectorY{
139 -0.70710678f, -1.f, -0.70710678f, 0.f, 0.f,
140 0.70710678f, 1.f, 0.70710678f};
141 flowX = vectorX[size_t(direction)]; flowY = vectorY[size_t(direction)];
142 }
143 if (!std::isfinite(flowX) || !std::isfinite(flowY)) {
144 fail(error, "terrain asset: invalid continuous flow vector"); return false;
145 }
146 raw.push_back(quantize8(flowX * 0.5f + 0.5f));
147 raw.push_back(quantize8(flowY * 0.5f + 0.5f));
148 raw.push_back(hydro.streamOrder.empty() ? 0 : hydro.streamOrder[i]);
149 const float lakeDepth = hydro.lakeDepth.empty() ? 0.f : hydro.lakeDepth[i];
150 raw.push_back(quantize8(maxHeight > minHeight ? lakeDepth / (maxHeight - minHeight) : 0.f));
151 raw.push_back(quantize8(climate.temperature[i])); raw.push_back(quantize8(climate.moisture[i]));
152 raw.push_back(hydro.rivers[i] ? 1 : 0); raw.push_back(uint8_t(climate.biomes[i]));
153 }
154 block.entry.rawSize = uint32_t(raw.size()); block.entry.checksum = checksum(raw);
155 std::vector<uint8_t> packed = packBits(raw);
156 block.entry.compressed = packed.size() < raw.size();
157 block.bytes = block.entry.compressed ? std::move(packed) : std::move(raw);
158 block.entry.storedSize = uint32_t(block.bytes.size()); blocks.push_back(std::move(block));
159 }
160 const uint64_t payloadStart = kHeaderSize + uint64_t(kEntrySize) * blocks.size(); uint64_t cursor = payloadStart;
161 for (Block &block : blocks) { block.entry.offset = cursor; cursor += block.bytes.size(); }
162 out.clear(); out.reserve(size_t(cursor)); out.insert(out.end(), {'E', 'V', 'T', 'R'});
163 putU16(out, kVersion); putU16(out, 0); putU32(out, uint32_t(w)); putU32(out, uint32_t(h));
164 putU32(out, uint32_t(chunkSize)); putU32(out, uint32_t(blocks.size()));
165 putFloat(out, minHeight); putFloat(out, maxHeight); putFloat(out, maxFlow); putU64(out, kHeaderSize);
166 for (const Block &block : blocks) {
167 const auto &e = block.entry; putI32(out, e.chunkX); putI32(out, e.chunkY); putU16(out, uint16_t(e.width)); putU16(out, uint16_t(e.height));
168 out.push_back(e.compressed ? 1 : 0); out.insert(out.end(), 3, 0); putU64(out, e.offset);
169 putU32(out, e.storedSize); putU32(out, e.rawSize); putU32(out, e.checksum);
170 }
171 for (const Block &block : blocks) out.insert(out.end(), block.bytes.begin(), block.bytes.end());
172 return true;
173}
174
175bool TerrainAsset::open(const uint8_t *data, size_t size, std::string *error) {
176 if (!data) { fail(error, "terrain asset: invalid source"); return false; }
177 auto source = std::make_shared<MemoryTerrainArchiveSource>(
178 std::span<const std::uint8_t>(data, size));
179 if (!openMetadata(data, size, size, error)) return false;
180 source_ = std::move(source);
181 return true;
182}
183
184Result<void> TerrainAsset::openSource(std::shared_ptr<const ITerrainArchiveSource> source) {
185 if (!source || source->size() < kHeaderSize)
187 DiagnosticCode::InvalidArgument, "terrain archive source is missing or truncated", {}, {},
188 "procgen.terrain-source"));
189 auto header = source->read(0, kHeaderSize);
190 if (!header) return Result<void>::failure(header.status());
191 const auto& headerBytes = header.value();
192 if (std::memcmp(headerBytes.data(), "EVTR", 4) != 0)
194 DiagnosticCode::ParseError, "terrain archive has invalid magic", {}, {},
195 "procgen.terrain-source"));
196 const std::uint32_t count = uint32_t(headerBytes[20]) | (uint32_t(headerBytes[21]) << 8) |
197 (uint32_t(headerBytes[22]) << 16) | (uint32_t(headerBytes[23]) << 24);
198 if (std::uint64_t(count) * kEntrySize > source->size() - kHeaderSize)
200 DiagnosticCode::ParseError, "terrain archive directory exceeds source size", {}, {},
201 "procgen.terrain-source"));
202 const std::size_t metadataSize = kHeaderSize + std::size_t(count) * kEntrySize;
203 auto metadata = source->read(0, metadataSize);
204 if (!metadata) return Result<void>::failure(metadata.status());
205 TerrainAsset next;
206 std::string error;
207 if (!next.openMetadata(metadata.value().data(), metadata.value().size(), source->size(), &error))
209 DiagnosticCode::ParseError, error, {}, {}, "procgen.terrain-source"));
210 next.source_ = std::move(source);
211 *this = std::move(next);
212 return Result<void>::success();
213}
214
215bool TerrainAsset::openMetadata(const uint8_t *data, size_t metadataSize,
216 std::uint64_t archiveSize, std::string *error) {
217 if (!data || metadataSize < kHeaderSize || std::memcmp(data, "EVTR", 4) != 0) { fail(error, "terrain asset: invalid magic or truncated header"); return false; }
218 const uint8_t *p = data + 4, *end = data + metadataSize; uint16_t version = 0, flags = 0; uint32_t w, h, cs, count; uint64_t directory;
219 float minH, maxH, maxFlow;
220 if (!getU16(p, end, version) || !getU16(p, end, flags) || !getU32(p, end, w) || !getU32(p, end, h) ||
221 !getU32(p, end, cs) || !getU32(p, end, count) || !getFloat(p, end, minH) || !getFloat(p, end, maxH) ||
222 !getFloat(p, end, maxFlow) || !getU64(p, end, directory) ||
223 version < 1 || version > kVersion || flags != 0 ||
224 w == 0 || h == 0 || cs == 0 || w > uint32_t(std::numeric_limits<int>::max()) ||
225 h > uint32_t(std::numeric_limits<int>::max()) || cs > uint32_t(std::numeric_limits<int>::max()) ||
226 !std::isfinite(minH) || !std::isfinite(maxH) || !std::isfinite(maxFlow) ||
227 minH > maxH || maxFlow < 1.f || directory != kHeaderSize ||
228 uint64_t(count) != ((uint64_t(w) + cs - 1) / cs) * ((uint64_t(h) + cs - 1) / cs) ||
229 directory > metadataSize || uint64_t(count) * kEntrySize > metadataSize - directory) {
230 fail(error, "terrain asset: unsupported or invalid header"); return false;
231 }
232 const uint64_t payloadStart = directory + uint64_t(count) * kEntrySize;
233 std::vector<TerrainChunkEntry> entries; entries.reserve(count); p = data + directory;
234 for (uint32_t i = 0; i < count; ++i) {
235 TerrainChunkEntry e; int32_t x, y; uint16_t cw, ch; uint8_t compression;
236 if (!getI32(p, end, x) || !getI32(p, end, y) || !getU16(p, end, cw) || !getU16(p, end, ch) || end - p < 4) {
237 fail(error, "terrain asset: truncated directory"); return false;
238 }
239 compression = p[0];
240 const bool reservedClear = p[1] == 0 && p[2] == 0 && p[3] == 0;
241 p += 4; e.chunkX = x; e.chunkY = y; e.width = cw; e.height = ch; e.compressed = compression == 1;
242 const bool coordinateValid = x >= 0 && y >= 0 && uint64_t(x) * cs < w && uint64_t(y) * cs < h;
243 const uint32_t expectedWidth = coordinateValid ? std::min(cs, w - uint32_t(x) * cs) : 0;
244 const uint32_t expectedHeight = coordinateValid ? std::min(cs, h - uint32_t(y) * cs) : 0;
245 if (compression > 1 || !getU64(p, end, e.offset) || !getU32(p, end, e.storedSize) || !getU32(p, end, e.rawSize) || !getU32(p, end, e.checksum) ||
246 !reservedClear || !coordinateValid || uint32_t(e.width) != expectedWidth ||
247 uint32_t(e.height) != expectedHeight ||
248 e.rawSize != uint64_t(e.width) * uint64_t(e.height) * bytesPerCell(version) ||
249 (!e.compressed && e.storedSize != e.rawSize) || e.offset < payloadStart ||
250 e.offset > archiveSize || e.storedSize > archiveSize - e.offset) {
251 fail(error, "terrain asset: invalid chunk entry"); return false;
252 }
253 entries.push_back(e);
254 }
255 std::vector<const TerrainChunkEntry *> intervals;
256 intervals.reserve(entries.size());
257 for (const auto &entry : entries) intervals.push_back(&entry);
258 std::sort(intervals.begin(), intervals.end(), [](const auto *a, const auto *b) {
259 return a->offset < b->offset;
260 });
261 for (size_t i = 1; i < intervals.size(); ++i) {
262 if (intervals[i - 1]->offset + intervals[i - 1]->storedSize > intervals[i]->offset) {
263 fail(error, "terrain asset: overlapping chunk payloads"); return false;
264 }
265 }
266 std::sort(entries.begin(), entries.end(), [](const auto& left, const auto& right) {
267 return left.chunkY < right.chunkY ||
268 (left.chunkY == right.chunkY && left.chunkX < right.chunkX);
269 });
270 for (std::size_t index = 1; index < entries.size(); ++index) {
271 if (entries[index - 1].chunkX == entries[index].chunkX &&
272 entries[index - 1].chunkY == entries[index].chunkY) {
273 fail(error, "terrain asset: duplicate chunk coordinate");
274 return false;
275 }
276 }
277 width_ = int(w); height_ = int(h); chunkSize_ = int(cs); version_ = version;
278 minHeight_ = minH; maxHeight_ = maxH; maxFlow_ = maxFlow;
279 chunks_ = std::move(entries); return true;
280}
281
282bool TerrainAsset::loadChunk(int chunkX, int chunkY, TerrainChunkData &out, std::string *error) const {
283 const TerrainChunkEntry* entry = findChunk(chunkX, chunkY);
284 if (!entry) { fail(error, "terrain asset: chunk not found"); return false; }
285 const auto it = entry;
286 std::vector<uint8_t> raw;
287 if (!source_) { fail(error, "terrain asset: archive source is unavailable"); return false; }
288 auto storedResult = source_->read(it->offset, it->storedSize);
289 if (!storedResult) { fail(error, "terrain asset: chunk source read failed"); return false; }
290 const auto& storedBytes = storedResult.value();
291 const uint8_t *stored = storedBytes.data();
292 if (it->compressed) { if (!unpackBits(stored, it->storedSize, it->rawSize, raw)) { fail(error, "terrain asset: invalid compressed chunk"); return false; } }
293 else raw = storedBytes;
294 if (raw.size() != it->rawSize || checksum(raw) != it->checksum) { fail(error, "terrain asset: chunk checksum mismatch"); return false; }
295 out = {}; out.chunkX = chunkX; out.chunkY = chunkY; out.width = it->width; out.height = it->height; out.heights.resize(out.width, out.height);
296 const size_t cells = size_t(out.width) * size_t(out.height); out.flowAccumulation.resize(cells); out.flowDirection.resize(cells); out.flowVectorX.resize(cells); out.flowVectorY.resize(cells); out.streamOrder.resize(cells); out.lakeDepth.resize(cells); out.temperature.resize(cells); out.moisture.resize(cells); out.rivers.resize(cells); out.biomes.resize(cells);
297 const uint8_t *p = raw.data();
298 for (size_t i = 0; i < cells; ++i) {
299 const uint16_t qh = uint16_t(p[0]) | uint16_t(p[1] << 8); p += 2;
300 out.heights.data()[i] = minHeight_ + (maxHeight_ - minHeight_) * (float(qh) / 65535.f);
301 out.flowAccumulation[i] = (float(*p++) / 255.f) * maxFlow_;
302 if (version_ >= 3) {
303 const uint8_t encodedDirection = *p++;
304 if (encodedDirection > 8) { fail(error, "terrain asset: invalid flow direction"); return false; }
305 out.flowDirection[i] = int8_t(int(encodedDirection) - 1);
306 } else out.flowDirection[i] = -1;
307 if (version_ >= 4) {
308 out.flowVectorX[i] = (float(*p++) / 255.f) * 2.f - 1.f;
309 out.flowVectorY[i] = (float(*p++) / 255.f) * 2.f - 1.f;
310 } else {
311 static constexpr std::array<float, 8> vectorX{
312 -0.70710678f, 0.f, 0.70710678f, -1.f, 1.f,
313 -0.70710678f, 0.f, 0.70710678f};
314 static constexpr std::array<float, 8> vectorY{
315 -0.70710678f, -1.f, -0.70710678f, 0.f, 0.f,
316 0.70710678f, 1.f, 0.70710678f};
317 const int direction = int(out.flowDirection[i]);
318 out.flowVectorX[i] = direction >= 0 ? vectorX[size_t(direction)] : 0.f;
319 out.flowVectorY[i] = direction >= 0 ? vectorY[size_t(direction)] : 0.f;
320 }
321 out.streamOrder[i] = version_ >= 5 ? *p++ : 0;
322 if (version_ >= 2)
323 out.lakeDepth[i] = (float(*p++) / 255.f) * (maxHeight_ - minHeight_);
324 else out.lakeDepth[i] = 0.f;
325 out.temperature[i] = float(*p++) / 255.f; out.moisture[i] = float(*p++) / 255.f;
326 out.rivers[i] = *p++; const uint8_t biome = *p++;
327 if (biome > uint8_t(Biome::Wetland)) { fail(error, "terrain asset: invalid biome value"); return false; }
328 out.biomes[i] = Biome(biome);
329 }
330 return true;
331}
332
333const TerrainChunkEntry* TerrainAsset::findChunk(int chunkX, int chunkY) const noexcept {
334 if (chunkX < 0 || chunkY < 0 || chunkSize_ <= 0) return nullptr;
335 const std::uint64_t columns = (std::uint64_t(width_) + std::uint64_t(chunkSize_) - 1) /
336 std::uint64_t(chunkSize_);
337 const std::uint64_t index = std::uint64_t(chunkY) * columns + std::uint64_t(chunkX);
338 if (index >= chunks_.size()) return nullptr;
339 const TerrainChunkEntry& entry = chunks_[static_cast<std::size_t>(index)];
340 return entry.chunkX == chunkX && entry.chunkY == chunkY ? &entry : nullptr;
341}
342
343} // namespace eve::procgen
double value
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
std::unordered_map< std::string, QuestRuntime > entries
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
float length
Definition CaveMesh.cpp:94
glm::vec4 p[6]
std::vector< std::uint8_t > stored
Definition Evpack.cpp:171
std::string message
float u
Definition Grass.cpp:233
std::vector< ClimateData > climate
Current moisture and cloud state per cell.
float v
HexVec3 left
HexVec3 right
int biome
int h
size_t offset
std::uint64_t bytes
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
std::weak_ptr< Run > run
Definition OnnxGpgpu.cpp:25
std::string error
Definition Package.cpp:60
std::array< PixelCell, kPixelChunkSize *kPixelChunkSize > cells
float begin
uint64_t token
RoadLaneDirection direction
std::uint32_t count
std::size_t cursor
int columns
float size
Definition TreeMesh.cpp:156
uint32_t index
const UnitySourceAsset & source
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
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
int getHeight() const
Returns the height.
Definition Heightmap.cpp:17
const std::vector< float > & data() const
Data.
Definition Heightmap.h:48
void resize(int width, int height)
Resize.
Definition Heightmap.cpp:10
int getWidth() const
Returns the width.
Definition Heightmap.cpp:16
Result< std::vector< std::uint8_t > > read(std::uint64_t offset, std::size_t length) const override
Reads .
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 open(const uint8_t *data, size_t size, std::string *error=nullptr)
Compatibility operation that opens only the header and directory.
static bool bake(const Heightmap &heightmap, const HydrologyMap &hydrology, const ClimateMap &climate, int chunkSize, std::vector< uint8_t > &out, std::string *error=nullptr)
Compatibility operation that bakes all terrain layers into EVTR.
bool loadChunk(int chunkX, int chunkY, TerrainChunkData &out, std::string *error=nullptr) const
Compatibility operation that loads and verifies one chunk.
Result< void > openSource(std::shared_ptr< const ITerrainArchiveSource > source)
Open only EVTR metadata from a shared immutable random-access source.
void putFloat(std::vector< std::uint8_t > &bytes, float value)
Append IEEE FLOAT to owned storage.
Biome
Biome public API.
ClimateMap public API.
HydrologyMap public API.
std::vector< float > flowVectorX
std::vector< uint8_t > rivers
std::vector< float > flowAccumulation
std::vector< float > flowVectorY
Normalized continuous downslope direction.
std::vector< int8_t > flowDirection
D8 neighbour index, or -1 for a sink.
std::vector< float > lakeDepth
Priority-Flood water depth; zero on drained terrain.
std::vector< uint8_t > streamOrder
Strahler order; zero outside the resolved river network.
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
Metadata for one independently compressed chunk in an EVTR archive.