载入中...
搜索中...
未找到
GltfDecode.cpp
浏览该文件的文档.
2#include <algorithm>
3#include <bit>
4#include <cmath>
6
8const Value* member(const Value::Object& object, std::string_view name) {
9 const auto found = object.find(std::string(name));
10 return found == object.end() ? nullptr : &found->second;
11}
12
13Result<std::uint64_t> unsignedValue(const Value* value, std::string path, bool required, std::uint64_t fallback) {
14 if (!value && !required) return Result<std::uint64_t>::success(fallback);
15 if (!value || !value->isInt64() || value->asInt() < 0)
17 "glTF field must be a non-negative integer",
18 path, {}, "asset.import"));
19 return Result<std::uint64_t>::success(static_cast<std::uint64_t>(value->asInt()));
20}
21
22std::uint32_t little32(std::span<const std::uint8_t> bytes, std::size_t offset) {
23 return std::uint32_t(bytes[offset]) | (std::uint32_t(bytes[offset + 1]) << 8) |
24 (std::uint32_t(bytes[offset + 2]) << 16) | (std::uint32_t(bytes[offset + 3]) << 24);
25}
26
27void put32(std::vector<std::uint8_t>& bytes, std::uint32_t value) {
28 for (unsigned shift = 0; shift != 32; shift += 8) bytes.push_back(static_cast<std::uint8_t>(value >> shift));
29}
30
31void putFloat(std::vector<std::uint8_t>& bytes, float value) { put32(bytes, std::bit_cast<std::uint32_t>(value)); }
32
33
35 if (request.documentBytes.empty() || request.documentBytes.size() > request.limits.maximumSourceBytes)
37 DiagnosticCode::InvalidArgument, "glTF document size is outside limits", {}, {}, "asset.import"));
38 std::string json;
39 std::vector<std::uint8_t> binary;
40 const auto bytes = std::span<const std::uint8_t>(request.documentBytes);
41 if (bytes.size() >= 12 && little32(bytes, 0) == 0x46546c67) {
42 if (little32(bytes, 4) != 2 || little32(bytes, 8) != bytes.size() || bytes.size() < 20)
44 Diagnostic::error(DiagnosticCode::ParseError, "GLB header is invalid", {}, {}, "asset.import"));
45 std::size_t cursor = 12;
46 const std::uint32_t jsonSize = little32(bytes, cursor);
47 const std::uint32_t jsonType = little32(bytes, cursor + 4);
48 cursor += 8;
49 if (jsonType != 0x4e4f534a || jsonSize > bytes.size() - cursor)
51 Diagnostic::error(DiagnosticCode::ParseError, "GLB JSON chunk is invalid", {}, {}, "asset.import"));
52 json.assign(reinterpret_cast<const char*>(bytes.data() + cursor), jsonSize);
53 while (!json.empty() && (json.back() == ' ' || json.back() == '\0')) json.pop_back();
54 cursor += jsonSize;
55 if (cursor < bytes.size()) {
56 if (bytes.size() - cursor < 8)
58 DiagnosticCode::ParseError, "GLB trailing chunk is truncated", {}, {}, "asset.import"));
59 const std::uint32_t binarySize = little32(bytes, cursor);
60 const std::uint32_t binaryType = little32(bytes, cursor + 4);
61 cursor += 8;
62 if (binaryType != 0x004e4942 || binarySize > bytes.size() - cursor || cursor + binarySize != bytes.size())
64 Diagnostic::error(DiagnosticCode::ParseError, "GLB BIN chunk is invalid", {}, {}, "asset.import"));
65 binary.assign(bytes.begin() + cursor, bytes.end());
66 }
67 } else {
68 json.assign(request.documentBytes.begin(), request.documentBytes.end());
69 }
70 auto parsed = Value::fromJson(json);
71 if (!parsed) return Result<Document>::failure(parsed.status());
72 return Result<Document>::success({std::move(parsed).takeValue(), std::move(binary)});
73}
74
75bool safeUri(std::string_view uri, const AssetImportLimits& limits) {
76 if (uri.empty() || uri.size() > limits.maximumStringBytes || uri.front() == '/' ||
77 uri.find('\\') != std::string_view::npos || uri.starts_with("data:") || uri.find(':') != std::string_view::npos)
78 return false;
79 std::size_t start = 0;
80 for (std::size_t index = 0; index <= uri.size(); ++index) {
81 if (index != uri.size() && uri[index] != '/') continue;
82 const auto segment = uri.substr(start, index - start);
83 if (segment.empty() || segment == "." || segment == "..") return false;
84 start = index + 1;
85 }
86 return true;
87}
88
91 const Value* buffersValue = member(root, "buffers");
92 const auto* buffers = buffersValue ? buffersValue->getIf<Value::Array>() : nullptr;
93 if (!buffers || buffers->empty())
95 DiagnosticCode::ParseError, "glTF buffers are required", "$.buffers", {}, "asset.import"));
96 std::vector<std::span<const std::uint8_t>> result;
97 result.reserve(buffers->size());
98 std::uint64_t totalBytes = 0;
99 for (std::size_t index = 0; index < buffers->size(); ++index) {
100 const auto* object = (*buffers)[index].getIf<Value::Object>();
101 if (!object)
103 Diagnostic::error(DiagnosticCode::ParseError, "glTF buffer must be an object", {}, {}, "asset.import"));
104 auto declared =
105 unsignedValue(member(*object, "byteLength"), "$.buffers[" + std::to_string(index) + "].byteLength");
106 if (!declared) return Result<std::vector<std::span<const std::uint8_t>>>::failure(declared.status());
107 std::span<const std::uint8_t> data;
108 const Value* uriValue = member(*object, "uri");
109 if (uriValue) {
110 if (!uriValue->isString() || !safeUri(uriValue->asString(), request.limits))
112 Diagnostic::error(DiagnosticCode::Unsupported, "external glTF buffer URI is unsafe or unsupported",
113 {}, {}, "asset.import"));
114 const auto found = request.externalResources.find(uriValue->asString());
115 if (found == request.externalResources.end())
117 Diagnostic::error(DiagnosticCode::NotFound, "external glTF buffer was not supplied",
118 uriValue->asString(), {}, "asset.import"));
119 data = found->second;
120 } else {
121 if (index != 0 || document.binaryChunk.empty())
123 Diagnostic::error(DiagnosticCode::ParseError, "buffer without URI requires the first GLB BIN chunk",
124 {}, {}, "asset.import"));
125 data = document.binaryChunk;
126 }
127 if (declared.value() > data.size())
129 DiagnosticCode::ParseError, "glTF buffer is shorter than byteLength", {}, {}, "asset.import"));
130 if (declared.value() > request.limits.maximumSourceBytes ||
131 totalBytes > request.limits.maximumSourceBytes - declared.value())
133 DiagnosticCode::InvalidArgument, "glTF buffer budget is exceeded", {}, {}, "asset.import"));
134 totalBytes += declared.value();
135 result.push_back(data.first(static_cast<std::size_t>(declared.value())));
136 }
137 return Result<std::vector<std::span<const std::uint8_t>>>::success(std::move(result));
138}
139
140
141std::uint32_t componentSize(std::uint32_t type) {
142 if (type == 5120 || type == 5121) return 1;
143 if (type == 5122 || type == 5123) return 2;
144 if (type == 5125 || type == 5126) return 4;
145 return 0;
146}
147
148Result<Accessor> accessorAt(const Value::Object& root, const std::vector<std::span<const std::uint8_t>>& buffers,
149 std::uint64_t accessorIndex, const AssetImportLimits& limits) {
150 const auto* accessors = member(root, "accessors") ? member(root, "accessors")->getIf<Value::Array>() : nullptr;
151 const auto* views = member(root, "bufferViews") ? member(root, "bufferViews")->getIf<Value::Array>() : nullptr;
152 if (!accessors || !views || accessorIndex >= accessors->size())
154 Diagnostic::error(DiagnosticCode::ParseError, "glTF accessor index is invalid", {}, {}, "asset.import"));
155 const auto* object = (*accessors)[accessorIndex].getIf<Value::Object>();
156 if (!object || member(*object, "sparse"))
158 DiagnosticCode::Unsupported, "sparse or malformed glTF accessor is unsupported", {}, {}, "asset.import"));
159 auto viewIndex = unsignedValue(member(*object, "bufferView"), "accessor.bufferView");
160 auto count = unsignedValue(member(*object, "count"), "accessor.count");
161 auto component = unsignedValue(member(*object, "componentType"), "accessor.componentType");
162 auto accessorOffset = unsignedValue(member(*object, "byteOffset"), "accessor.byteOffset", false, 0);
163 if (!viewIndex || !count || !component || !accessorOffset)
165 Diagnostic::error(DiagnosticCode::ParseError, "glTF accessor fields are invalid", {}, {}, "asset.import"));
166 if (viewIndex.value() >= views->size() || count.value() == 0 ||
167 count.value() > std::max(limits.maximumVerticesPerPrimitive, limits.maximumIndicesPerPrimitive))
169 Diagnostic::error(DiagnosticCode::InvalidArgument, "glTF accessor exceeds limits", {}, {}, "asset.import"));
170 const Value* typeValue = member(*object, "type");
171 if (!typeValue || !typeValue->isString())
173 Diagnostic::error(DiagnosticCode::ParseError, "glTF accessor type is missing", {}, {}, "asset.import"));
174 std::uint32_t components = 0;
175 if (typeValue->asString() == "SCALAR")
176 components = 1;
177 else if (typeValue->asString() == "VEC2")
178 components = 2;
179 else if (typeValue->asString() == "VEC3")
180 components = 3;
181 else if (typeValue->asString() == "VEC4")
182 components = 4;
183 else if (typeValue->asString() == "MAT4")
184 components = 16;
185 else
187 DiagnosticCode::Unsupported, "glTF accessor shape is unsupported", {}, {}, "asset.import"));
188 const auto* view = (*views)[viewIndex.value()].getIf<Value::Object>();
189 if (!view)
191 Diagnostic::error(DiagnosticCode::ParseError, "glTF bufferView is malformed", {}, {}, "asset.import"));
192 auto bufferIndex = unsignedValue(member(*view, "buffer"), "bufferView.buffer");
193 auto viewOffset = unsignedValue(member(*view, "byteOffset"), "bufferView.byteOffset", false, 0);
194 auto viewLength = unsignedValue(member(*view, "byteLength"), "bufferView.byteLength");
195 auto byteStride = unsignedValue(member(*view, "byteStride"), "bufferView.byteStride", false, 0);
196 if (!bufferIndex || !viewOffset || !viewLength || !byteStride || bufferIndex.value() >= buffers.size())
198 DiagnosticCode::ParseError, "glTF bufferView fields are invalid", {}, {}, "asset.import"));
199 const std::uint32_t elementSize = componentSize(static_cast<std::uint32_t>(component.value())) * components;
200 const std::uint64_t stride = byteStride.value() == 0 ? elementSize : byteStride.value();
201 if (elementSize == 0 || stride < elementSize || stride > 252 ||
202 viewOffset.value() > buffers[bufferIndex.value()].size() ||
203 viewLength.value() > buffers[bufferIndex.value()].size() - viewOffset.value() ||
204 accessorOffset.value() > viewLength.value())
206 Diagnostic::error(DiagnosticCode::ParseError, "glTF accessor layout is invalid", {}, {}, "asset.import"));
207 const std::uint64_t required = (count.value() - 1) * stride + elementSize;
208 if (required > viewLength.value() - accessorOffset.value())
210 Diagnostic::error(DiagnosticCode::ParseError, "glTF accessor range is truncated", {}, {}, "asset.import"));
211 const auto* normalized = member(*object, "normalized");
212 if (normalized && !normalized->isBool())
214 DiagnosticCode::ParseError, "glTF normalized flag must be boolean", {}, {}, "asset.import"));
216 {buffers[bufferIndex.value()], static_cast<std::size_t>(viewOffset.value() + accessorOffset.value()),
217 static_cast<std::size_t>(stride), static_cast<std::uint32_t>(count.value()),
218 static_cast<std::uint32_t>(component.value()), components, normalized && normalized->asBool()});
219}
220
221Result<float> readFloat(const Accessor& accessor, std::uint32_t element, std::uint32_t component) {
222 if (accessor.componentType != 5126 || accessor.normalized || element >= accessor.count ||
223 component >= accessor.components)
225 DiagnosticCode::Unsupported, "mesh attribute must use FLOAT components", {}, {}, "asset.import"));
226 const std::size_t offset = accessor.offset + std::size_t(element) * accessor.stride + component * 4;
227 const float value = std::bit_cast<float>(little32(accessor.buffer, offset));
228 if (!std::isfinite(value))
230 DiagnosticCode::ParseError, "mesh attribute contains non-finite values", {}, {}, "asset.import"));
232}
233
234Result<std::uint32_t> readIndex(const Accessor& accessor, std::uint32_t element) {
235 if (accessor.components != 1)
237 Diagnostic::error(DiagnosticCode::ParseError, "index accessor must be SCALAR", {}, {}, "asset.import"));
238 const std::size_t offset = accessor.offset + std::size_t(element) * accessor.stride;
239 if (accessor.componentType == 5121) return Result<std::uint32_t>::success(accessor.buffer[offset]);
240 if (accessor.componentType == 5123)
241 return Result<std::uint32_t>::success(std::uint32_t(accessor.buffer[offset]) |
242 (std::uint32_t(accessor.buffer[offset + 1]) << 8));
243 if (accessor.componentType == 5125) return Result<std::uint32_t>::success(little32(accessor.buffer, offset));
245 Diagnostic::error(DiagnosticCode::Unsupported, "indices must use UNSIGNED_BYTE, UNSIGNED_SHORT or UNSIGNED_INT",
246 {}, {}, "asset.import"));
247}
248
249} // namespace eve::asset_import::gltf
double value
Duration start
int root
Definition AnimSmr.cpp:119
const GltfImportRequest & request
size_t offset
bool required
std::uint64_t bytes
std::string name
std::string path
Definition PlayHost.cpp:110
std::string uri
glm::mat4 view
bool found
std::uint32_t count
std::string element
std::size_t cursor
float(ui::Theme::* member)[4]
uint32_t index
const AssetImportLimits & limits
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
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
bool isString() const noexcept
Return true when this value is a string.
Definition Value.h:95
std::vector< Value > Array
Definition Value.h:33
const T * getIf() const noexcept
Return a typed pointer, or nullptr when the kind differs.
Definition Value.h:180
Value * find(const std::string &key) noexcept
Return an object member, or nullptr when absent.
Definition Value.cpp:124
Result< Accessor > accessorAt(const Value::Object &root, const std::vector< std::span< const std::uint8_t > > &buffers, std::uint64_t accessorIndex, const AssetImportLimits &limits)
Validate an accessor retaining only buffer spans borrowed for this import.
Result< float > readFloat(const Accessor &accessor, std::uint32_t element, std::uint32_t component)
Read one bounded finite FLOAT component.
void put32(std::vector< std::uint8_t > &bytes, std::uint32_t value)
Append little-endian uint32 to owned storage.
Result< std::vector< std::span< const std::uint8_t > > > resolveBuffers(const Value::Object &root, const Document &document, const GltfImportRequest &request)
Resolve immutable spans borrowed from document/request; both must outlive their use.
Result< std::uint64_t > unsignedValue(const Value *value, std::string path, bool required, std::uint64_t fallback)
Validate an optional integer field. @ownership Borrowed nullable field owned by the input document....
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< Document > parseDocument(const GltfImportRequest &request)
Parse a bounded document into an owning result.
Result< std::uint32_t > readIndex(const Accessor &accessor, std::uint32_t element)
Read one bounded unsigned scalar index.
std::uint32_t componentSize(std::uint32_t type)
Return encoded component byte size, or zero for an unknown type.
bool safeUri(std::string_view uri, const AssetImportLimits &limits)
void putFloat(std::vector< std::uint8_t > &bytes, float value)
Append IEEE FLOAT to owned storage.
Parser and allocation budgets applied to untrusted importer inputs.
Untrusted .gltf/.glb input plus explicitly supplied external URI bytes.
Borrowed validated accessor; its source document/resources must outlive synchronous reads.
Definition GltfDecode.h:13
std::span< const std::uint8_t > buffer
Definition GltfDecode.h:14
Owning parsed document; keep alive while resolved buffer spans are used.
Definition GltfDecode.h:8
std::vector< std::uint8_t > binaryChunk
Definition GltfDecode.h:10