载入中...
搜索中...
未找到
GtsTerrainLodCodec.cpp
浏览该文件的文档.
2
3#include "common/Value.h"
4
5#include <cmath>
6#include <cstdint>
7#include <limits>
8#include <set>
9
10namespace eve::procgen {
11namespace {
12constexpr std::size_t kMaxJsonBytes = 256U * 1024U * 1024U, kMaxTiles = 65536, kMaxLevels = 32,
13 kMaxVertices = 8U * 1024U * 1024U;
14bool exact(const Value::Object& o, std::initializer_list<const char*> names) {
15 if (o.size() != names.size()) return false;
16 for (auto* n : names)
17 if (!o.contains(n)) return false;
18 return true;
19}
20const double* num(const Value& v) { return v.getIf<double>(); }
21Value options(const GtsMeshSimplificationOptions& o) {
22 return Value::Object{{"aggressiveness", o.aggressiveness},
23 {"enableSmartLink", o.enableSmartLink},
24 {"maxIterationCount", o.maxIterationCount},
25 {"preserveBorderEdges", o.preserveBorderEdges},
26 {"preserveSurfaceCurvature", o.preserveSurfaceCurvature},
27 {"preserveUvFoldoverEdges", o.preserveUvFoldoverEdges},
28 {"preserveUvSeamEdges", o.preserveUvSeamEdges},
29 {"vertexLinkDistance", o.vertexLinkDistance}};
30}
31bool decodeOptions(const Value& v, GtsMeshSimplificationOptions& o) {
32 auto* p = v.getIf<Value::Object>();
33 if (!p || !exact(*p, {"aggressiveness", "enableSmartLink", "maxIterationCount", "preserveBorderEdges",
34 "preserveSurfaceCurvature", "preserveUvFoldoverEdges", "preserveUvSeamEdges",
35 "vertexLinkDistance"}))
36 return false;
37 auto ag = num(p->at("aggressiveness")), link = num(p->at("vertexLinkDistance"));
38 auto count = p->at("maxIterationCount").getIf<int64_t>();
39 auto smart = p->at("enableSmartLink").getIf<bool>(), border = p->at("preserveBorderEdges").getIf<bool>(),
40 curve = p->at("preserveSurfaceCurvature").getIf<bool>(), fold = p->at("preserveUvFoldoverEdges").getIf<bool>(),
41 seam = p->at("preserveUvSeamEdges").getIf<bool>();
42 if (!ag || !link || !count || !smart || !border || !curve || !fold || !seam || !std::isfinite(*ag) || *ag <= 0 ||
43 !std::isfinite(*link) || *link < 0 || *count <= 0 || *count > 100000)
44 return false;
45 o.aggressiveness = *ag;
46 o.vertexLinkDistance = *link;
47 o.maxIterationCount = int(*count);
48 o.enableSmartLink = *smart;
49 o.preserveBorderEdges = *border;
50 o.preserveSurfaceCurvature = *curve;
51 o.preserveUvFoldoverEdges = *fold;
52 o.preserveUvSeamEdges = *seam;
53 return true;
54}
55Value encodeMesh(const MeshBuild& m) {
56 Value::Array p, n, u, c, i, g;
57 p.reserve(m.positions().size());
58 n.reserve(m.normals().size());
59 u.reserve(m.uvs().size());
60 c.reserve(m.colors().size());
61 i.reserve(m.indices().size());
62 g.reserve(m.getIndexCount() / 3);
63 for (float x : m.positions()) p.emplace_back(x);
64 for (float x : m.normals()) n.emplace_back(x);
65 for (float x : m.uvs()) u.emplace_back(x);
66 for (float x : m.colors()) c.emplace_back(x);
67 for (auto x : m.indices()) i.emplace_back(int64_t(x));
68 for (int t = 0; t < m.getIndexCount() / 3; ++t) {
69 int group = m.getTriangleGroup(t);
70 g.emplace_back(group >= 0 ? m.getGroupName(group) : std::string{});
71 }
72 return Value::Object{{"colors", std::move(c)}, {"groups", std::move(g)}, {"indices", std::move(i)},
73 {"normals", std::move(n)}, {"positions", std::move(p)}, {"uvs", std::move(u)}};
74}
75bool decodeFloatArray(const Value& v, std::vector<float>& out, std::size_t max) {
76 auto* a = v.getIf<Value::Array>();
77 if (!a || a->size() > max) return false;
78 out.reserve(a->size());
79 for (auto& x : *a) {
80 auto* d = num(x);
81 if (!d || !std::isfinite(*d) || *d < -std::numeric_limits<float>::max() ||
82 *d > std::numeric_limits<float>::max())
83 return false;
84 out.push_back(float(*d));
85 }
86 return true;
87}
88bool decodeMesh(const Value& v, MeshBuild& out, bool legacy) {
89 auto* o = v.getIf<Value::Object>();
90 if (!o || !(legacy ? exact(*o, {"groups", "indices", "normals", "positions", "uvs"})
91 : exact(*o, {"colors", "groups", "indices", "normals", "positions", "uvs"})))
92 return false;
93 std::vector<float> p, n, u, c;
94 if (!decodeFloatArray(o->at("positions"), p, kMaxVertices * 3) ||
95 !decodeFloatArray(o->at("normals"), n, kMaxVertices * 3) ||
96 !decodeFloatArray(o->at("uvs"), u, kMaxVertices * 2) ||
97 (!legacy && !decodeFloatArray(o->at("colors"), c, kMaxVertices * 4)) || p.size() % 3 || u.size() % 2 ||
98 n.size() != p.size() || u.size() / 2 != p.size() / 3 || (!c.empty() && c.size() / 4 != p.size() / 3))
99 return false;
100 auto* indices = o->at("indices").getIf<Value::Array>();
101 auto* groups = o->at("groups").getIf<Value::Array>();
102 if (!indices || indices->size() % 3 || indices->size() > kMaxVertices * 6 || !groups ||
103 groups->size() != indices->size() / 3)
104 return false;
105 MeshBuild m;
106 for (size_t x = 0; x < p.size() / 3; ++x)
107 m.addVertex(p[x * 3], p[x * 3 + 1], p[x * 3 + 2], n[x * 3], n[x * 3 + 1], n[x * 3 + 2], u[x * 2], u[x * 2 + 1]);
108 if (!c.empty() && !m.setVertexColors(std::move(c)).ok()) return false;
109 for (size_t t = 0; t < groups->size(); ++t) {
110 auto* name = (*groups)[t].getIf<std::string>();
111 if (!name || name->size() > 1024) return false;
112 uint32_t ids[3];
113 for (int corner = 0; corner < 3; ++corner) {
114 auto* id = (*indices)[t * 3 + corner].getIf<int64_t>();
115 if (!id || *id < 0 || *id >= int64_t(p.size() / 3)) return false;
116 ids[corner] = uint32_t(*id);
117 }
118 if (ids[0] == ids[1] || ids[1] == ids[2] || ids[0] == ids[2]) return false;
119 m.setActiveGroup(*name);
120 m.addTriangle(ids[0], ids[1], ids[2]);
121 }
122 out = std::move(m);
123 return true;
124}
125} // namespace
126
128 Value::Array qualities;
129 for (float quality : lodQuality_) qualities.emplace_back(quality);
130 return Value(Value::Object{{"lodCount", lodCount_},
131 {"lodQuality", std::move(qualities)},
132 {"saveResolution", saveResolution_},
133 {"schema", "eve.procgen.gts-terrain-mesh-settings"},
134 {"subTiles", subTiles_},
135 {"version", 1}})
136 .toJson();
137}
138
140 if (json.size() > 64U * 1024U)
142 "GTS terrain mesh settings JSON exceeds size limit",
143 "procgen.gtsTerrainLod.codec"));
144 auto parsed = Value::fromJson(json);
145 if (!parsed) return Result<void>::failure(parsed.status());
146 auto* root = parsed.value().getIf<Value::Object>();
147 if (!root || !exact(*root, {"lodCount", "lodQuality", "saveResolution", "schema", "subTiles", "version"}))
149 "GTS terrain mesh settings fields are invalid",
150 "procgen.gtsTerrainLod.codec"));
151 auto* schema = root->at("schema").getIf<std::string>();
152 auto* version = root->at("version").getIf<int64_t>();
153 auto* resolution = root->at("saveResolution").getIf<int64_t>();
154 auto* count = root->at("lodCount").getIf<int64_t>();
155 auto* subTiles = root->at("subTiles").getIf<int64_t>();
156 auto* qualities = root->at("lodQuality").getIf<Value::Array>();
157 if (!schema || *schema != "eve.procgen.gts-terrain-mesh-settings" || !version || *version != 1 || !resolution ||
158 *resolution < 0 || *resolution > 4 || !count || *count < 1 || *count > 4 || !subTiles || *subTiles < 0 ||
159 *subTiles > 5 || !qualities || qualities->size() != 4)
161 "GTS terrain mesh settings schema or values are invalid",
162 "procgen.gtsTerrainLod.codec"));
163 GtsTerrainMeshSettings candidate;
164 auto a = candidate.setSaveResolution(int(*resolution));
165 if (!a) return a;
166 auto b = candidate.setLodCount(int(*count));
167 if (!b) return b;
168 auto c = candidate.setSubTiles(int(*subTiles));
169 if (!c) return c;
170 for (int i = 0; i < 4; ++i) {
171 auto* quality = num((*qualities)[size_t(i)]);
172 if (!quality)
174 "GTS terrain mesh LOD quality is invalid",
175 "procgen.gtsTerrainLod.codec"));
176 auto set = candidate.setLodQuality(i, float(*quality));
177 if (!set) return set;
178 }
179 *this = std::move(candidate);
180 return Result<void>::success();
181}
182
184 if (tiles_.size() > kMaxTiles || settings_.empty() || settings_.size() > kMaxLevels)
186 "GTS terrain LOD set exceeds codec limits",
187 "procgen.gtsTerrainLod.codec"));
189 for (auto& s : settings_)
190 settings.emplace_back(Value::Object{{"options", options(s.simplification)},
191 {"quality", s.quality},
192 {"transitionHeight", s.screenRelativeTransitionHeight}});
193 for (auto& t : tiles_) {
194 Value::Array levels;
195 for (auto& m : t.levels) levels.emplace_back(encodeMesh(m));
196 tiles.emplace_back(
197 Value::Object{{"levels", std::move(levels)}, {"offsetX", t.offsetX}, {"offsetZ", t.offsetZ}});
198 }
199 auto json = Value(Value::Object{{"columns", columns_},
200 {"rows", rows_},
201 {"schema", "eve.procgen.gts-terrain-lod"},
202 {"settings", std::move(settings)},
203 {"tiles", std::move(tiles)},
204 {"version", 2}})
205 .toJson();
206 if (!json) return json;
207 if (json.value().size() > kMaxJsonBytes)
209 DiagnosticCode::InvalidArgument, "GTS terrain LOD JSON exceeds size limit", "procgen.gtsTerrainLod.codec"));
210 return json;
211}
212
213Result<void> GtsTerrainLodSet::restoreJson(const std::string& json) {
214 if (json.size() > kMaxJsonBytes)
216 DiagnosticCode::InvalidArgument, "GTS terrain LOD JSON exceeds size limit", "procgen.gtsTerrainLod.codec"));
217 auto parsed = Value::fromJson(json);
218 if (!parsed) return Result<void>::failure(parsed.status());
219 auto* root = parsed.value().getIf<Value::Object>();
220 if (!root || !exact(*root, {"columns", "rows", "schema", "settings", "tiles", "version"}))
222 DiagnosticCode::InvalidArgument, "GTS terrain LOD root fields are invalid", "procgen.gtsTerrainLod.codec"));
223 auto* schema = root->at("schema").getIf<std::string>();
224 auto* version = root->at("version").getIf<int64_t>();
225 auto* columns = root->at("columns").getIf<int64_t>();
226 auto* rows = root->at("rows").getIf<int64_t>();
227 auto* settings = root->at("settings").getIf<Value::Array>();
228 auto* tiles = root->at("tiles").getIf<Value::Array>();
229 if (!schema || *schema != "eve.procgen.gts-terrain-lod" || !version || (*version != 1 && *version != 2) ||
230 !columns || !rows || *columns <= 0 || *rows <= 0 || *columns > 65536 || *rows > 65536 || !settings ||
231 settings->empty() || settings->size() > kMaxLevels || !tiles || tiles->size() > kMaxTiles ||
232 uint64_t(*columns) * uint64_t(*rows) != tiles->size())
234 "GTS terrain LOD schema, layout or counts are invalid",
235 "procgen.gtsTerrainLod.codec"));
236 GtsTerrainLodSet candidate;
237 candidate.columns_ = int(*columns);
238 candidate.rows_ = int(*rows);
239 float previous = 1;
240 for (auto& encoded : *settings) {
241 auto* o = encoded.getIf<Value::Object>();
242 if (!o || !exact(*o, {"options", "quality", "transitionHeight"}))
244 "GTS terrain LOD setting fields are invalid",
245 "procgen.gtsTerrainLod.codec"));
246 auto * q = num(o->at("quality")), *h = num(o->at("transitionHeight"));
248 if (!q || !h || !std::isfinite(*q) || *q < 0 || *q > 1 || !std::isfinite(*h) || *h < 0 || *h >= previous ||
249 !decodeOptions(o->at("options"), s.simplification))
251 "GTS terrain LOD setting values are invalid",
252 "procgen.gtsTerrainLod.codec"));
253 s.quality = float(*q);
254 s.screenRelativeTransitionHeight = float(*h);
255 previous = s.screenRelativeTransitionHeight;
256 candidate.settings_.push_back(s);
257 }
258 for (auto& encoded : *tiles) {
259 auto* o = encoded.getIf<Value::Object>();
260 if (!o || !exact(*o, {"levels", "offsetX", "offsetZ"}))
262 "GTS terrain LOD tile fields are invalid",
263 "procgen.gtsTerrainLod.codec"));
264 auto *x = num(o->at("offsetX")), *z = num(o->at("offsetZ"));
265 auto* levels = o->at("levels").getIf<Value::Array>();
266 if (!x || !z || !std::isfinite(*x) || !std::isfinite(*z) || !levels ||
267 levels->size() != candidate.settings_.size())
269 "GTS terrain LOD tile values are invalid",
270 "procgen.gtsTerrainLod.codec"));
272 tile.offsetX = float(*x);
273 tile.offsetZ = float(*z);
274 for (auto& m : *levels) {
275 MeshBuild decoded;
276 if (!decodeMesh(m, decoded, *version == 1))
278 DiagnosticCode::InvalidArgument, "GTS terrain LOD mesh is invalid", "procgen.gtsTerrainLod.codec"));
279 tile.levels.push_back(std::move(decoded));
280 }
281 candidate.tiles_.push_back(std::move(tile));
282 }
283 *this = std::move(candidate);
284 return Result<void>::success();
285}
286} // namespace eve::procgen
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int root
Definition AnimSmr.cpp:119
const std::string & s
building::EdgeCurveGroup group
glm::vec4 p[6]
int rows
tensor::Graph g
Definition GpuGraph.cpp:7
glm::uvec4 ids
float u
Definition Grass.cpp:233
glm::vec3 n
Definition Grass.cpp:63
std::array< double, 10 > q
bool seam
bool border
std::vector< std::uint32_t > indices
std::vector< float > normals
std::vector< float > positions
float v
std::int32_t c
int h
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
graphics::Canvas * previous
uint32_t groups
Definition OnnxGpgpu.cpp:39
float d
float t
std::vector< float > colors
std::string string
std::uint32_t count
const SquirrelValueOptions & options
Heightmap curve
TerrainThermalSettings settings
int columns
Owning, renderer-independent dynamic values.
float m[16]
std::vector< WfcTile > tiles
Definition WfcSimple.cpp:22
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
static Result< Value > fromJson(std::string_view json)
Parse one strict JSON value into an owning Value.
Definition Value.cpp:57
std::map< std::string, Value > Object
Definition Value.h:34
std::vector< Value > Array
Definition Value.h:33
Owned result of the GTS split-and-sequential-simplify pipeline.
Result< std::string > snapshotJson() const
Serialize the complete owned LOD set as schema-versioned JSON.
Schema-versioned native counterpart of GTSMeshSettings.
Result< void > setLodQuality(int index, float percent)
Set one of the four simplification percentages in the range 0 through 100.
Result< void > setSaveResolution(int value)
Atomically set the SaveResolution enum value.
Result< void > setLodCount(int value)
Atomically set the active LOD count.
Result< std::string > snapshotJson() const
Serialize as strict eve.procgen.gts-terrain-mesh-settings version 1 JSON.
Result< void > setSubTiles(int value)
Atomically set the equal X/Z split-plane count.
Result< void > restoreJson(const std::string &json)
Atomically restore settings from strict schema-versioned JSON.
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
Definition MeshBuild.h:19
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
Configuration for one sequential GTS terrain mesh LOD.
One split terrain tile and all of its local-space LOD meshes.
std::vector< MeshBuild > levels