载入中...
搜索中...
未找到
CanonicalMesh.cpp
浏览该文件的文档.
2#include <algorithm>
3
4#include <bit>
5#include <cmath>
6#include <limits>
7
8namespace eve::asset {
9namespace {
10
11std::uint32_t little32(std::span<const std::uint8_t> bytes, std::size_t offset) {
12 return std::uint32_t(bytes[offset]) | (std::uint32_t(bytes[offset + 1]) << 8) |
13 (std::uint32_t(bytes[offset + 2]) << 16) | (std::uint32_t(bytes[offset + 3]) << 24);
14}
15
16float littleFloat(std::span<const std::uint8_t> bytes, std::size_t offset) {
17 return std::bit_cast<float>(little32(bytes, offset));
18}
19
20} // namespace
21
23 static constexpr std::uint8_t magic[] = {'E', 'V', 'M', 'E', 'S', 'H', 0};
24 if (bytes.size() < 24 || !std::equal(std::begin(magic), std::end(magic), bytes.begin()) ||
25 (bytes[7] != 1 && bytes[7] != 2 && bytes[7] != 3))
27 Diagnostic::error(DiagnosticCode::ParseError, "canonical mesh header is invalid", {}, {}, "asset.mesh"));
28 const auto vertexCount = little32(bytes, 8), indexCount = little32(bytes, 12);
29 const auto flags = little32(bytes, 16), countField = little32(bytes, 20);
30 const bool legacy = bytes[7] == 1;
31 const uint32_t candidateUvCount = legacy ? ((flags & 2u) ? 1u : 0u) : countField;
32 const uint64_t standardFloats =
33 3ull + ((flags & 1u) ? 3ull : 0ull) + uint64_t(candidateUvCount) * 2ull + ((flags & 2u) ? 4ull : 0ull);
34 const uint64_t standardSize = 24ull + (legacy ? 0ull : uint64_t(candidateUvCount) * 4ull) +
35 uint64_t(vertexCount) * standardFloats * 4ull + uint64_t(indexCount) * 4ull;
36 const bool standardV3 = bytes[7] == 3 && flags <= 3u && standardSize == bytes.size();
37 const bool extended = bytes[7] == 3 && !standardV3;
38 const bool colored = standardV3 && (flags & 2u);
39 if (extended && bytes.size() < 28)
41 DiagnosticCode::ParseError, "canonical attribute header truncated", {}, {}, "asset.mesh"));
42 if (!vertexCount || !indexCount || indexCount % 3 || flags > (legacy || standardV3 ? 3u : 1u) ||
43 (legacy && countField))
45 Diagnostic::error(DiagnosticCode::ParseError, "canonical mesh metadata is invalid", {}, {}, "asset.mesh"));
46 if (vertexCount > limits.maximumVertices || indexCount > limits.maximumIndices)
48 Diagnostic::error(DiagnosticCode::InvalidArgument, "canonical mesh exceeds limits", {}, {}, "asset.mesh"));
49 const uint32_t uvCount = legacy ? ((flags & 2u) ? 1u : 0u) : countField;
50 const uint32_t attributeCount = extended ? little32(bytes, 24) : 0;
51 const uint64_t uvOffset = extended ? 28ull : 24ull;
52 const uint64_t attributeOffset = uvOffset + (legacy ? 0ull : uint64_t(uvCount) * 4);
53 const uint64_t header = attributeOffset + uint64_t(attributeCount) * 72;
54 uint64_t floatsPerVertex =
55 3ull + ((flags & 1u) ? 3 : 0) + uint64_t(uvCount) * 2 + (colored ? 4ull : 0ull);
56 if (header > bytes.size() || header > limits.maximumDecodedBytes)
58 DiagnosticCode::InvalidArgument, "canonical descriptors exceed budget", {}, {}, "asset.mesh"));
59 CanonicalMeshData result;
60 std::string previousName;
61 for (uint32_t i = 0; i < attributeCount; ++i) {
62 const auto at = size_t(attributeOffset + uint64_t(i) * 72);
63 std::string name;
64 bool ended = false;
65 for (unsigned j = 0; j < 64; ++j) {
66 const auto c = bytes[at + j];
67 if (!c) {
68 ended = true;
69 continue;
70 }
71 if (ended || !((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'))
73 Diagnostic::error(DiagnosticCode::ParseError, "invalid attribute semantic", {}, {}, "asset.mesh"));
74 name.push_back(char(c));
75 }
76 const auto components = little32(bytes, at + 64);
77 if (!ended || name.empty() || name <= previousName || name == "POSITION" || name == "NORMAL" ||
78 name.starts_with("TEXCOORD_") || !components || components > 4 || little32(bytes, at + 68))
80 Diagnostic::error(DiagnosticCode::ParseError, "invalid attribute descriptor", {}, {}, "asset.mesh"));
81 previousName = name;
82 floatsPerVertex += components;
83 result.attributes.emplace(std::move(name), CanonicalMeshAttribute{components, {}});
84 }
85 // Bound the product through the budget before multiplying untrusted counts.
86 const uint64_t indexBytes = uint64_t(indexCount) * 4;
87 if (header > bytes.size() || header > limits.maximumDecodedBytes ||
88 indexBytes > limits.maximumDecodedBytes - header ||
89 floatsPerVertex > (limits.maximumDecodedBytes - header - indexBytes) / 4 / vertexCount)
91 DiagnosticCode::InvalidArgument, "canonical mesh exceeds byte budget", {}, {}, "asset.mesh"));
92 const uint64_t expected = header + uint64_t(vertexCount) * floatsPerVertex * 4 + uint64_t(indexCount) * 4;
93 if (expected != bytes.size() || expected > limits.maximumDecodedBytes)
95 DiagnosticCode::InvalidArgument, "canonical mesh byte size is invalid", {}, {}, "asset.mesh"));
96 result.positions.reserve(std::size_t(vertexCount) * 3);
97 if (flags & 1u) result.normals.reserve(std::size_t(vertexCount) * 3);
98 if (colored) result.colors.reserve(std::size_t(vertexCount) * 4);
99 uint32_t previous = 0;
100 for (uint32_t set = 0; set < uvCount; ++set) {
101 const auto id = legacy ? 0u : little32(bytes, size_t(uvOffset) + size_t(set) * 4);
102 if (set && id <= previous)
104 DiagnosticCode::ParseError, "UV set descriptors must be strictly increasing", {}, {}, "asset.mesh"));
105 previous = id;
106 result.texcoords[id].reserve(size_t(vertexCount) * 2);
107 }
108 for (auto& [name, attribute] : result.attributes)
109 attribute.values.reserve(size_t(vertexCount) * attribute.components);
110 std::size_t cursor = size_t(header);
111 auto appendFloats = [&](std::vector<float>& output, std::uint32_t count) -> Result<void> {
112 for (std::uint32_t index = 0; index < count; ++index) {
113 const float value = littleFloat(bytes, cursor);
114 cursor += 4;
115 if (!std::isfinite(value))
117 DiagnosticCode::ParseError, "canonical mesh contains a non-finite value", {}, {}, "asset.mesh"));
118 output.push_back(value);
119 }
120 return Result<void>::success();
121 };
122 for (std::uint32_t vertex = 0; vertex < vertexCount; ++vertex) {
123 auto positions = appendFloats(result.positions, 3);
125 if (flags & 1u) {
126 auto normals = appendFloats(result.normals, 3);
128 }
129 for (auto& [set, values] : result.texcoords) {
130 auto texcoords = appendFloats(values, 2);
132 }
133 for (auto& [name, attribute] : result.attributes) {
134 auto values = appendFloats(attribute.values, attribute.components);
135 if (!values) return Result<CanonicalMeshData>::failure(values.status());
136 }
137 if (colored) {
138 auto colors = appendFloats(result.colors, 4);
139 if (!colors) return Result<CanonicalMeshData>::failure(colors.status());
140 }
141 }
142 result.indices.reserve(indexCount);
143 for (std::uint32_t index = 0; index < indexCount; ++index) {
144 const std::uint32_t value = little32(bytes, cursor);
145 cursor += 4;
146 if (value >= vertexCount)
148 DiagnosticCode::ParseError, "canonical mesh index exceeds vertex count", {}, {}, "asset.mesh"));
149 result.indices.push_back(value);
150 }
151 return Result<CanonicalMeshData>::success(std::move(result));
152}
153
154} // namespace eve::asset
double value
std::string output
std::map< std::string, Var > values
std::uint32_t vertexCount
std::uint32_t indexCount
float u
Definition Grass.cpp:233
std::vector< float > texcoords
std::vector< float > normals
std::vector< float > positions
std::int32_t c
size_t offset
std::uint64_t bytes
bool ended
std::string name
graphics::Canvas * previous
std::string id
Definition PlayHost.cpp:108
std::vector< float > colors
std::uint32_t count
std::size_t cursor
uint32_t index
const AssetImportLimits & limits
std::size_t at
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
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< CanonicalMeshData > decodeCanonicalMesh(std::span< const std::uint8_t > bytes, const CanonicalMeshLimits &limits)
Decode canonical EVMESH binary v1, v2 or v3 into independently owned arrays.
Owning float vertex attribute; one to four components per vertex.
Owning CPU mesh snapshot; UV arrays are packed ST pairs indexed by source set number....
std::vector< float > colors
std::vector< std::uint32_t > indices
std::vector< float > normals
std::map< std::string, CanonicalMeshAttribute > attributes
std::map< std::uint32_t, std::vector< float > > texcoords
std::vector< float > positions
Allocation limits checked before decoding any vertex arrays. UV count is byte-budget bounded.