载入中...
搜索中...
未找到
HexTerrainBake.cpp
浏览该文件的文档.
1
7
8#include "common/Diagnostic.h"
9
10#include <cstddef>
11#include <string>
12#include <string_view>
13
14namespace eve::hexmap {
15namespace {
16
17[[nodiscard]]
18[[nodiscard]] Result<HexTerrainBake> invalidBake(std::string message) {
21}
22
23void writeU32LE(unsigned char* bytes, std::uint32_t value) noexcept {
24 bytes[0] = static_cast<unsigned char>(value & 0xffu);
25 bytes[1] = static_cast<unsigned char>((value >> 8) & 0xffu);
26 bytes[2] = static_cast<unsigned char>((value >> 16) & 0xffu);
27 bytes[3] = static_cast<unsigned char>((value >> 24) & 0xffu);
28}
29
30[[nodiscard]] std::uint32_t readU32LE(const unsigned char* bytes) noexcept {
31 return static_cast<std::uint32_t>(bytes[0]) | (static_cast<std::uint32_t>(bytes[1]) << 8) |
32 (static_cast<std::uint32_t>(bytes[2]) << 16) | (static_cast<std::uint32_t>(bytes[3]) << 24);
33}
34
35[[nodiscard]] std::string packCells(const std::vector<HexCellData>& cells) {
36 std::string packed(cells.size() * 8u, '\0');
37 auto* bytes = reinterpret_cast<unsigned char*>(packed.data());
38 for (std::size_t i = 0; i < cells.size(); ++i) {
39 writeU32LE(bytes + i * 8u, cells[i].values.raw());
40 writeU32LE(bytes + i * 8u + 4u, cells[i].flags.raw());
41 }
42 return packed;
43}
44
45[[nodiscard]] Result<std::vector<HexCellData>> unpackCells(std::string_view packed, std::size_t expected) {
46 if (packed.size() != expected * 8u)
47 return Result<std::vector<HexCellData>>::failure(
49 "hex terrain bake cell payload length does not match the cell count", "hexmap"));
50 std::vector<HexCellData> cells(expected);
51 const auto* bytes = reinterpret_cast<const unsigned char*>(packed.data());
52 for (std::size_t i = 0; i < expected; ++i) {
53 cells[i].values = HexValues{readU32LE(bytes + i * 8u)};
54 cells[i].flags = HexFlags{readU32LE(bytes + i * 8u + 4u)};
55 }
56 return Result<std::vector<HexCellData>>::success(std::move(cells));
57}
58
59[[nodiscard]] Result<std::int32_t> readInt32(const Value& object, const char* key) {
60 const Value* field = object.find(key);
61 if (field == nullptr || !field->isNumeric())
64 std::string("hex terrain bake is missing numeric '") + key + "'", "hexmap"));
65 if (field->isInt64()) return Result<std::int32_t>::success(static_cast<std::int32_t>(field->asInt()));
66 return Result<std::int32_t>::success(static_cast<std::int32_t>(field->asDouble()));
67}
68
69[[nodiscard]] Result<std::uint32_t> readUInt32(const Value& object, const char* key) {
70 auto parsed = readInt32(object, key);
71 if (!parsed) return Result<std::uint32_t>::failure(parsed.status());
72 if (parsed.value() < 0)
75 std::string("hex terrain bake '") + key + "' must not be negative", "hexmap"));
76 return Result<std::uint32_t>::success(static_cast<std::uint32_t>(parsed.value()));
77}
78
79} // namespace
80
82 HexTerrainBake bake;
84 bake.cellCountX = map.cellCountX();
85 bake.cellCountZ = map.cellCountZ();
86 bake.cellCount = map.cellCount();
87 bake.seed = map.seed();
88 bake.cells.resize(static_cast<std::size_t>(bake.cellCount));
89 for (std::int32_t index = 0; index < bake.cellCount; ++index) {
90 const HexCellData* cell = map.cellAt(index);
91 if (cell != nullptr) bake.cells[static_cast<std::size_t>(index)] = *cell;
92 }
93 return bake;
94}
95
97 HexTerrainBake bake;
99 bake.cellCount = map.cellCount();
100 bake.subdivision = map.subdivision();
101 bake.radius = map.sphereRadius();
102 bake.seed = map.seed();
103 bake.cells.resize(static_cast<std::size_t>(bake.cellCount));
104 for (std::int32_t cell = 0; cell < bake.cellCount; ++cell) {
105 const HexCellData* record = map.cellAt(cell);
106 if (record != nullptr) bake.cells[static_cast<std::size_t>(cell)] = *record;
107 }
108 return bake;
109}
110
114 payload.set("kind", Value("hex.sphere"));
115 payload.set("subdivision", Value(static_cast<std::int64_t>(bake.subdivision)));
116 payload.set("radius", Value(static_cast<double>(bake.radius)));
117 payload.set("cellCount", Value(static_cast<std::int64_t>(bake.cellCount)));
118 } else {
119 payload.set("kind", Value("hex.terrain"));
120 payload.set("cellCountX", Value(static_cast<std::int64_t>(bake.cellCountX)));
121 payload.set("cellCountZ", Value(static_cast<std::int64_t>(bake.cellCountZ)));
122 }
123 payload.set("seed", Value(static_cast<std::int64_t>(bake.seed)));
124 payload.set("cells", Value(packCells(bake.cells)));
125 return payload;
126}
127
129 if (!value.isObject()) return invalidBake("hex terrain bake must be an object");
130 const Value* kindField = value.find("kind");
131 if (kindField == nullptr || !kindField->isString()) return invalidBake("hex terrain bake is missing string 'kind'");
132 const std::string& kind = kindField->asString();
133
134 HexTerrainBake bake;
135 if (kind == "hex.sphere") {
137 auto subdivision = readInt32(value, "subdivision");
138 if (!subdivision) return Result<HexTerrainBake>::failure(subdivision.status());
139 bake.subdivision = subdivision.value();
140 const Value* radiusField = value.find("radius");
141 if (radiusField == nullptr || !radiusField->isNumeric())
142 return invalidBake("hex sphere bake is missing numeric 'radius'");
143 bake.radius = radiusField->isDouble() ? static_cast<float>(radiusField->asDouble())
144 : static_cast<float>(radiusField->asInt());
145 auto cellCount = readInt32(value, "cellCount");
146 if (!cellCount) return Result<HexTerrainBake>::failure(cellCount.status());
147 bake.cellCount = cellCount.value();
148 } else if (kind == "hex.terrain") {
150 auto cellCountX = readInt32(value, "cellCountX");
151 auto cellCountZ = readInt32(value, "cellCountZ");
152 if (!cellCountX) return Result<HexTerrainBake>::failure(cellCountX.status());
153 if (!cellCountZ) return Result<HexTerrainBake>::failure(cellCountZ.status());
154 bake.cellCountX = cellCountX.value();
155 bake.cellCountZ = cellCountZ.value();
156 bake.cellCount = bake.cellCountX * bake.cellCountZ;
157 } else {
158 return invalidBake("hex terrain bake kind must be 'hex.terrain' or 'hex.sphere'");
159 }
160
161 auto seed = readUInt32(value, "seed");
162 if (!seed) return Result<HexTerrainBake>::failure(seed.status());
163 bake.seed = seed.value();
164
165 const Value* cellsField = value.find("cells");
166 if (cellsField == nullptr || !cellsField->isString())
167 return invalidBake("hex terrain bake is missing packed 'cells'");
168 if (bake.cellCount < 0) return invalidBake("hex terrain bake cell count must not be negative");
169 auto cells = unpackCells(cellsField->asString(), static_cast<std::size_t>(bake.cellCount));
170 if (!cells) return Result<HexTerrainBake>::failure(cells.status());
171 bake.cells = std::move(cells).takeValue();
172 return Result<HexTerrainBake>::success(std::move(bake));
173}
174
176 if (bake.kind != HexTerrainBake::Kind::Planar) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "applyHexTerrain requires a planar bake", "hexmap"));
177 if (bake.cellCountX <= 0 || bake.cellCountZ <= 0) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "planar bake size must be positive", "hexmap"));
178 if (static_cast<std::int32_t>(bake.cells.size()) != bake.cellCountX * bake.cellCountZ)
179 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "planar bake cell count does not match width and height", "hexmap"));
180
181 auto reset = map.reset(bake.cellCountX, bake.cellCountZ, bake.seed);
182 if (!reset) return reset;
183 for (std::int32_t index = 0; index < map.cellCount(); ++index) {
184 const HexCellData& cell = bake.cells[static_cast<std::size_t>(index)];
185 auto written = map.setCellState(map.coordinatesAt(index), cell.values, cell.flags);
186 if (!written) return written;
187 }
188 map.markAllChunksDirty();
189 return Result<void>::success();
190}
191
194 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "applyHexSphereTerrain requires a sphere bake", "hexmap"));
195 if (map.empty()) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "applyHexSphereTerrain requires an existing sphere topology", "hexmap"));
196 if (bake.cellCount <= 0 || static_cast<std::int32_t>(bake.cells.size()) != bake.cellCount)
197 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "sphere bake cell count is inconsistent", "hexmap"));
198 if (map.cellCount() != bake.cellCount || map.subdivision() != bake.subdivision)
199 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "sphere bake does not match the live topology; call newSphere with the same subdivision", "hexmap"));
200
201 for (std::int32_t cell = 0; cell < bake.cellCount; ++cell) {
202 const HexCellData& record = bake.cells[static_cast<std::size_t>(cell)];
203 auto written = map.setCellState(cell, record.values, record.flags);
204 if (!written) return written;
205 }
206 map.markAllDirty();
207 return Result<void>::success();
208}
209
210} // namespace eve::hexmap
double value
Value::Object payload
Stable, structured diagnostics shared by engine modules.
std::map< std::string, Var > values
std::string message
std::uint32_t key
Terrain bake hexmap applies; procgen produces the payload.
TokenKind kind
std::uint64_t bytes
std::array< PixelCell, kPixelChunkSize *kPixelChunkSize > cells
std::uint32_t seed
Definition PointSet.cpp:807
Cell cell
uint32_t index
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
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
const std::string & asString() const
Return the string payload; the caller must have checked the kind.
Definition Value.cpp:87
bool isNumeric() const noexcept
Return whether this value is an integer or finite number.
Definition Value.h:159
double asDouble() const
Return the Double payload; the caller must have checked the kind.
Definition Value.cpp:75
void set(const std::string &key, Value value)
Insert or replace an object member.
Definition Value.cpp:118
bool isString() const noexcept
Return true when this value is a string.
Definition Value.h:95
std::int64_t asInt() const
Return the Int64 payload; the caller must have checked the kind.
Definition Value.cpp:69
static Value object(Object value)
Compatibility factory for an object value.
Definition Value.h:114
bool isDouble() const noexcept
Return true when this value is a Double.
Definition Value.h:93
Value * find(const std::string &key) noexcept
Return an object member, or nullptr when absent.
Definition Value.cpp:124
An editable, chunked, pointy-top hex map.
Definition HexMap.h:72
HexCoordinates coordinatesAt(std::int32_t index) const noexcept
Coordinates of a linear cell index; out-of-range indices return (0, 0).
Definition HexMap.cpp:69
std::uint32_t seed() const noexcept
The seed this map was built with.
Definition HexMap.h:106
std::int32_t cellCountX() const noexcept
Number of columns.
Definition HexMap.h:94
void markAllChunksDirty() noexcept
Marks every chunk dirty.
Definition HexMap.cpp:152
Result< void > setCellState(HexCoordinates c, HexValues values, HexFlags flags)
Writes a complete cell record verbatim.
Definition HexMap.cpp:505
std::int32_t cellCount() const noexcept
Total number of cells.
Definition HexMap.h:104
Result< void > reset(std::int32_t cellCountX, std::int32_t cellCountZ, std::uint32_t seed)
Replaces the grid with a new cellCountX * cellCountZ map.
Definition HexMap.cpp:37
std::int32_t cellCountZ() const noexcept
Number of rows.
Definition HexMap.h:96
const HexCellData * cellAt(std::int32_t index) const noexcept
Cell record by linear index, or null when out of range. @ownership Borrowed; the map owns the record....
Definition HexMap.cpp:74
An editable hex map wrapped onto a sphere.
std::int32_t subdivision() const noexcept
The subdivision level this map was built at.
std::uint32_t seed() const noexcept
The seed this map was built with.
void markAllDirty() noexcept
Marks every cell dirty.
bool empty() const noexcept
Whether the map holds any cell.
float sphereRadius() const noexcept
Sphere radius in world units.
const HexCellData * cellAt(HexSphereCell cell) const noexcept
Cell record by id, or null when out of range. @ownership Borrowed; the map owns the record....
std::int32_t cellCount() const noexcept
Number of cells (10 * f^2 + 2).
Result< void > setCellState(HexSphereCell c, HexValues values, HexFlags flags)
Writes a complete cell record verbatim.
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
HexTerrainBake snapshotHexSphereTerrain(const HexSphereMap &map)
Snapshot every cell of a spherical map into a bake.
Result< void > applyHexTerrain(HexMap &map, const HexTerrainBake &bake)
Replace a planar map's cells with a bake.
Result< HexTerrainBake > hexTerrainBakeFromValue(const Value &value)
Parse a script bake payload produced by hexTerrainBakeToValue.
HexTerrainBake snapshotHexTerrain(const HexMap &map)
Snapshot every cell of a planar map into a bake.
Result< void > applyHexSphereTerrain(HexSphereMap &map, const HexTerrainBake &bake)
Replace a spherical map's cells with a bake.
Value hexTerrainBakeToValue(const HexTerrainBake &bake)
Project a bake into the script Result payload.
const EditorValue * field(const EditorValue &value, const char *name)
One cell of the hex map: packed values plus packed flags.
Definition HexCell.h:244
Packed terrain produced by a generator and applied onto a hex map.
std::vector< HexCellData > cells