载入中...
搜索中...
未找到
TerrainDocumentCodec.cpp
浏览该文件的文档.
2
4
5#include <cmath>
6#include <limits>
7#include <set>
8#include <string>
9
10namespace eve::heightmap_target {
11namespace {
12
13const editor::EditorValue* field(const editor::EditorValue::Object& object, std::string_view name) {
14 const auto found = object.find(std::string(name));
15 return found == object.end() ? nullptr : &found->second;
16}
17
18bool dimension(const editor::EditorValue* value, std::uint32_t& out) {
19 const auto* integer = value ? value->getIf<std::int64_t>() : nullptr;
20 if (!integer || *integer <= 0 || std::uint64_t(*integer) > std::numeric_limits<std::uint32_t>::max()) return false;
21 out = static_cast<std::uint32_t>(*integer);
22 return true;
23}
24
25bool finiteNumber(const editor::EditorValue* value, float& out) {
26 if (!value) return false;
27 double number = 0.0;
28 if (const auto* integer = value->getIf<std::int64_t>())
29 number = static_cast<double>(*integer);
30 else if (const auto* real = value->getIf<double>())
31 number = *real;
32 else
33 return false;
34 if (!std::isfinite(number) || number < -std::numeric_limits<float>::max() ||
35 number > std::numeric_limits<float>::max())
36 return false;
37 out = static_cast<float>(number);
38 return true;
39}
40
41} // namespace
42
45 const std::uint64_t width = static_cast<std::uint64_t>(heightmap.getWidth());
46 const std::uint64_t height = static_cast<std::uint64_t>(heightmap.getHeight());
47 const std::uint64_t samples = width * height;
48 if (width == 0 || height == 0 || width > limits.maximumDimension || height > limits.maximumDimension ||
49 samples > limits.maximumSamples || !std::isfinite(spacingX) || !std::isfinite(spacingZ) || spacingX <= 0.0F ||
50 spacingZ <= 0.0F || samples != heightmap.data().size())
51 return editing::failed<editor::EditorValue>(editor::EditorStatus::Rejected,
52 editor::RuleId("editor.terrain.document-invalid"),
53 "Terrain dimensions, spacing, or sample count is invalid");
55 heights.reserve(heightmap.data().size());
56 for (float value : heightmap.data()) {
57 if (!std::isfinite(value))
58 return editing::failed<editor::EditorValue>(editor::EditorStatus::Rejected,
59 editor::RuleId("editor.terrain.document-invalid"),
60 "Terrain contains a non-finite height");
61 heights.emplace_back(static_cast<double>(value));
62 }
63 return editing::applied<editor::EditorValue>(
64 editor::EditorValue::Object{{"schema", "eve.terrain-editor-document"},
65 {"schemaVersion", std::int64_t{1}},
66 {"width", static_cast<std::int64_t>(width)},
67 {"height", static_cast<std::int64_t>(height)},
68 {"spacingX", static_cast<double>(spacingX)},
69 {"spacingZ", static_cast<double>(spacingZ)},
70 {"heights", std::move(heights)}});
71}
72
75 const auto* object = value.getIf<editor::EditorValue::Object>();
76 static const std::set<std::string, std::less<>> allowed{"schema", "schemaVersion", "width", "height",
77 "spacingX", "spacingZ", "heights"};
78 if (!object || object->size() != allowed.size())
79 return editing::failed<TerrainDocumentData>(editor::EditorStatus::Rejected,
80 editor::RuleId("editor.terrain.document-invalid"),
81 "Terrain document fields do not match schema version 1");
82 for (const auto& [name, ignored] : *object) {
83 (void)ignored;
84 if (!allowed.contains(name))
85 return editing::failed<TerrainDocumentData>(editor::EditorStatus::Rejected,
86 editor::RuleId("editor.terrain.document-invalid"),
87 "Terrain document contains an unknown field: " + name);
88 }
89 const auto* schema = field(*object, "schema");
90 const auto* version = field(*object, "schemaVersion");
91 const auto* schemaText = schema ? schema->getIf<std::string>() : nullptr;
92 const auto* versionNumber = version ? version->getIf<std::int64_t>() : nullptr;
93 if (!schemaText || *schemaText != "eve.terrain-editor-document" || !versionNumber || *versionNumber != 1)
94 return editing::failed<TerrainDocumentData>(editor::EditorStatus::Rejected,
95 editor::RuleId("editor.terrain.document-invalid"),
96 "Terrain document schema or version is unsupported");
97 std::uint32_t width = 0, height = 0;
98 float spacingX = 0.0F, spacingZ = 0.0F;
99 if (!dimension(field(*object, "width"), width) || !dimension(field(*object, "height"), height) ||
100 width > limits.maximumDimension || height > limits.maximumDimension ||
101 std::uint64_t(width) * height > limits.maximumSamples || !finiteNumber(field(*object, "spacingX"), spacingX) ||
102 !finiteNumber(field(*object, "spacingZ"), spacingZ) || spacingX <= 0.0F || spacingZ <= 0.0F)
103 return editing::failed<TerrainDocumentData>(editor::EditorStatus::Rejected,
104 editor::RuleId("editor.terrain.document-invalid"),
105 "Terrain document dimensions or spacing is invalid");
106 const auto* heightsValue = field(*object, "heights");
107 const auto* heights = heightsValue ? heightsValue->getIf<editor::EditorValue::Array>() : nullptr;
108 if (!heights || heights->size() != std::uint64_t(width) * height)
109 return editing::failed<TerrainDocumentData>(editor::EditorStatus::Rejected,
110 editor::RuleId("editor.terrain.document-invalid"),
111 "Terrain document height count does not match dimensions");
112 TerrainDocumentData result;
113 result.heightmap.resize(static_cast<int>(width), static_cast<int>(height));
114 result.spacingX = spacingX;
115 result.spacingZ = spacingZ;
116 for (std::size_t index = 0; index < heights->size(); ++index) {
117 float sample = 0.0F;
118 if (!finiteNumber(&(*heights)[index], sample))
119 return editing::failed<TerrainDocumentData>(editor::EditorStatus::Rejected,
120 editor::RuleId("editor.terrain.document-invalid"),
121 "Terrain document contains a non-finite height");
122 result.heightmap.data()[index] = sample;
123 }
124 return editing::applied<TerrainDocumentData>(std::move(result));
125}
126
127} // namespace eve::heightmap_target
double value
std::uint32_t height
std::uint32_t width
std::string name
double number
bool found
Versioned editor persistence for editable heightfields.
double spacingZ
double spacingX
Json object
uint32_t index
const AssetImportLimits & limits
Move-only operation result carrying either a value or Status.
Definition Result.h:155
UUID-backed identifier adapter for legacy textual boundaries.
Definition Identity.h:314
Deterministic owning value tree shared by authoring hosts.
std::vector< Value > Array
std::map< std::string, Value > Object
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
eve::editing::Value EditorValue
Compatibility alias for the UI-independent authoring value tree.
Definition EditorValue.h:8
editor::Result< TerrainDocumentData > decodeTerrainDocument(const editor::EditorValue &value, const TerrainDocumentLimits &limits)
Validate and atomically decode one terrain editor snapshot.
editor::Result< editor::EditorValue > encodeTerrainDocument(const procgen::Heightmap &heightmap, float spacingX, float spacingZ, const TerrainDocumentLimits &limits)
Encode the current authoritative heightmap as eve.terrain-editor-document/1.
const EditorValue * field(const EditorValue &value, const char *name)
int64_t integer(const RuntimeTensor &v, size_t i=0)
Integer.
Owning editable terrain state restored from one document snapshot.
Allocation limits checked before an editor terrain document is materialized.