载入中...
搜索中...
未找到
EvpackTerrainLoader.cpp
浏览该文件的文档.
2
3#include "common/Value.h"
5
6#include <algorithm>
7#include <bit>
8#include <cmath>
9#include <limits>
10
11namespace eve::asset_procgen {
12namespace {
13
14std::uint32_t little32(std::span<const std::uint8_t> bytes, std::size_t offset) {
15 return std::uint32_t(bytes[offset]) | (std::uint32_t(bytes[offset + 1]) << 8) |
16 (std::uint32_t(bytes[offset + 2]) << 16) | (std::uint32_t(bytes[offset + 3]) << 24);
17}
18
19float littleFloat(std::span<const std::uint8_t> bytes, std::size_t offset) {
20 return std::bit_cast<float>(little32(bytes, offset));
21}
22
23const Value* field(const Value::Object& object, std::string_view name) {
24 const auto found = object.find(std::string(name));
25 return found == object.end() ? nullptr : &found->second;
26}
27
28bool numericEquals(const Value* value, double expected) {
29 if (!value || !value->isNumeric()) return false;
30 const double actual = value->isInt64() ? double(value->asInt()) : value->asDouble();
31 return std::isfinite(actual) && actual == expected;
32}
33
34} // namespace
35
37 const AssetRef& terrainRef, const asset::EvpackCapabilities& capabilities,
38 const TerrainAssetLoadLimits& limits) const {
39 auto payload = reader_.read(terrainRef, "eve.terrain/1", capabilities,
40 limits.maximumDecodedBytes);
41 if (!payload) return Result<LoadedTerrain>::failure(payload.status());
42 const asset::RuntimeAssetChunk* definition = nullptr;
43 const asset::RuntimeAssetChunk* bulk = nullptr;
44 for (const auto& chunk : payload.value().chunks) {
45 if (chunk.kind == asset::EvpackChunkKind::Definition) {
46 if (definition)
48 "terrain has duplicate definition chunks", {},
49 {}, "asset.procgen.terrain"));
50 definition = &chunk;
51 } else if (chunk.kind == asset::EvpackChunkKind::Bulk) {
52 if (bulk)
54 DiagnosticCode::Conflict, "terrain has duplicate bulk chunks", {}, {}, "asset.procgen.terrain"));
55 bulk = &chunk;
56 }
57 }
58 static constexpr std::uint8_t magic[] = {'E', 'V', 'T', 'R', 'N', 0, 1, 0};
59 if (!definition || !bulk || bulk->bytes.size() < 24 ||
60 !std::equal(std::begin(magic), std::end(magic), bulk->bytes.begin()))
62 "terrain definition or EVTRN payload is invalid", {},
63 {}, "asset.procgen.terrain"));
64 const std::uint32_t width = little32(bulk->bytes, 8);
65 const std::uint32_t height = little32(bulk->bytes, 12);
66 const float spacingX = littleFloat(bulk->bytes, 16);
67 const float spacingZ = littleFloat(bulk->bytes, 20);
68 const std::uint64_t samples = std::uint64_t(width) * height;
69 if (width == 0 || height == 0 || width > limits.maximumDimension ||
70 height > limits.maximumDimension || samples > limits.maximumSamples ||
71 !std::isfinite(spacingX) || !std::isfinite(spacingZ) || spacingX <= 0.f ||
72 spacingZ <= 0.f || samples > (std::numeric_limits<std::uint64_t>::max() - 24) / 4 ||
73 samples * 4 + 24 != bulk->bytes.size())
75 DiagnosticCode::InvalidArgument, "terrain dimensions, spacing, or payload size is invalid", {}, {},
76 "asset.procgen.terrain"));
77 asset::RuntimeDefinitionLimits definitionLimits;
78 definitionLimits.maximumBytes = limits.maximumDecodedBytes;
79 auto metadata = asset::decodeRuntimeDefinition(definition->bytes, definitionLimits);
80 if (!metadata) return Result<LoadedTerrain>::failure(metadata.status());
81 const auto* root = metadata.value().getIf<Value::Object>();
82 const Value* schema = root ? field(*root, "schema") : nullptr;
83 const Value* version = root ? field(*root, "schemaVersion") : nullptr;
84 const Value* coordinate = root ? field(*root, "coordinateSystem") : nullptr;
85 const Value* unit = root ? field(*root, "heightUnit") : nullptr;
86 if (!schema || !schema->isString() || schema->asString() != "eve.terrain" ||
87 !version || !version->isInt64() || version->asInt() != 1 || !coordinate ||
88 !coordinate->isString() ||
89 coordinate->asString() != "right-handed-x-right-y-up-minus-z-forward" ||
90 !unit || !unit->isString() || unit->asString() != "meter" ||
91 !numericEquals(root ? field(*root, "width") : nullptr, width) ||
92 !numericEquals(root ? field(*root, "height") : nullptr, height) ||
93 !numericEquals(root ? field(*root, "spacingX") : nullptr, spacingX) ||
94 !numericEquals(root ? field(*root, "spacingZ") : nullptr, spacingZ))
96 "terrain metadata does not match EVTRN payload", {}, {},
97 "asset.procgen.terrain"));
98 procgen::Heightmap heightmap(static_cast<int>(width), static_cast<int>(height));
99 std::size_t cursor = 24;
100 for (std::uint32_t z = 0; z < height; ++z) {
101 for (std::uint32_t x = 0; x < width; ++x) {
102 const float value = littleFloat(bulk->bytes, cursor);
103 cursor += 4;
104 if (!std::isfinite(value))
106 "terrain contains a non-finite height", {}, {},
107 "asset.procgen.terrain"));
108 heightmap.setHeight(static_cast<int>(x), static_cast<int>(z), value);
109 }
110 }
111 // SpatialData currently has one cell-size axis. Preserve the exact map and reject
112 // anisotropic sampling rather than silently distorting the procgen domain.
113 if (spacingX != spacingZ)
115 DiagnosticCode::Unsupported, "anisotropic terrain spacing is not supported by SpatialData", {}, {},
116 "asset.procgen.terrain"));
117 auto spatial = procgen::SpatialData::heightfield(heightmap, 0.f, 0.f, spacingX, 1.f);
119 {terrainRef, std::move(heightmap), std::move(spatial), spacingX, spacingZ,
120 std::move(payload).takeValue().variant});
121}
122
123} // namespace eve::asset_procgen
double value
Value::Object payload
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int root
Definition AnimSmr.cpp:119
eve::action::ActionSpatialBinding spatial
Runtime decoding of canonical terrain assets.
std::uint32_t height
std::uint32_t width
size_t offset
std::uint64_t bytes
std::string name
bool found
JSON-free versioned runtime metadata codec.
TacticalUnit * unit
std::size_t cursor
double spacingZ
double spacingX
const AssetImportLimits & limits
Owning, renderer-independent dynamic values.
Stable asset identity backed by PersistentId.
Definition ResourceRef.h:53
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 isInt64() const noexcept
Return true when this value is an Int64.
Definition Value.h:91
std::map< std::string, Value > Object
Definition Value.h:34
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
Result< RuntimeAssetPayload > read(const AssetRef &asset, std::string_view expectedType, const EvpackCapabilities &capabilities, std::uint64_t maximumDecodedBytes) const
Resolve, type-check, variant-select and decode a canonical runtime asset.
Result< LoadedTerrain > load(const AssetRef &terrain, const asset::EvpackCapabilities &capabilities, const TerrainAssetLoadLimits &limits={}) const
Decode and validate one canonical terrain without mutating a world.
In-memory terrain heightmap: a dense float grid (row-major, index = y * width + x) materialized from ...
Definition Heightmap.h:21
void setHeight(int x, int y, float h)
Sets the height.
Definition Heightmap.cpp:23
static SpatialData heightfield(const Heightmap &heightmap, float originX, float originZ, float cellSize, float heightScale)
Create a sampled heightfield surface in world space.
std::uint32_t little32(std::span< const std::uint8_t > bytes, std::size_t offset)
Read a uint32 after the caller has bounded the byte range.
Result< Value > decodeRuntimeDefinition(std::span< const std::uint8_t > bytes, const RuntimeDefinitionLimits &limits)
Decode a bounded EVDEF\0\1 metadata tree without a JSON parser.
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
const EditorValue * field(const EditorValue &value, const char *name)
Actual runtime device capabilities used for variant selection.
Definition Evpack.h:92
One decoded runtime chunk with its admitted semantic metadata.
std::vector< std::uint8_t > bytes
Structural and allocation limits for untrusted EVDEF metadata.
Explicit resource limits applied before terrain allocation.