1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "gltf/gltf2_loader.h"
17
18 #include <algorithm>
19 #include <cctype>
20 #include <charconv>
21 #include <cstdint>
22 #include <optional>
23
24 #include <base/containers/fixed_string.h>
25 #include <base/containers/string.h>
26 #include <base/containers/string_view.h>
27 #include <base/containers/unique_ptr.h>
28 #include <base/containers/vector.h>
29 #include <core/io/intf_file_manager.h>
30 #include <core/log.h>
31 #include <core/namespace.h>
32 #include <core/perf/cpu_perf_scope.h>
33
34 #include "gltf/data.h"
35 #include "gltf/gltf2_util.h"
36 #include "util/json_util.h"
37 #include "util/log.h"
38
39 namespace {
40 #include <3d/shaders/common/3d_dm_structures_common.h>
41 }
42
SetError(CORE3D_NS::GLTF2::LoadResult & loadResult,const BASE_NS::string_view message)43 inline void SetError(CORE3D_NS::GLTF2::LoadResult& loadResult, const BASE_NS::string_view message)
44 {
45 loadResult.error += message + '\n';
46 loadResult.success = false;
47 }
48
49 #define RETURN_WITH_ERROR(loadResult, message) \
50 { \
51 SetError((loadResult), (message)); \
52 return false; \
53 }
54
55 CORE3D_BEGIN_NAMESPACE()
56 using namespace BASE_NS;
57 using namespace CORE_NS;
58
59 namespace GLTF2 {
60 namespace {
61 const string_view SUPPORTED_EXTENSIONS[] = {
62 #if defined(GLTF2_EXTENSION_IGFX_COMPRESSED)
63 "IGFX_compressed",
64 #endif
65 #if defined(GLTF2_EXTENSION_KHR_LIGHTS)
66 "KHR_lights_punctual",
67 #endif
68 #if defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
69 "KHR_lights_pbr",
70 #endif
71 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_CLEARCOAT)
72 "KHR_materials_clearcoat",
73 #endif
74 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_EMISSIVE_STRENGTH)
75 "KHR_materials_emissive_strength",
76 #endif
77 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_IOR)
78 "KHR_materials_ior",
79 #endif
80 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_PBRSPECULARGLOSSINESS)
81 "KHR_materials_pbrSpecularGlossiness",
82 #endif
83 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SHEEN)
84 "KHR_materials_sheen",
85 #endif
86 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SPECULAR)
87 "KHR_materials_specular",
88 #endif
89 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_TRANSMISSION)
90 "KHR_materials_transmission",
91 #endif
92 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_UNLIT)
93 "KHR_materials_unlit",
94 #endif
95 #if defined(GLTF2_EXTENSION_KHR_MESH_QUANTIZATION)
96 "KHR_mesh_quantization",
97 #endif
98 #if defined(GLTF2_EXTENSION_KHR_TEXTURE_BASISU)
99 "KHR_texture_basisu",
100 #endif
101 #if defined(GLTF2_EXTENSION_KHR_TEXTURE_TRANSFORM)
102 "KHR_texture_transform",
103 #endif
104 #if defined(GLTF2_EXTENSION_HW_XR_EXT)
105 "HW_XR_EXT",
106 #endif
107 #if defined(GLTF2_EXTENSION_EXT_LIGHTS_IMAGE_BASED)
108 "EXT_lights_image_based",
109 #endif
110 "MSFT_texture_dds",
111 // legacy stuff found potentially in animoji models
112 "IGFX_lights",
113 "IGFX_environment",
114 };
115
116 // Replace percent-encoded characters from the URI.
117 // NOTE: glTF spec says "Reserved characters must be percent-encoded, per RFC 3986, Section 2.2.". The RFC says that
118 // for consistency, percent encoded unreserved characters (alphanum, '-', '.', '_', '~') should be also decoded. The RFC
119 // doesn't list space as reserved, but it' s still somehow expected.
120 // -> Not limiting to reserved and unreserved characters, but converts everything in the range of [%00, %FF].
DecodeUri(string & uri)121 void DecodeUri(string& uri)
122 {
123 string::size_type pos = 0;
124 while ((pos = uri.find('%', pos)) != string::npos) {
125 // there should be at least two characters after '%'
126 if ((pos + 2) < uri.size()) {
127 // start converting after '%'
128 const auto begin = uri.data() + (pos + 1);
129 // convert up to two characters
130 const auto end = begin + 2;
131 uint32_t val;
132 if (const auto result = std::from_chars(begin, end, val, 16);
133 (result.ec == std::errc()) && result.ptr == end) {
134 // replace '%' with the hex value converted to char
135 *(begin - 1) = static_cast<char>(val);
136 // remove the encoding
137 uri.erase(pos + 1, 2);
138 }
139 }
140 pos++;
141 }
142 }
143
144 template<typename EnumType, typename InputType>
RangedEnumCast(LoadResult & loadResult,EnumType & out,InputType input)145 bool RangedEnumCast(LoadResult& loadResult, EnumType& out, InputType input)
146 {
147 if (input >= static_cast<int>(EnumType::BEGIN) && input < static_cast<int>(EnumType::COUNT)) {
148 out = static_cast<EnumType>(input);
149 return true;
150 }
151
152 RETURN_WITH_ERROR(loadResult, "Invalid enum cast");
153 }
154
155 template<typename Parser>
ParseObject(LoadResult & loadResult,const json::value & jsonObject,Parser parser)156 bool ParseObject(LoadResult& loadResult, const json::value& jsonObject, Parser parser)
157 {
158 if (!jsonObject.is_object()) {
159 RETURN_WITH_ERROR(loadResult, "Parsing GLTF failed: expected JSON object");
160 }
161
162 return parser(loadResult, jsonObject);
163 }
164
165 template<typename Parser>
ParseObject(LoadResult & loadResult,const json::value & jsonObject,const string_view name,Parser parser)166 bool ParseObject(LoadResult& loadResult, const json::value& jsonObject, const string_view name, Parser parser)
167 {
168 if (auto it = jsonObject.find(name); it) {
169 return ParseObject(loadResult, *it, parser);
170 }
171
172 return true;
173 }
174
175 template<typename Parser>
ForEachInArray(LoadResult & loadResult,const json::value & jsonArray,Parser parser)176 bool ForEachInArray(LoadResult& loadResult, const json::value& jsonArray, Parser parser)
177 {
178 if (!jsonArray.is_array()) {
179 RETURN_WITH_ERROR(loadResult, "Parsing GLTF failed: expected JSON array");
180 }
181
182 for (const auto& item : jsonArray.array_) {
183 if (!parser(loadResult, item)) {
184 return false;
185 }
186 }
187
188 return true;
189 }
190
191 template<typename Parser>
ForEachInArray(LoadResult & loadResult,const json::value & jsonObject,const string_view name,Parser parser)192 bool ForEachInArray(LoadResult& loadResult, const json::value& jsonObject, const string_view name, Parser parser)
193 {
194 if (auto it = jsonObject.find(name); it) {
195 return ForEachInArray(loadResult, *it, parser);
196 }
197
198 return true;
199 }
200
201 template<typename Parser>
ForEachObjectInArray(LoadResult & loadResult,const json::value & jsonObject,Parser parser)202 bool ForEachObjectInArray(LoadResult& loadResult, const json::value& jsonObject, Parser parser)
203 {
204 return ForEachInArray(loadResult, jsonObject, [parser](LoadResult& loadResult, const json::value& item) -> bool {
205 return ParseObject(loadResult, item,
206 [parser](LoadResult& loadResult, const json::value& item) -> bool { return parser(loadResult, item); });
207 });
208 }
209
210 template<typename Parser>
ForEachObjectInArray(LoadResult & loadResult,const json::value & jsonObject,const string_view name,Parser parser)211 bool ForEachObjectInArray(LoadResult& loadResult, const json::value& jsonObject, const string_view name, Parser parser)
212 {
213 return ForEachInArray(
214 loadResult, jsonObject, name, [parser](LoadResult& loadResult, const json::value& item) -> bool {
215 return ParseObject(loadResult, item,
216 [parser](LoadResult& loadResult, const json::value& item) -> bool { return parser(loadResult, item); });
217 });
218 }
219
ParseOptionalString(LoadResult & loadResult,string & out,const json::value & jsonObject,const string_view name,const string_view defaultValue)220 bool ParseOptionalString(LoadResult& loadResult, string& out, const json::value& jsonObject, const string_view name,
221 const string_view defaultValue)
222 {
223 if (const auto value = jsonObject.find(name); value) {
224 if (!value->is_string()) {
225 RETURN_WITH_ERROR(loadResult, "expected string");
226 }
227 if (value->string_.find('\\') != string::npos) {
228 out = json::unescape(value->string_);
229 } else {
230 out = value->string_;
231 }
232
233 return true;
234 }
235
236 out = defaultValue;
237 return true;
238 }
239
240 template<typename Number>
ConvertStringToValue(const string_view str,Number & value)241 void ConvertStringToValue(const string_view str, Number& value)
242 {
243 #if defined(__OHOS_PLATFORM__) || defined(__linux__) || defined(__APPLE__)
244 if constexpr (std::is_integral_v<Number>) {
245 std::from_chars(str.data(), str.data() + str.size(), value);
246 } else {
247 value = strtof(str.data(), nullptr);
248 }
249 #else
250 std::from_chars(str.data(), str.data() + str.size(), value);
251 #endif
252 }
253
254 template<typename T>
ParseOptionalNumber(LoadResult & loadResult,T & out,const json::value & jsonObject,const string_view name,T defaultValue)255 bool ParseOptionalNumber(
256 LoadResult& loadResult, T& out, const json::value& jsonObject, const string_view name, T defaultValue)
257 {
258 if (const auto it = jsonObject.find(name); it) {
259 if (it->is_number()) {
260 out = it->as_number<T>();
261 return true;
262 } else if (it->is_string()) {
263 // Some glTF files have strings in place of numbers, so we try to perform auto conversion here.
264 ConvertStringToValue(it->string_, out);
265 CORE_LOG_W("Expected number when parsing but found string (performing auto conversion to number type).");
266 return true;
267 } else {
268 out = defaultValue;
269 RETURN_WITH_ERROR(loadResult, "expected number");
270 }
271 }
272 out = defaultValue;
273 return true;
274 }
275
ParseOptionalBoolean(LoadResult & loadResult,bool & out,const json::value & jsonObject,const string_view name,bool defaultValue)276 bool ParseOptionalBoolean(
277 LoadResult& loadResult, bool& out, const json::value& jsonObject, const string_view name, bool defaultValue)
278 {
279 if (const auto value = jsonObject.find(name); value) {
280 if (value->is_boolean()) {
281 out = value->boolean_;
282 return true;
283 } else {
284 RETURN_WITH_ERROR(loadResult, "expected boolean");
285 }
286 }
287
288 out = defaultValue;
289 return true;
290 }
291
292 template<typename T>
ParseOptionalNumberArray(LoadResult & loadResult,vector<T> & out,const json::value & jsonObject,const string_view name,vector<T> defaultValue,uint32_t minSize=0,uint32_t maxSize=std::numeric_limits<int>::max ())293 bool ParseOptionalNumberArray(LoadResult& loadResult, vector<T>& out, const json::value& jsonObject,
294 const string_view name, vector<T> defaultValue, uint32_t minSize = 0,
295 uint32_t maxSize = std::numeric_limits<int>::max())
296 {
297 const auto* it = jsonObject.find(name);
298 if (!it) {
299 out = defaultValue;
300 return true;
301 }
302 if (!it->is_array()) {
303 RETURN_WITH_ERROR(loadResult, "expected array");
304 }
305 const auto view =
306 array_view(it->array_.data(), BASE_NS::Math::min(static_cast<uint32_t>(it->array_.size()), maxSize));
307 out.reserve(view.size());
308 for (const auto& item : view) {
309 if (!item.is_number()) {
310 RETURN_WITH_ERROR(loadResult, "expected array of numbers");
311 }
312 out.push_back(item.as_number<T>());
313 }
314
315 if (out.size() >= minSize) {
316 return true;
317 }
318
319 out = defaultValue;
320 return true;
321 }
322
323 /** Tries to parse a Core::Math object (e.g. Vec4, Quat, and Mat4X4) from json. */
324 template<typename T>
ParseOptionalMath(LoadResult & loadResult,T & out,const json::value & jsonObject,const string_view name,T defaultValue)325 bool ParseOptionalMath(
326 LoadResult& loadResult, T& out, const json::value& jsonObject, const string_view name, T defaultValue)
327 {
328 if (auto it = jsonObject.find(name); it) {
329 if (it->is_array() && (it->array_.size() == countof(out.data))) {
330 auto& array = it->array_;
331 std::transform(array.begin(), array.end(), out.data, [](const json::value& item) {
332 if (item.is_number()) {
333 return item.as_number<float>();
334 } else if (item.is_string()) {
335 float value;
336 ConvertStringToValue(item.string_, value);
337 return value;
338 } else {
339 return 0.f;
340 }
341 });
342 return true;
343 } else {
344 RETURN_WITH_ERROR(loadResult, "expected array of size when parsing");
345 }
346 } else {
347 out = defaultValue;
348 return true;
349 }
350 }
351
ParseVersion(string_view version)352 std::optional<std::pair<uint32_t, uint32_t>> ParseVersion(string_view version)
353 {
354 uint32_t min = 0;
355 uint32_t maj = 0;
356 if (const auto delim = version.find('.'); delim == string::npos) {
357 return {};
358 } else if (auto result = std::from_chars(version.data(), version.data() + delim, maj, 10);
359 result.ec != std::errc() || *(result.ptr) != '.') {
360 return {};
361 } else if (auto result2 = std::from_chars(result.ptr + 1, version.data() + version.size(), min, 10);
362 result2.ec != std::errc() || *(result2.ptr) != '\0') {
363 return {};
364 }
365 return std::make_pair(maj, min);
366 }
367
ParseBuffer(LoadResult & loadResult,const json::value & jsonData)368 bool ParseBuffer(LoadResult& loadResult, const json::value& jsonData)
369 {
370 bool result = true;
371
372 int32_t length = 0;
373 if (!SafeGetJsonValue(jsonData, "byteLength", loadResult.error, length)) {
374 SetError(loadResult, "Failed to read buffer.byteLength");
375 result = false;
376 }
377
378 if (length < 1) {
379 SetError(loadResult, "buffer.byteLength was smaller than 1 byte");
380 result = false;
381 }
382
383 string uri;
384 if (!ParseOptionalString(loadResult, uri, jsonData, "uri", string())) {
385 result = false;
386 }
387
388 auto buffer = make_unique<Buffer>();
389 if (result) {
390 DecodeUri(uri);
391 buffer->uri = move(uri);
392 buffer->byteLength = size_t(length);
393 }
394
395 loadResult.data->buffers.push_back(move(buffer));
396
397 return result;
398 }
399
BufferViewBuffer(LoadResult & loadResult,const json::value & jsonData)400 std::optional<Buffer*> BufferViewBuffer(LoadResult& loadResult, const json::value& jsonData)
401 {
402 int index = 0;
403 if (!SafeGetJsonValue(jsonData, "buffer", loadResult.error, index)) {
404 SetError(loadResult, "Failed to read bufferView.buffer");
405 return std::nullopt;
406 } else if (index < 0 || size_t(index) >= loadResult.data->buffers.size()) {
407 SetError(loadResult, "bufferView.buffer isn't valid index");
408 return std::nullopt;
409 } else {
410 return loadResult.data->buffers[(unsigned int)index].get();
411 }
412 }
413
BufferViewByteLength(LoadResult & loadResult,const json::value & jsonData)414 std::optional<int> BufferViewByteLength(LoadResult& loadResult, const json::value& jsonData)
415 {
416 int length = 0;
417 if (!SafeGetJsonValue(jsonData, "byteLength", loadResult.error, length)) {
418 SetError(loadResult, "Failed to read bufferView.byteLength");
419 return std::nullopt;
420 } else if (length < 1) {
421 SetError(loadResult, "bufferView.byteLength was smaller than 1 byte");
422 return std::nullopt;
423 }
424 return length;
425 }
426
BufferViewByteOffset(LoadResult & loadResult,const json::value & jsonData,std::optional<Buffer * > buffer,std::optional<int> byteLength)427 std::optional<int> BufferViewByteOffset(
428 LoadResult& loadResult, const json::value& jsonData, std::optional<Buffer*> buffer, std::optional<int> byteLength)
429 {
430 int offset;
431 if (!ParseOptionalNumber<int>(loadResult, offset, jsonData, "byteOffset", 0)) { // "default": 0
432 return std::nullopt;
433 } else if (offset < 0) {
434 SetError(loadResult, "bufferView.byteOffset isn't valid offset");
435 return std::nullopt;
436 } else if (!buffer || !(*buffer) || !byteLength || ((*buffer)->byteLength < (size_t(offset) + *byteLength))) {
437 SetError(loadResult, "bufferView.byteLength is larger than buffer.byteLength");
438 return std::nullopt;
439 }
440 return offset;
441 }
442
BufferViewByteStride(LoadResult & loadResult,const json::value & jsonData)443 std::optional<int> BufferViewByteStride(LoadResult& loadResult, const json::value& jsonData)
444 {
445 // "default": 0 "minimum": 4, "maximum": 252, "multipleOf":
446 int stride;
447 if (!ParseOptionalNumber<int>(loadResult, stride, jsonData, "byteStride", 0)) {
448 return std::nullopt;
449 } else if ((stride < 4 && stride != 0) || stride > 252 || (stride % 4)) {
450 SetError(loadResult, "bufferView.byteStride isn't valid stride");
451 return std::nullopt;
452 }
453 return stride;
454 }
455
BufferViewTarget(LoadResult & loadResult,const json::value & jsonData)456 std::optional<BufferTarget> BufferViewTarget(LoadResult& loadResult, const json::value& jsonData)
457 {
458 // "default": NOT_DEFINED if set then ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER
459 int target;
460 if (!ParseOptionalNumber<int>(
461 loadResult, target, jsonData, "target", static_cast<int>(BufferTarget::NOT_DEFINED))) {
462 return std::nullopt;
463 } else if (target != static_cast<int>(BufferTarget::NOT_DEFINED) &&
464 target != static_cast<int>(BufferTarget::ARRAY_BUFFER) &&
465 target != static_cast<int>(BufferTarget::ELEMENT_ARRAY_BUFFER)) {
466 SetError(loadResult, "bufferView.target isn't valid target");
467 return std::nullopt;
468 }
469 return static_cast<BufferTarget>(target);
470 }
471
ParseBufferView(LoadResult & loadResult,const json::value & jsonData)472 bool ParseBufferView(LoadResult& loadResult, const json::value& jsonData)
473 {
474 const auto buffer = BufferViewBuffer(loadResult, jsonData);
475 const auto byteLength = BufferViewByteLength(loadResult, jsonData);
476 const auto offset = BufferViewByteOffset(loadResult, jsonData, buffer, byteLength);
477 const auto stride = BufferViewByteStride(loadResult, jsonData);
478 const auto target = BufferViewTarget(loadResult, jsonData);
479
480 const auto result = byteLength && buffer && offset && stride && target;
481 if (result) {
482 auto view = make_unique<BufferView>();
483 view->buffer = *buffer;
484 view->byteLength = size_t(*byteLength);
485 view->byteOffset = size_t(*offset);
486 view->byteStride = size_t(*stride);
487 view->target = *target;
488 loadResult.data->bufferViews.push_back(move(view));
489 } else {
490 loadResult.data->bufferViews.emplace_back();
491 }
492
493 return result;
494 }
495
AccessorComponentType(LoadResult & loadResult,const json::value & jsonData)496 std::optional<ComponentType> AccessorComponentType(LoadResult& loadResult, const json::value& jsonData)
497 {
498 int componentType = 0;
499 if (!SafeGetJsonValue(jsonData, "componentType", loadResult.error, componentType)) {
500 SetError(loadResult, "Failed to read accessor.componentType");
501 return std::nullopt;
502 }
503 return static_cast<ComponentType>(componentType);
504 }
505
AccessorCount(LoadResult & loadResult,const json::value & jsonData)506 std::optional<uint32_t> AccessorCount(LoadResult& loadResult, const json::value& jsonData)
507 {
508 int count = 0;
509 if (!SafeGetJsonValue(jsonData, "count", loadResult.error, count)) {
510 SetError(loadResult, "Failed to read accessor.count");
511 return std::nullopt;
512 } else if (count < 1) {
513 SetError(loadResult, "Accessor.count is invalid");
514 return std::nullopt;
515 }
516 return count;
517 }
518
AccessorType(LoadResult & loadResult,const json::value & jsonData)519 std::optional<DataType> AccessorType(LoadResult& loadResult, const json::value& jsonData)
520 {
521 string_view type;
522 if (!SafeGetJsonValue(jsonData, "type", loadResult.error, type)) {
523 SetError(loadResult, "Failed to read accessor.type");
524 return std::nullopt;
525 }
526
527 DataType datatype;
528 if (!GetDataType(type, datatype)) {
529 SetError(loadResult, "Invalid or unsupported data type");
530 return std::nullopt;
531 }
532 return datatype;
533 }
534
AccessorBufferView(LoadResult & loadResult,const json::value & jsonData)535 std::optional<BufferView*> AccessorBufferView(LoadResult& loadResult, const json::value& jsonData)
536 {
537 BufferView* bufferView = nullptr;
538
539 size_t bufferIndex;
540 if (!ParseOptionalNumber<size_t>(loadResult, bufferIndex, jsonData, "bufferView", GLTF_INVALID_INDEX)) {
541 return std::nullopt;
542 } else if (bufferIndex != GLTF_INVALID_INDEX) {
543 if (bufferIndex < loadResult.data->bufferViews.size()) {
544 bufferView = loadResult.data->bufferViews[bufferIndex].get();
545 } else {
546 SetError(loadResult, "Accessor.bufferView is invalid");
547 return std::nullopt;
548 }
549 }
550 return bufferView;
551 }
552
AccessorByteOffset(LoadResult & loadResult,const json::value & jsonData,std::optional<BufferView * > bufferView)553 std::optional<uint32_t> AccessorByteOffset(
554 LoadResult& loadResult, const json::value& jsonData, std::optional<BufferView*> bufferView)
555 {
556 uint32_t byteOffset;
557 if (!ParseOptionalNumber<uint32_t>(loadResult, byteOffset, jsonData, "byteOffset", 0)) {
558 return false;
559 } else if (bufferView && (*bufferView) && ((*bufferView)->byteLength <= byteOffset)) {
560 SetError(loadResult, "Accessor.byteOffset isn't valid offset");
561 return std::nullopt;
562 }
563 return byteOffset;
564 }
565
AccessorNormalized(LoadResult & loadResult,const json::value & jsonData)566 std::optional<bool> AccessorNormalized(LoadResult& loadResult, const json::value& jsonData)
567 {
568 bool normalized = false;
569 if (!ParseOptionalBoolean(loadResult, normalized, jsonData, "normalized", false)) {
570 return std::nullopt;
571 }
572 return normalized;
573 }
574
AccessorMin(LoadResult & loadResult,const json::value & jsonData)575 std::optional<vector<float>> AccessorMin(LoadResult& loadResult, const json::value& jsonData)
576 {
577 vector<float> min;
578 if (!ParseOptionalNumberArray(loadResult, min, jsonData, "min", vector<float>(), 1U, 16U)) {
579 return std::nullopt;
580 }
581 return move(min);
582 }
583
AccessorMax(LoadResult & loadResult,const json::value & jsonData)584 std::optional<vector<float>> AccessorMax(LoadResult& loadResult, const json::value& jsonData)
585 {
586 vector<float> max;
587 if (!ParseOptionalNumberArray(loadResult, max, jsonData, "max", vector<float>(), 1U, 16U)) {
588 return std::nullopt;
589 }
590 return move(max);
591 }
592
AccessorSparseIndices(LoadResult & loadResult,const json::value & jsonData)593 std::optional<SparseIndices> AccessorSparseIndices(LoadResult& loadResult, const json::value& jsonData)
594 {
595 SparseIndices indices;
596 size_t bufferViewIndex;
597 if (!SafeGetJsonValue(jsonData, "bufferView", loadResult.error, bufferViewIndex)) {
598 SetError(loadResult, "Failed to read Sparse.indices.bufferView");
599 return std::nullopt;
600 }
601
602 if (bufferViewIndex < loadResult.data->bufferViews.size()) {
603 indices.bufferView = loadResult.data->bufferViews[bufferViewIndex].get();
604 } else {
605 SetError(loadResult, "Sparse.indices.bufferView is invalid");
606 return std::nullopt;
607 }
608
609 int32_t componentType;
610 if (!SafeGetJsonValue(jsonData, "componentType", loadResult.error, componentType)) {
611 SetError(loadResult, "Failed to read Sparse.indices.componentType");
612 return std::nullopt;
613 }
614
615 indices.componentType = static_cast<ComponentType>(componentType);
616
617 if (!ParseOptionalNumber<uint32_t>(loadResult, indices.byteOffset, jsonData, "bufferOffset", 0)) {
618 return std::nullopt;
619 }
620
621 return indices;
622 }
623
AccessorSparseValues(LoadResult & loadResult,const json::value & jsonData)624 std::optional<SparseValues> AccessorSparseValues(LoadResult& loadResult, const json::value& jsonData)
625 {
626 SparseValues values;
627 size_t bufferViewIndex;
628 if (!SafeGetJsonValue(jsonData, "bufferView", loadResult.error, bufferViewIndex)) {
629 SetError(loadResult, "Failed to read Sparse.values.bufferView");
630 return std::nullopt;
631 } else if (bufferViewIndex < loadResult.data->bufferViews.size()) {
632 values.bufferView = loadResult.data->bufferViews[bufferViewIndex].get();
633 } else {
634 SetError(loadResult, "Sparse.values.bufferView is invalid");
635 return std::nullopt;
636 }
637
638 if (!ParseOptionalNumber<uint32_t>(loadResult, values.byteOffset, jsonData, "bufferOffset", 0)) {
639 return std::nullopt;
640 }
641
642 return values;
643 }
644
AccessorSparse(LoadResult & loadResult,const json::value & jsonData)645 std::optional<Sparse> AccessorSparse(LoadResult& loadResult, const json::value& jsonData)
646 {
647 Sparse sparse;
648 if (auto sparseJson = jsonData.find("sparse"); sparseJson) {
649 if (!SafeGetJsonValue(*sparseJson, "count", loadResult.error, sparse.count)) {
650 SetError(loadResult, "Failed to read sparse.count");
651 return std::nullopt;
652 }
653
654 if (auto it = sparseJson->find("indices"); it) {
655 if (auto ret = AccessorSparseIndices(loadResult, *it); ret) {
656 sparse.indices = *ret;
657 } else {
658 return std::nullopt;
659 }
660 } else {
661 SetError(loadResult, "Failed to read sparse.indices");
662 return std::nullopt;
663 }
664
665 if (auto it = sparseJson->find("values"); it) {
666 if (auto ret = AccessorSparseValues(loadResult, *it); ret) {
667 sparse.values = *ret;
668 } else {
669 return std::nullopt;
670 }
671 } else {
672 SetError(loadResult, "Failed to read sparse.values");
673 return std::nullopt;
674 }
675 }
676 return sparse;
677 }
678
ValidateAccessor(LoadResult & loadResult,ComponentType componentType,uint32_t count,DataType dataType,const BufferView * bufferView,uint32_t byteOffset,const array_view<const float> min,const array_view<const float> max)679 bool ValidateAccessor(LoadResult& loadResult, ComponentType componentType, uint32_t count, DataType dataType,
680 const BufferView* bufferView, uint32_t byteOffset, const array_view<const float> min,
681 const array_view<const float> max)
682 {
683 if (const size_t elementSize = GetComponentsCount(dataType) * GetComponentByteSize(componentType); elementSize) {
684 if (bufferView) {
685 // check count against buffer size
686 if (byteOffset > bufferView->byteLength) {
687 SetError(loadResult, "Accessor.byteOffset is invalid");
688 return false;
689 }
690 const auto bufferSize = bufferView->byteLength - byteOffset;
691 const auto elementCount = bufferSize / elementSize;
692
693 if (count > elementCount) {
694 SetError(loadResult, "Accessor.count is invalid");
695 return false;
696 }
697 }
698 } else {
699 SetError(loadResult, "Accessor.type or componentType is invalid");
700 return false;
701 }
702 const auto minVecSize = min.size();
703 const auto maxVecSize = max.size();
704 if (minVecSize == maxVecSize) {
705 if (minVecSize && minVecSize != GetComponentsCount(dataType)) {
706 SetError(loadResult, "Accessor.min and max vector size doesn't match component count");
707 return false;
708 }
709 } else {
710 SetError(loadResult, "Accessor.min and max vectors have different size");
711 return false;
712 }
713 return true;
714 }
715
716 template<typename T>
GetView(const std::optional<vector<T>> & potential)717 array_view<const T> GetView(const std::optional<vector<T>>& potential)
718 {
719 if (potential.has_value()) {
720 return array_view<const float>(potential.value());
721 }
722 return {};
723 }
724
ParseAccessor(LoadResult & loadResult,const json::value & jsonData)725 bool ParseAccessor(LoadResult& loadResult, const json::value& jsonData)
726 {
727 const auto componentType = AccessorComponentType(loadResult, jsonData);
728 const auto count = AccessorCount(loadResult, jsonData);
729 const auto datatype = AccessorType(loadResult, jsonData);
730
731 const auto bufferView = AccessorBufferView(loadResult, jsonData);
732 const auto byteOffset = AccessorByteOffset(loadResult, jsonData, bufferView);
733 const auto normalized = AccessorNormalized(loadResult, jsonData);
734 auto max = AccessorMax(loadResult, jsonData);
735 auto min = AccessorMin(loadResult, jsonData);
736 auto sparse = AccessorSparse(loadResult, jsonData);
737
738 const auto minView = GetView(min);
739 const auto maxView = GetView(max);
740
741 bool result = true;
742 if (!ValidateAccessor(loadResult, componentType.value_or(ComponentType::INVALID), count.value_or(0U),
743 datatype.value_or(DataType::INVALID), bufferView.value_or(nullptr), byteOffset.value_or(0U), minView,
744 maxView)) {
745 result = false;
746 }
747 if (count && sparse) {
748 const auto& sparseRef = *sparse;
749 if (sparseRef.count > *count) {
750 SetError(loadResult, "Accessor.sparse.count is invalid");
751 return false;
752 }
753 if (!ValidateAccessor(loadResult, sparseRef.indices.componentType, sparseRef.count, DataType::SCALAR,
754 sparseRef.indices.bufferView, sparseRef.indices.byteOffset, array_view<const float> {},
755 array_view<const float> {})) {
756 SetError(loadResult, "Accessor.sparse.indices is invalid");
757 return false;
758 }
759 if (!ValidateAccessor(loadResult, componentType.value_or(ComponentType::INVALID), sparseRef.count,
760 datatype.value_or(DataType::INVALID), sparseRef.values.bufferView, sparseRef.values.byteOffset, minView,
761 maxView)) {
762 SetError(loadResult, "Accessor.sparse.values is invalid");
763 return false;
764 }
765 }
766
767 auto accessor = make_unique<Accessor>();
768 result =
769 result && componentType && count && datatype && bufferView && byteOffset && normalized && max && min && sparse;
770 if (result) {
771 accessor->componentType = static_cast<ComponentType>(*componentType);
772 accessor->count = *count;
773 accessor->type = *datatype;
774 accessor->bufferView = *bufferView;
775 accessor->byteOffset = *byteOffset;
776 accessor->max = move(*max);
777 accessor->min = move(*min);
778 accessor->normalized = *normalized;
779 accessor->sparse = move(*sparse);
780 }
781 loadResult.data->accessors.push_back(move(accessor));
782
783 return result;
784 }
785
ParseTextureInfo(LoadResult & loadResult,TextureInfo & info,const json::value & jsonData)786 bool ParseTextureInfo(LoadResult& loadResult, TextureInfo& info, const json::value& jsonData)
787 {
788 if (!ParseOptionalNumber<uint32_t>(loadResult, info.index, jsonData, "index", GLTF_INVALID_INDEX)) {
789 return false;
790 }
791
792 if (info.index != GLTF_INVALID_INDEX && info.index < loadResult.data->textures.size()) {
793 info.texture = loadResult.data->textures[info.index].get();
794 }
795
796 if (!ParseOptionalNumber<uint32_t>(loadResult, info.texCoordIndex, jsonData, "texCoord", GLTF_INVALID_INDEX)) {
797 return false;
798 }
799 #if defined(GLTF2_EXTENSION_KHR_TEXTURE_TRANSFORM)
800 const auto textureTransformParser = [&info](LoadResult& loadResult, const json::value& extensions) -> bool {
801 return ParseObject(loadResult, extensions, "KHR_texture_transform",
802 [&info](LoadResult& loadResult, const json::value& textureTransform) {
803 if (!ParseOptionalMath(loadResult, info.transform.offset, textureTransform, "offset", { 0.0f, 0.0f })) {
804 return false;
805 }
806
807 if (!ParseOptionalNumber(loadResult, info.transform.rotation, textureTransform, "rotation", 0.0f)) {
808 return false;
809 }
810
811 if (!ParseOptionalMath(loadResult, info.transform.scale, textureTransform, "scale", { 1.0f, 1.0f })) {
812 return false;
813 }
814
815 if (!ParseOptionalNumber(
816 loadResult, info.transform.texCoordIndex, textureTransform, "texCoord", GLTF_INVALID_INDEX)) {
817 return false;
818 }
819 return true;
820 });
821 };
822 if (!ParseObject(loadResult, jsonData, "extensions", textureTransformParser)) {
823 return false;
824 }
825 #endif
826 return true;
827 }
828
ParseMetallicRoughness(LoadResult & loadResult,const json::value & jsonData,MetallicRoughness & metallicRoughness)829 bool ParseMetallicRoughness(LoadResult& loadResult, const json::value& jsonData, MetallicRoughness& metallicRoughness)
830 {
831 if (auto roughnessJson = jsonData.find("pbrMetallicRoughness"); roughnessJson) {
832 if (!ParseOptionalMath(loadResult, metallicRoughness.baseColorFactor, *roughnessJson, "baseColorFactor",
833 metallicRoughness.baseColorFactor)) {
834 return false;
835 }
836
837 if (!ParseObject(loadResult, *roughnessJson, "baseColorTexture",
838 [&metallicRoughness](LoadResult& loadResult, const json::value& baseJson) -> bool {
839 return ParseTextureInfo(loadResult, metallicRoughness.baseColorTexture, baseJson);
840 })) {
841 return false;
842 }
843
844 if (!ParseObject(loadResult, *roughnessJson, "metallicRoughnessTexture",
845 [&metallicRoughness](LoadResult& loadResult, const json::value& baseJson) -> bool {
846 return ParseTextureInfo(loadResult, metallicRoughness.metallicRoughnessTexture, baseJson);
847 })) {
848 return false;
849 }
850
851 if (!ParseOptionalNumber<float>(loadResult, metallicRoughness.metallicFactor, *roughnessJson, "metallicFactor",
852 metallicRoughness.metallicFactor)) {
853 return false;
854 }
855
856 if (!ParseOptionalNumber<float>(loadResult, metallicRoughness.roughnessFactor, *roughnessJson,
857 "roughnessFactor", metallicRoughness.roughnessFactor)) {
858 return false;
859 }
860 }
861 return true;
862 }
863
TextureSampler(LoadResult & loadResult,const json::value & jsonData)864 std::optional<Sampler*> TextureSampler(LoadResult& loadResult, const json::value& jsonData)
865 {
866 size_t samplerIndex;
867 if (!ParseOptionalNumber<size_t>(loadResult, samplerIndex, jsonData, "sampler", GLTF_INVALID_INDEX)) {
868 return std::nullopt;
869 }
870
871 Sampler* sampler = loadResult.data->defaultSampler.get();
872 if (samplerIndex != GLTF_INVALID_INDEX) {
873 if (samplerIndex < loadResult.data->samplers.size()) {
874 sampler = loadResult.data->samplers[samplerIndex].get();
875 } else {
876 SetError(loadResult, "Invalid sampler index");
877 return std::nullopt;
878 }
879 }
880 return sampler;
881 }
882
TextureSource(LoadResult & loadResult,const json::value & jsonData)883 std::optional<Image*> TextureSource(LoadResult& loadResult, const json::value& jsonData)
884 {
885 size_t imageIndex;
886 if (!ParseOptionalNumber<size_t>(loadResult, imageIndex, jsonData, "source", GLTF_INVALID_INDEX)) {
887 return std::nullopt;
888 }
889
890 Image* image = nullptr;
891 if (imageIndex != GLTF_INVALID_INDEX) {
892 if (imageIndex < loadResult.data->images.size()) {
893 image = loadResult.data->images[imageIndex].get();
894 } else {
895 SetError(loadResult, "Invalid image index");
896 return std::nullopt;
897 }
898 }
899 return image;
900 }
901
ParseTexture(LoadResult & loadResult,const json::value & jsonData)902 bool ParseTexture(LoadResult& loadResult, const json::value& jsonData)
903 {
904 auto sampler = TextureSampler(loadResult, jsonData);
905 auto image = TextureSource(loadResult, jsonData);
906 if (auto jsonExtensions = jsonData.find("extensions"); jsonExtensions) {
907 if (auto jsonDds = jsonExtensions->find("MSFT_texture_dds"); jsonDds) {
908 image = TextureSource(loadResult, *jsonDds);
909 }
910 #if defined(GLTF2_EXTENSION_KHR_TEXTURE_BASISU)
911 if (auto jsonBasisu = jsonExtensions->find("KHR_texture_basisu"); jsonBasisu) {
912 image = TextureSource(loadResult, *jsonBasisu);
913 }
914 #endif
915 }
916 const auto result = (sampler && image);
917 auto texture = make_unique<Texture>();
918 if (result) {
919 texture->sampler = *sampler;
920 texture->image = *image;
921 }
922 loadResult.data->textures.push_back(move(texture));
923
924 return result;
925 }
926
ParseImage(LoadResult & loadResult,const json::value & jsonData)927 bool ParseImage(LoadResult& loadResult, const json::value& jsonData)
928 {
929 auto image = make_unique<Image>();
930 if (!ParseOptionalString(loadResult, image->uri, jsonData, "uri", string())) {
931 return false;
932 }
933
934 DecodeUri(image->uri);
935
936 size_t bufferIndex;
937 if (!ParseOptionalNumber<size_t>(loadResult, bufferIndex, jsonData, "bufferView", GLTF_INVALID_INDEX)) {
938 return false;
939 }
940
941 if (bufferIndex != GLTF_INVALID_INDEX && bufferIndex < loadResult.data->bufferViews.size()) {
942 image->bufferView = loadResult.data->bufferViews[bufferIndex].get();
943
944 string imageType;
945 if (!ParseOptionalString(loadResult, imageType, jsonData, "mimeType", "")) {
946 return false;
947 }
948
949 if (!GetMimeType(imageType, image->type)) {
950 RETURN_WITH_ERROR(loadResult, "Invalid mime type.");
951 }
952 }
953
954 loadResult.data->images.push_back(move(image));
955
956 return true;
957 }
958
ParseFilterMode(LoadResult & loadResult,FilterMode & out,const json::value & jsonData,const string & filterName)959 bool ParseFilterMode(LoadResult& loadResult, FilterMode& out, const json::value& jsonData, const string& filterName)
960 {
961 int value = 0;
962 if (!ParseOptionalNumber<int>(loadResult, value, jsonData, filterName, static_cast<int>(FilterMode::LINEAR))) {
963 return false;
964 }
965
966 out = (FilterMode)value;
967
968 switch (out) {
969 case FilterMode::NEAREST:
970 case FilterMode::LINEAR:
971 case FilterMode::NEAREST_MIPMAP_NEAREST:
972 case FilterMode::LINEAR_MIPMAP_NEAREST:
973 case FilterMode::NEAREST_MIPMAP_LINEAR:
974 case FilterMode::LINEAR_MIPMAP_LINEAR:
975 return true;
976 default:
977 break;
978 }
979
980 RETURN_WITH_ERROR(loadResult, "Invalid filter mode");
981 }
982
ParseWrapMode(LoadResult & loadResult,WrapMode & out,const json::value & jsonData,const string & wrapModeName)983 bool ParseWrapMode(LoadResult& loadResult, WrapMode& out, const json::value& jsonData, const string& wrapModeName)
984 {
985 int value = 0;
986 if (!ParseOptionalNumber(loadResult, value, jsonData, wrapModeName, static_cast<int>(WrapMode::REPEAT))) {
987 return false;
988 }
989
990 out = (WrapMode)value;
991
992 switch (out) {
993 case WrapMode::CLAMP_TO_EDGE:
994 case WrapMode::MIRRORED_REPEAT:
995 case WrapMode::REPEAT:
996 return true;
997 default:
998 break;
999 }
1000
1001 RETURN_WITH_ERROR(loadResult, "Invalid wrap mode");
1002 }
1003
ParseSampler(LoadResult & loadResult,const json::value & jsonData)1004 bool ParseSampler(LoadResult& loadResult, const json::value& jsonData)
1005 {
1006 bool result = true;
1007
1008 FilterMode magFilter;
1009 if (!ParseFilterMode(loadResult, magFilter, jsonData, "magFilter")) {
1010 result = false;
1011 }
1012
1013 FilterMode minFilter;
1014 if (!ParseFilterMode(loadResult, minFilter, jsonData, "minFilter")) {
1015 result = false;
1016 }
1017
1018 WrapMode wrapS;
1019 if (!ParseWrapMode(loadResult, wrapS, jsonData, "wrapS")) {
1020 result = false;
1021 }
1022
1023 WrapMode wrapT;
1024 if (!ParseWrapMode(loadResult, wrapT, jsonData, "wrapT")) {
1025 result = false;
1026 }
1027
1028 auto sampler = make_unique<Sampler>();
1029 if (result) {
1030 sampler->magFilter = magFilter;
1031 sampler->minFilter = minFilter;
1032 sampler->wrapS = wrapS;
1033 sampler->wrapT = wrapT;
1034 }
1035
1036 loadResult.data->samplers.push_back(move(sampler));
1037
1038 return result;
1039 }
1040
ParseNormalTexture(LoadResult & loadResult,const json::value & jsonData,NormalTexture & normalTexture)1041 bool ParseNormalTexture(LoadResult& loadResult, const json::value& jsonData, NormalTexture& normalTexture)
1042 {
1043 if (auto normalJson = jsonData.find("normalTexture"); normalJson) {
1044 if (!ParseTextureInfo(loadResult, normalTexture.textureInfo, *normalJson)) {
1045 return false;
1046 }
1047
1048 if (!ParseOptionalNumber<float>(loadResult, normalTexture.scale, *normalJson, "scale", normalTexture.scale)) {
1049 return false;
1050 }
1051 }
1052
1053 return true;
1054 }
1055
ParseOcclusionTexture(LoadResult & loadResult,const json::value & jsonData,OcclusionTexture & occlusionTexture)1056 bool ParseOcclusionTexture(LoadResult& loadResult, const json::value& jsonData, OcclusionTexture& occlusionTexture)
1057 {
1058 if (auto occlusionJson = jsonData.find("occlusionTexture"); occlusionJson) {
1059 if (!ParseTextureInfo(loadResult, occlusionTexture.textureInfo, *occlusionJson)) {
1060 return false;
1061 }
1062
1063 if (!ParseOptionalNumber<float>(
1064 loadResult, occlusionTexture.strength, *occlusionJson, "strength", occlusionTexture.strength)) {
1065 return false;
1066 }
1067 }
1068
1069 return true;
1070 }
1071
ParseEmissiveTexture(LoadResult & loadResult,const json::value & jsonData,TextureInfo & emissiveTexture)1072 bool ParseEmissiveTexture(LoadResult& loadResult, const json::value& jsonData, TextureInfo& emissiveTexture)
1073 {
1074 if (auto emissiveJson = jsonData.find("emissiveTexture"); emissiveJson) {
1075 if (!ParseTextureInfo(loadResult, emissiveTexture, *emissiveJson)) {
1076 return false;
1077 }
1078 }
1079
1080 return true;
1081 }
1082
ParseMaterialExtras(LoadResult & loadResult,const json::value & jsonData,Material & material)1083 bool ParseMaterialExtras(LoadResult& loadResult, const json::value& jsonData, Material& material)
1084 {
1085 if (auto extrasJson = jsonData.find("extras"); extrasJson) {
1086 #if defined(GLTF2_EXTRAS_CLEAR_COAT_MATERIAL)
1087 const auto parseClearCoat = [&material](LoadResult& loadResult, const json::value& materialJson) -> bool {
1088 if (!ParseOptionalNumber<float>(
1089 loadResult, material.clearcoat.factor, materialJson, "factor", material.clearcoat.factor)) {
1090 return false;
1091 }
1092
1093 if (!ParseOptionalNumber<float>(loadResult, material.clearcoat.roughness, materialJson, "roughness",
1094 material.clearcoat.roughness)) {
1095 return false;
1096 }
1097
1098 return true;
1099 };
1100 if (!ParseObject(loadResult, *extrasJson, "clearCoat", parseClearCoat)) {
1101 return false;
1102 }
1103 #endif
1104 }
1105 return true;
1106 }
1107
1108 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_CLEARCOAT)
ParseKhrMaterialsClearcoat(LoadResult & loadResult,const json::value & jsonData,Material::Clearcoat & clearcoat)1109 bool ParseKhrMaterialsClearcoat(LoadResult& loadResult, const json::value& jsonData, Material::Clearcoat& clearcoat)
1110 {
1111 if (auto clearcoatJson = jsonData.find("KHR_materials_clearcoat"); clearcoatJson) {
1112 // clearcoatFactor
1113 if (!ParseOptionalNumber(loadResult, clearcoat.factor, *clearcoatJson, "clearcoatFactor", 0.f)) {
1114 return false;
1115 }
1116
1117 // clearcoatTexture
1118 const auto parseClearcoatTexture = [&textureInfo = clearcoat.texture](
1119 LoadResult& loadResult, const json::value& clearcoat) -> bool {
1120 return ParseTextureInfo(loadResult, textureInfo, clearcoat);
1121 };
1122 if (!ParseObject(loadResult, *clearcoatJson, "clearcoatTexture", parseClearcoatTexture)) {
1123 return false;
1124 }
1125
1126 // clearcoaRoughnessFactor
1127 if (!ParseOptionalNumber(loadResult, clearcoat.roughness, *clearcoatJson, "clearcoatRoughnessFactor", 0.f)) {
1128 return false;
1129 }
1130
1131 // clearcoatRougnessTexture
1132 const auto parseClearcoatRoughnessTexture = [&textureInfo = clearcoat.roughnessTexture](
1133 LoadResult& loadResult, const json::value& clearcoat) -> bool {
1134 return ParseTextureInfo(loadResult, textureInfo, clearcoat);
1135 };
1136 if (!ParseObject(loadResult, *clearcoatJson, "clearcoatRoughnessTexture", parseClearcoatRoughnessTexture)) {
1137 return false;
1138 }
1139
1140 // clearcoatNormalTexture
1141 const auto parseClearcoatNormalTexture = [&normalTexture = clearcoat.normalTexture](
1142 LoadResult& loadResult, const json::value& clearcoat) -> bool {
1143 if (!ParseTextureInfo(loadResult, normalTexture.textureInfo, clearcoat)) {
1144 return false;
1145 }
1146
1147 if (!ParseOptionalNumber(loadResult, normalTexture.scale, clearcoat, "scale", normalTexture.scale)) {
1148 return false;
1149 }
1150 return true;
1151 };
1152 if (!ParseObject(loadResult, *clearcoatJson, "clearcoatNormalTexture", parseClearcoatNormalTexture)) {
1153 return false;
1154 }
1155 return true;
1156 }
1157 return true;
1158 }
1159 #endif
1160
1161 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_EMISSIVE_STRENGTH)
ParseKhrMaterialsEmissiveStrength(LoadResult & loadResult,const json::value & jsonData,Material & material)1162 bool ParseKhrMaterialsEmissiveStrength(LoadResult& loadResult, const json::value& jsonData, Material& material)
1163 {
1164 if (auto emissiveStrengthJson = jsonData.find("KHR_materials_emissive_strength"); emissiveStrengthJson) {
1165 return ParseOptionalNumber(
1166 loadResult, material.emissiveFactor.w, *emissiveStrengthJson, "emissiveStrength", 1.f);
1167 }
1168 return true;
1169 }
1170 #endif
1171
1172 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_IOR)
ParseKhrMaterialsIor(LoadResult & loadResult,const json::value & jsonData,Material::Ior & ior)1173 bool ParseKhrMaterialsIor(LoadResult& loadResult, const json::value& jsonData, Material::Ior& ior)
1174 {
1175 if (auto iorJson = jsonData.find("KHR_materials_ior"); iorJson) {
1176 return ParseOptionalNumber(loadResult, ior.ior, *iorJson, "ior", ior.ior);
1177 }
1178 return true;
1179 }
1180 #endif
1181
1182 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_PBRSPECULARGLOSSINESS)
ParseKhrMaterialsPbrSpecularGlossiness(LoadResult & loadResult,const json::value & jsonData,Material & material)1183 bool ParseKhrMaterialsPbrSpecularGlossiness(LoadResult& loadResult, const json::value& jsonData, Material& material)
1184 {
1185 if (auto specGlossJson = jsonData.find("KHR_materials_pbrSpecularGlossiness"); specGlossJson) {
1186 material.type = Material::Type::SpecularGlossiness;
1187
1188 if (!ParseOptionalMath(loadResult, material.specularGlossiness.diffuseFactor, *specGlossJson, "diffuseFactor",
1189 material.specularGlossiness.diffuseFactor)) {
1190 return false;
1191 }
1192
1193 const auto parseDiffuseTexture = [&material](LoadResult& loadResult, const json::value& jsonData) -> bool {
1194 return ParseTextureInfo(loadResult, material.specularGlossiness.diffuseTexture, jsonData);
1195 };
1196 if (!ParseObject(loadResult, *specGlossJson, "diffuseTexture", parseDiffuseTexture)) {
1197 return false;
1198 }
1199
1200 if (!ParseOptionalMath(loadResult, material.specularGlossiness.specularFactor, *specGlossJson, "specularFactor",
1201 material.specularGlossiness.specularFactor)) {
1202 return false;
1203 }
1204
1205 if (!ParseOptionalNumber<float>(
1206 loadResult, material.specularGlossiness.glossinessFactor, *specGlossJson, "glossinessFactor", 1.0f)) {
1207 return false;
1208 }
1209
1210 const auto parseSpecularGlossinessTexture = [&material](
1211 LoadResult& loadResult, const json::value& jsonData) -> bool {
1212 return ParseTextureInfo(loadResult, material.specularGlossiness.specularGlossinessTexture, jsonData);
1213 };
1214 if (!ParseObject(loadResult, *specGlossJson, "specularGlossinessTexture", parseSpecularGlossinessTexture)) {
1215 return false;
1216 }
1217 }
1218 return true;
1219 }
1220 #endif
1221
1222 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SHEEN)
ParseKhrMaterialsSheen(LoadResult & loadResult,const json::value & jsonData,Material::Sheen & sheen)1223 bool ParseKhrMaterialsSheen(LoadResult& loadResult, const json::value& jsonData, Material::Sheen& sheen)
1224 {
1225 if (auto sheenJson = jsonData.find("KHR_materials_sheen"); sheenJson) {
1226 // sheenColorFactor
1227 if (!ParseOptionalMath(loadResult, sheen.factor, *sheenJson, "sheenColorFactor", {})) {
1228 return false;
1229 }
1230
1231 // sheenColorTexture
1232 const auto parseSheenTexture = [&textureInfo = sheen.texture](
1233 LoadResult& loadResult, const json::value& sheen) -> bool {
1234 return ParseTextureInfo(loadResult, textureInfo, sheen);
1235 };
1236 if (!ParseObject(loadResult, *sheenJson, "sheenColorTexture", parseSheenTexture)) {
1237 return false;
1238 }
1239
1240 // sheenRoughnessFactor
1241 if (!ParseOptionalNumber(loadResult, sheen.roughness, *sheenJson, "sheenRoughnessFactor", 0.f)) {
1242 return false;
1243 }
1244
1245 // sheenRougnessTexture
1246 const auto parseSheenRoughnessTexture = [&textureInfo = sheen.roughnessTexture](
1247 LoadResult& loadResult, const json::value& sheen) -> bool {
1248 return ParseTextureInfo(loadResult, textureInfo, sheen);
1249 };
1250 if (!ParseObject(loadResult, *sheenJson, "sheenRoughnessTexture", parseSheenRoughnessTexture)) {
1251 return false;
1252 }
1253 }
1254 return true;
1255 }
1256 #endif
1257
1258 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SPECULAR)
ParseKhrMaterialsSpecular(LoadResult & loadResult,const json::value & jsonData,Material::Specular & specular)1259 bool ParseKhrMaterialsSpecular(LoadResult& loadResult, const json::value& jsonData, Material::Specular& specular)
1260 {
1261 if (auto specularJson = jsonData.find("KHR_materials_specular"); specularJson) {
1262 // specularFactor
1263 if (!ParseOptionalNumber(loadResult, specular.factor, *specularJson, "specularFactor", 1.f)) {
1264 return false;
1265 }
1266
1267 // specularTexture
1268 const auto parseSpecularTexture = [&textureInfo = specular.texture](
1269 LoadResult& loadResult, const json::value& specular) -> bool {
1270 return ParseTextureInfo(loadResult, textureInfo, specular);
1271 };
1272 if (!ParseObject(loadResult, *specularJson, "specularTexture", parseSpecularTexture)) {
1273 return false;
1274 }
1275
1276 // specularColorFactor
1277 if (!ParseOptionalMath(loadResult, specular.color, *specularJson, "specularColorFactor", specular.color)) {
1278 return false;
1279 }
1280
1281 // specularColorTexture
1282 const auto parseSpecularColorTexture = [&textureInfo = specular.colorTexture](
1283 LoadResult& loadResult, const json::value& specular) -> bool {
1284 return ParseTextureInfo(loadResult, textureInfo, specular);
1285 };
1286 if (!ParseObject(loadResult, *specularJson, "specularColorTexture", parseSpecularColorTexture)) {
1287 return false;
1288 }
1289 }
1290 return true;
1291 }
1292 #endif
1293
1294 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_TRANSMISSION)
ParseKhrMaterialsTransmission(LoadResult & loadResult,const json::value & jsonData,Material::Transmission & transmission)1295 bool ParseKhrMaterialsTransmission(
1296 LoadResult& loadResult, const json::value& jsonData, Material::Transmission& transmission)
1297 {
1298 if (auto transmissionJson = jsonData.find("KHR_materials_transmission"); transmissionJson) {
1299 // transmissionFactor
1300 if (!ParseOptionalNumber(loadResult, transmission.factor, *transmissionJson, "transmissionFactor", 0.f)) {
1301 return false;
1302 }
1303
1304 // transmissionTexture
1305 const auto parseTransmissionTexture = [&textureInfo = transmission.texture](
1306 LoadResult& loadResult, const json::value& transmission) -> bool {
1307 return ParseTextureInfo(loadResult, textureInfo, transmission);
1308 };
1309 if (!ParseObject(loadResult, *transmissionJson, "transmissionTexture", parseTransmissionTexture)) {
1310 return false;
1311 }
1312 }
1313 return true;
1314 }
1315 #endif
1316
ParseMaterialExtensions(LoadResult & loadResult,const json::value & jsonData,Material & material)1317 bool ParseMaterialExtensions(LoadResult& loadResult, const json::value& jsonData, Material& material)
1318 {
1319 if (auto extensionsJson = jsonData.find("extensions"); extensionsJson) {
1320 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_CLEARCOAT)
1321 if (!ParseKhrMaterialsClearcoat(loadResult, *extensionsJson, material.clearcoat)) {
1322 return false;
1323 }
1324 #endif
1325 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_EMISSIVE_STRENGTH)
1326 if (!ParseKhrMaterialsEmissiveStrength(loadResult, *extensionsJson, material)) {
1327 return false;
1328 }
1329 #endif
1330 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_IOR)
1331 if (!ParseKhrMaterialsIor(loadResult, *extensionsJson, material.ior)) {
1332 return false;
1333 }
1334 #endif
1335 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_PBRSPECULARGLOSSINESS)
1336 if (!ParseKhrMaterialsPbrSpecularGlossiness(loadResult, *extensionsJson, material)) {
1337 return false;
1338 }
1339 #endif
1340 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SHEEN)
1341 if (!ParseKhrMaterialsSheen(loadResult, *extensionsJson, material.sheen)) {
1342 return false;
1343 }
1344 #endif
1345 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SPECULAR)
1346 if (!ParseKhrMaterialsSpecular(loadResult, *extensionsJson, material.specular)) {
1347 return false;
1348 }
1349 #endif
1350 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_TRANSMISSION)
1351 if (!ParseKhrMaterialsTransmission(loadResult, *extensionsJson, material.transmission)) {
1352 return false;
1353 }
1354 #endif
1355 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_UNLIT)
1356 const auto parseUnlitExtension = [&material](LoadResult& loadResult, const json::value& materialJson) -> bool {
1357 material.type = Material::Type::Unlit;
1358 return true;
1359 };
1360
1361 if (!ParseObject(loadResult, *extensionsJson, "KHR_materials_unlit", parseUnlitExtension)) {
1362 // Parsing of materials_unlit failed.
1363 return false;
1364 }
1365 #endif
1366 return true;
1367 }
1368 return true;
1369 }
1370
ParseMaterial(LoadResult & loadResult,const json::value & jsonData)1371 bool ParseMaterial(LoadResult& loadResult, const json::value& jsonData)
1372 {
1373 bool result = true;
1374
1375 auto material = make_unique<Material>();
1376 if (!ParseMetallicRoughness(loadResult, jsonData, material->metallicRoughness)) {
1377 result = false;
1378 }
1379
1380 if (!ParseNormalTexture(loadResult, jsonData, material->normalTexture)) {
1381 result = false;
1382 }
1383
1384 if (!ParseOcclusionTexture(loadResult, jsonData, material->occlusionTexture)) {
1385 result = false;
1386 }
1387
1388 if (!ParseEmissiveTexture(loadResult, jsonData, material->emissiveTexture)) {
1389 result = false;
1390 }
1391
1392 if (!ParseOptionalString(
1393 loadResult, material->name, jsonData, "name", "material_" + to_string(loadResult.data->materials.size()))) {
1394 result = false;
1395 }
1396
1397 if (Math::Vec3 emissive; !ParseOptionalMath(loadResult, emissive, jsonData, "emissiveFactor", emissive)) {
1398 result = false;
1399 } else {
1400 material->emissiveFactor.x = emissive.x;
1401 material->emissiveFactor.y = emissive.y;
1402 material->emissiveFactor.z = emissive.z;
1403 }
1404
1405 string alphaMode;
1406 if (!ParseOptionalString(loadResult, alphaMode, jsonData, "alphaMode", "OPAQUE")) {
1407 result = false;
1408 }
1409
1410 if (!GetAlphaMode(alphaMode, material->alphaMode)) {
1411 SetError(loadResult, "Invalid alpha mode.");
1412 result = false;
1413 }
1414
1415 if (!ParseOptionalNumber<float>(
1416 loadResult, material->alphaCutoff, jsonData, "alphaCutoff", material->alphaCutoff)) {
1417 result = false;
1418 }
1419
1420 if (!ParseOptionalBoolean(loadResult, material->doubleSided, jsonData, "doubleSided", false)) {
1421 result = false;
1422 }
1423
1424 if (!ParseMaterialExtras(loadResult, jsonData, *material)) {
1425 result = false;
1426 }
1427
1428 if (!ParseMaterialExtensions(loadResult, jsonData, *material)) {
1429 result = false;
1430 }
1431
1432 loadResult.data->materials.push_back(move(material));
1433
1434 return result;
1435 }
1436
PrimitiveAttributes(LoadResult & loadResult,const json::value & jsonData,MeshPrimitive & meshPrimitive)1437 bool PrimitiveAttributes(LoadResult& loadResult, const json::value& jsonData, MeshPrimitive& meshPrimitive)
1438 {
1439 if (const auto* attirbutesJson = jsonData.find("attributes"); attirbutesJson && attirbutesJson->is_object()) {
1440 for (const auto& it : attirbutesJson->object_) {
1441 if (it.value.is_number()) {
1442 Attribute attribute;
1443
1444 if (!GetAttributeType(it.key, attribute.attribute)) {
1445 RETURN_WITH_ERROR(loadResult, "Invalid attribute type.");
1446 }
1447
1448 const uint32_t accessor = it.value.as_number<uint32_t>();
1449 if (accessor < loadResult.data->accessors.size()) {
1450 attribute.accessor = loadResult.data->accessors[accessor].get();
1451
1452 auto const validationResult = ValidatePrimitiveAttribute(
1453 attribute.attribute.type, attribute.accessor->type, attribute.accessor->componentType);
1454 if (!validationResult.empty()) {
1455 #if defined(GLTF2_EXTENSION_KHR_MESH_QUANTIZATION)
1456 if (loadResult.data->quantization) {
1457 auto const extendedValidationResult = ValidatePrimitiveAttributeQuatization(
1458 attribute.attribute.type, attribute.accessor->type, attribute.accessor->componentType);
1459 if (!extendedValidationResult.empty()) {
1460 RETURN_WITH_ERROR(loadResult, extendedValidationResult);
1461 }
1462 } else {
1463 #else
1464 {
1465 #endif
1466 RETURN_WITH_ERROR(loadResult, validationResult);
1467 }
1468 }
1469
1470 meshPrimitive.attributes.push_back(attribute);
1471 }
1472 }
1473 }
1474 if (std::none_of(meshPrimitive.attributes.begin(), meshPrimitive.attributes.end(),
1475 [](const Attribute& attr) { return attr.attribute.type == AttributeType::POSITION; })) {
1476 RETURN_WITH_ERROR(loadResult, "Primitive must have POSITION attribute.");
1477 }
1478 } else {
1479 RETURN_WITH_ERROR(loadResult, "Missing primitive.attributes.");
1480 }
1481 return true;
1482 }
1483
1484 bool PrimitiveTargets(
1485 LoadResult& loadResult, const json::value& jsonData, MeshPrimitive& meshPrimitive, bool compressed)
1486 {
1487 return ForEachInArray(loadResult, jsonData, "targets",
1488 [&meshPrimitive, compressed](LoadResult& loadResult, const json::value& target) -> bool {
1489 MorphTarget mTarget;
1490 #ifdef GLTF2_EXTENSION_IGFX_COMPRESSED
1491 mTarget.iGfxCompressed = compressed;
1492 #endif
1493 for (const auto& it : target.object_) {
1494 if (it.value.is_number()) {
1495 Attribute attribute;
1496
1497 if (!GetAttributeType(it.key, attribute.attribute)) {
1498 RETURN_WITH_ERROR(loadResult, "Invalid attribute type.");
1499 }
1500
1501 const uint32_t accessor = it.value.as_number<uint32_t>();
1502
1503 if (accessor < loadResult.data->accessors.size()) {
1504 attribute.accessor = loadResult.data->accessors[accessor].get();
1505
1506 auto const validationResult = ValidateMorphTargetAttribute(
1507 attribute.attribute.type, attribute.accessor->type, attribute.accessor->componentType);
1508 if (!validationResult.empty()) {
1509 #if defined(GLTF2_EXTENSION_KHR_MESH_QUANTIZATION)
1510 if (loadResult.data->quantization) {
1511 auto const extendedValidationResult =
1512 ValidateMorphTargetAttributeQuantization(attribute.attribute.type,
1513 attribute.accessor->type, attribute.accessor->componentType);
1514 if (!extendedValidationResult.empty()) {
1515 RETURN_WITH_ERROR(loadResult, extendedValidationResult);
1516 }
1517 } else {
1518 #else
1519 {
1520 #endif
1521 RETURN_WITH_ERROR(loadResult, validationResult);
1522 }
1523 }
1524
1525 mTarget.target.push_back(attribute);
1526 }
1527 }
1528 }
1529
1530 meshPrimitive.targets.push_back(move(mTarget));
1531
1532 return true;
1533 });
1534 }
1535
1536 bool ParsePrimitive(LoadResult& loadResult, vector<MeshPrimitive>& primitives, const json::value& jsonData)
1537 {
1538 MeshPrimitive meshPrimitive;
1539
1540 if (!PrimitiveAttributes(loadResult, jsonData, meshPrimitive)) {
1541 return false;
1542 }
1543
1544 size_t indices;
1545 if (!ParseOptionalNumber<size_t>(loadResult, indices, jsonData, "indices", GLTF_INVALID_INDEX)) {
1546 return false;
1547 }
1548 if (indices != GLTF_INVALID_INDEX && indices < loadResult.data->accessors.size()) {
1549 meshPrimitive.indices = loadResult.data->accessors[indices].get();
1550 }
1551
1552 if (!ParseOptionalNumber<uint32_t>(
1553 loadResult, meshPrimitive.materialIndex, jsonData, "material", GLTF_INVALID_INDEX)) {
1554 return false;
1555 }
1556 if (meshPrimitive.materialIndex != GLTF_INVALID_INDEX &&
1557 meshPrimitive.materialIndex < loadResult.data->materials.size()) {
1558 meshPrimitive.material = loadResult.data->materials[meshPrimitive.materialIndex].get();
1559 } else {
1560 meshPrimitive.material = loadResult.data->defaultMaterial.get();
1561 }
1562
1563 int mode;
1564 if (!ParseOptionalNumber<int>(loadResult, mode, jsonData, "mode", static_cast<int>(RenderMode::TRIANGLES))) {
1565 return false;
1566 }
1567
1568 if (!RangedEnumCast<RenderMode>(loadResult, meshPrimitive.mode, mode)) {
1569 return false;
1570 }
1571
1572 bool compressed = false;
1573
1574 if (const auto& extensionsJson = jsonData.find("extensions"); extensionsJson) {
1575 #ifdef GLTF2_EXTENSION_IGFX_COMPRESSED
1576 if (const auto& compressedJson = extensionsJson->find("IGFX_compressed"); compressedJson) {
1577 compressed = true;
1578 if (!PrimitiveTargets(loadResult, *compressedJson, meshPrimitive, compressed)) {
1579 return false;
1580 }
1581 }
1582 #endif
1583 }
1584
1585 if (!compressed) {
1586 if (!PrimitiveTargets(loadResult, jsonData, meshPrimitive, compressed)) {
1587 return false;
1588 }
1589 }
1590
1591 primitives.push_back(move(meshPrimitive));
1592
1593 return true;
1594 }
1595
1596 bool MeshExtras(LoadResult& loadResult, const json::value& jsonData, array_view<MeshPrimitive> primitives)
1597 {
1598 size_t index = 0;
1599 return ForEachInArray(loadResult, jsonData, "targetNames",
1600 [&primitives, &index](LoadResult& loadResult, const json::value& targetName) -> bool {
1601 if (!targetName.is_string()) {
1602 RETURN_WITH_ERROR(loadResult, "mesh.extras.targetNames should be an array of strings");
1603 }
1604
1605 auto name = targetName.string_;
1606
1607 for (auto& primitive : primitives) {
1608 if (index < primitive.targets.size()) {
1609 primitive.targets[index].name = name;
1610 }
1611 }
1612
1613 index++;
1614
1615 return true;
1616 });
1617 }
1618
1619 bool ParseMesh(LoadResult& loadResult, const json::value& jsonData)
1620 {
1621 bool result = true;
1622
1623 string name;
1624 if (!ParseOptionalString(loadResult, name, jsonData, "name", "")) {
1625 return false;
1626 }
1627
1628 vector<MeshPrimitive> primitives;
1629 if (auto const primitivesJson = jsonData.find("primitives"); primitivesJson) {
1630 if (!ForEachInArray(
1631 loadResult, *primitivesJson, [&primitives](LoadResult& loadResult, const json::value& item) -> bool {
1632 return ParsePrimitive(loadResult, primitives, item);
1633 })) {
1634 return false;
1635 }
1636 }
1637
1638 vector<float> weights;
1639 const auto parseWeights = [&weights](LoadResult& loadResult, const json::value& weight) -> bool {
1640 if (weight.is_number()) {
1641 weights.push_back(weight.as_number<float>());
1642 }
1643 return true;
1644 };
1645
1646 if (!ForEachInArray(loadResult, jsonData, "weights", parseWeights)) {
1647 return false;
1648 }
1649
1650 // validate morph target counts
1651 for (size_t i = 1; i < primitives.size(); i++) {
1652 if (primitives[i].targets.size() != primitives[0].targets.size()) {
1653 SetError(loadResult,
1654 "Morph target count mismatch: each primitive of a mesh should have same amount of morph targets");
1655 result = false;
1656 }
1657 }
1658
1659 const auto parseExtras = [&primitives](LoadResult& loadResult, const json::value& extras) -> bool {
1660 return MeshExtras(loadResult, extras, primitives);
1661 };
1662
1663 if (!ParseObject(loadResult, jsonData, "extras", parseExtras)) {
1664 return false;
1665 }
1666
1667 auto mesh = make_unique<Mesh>();
1668 if (result) {
1669 mesh->name = move(name);
1670 mesh->weights = move(weights);
1671 mesh->primitives = move(primitives);
1672 }
1673
1674 loadResult.data->meshes.push_back(move(mesh));
1675
1676 return true;
1677 }
1678
1679 bool CameraPerspective(
1680 LoadResult& loadResult, const json::value& jsonData, Camera::Attributes::Perspective& perspective)
1681 {
1682 if (!ParseOptionalNumber<float>(loadResult, perspective.aspect, jsonData, "aspectRatio", -1.f)) {
1683 return false;
1684 }
1685 if (!ParseOptionalNumber<float>(loadResult, perspective.yfov, jsonData, "yfov", -1.f)) { // required
1686 return false;
1687 }
1688 if (!ParseOptionalNumber<float>(loadResult, perspective.zfar, jsonData, "zfar", -1.f)) {
1689 return false;
1690 }
1691 if (!ParseOptionalNumber<float>(loadResult, perspective.znear, jsonData, "znear", -1.f)) { // required
1692 return false;
1693 }
1694 if (perspective.yfov < 0 || perspective.znear < 0) {
1695 RETURN_WITH_ERROR(loadResult, "Invalid camera properties (perspective)");
1696 }
1697
1698 return true;
1699 }
1700
1701 bool CameraOrthographic(LoadResult& loadResult, const json::value& jsonData, Camera::Attributes::Ortho& ortho)
1702 {
1703 if (!ParseOptionalNumber<float>(loadResult, ortho.xmag, jsonData, "xmag", 0)) { // required
1704 return false;
1705 }
1706 if (!ParseOptionalNumber<float>(loadResult, ortho.ymag, jsonData, "ymag", 0)) { // required
1707 return false;
1708 }
1709 if (!ParseOptionalNumber<float>(loadResult, ortho.zfar, jsonData, "zfar", -1.f)) { // required
1710 return false;
1711 }
1712 if (!ParseOptionalNumber<float>(loadResult, ortho.znear, jsonData, "znear", -1.f)) { // required
1713 return false;
1714 }
1715 if (ortho.zfar < 0 || ortho.znear < 0 || ortho.xmag == 0 || ortho.ymag == 0) {
1716 RETURN_WITH_ERROR(loadResult, "Invalid camera properties (ortho)");
1717 }
1718 return true;
1719 }
1720
1721 bool ParseCamera(LoadResult& loadResult, const json::value& jsonData)
1722 {
1723 bool result = true;
1724
1725 auto camera = make_unique<Camera>();
1726 if (!ParseOptionalString(loadResult, camera->name, jsonData, "name", "")) {
1727 result = false;
1728 }
1729 string cameraType;
1730 if (!ParseOptionalString(loadResult, cameraType, jsonData, "type", "")) {
1731 result = false;
1732 }
1733 if (!GetCameraType(cameraType, camera->type)) {
1734 SetError(loadResult, "Invalid camera type");
1735 result = false;
1736 }
1737
1738 switch (camera->type) {
1739 case CameraType::PERSPECTIVE: {
1740 const auto parser = [&camera](LoadResult& loadResult, const json::value& perspective) -> bool {
1741 return CameraPerspective(loadResult, perspective, camera->attributes.perspective);
1742 };
1743
1744 if (!ParseObject(loadResult, jsonData, "perspective", parser)) {
1745 result = false;
1746 }
1747 break;
1748 }
1749
1750 case CameraType::ORTHOGRAPHIC: {
1751 const auto parser = [&camera](LoadResult& loadResult, const json::value& orthographic) -> bool {
1752 return CameraOrthographic(loadResult, orthographic, camera->attributes.ortho);
1753 };
1754
1755 if (!ParseObject(loadResult, jsonData, "orthographic", parser)) {
1756 result = false;
1757 }
1758 break;
1759 }
1760
1761 default:
1762 case CameraType::INVALID: {
1763 SetError(loadResult, "Invalid camera type");
1764 result = false;
1765 }
1766 }
1767
1768 loadResult.data->cameras.push_back(move(camera));
1769 return result;
1770 }
1771
1772 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
1773 bool LightSpot(LoadResult& loadResult, const json::value& jsonData, decltype(KHRLight::positional.spot)& spot)
1774 {
1775 return ParseObject(
1776 loadResult, jsonData, "spot", [&spot](LoadResult& loadResult, const json::value& jsonData) -> bool {
1777 if (!ParseOptionalNumber<float>(loadResult, spot.innerAngle, jsonData, "innerConeAngle", 0.f)) {
1778 return false;
1779 }
1780
1781 if (!ParseOptionalNumber<float>(
1782 loadResult, spot.outerAngle, jsonData, "outerConeAngle", 0.785398163397448f)) {
1783 return false;
1784 }
1785
1786 return true;
1787 });
1788 }
1789
1790 bool LightShadow(LoadResult& loadResult, const json::value& jsonData, decltype(KHRLight::shadow)& shadow)
1791 {
1792 return ParseObject(
1793 loadResult, jsonData, "shadow", [&shadow](LoadResult& loadResult, const json::value& jsonData) -> bool {
1794 if (!ParseOptionalBoolean(loadResult, shadow.shadowCaster, jsonData, "caster", false)) {
1795 return false;
1796 }
1797
1798 if (!ParseOptionalNumber<float>(
1799 loadResult, shadow.nearClipDistance, jsonData, "znear", shadow.nearClipDistance)) {
1800 return false;
1801 }
1802
1803 if (!ParseOptionalNumber<float>(
1804 loadResult, shadow.farClipDistance, jsonData, "zfar", shadow.farClipDistance)) {
1805 return false;
1806 }
1807 return true;
1808 });
1809 }
1810
1811 bool ParseKHRLight(LoadResult& loadResult, const json::value& jsonData)
1812 {
1813 bool result = true;
1814
1815 auto light = make_unique<KHRLight>();
1816
1817 if (!ParseOptionalString(loadResult, light->name, jsonData, "name", "")) {
1818 result = false;
1819 }
1820
1821 string lightType;
1822 if (!ParseOptionalString(loadResult, lightType, jsonData, "type", "")) {
1823 result = false;
1824 }
1825
1826 if (!GetLightType(lightType, light->type)) {
1827 SetError(loadResult, "Invalid light type.");
1828 result = false;
1829 }
1830
1831 const auto parsePositionalInfo = [&light](LoadResult& loadResult, const json::value& positional) -> bool {
1832 return ParseOptionalNumber<float>(loadResult, light->positional.range, positional, "range", 0.0f);
1833 };
1834
1835 if (!ParseObject(loadResult, jsonData, "positional", parsePositionalInfo)) {
1836 result = false;
1837 }
1838
1839 if (!ParseOptionalNumber<float>(loadResult, light->positional.range, jsonData, "range", 0.0f)) {
1840 return false;
1841 }
1842
1843 if (!LightSpot(loadResult, jsonData, light->positional.spot)) {
1844 result = false;
1845 }
1846
1847 if (!ParseOptionalMath(loadResult, light->color, jsonData, "color", light->color)) {
1848 result = false;
1849 }
1850
1851 // blender uses strength
1852 if (!ParseOptionalNumber<float>(loadResult, light->intensity, jsonData, "strength", 1.0f)) {
1853 result = false;
1854 }
1855
1856 // khronos uses intensity
1857 if (!ParseOptionalNumber<float>(loadResult, light->intensity, jsonData, "intensity", light->intensity)) {
1858 result = false;
1859 }
1860 if (!LightShadow(loadResult, jsonData, light->shadow)) {
1861 result = false;
1862 }
1863
1864 loadResult.data->lights.push_back(move(light));
1865
1866 return result;
1867 }
1868 #endif
1869
1870 #if defined(GLTF2_EXTENSION_EXT_LIGHTS_IMAGE_BASED)
1871 bool ImageBasedLightIrradianceCoefficients(
1872 LoadResult& loadResult, const json::value& jsonData, vector<ImageBasedLight::LightingCoeff>& irradianceCoefficients)
1873 {
1874 const auto parseIrradianceCoefficients = [&irradianceCoefficients](
1875 LoadResult& loadResult, const json::value& mipLevelJson) -> bool {
1876 ImageBasedLight::LightingCoeff coeff;
1877 if (mipLevelJson.is_array()) {
1878 coeff.reserve(mipLevelJson.array_.size());
1879 std::transform(mipLevelJson.array_.begin(), mipLevelJson.array_.end(), std::back_inserter(coeff),
1880 [](const json::value& item) { return item.is_number() ? item.as_number<float>() : 0.f; });
1881 }
1882
1883 if (coeff.size() != 3) {
1884 return false;
1885 }
1886
1887 irradianceCoefficients.push_back(move(coeff));
1888 return true;
1889 };
1890
1891 return ForEachInArray(loadResult, jsonData, "irradianceCoefficients", parseIrradianceCoefficients);
1892 }
1893
1894 bool ImageBasedLightSpecularImages(
1895 LoadResult& loadResult, const json::value& jsonData, vector<ImageBasedLight::CubemapMipLevel>& specularImages)
1896 {
1897 const auto parseCubeMipLevel = [&specularImages](LoadResult& loadResult, const json::value& mipLevelJson) -> bool {
1898 ImageBasedLight::CubemapMipLevel mipLevel;
1899 static constexpr size_t requiredLevels = 6U;
1900 if (mipLevelJson.is_array() && (mipLevelJson.array_.size() == requiredLevels)) {
1901 mipLevel.reserve(mipLevelJson.array_.size());
1902 std::transform(mipLevelJson.array_.begin(), mipLevelJson.array_.end(), std::back_inserter(mipLevel),
1903 [](const json::value& item) {
1904 return item.is_number() ? item.as_number<size_t>() : GLTF_INVALID_INDEX;
1905 });
1906 }
1907 if (mipLevel.size() != requiredLevels) {
1908 return false;
1909 }
1910 if (std::any_of(mipLevel.cbegin(), mipLevel.cend(),
1911 [images = loadResult.data->images.size()](const size_t& index) { return index >= images; })) {
1912 return false;
1913 }
1914 specularImages.push_back(move(mipLevel));
1915 return true;
1916 };
1917
1918 return ForEachInArray(loadResult, jsonData, "specularImages", parseCubeMipLevel);
1919 }
1920
1921 bool ParseImageBasedLight(LoadResult& loadResult, const json::value& jsonData)
1922 {
1923 bool result = true;
1924
1925 auto light = make_unique<ImageBasedLight>();
1926
1927 if (!ParseOptionalString(loadResult, light->name, jsonData, "name", "")) {
1928 result = false;
1929 }
1930
1931 if (!ParseOptionalMath(loadResult, light->rotation, jsonData, "rotation", light->rotation)) {
1932 result = false;
1933 }
1934
1935 if (!ParseOptionalNumber<float>(loadResult, light->intensity, jsonData, "intensity", light->intensity)) {
1936 result = false;
1937 }
1938
1939 if (!ImageBasedLightIrradianceCoefficients(loadResult, jsonData, light->irradianceCoefficients)) {
1940 result = false;
1941 }
1942
1943 if (!ImageBasedLightSpecularImages(loadResult, jsonData, light->specularImages)) {
1944 result = false;
1945 }
1946
1947 if (!ParseOptionalNumber<uint32_t>(
1948 loadResult, light->specularImageSize, jsonData, "specularImageSize", light->specularImageSize)) {
1949 result = false;
1950 }
1951
1952 const auto parseExtras = [&light](LoadResult& loadResult, const json::value& e) -> bool {
1953 if (!ParseOptionalNumber(loadResult, light->skymapImage, e, "skymapImage", light->skymapImage)) {
1954 return false;
1955 }
1956
1957 if (!ParseOptionalNumber(
1958 loadResult, light->skymapImageLodLevel, e, "skymapImageLodLevel", light->skymapImageLodLevel)) {
1959 return false;
1960 }
1961
1962 if (!ParseOptionalNumber(
1963 loadResult, light->specularCubeImage, e, "specularCubeImage", light->specularCubeImage)) {
1964 return false;
1965 }
1966
1967 return true;
1968 };
1969
1970 if (!ParseObject(loadResult, jsonData, "extras", parseExtras)) {
1971 result = false;
1972 }
1973
1974 loadResult.data->imageBasedLights.push_back(move(light));
1975
1976 return result;
1977 }
1978 #endif
1979
1980 bool NodeName(LoadResult& loadResult, const json::value& jsonData, string& name)
1981 {
1982 if (!ParseOptionalString(loadResult, name, jsonData, "name", "")) {
1983 return false;
1984 }
1985
1986 if (name.empty()) {
1987 name = "node_" + to_string(loadResult.data->nodes.size());
1988 }
1989 return true;
1990 }
1991
1992 bool NodeMesh(LoadResult& loadResult, const json::value& jsonData, Mesh*& mesh)
1993 {
1994 size_t meshIndex;
1995 if (!ParseOptionalNumber<size_t>(loadResult, meshIndex, jsonData, "mesh", GLTF_INVALID_INDEX)) {
1996 return false;
1997 }
1998
1999 if (meshIndex != GLTF_INVALID_INDEX) {
2000 if (meshIndex < loadResult.data->meshes.size()) {
2001 mesh = loadResult.data->meshes[meshIndex].get();
2002 } else {
2003 SetError(loadResult, "Node refers to invalid mesh index");
2004 return false;
2005 }
2006 }
2007 return true;
2008 }
2009
2010 bool NodeCamera(LoadResult& loadResult, const json::value& jsonData, Camera*& camera)
2011 {
2012 size_t cameraIndex;
2013 if (!ParseOptionalNumber<size_t>(loadResult, cameraIndex, jsonData, "camera", GLTF_INVALID_INDEX)) {
2014 return false;
2015 }
2016
2017 if (cameraIndex != GLTF_INVALID_INDEX) {
2018 if (size_t(cameraIndex) < loadResult.data->cameras.size()) {
2019 camera = loadResult.data->cameras[cameraIndex].get();
2020 } else {
2021 SetError(loadResult, "Node refers to invalid camera index");
2022 return false;
2023 }
2024 }
2025 return true;
2026 }
2027
2028 struct ExtensionData {
2029 size_t lightIndex;
2030 bool compressed;
2031 };
2032
2033 std::optional<ExtensionData> NodeExtensions(LoadResult& loadResult, const json::value& jsonData, Node& node)
2034 {
2035 ExtensionData data { GLTF_INVALID_INDEX, false };
2036
2037 const auto parseExtensions = [&data, &node](LoadResult& loadResult, const json::value& extensions) -> bool {
2038 #if defined(GLTF2_EXTENSION_KHR_LIGHTS)
2039 const auto parseLight = [&data](LoadResult& loadResult, const json::value& light) -> bool {
2040 if (!ParseOptionalNumber<size_t>(loadResult, data.lightIndex, light, "light", GLTF_INVALID_INDEX)) {
2041 return false;
2042 }
2043
2044 return true;
2045 };
2046
2047 if (!ParseObject(loadResult, extensions, "KHR_lights_punctual", parseLight)) {
2048 return false;
2049 }
2050 #endif
2051
2052 #if defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
2053 if (data.lightIndex == GLTF_INVALID_INDEX) {
2054 const auto parseLightPbr = [&data](LoadResult& loadResult, const json::value& light) -> bool {
2055 if (!ParseOptionalNumber<size_t>(loadResult, data.lightIndex, light, "light", GLTF_INVALID_INDEX)) {
2056 return false;
2057 }
2058
2059 if (data.lightIndex != GLTF_INVALID_INDEX) {
2060 data.lightIndex += loadResult.data->pbrLightOffset;
2061 }
2062
2063 return true;
2064 };
2065
2066 if (!ParseObject(loadResult, extensions, "KHR_lights_pbr", parseLightPbr)) {
2067 return false;
2068 }
2069 }
2070 #endif
2071
2072 #ifdef GLTF2_EXTENSION_IGFX_COMPRESSED
2073 const auto parseCompressed = [&data, &weights = node.weights](
2074 LoadResult& loadResult, const json::value& compressedJson) {
2075 data.compressed = true;
2076 return ParseOptionalNumberArray(loadResult, weights, compressedJson, "weights", vector<float>());
2077 };
2078 if (!ParseObject(loadResult, extensions, "IGFX_compressed", parseCompressed)) {
2079 return false;
2080 }
2081 #endif
2082 return true;
2083 };
2084
2085 if (!ParseObject(loadResult, jsonData, "extensions", parseExtensions)) {
2086 return std::nullopt;
2087 }
2088 return data;
2089 }
2090
2091 bool NodeChildren(LoadResult& loadResult, const json::value& jsonData, Node& node)
2092 {
2093 return ForEachInArray(
2094 loadResult, jsonData, "children", [&node](LoadResult& loadResult, const json::value& item) -> bool {
2095 if (item.is_number()) {
2096 // indices will be later resolved to pointers when all nodes have been parsed
2097 // this is required since children may come later than parents
2098 node.tmpChildren.push_back(item.as_number<size_t>());
2099 return true;
2100 } else {
2101 node.tmpChildren.push_back(GLTF_INVALID_INDEX);
2102 SetError(loadResult, "Node children index was expected to be number");
2103 return false;
2104 }
2105 });
2106 }
2107
2108 bool NodeTransform(LoadResult& loadResult, const json::value& jsonData, Node& node)
2109 {
2110 bool result = true;
2111 if (auto const pos = jsonData.find("matrix"); pos) {
2112 if (ParseOptionalMath(loadResult, node.matrix, jsonData, "matrix", node.matrix)) {
2113 node.usesTRS = false;
2114 }
2115 } else {
2116 if (!ParseOptionalMath(loadResult, node.translation, jsonData, "translation", node.translation)) {
2117 result = false;
2118 }
2119
2120 // order is x,y,z,w as defined in gltf
2121 if (!ParseOptionalMath(loadResult, node.rotation, jsonData, "rotation", node.rotation)) {
2122 result = false;
2123 }
2124
2125 if (!ParseOptionalMath(loadResult, node.scale, jsonData, "scale", node.scale)) {
2126 result = false;
2127 }
2128 }
2129 return result;
2130 }
2131
2132 bool NodeExtras(LoadResult& loadResult, const json::value& jsonData, Node& node)
2133 {
2134 #if defined(GLTF2_EXTRAS_RSDZ)
2135 const auto parseExtras = [&node](LoadResult& loadResult, const json::value& extras) -> bool {
2136 ParseOptionalString(loadResult, node.modelIdRSDZ, extras, "modelId", "");
2137 return true;
2138 };
2139 if (!ParseObject(loadResult, jsonData, "extras", parseExtras)) {
2140 return false;
2141 }
2142 #endif
2143 return true;
2144 }
2145
2146 bool ParseNode(LoadResult& loadResult, const json::value& jsonData)
2147 {
2148 auto node = make_unique<Node>();
2149
2150 bool result = NodeName(loadResult, jsonData, node->name);
2151
2152 if (!NodeMesh(loadResult, jsonData, node->mesh)) {
2153 result = false;
2154 }
2155
2156 if (!NodeCamera(loadResult, jsonData, node->camera)) {
2157 result = false;
2158 }
2159
2160 if (auto extensionData = NodeExtensions(loadResult, jsonData, *node); extensionData) {
2161 if (!extensionData->compressed) {
2162 if (!ParseOptionalNumberArray(loadResult, node->weights, jsonData, "weights", vector<float>())) {
2163 result = false;
2164 }
2165 }
2166 if (!node->mesh && node->weights.size() > 0) {
2167 SetError(loadResult, "No mesh defined for node using morph target weights");
2168 result = false;
2169 }
2170 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
2171 if (extensionData->lightIndex != GLTF_INVALID_INDEX) {
2172 if (extensionData->lightIndex < loadResult.data->lights.size()) {
2173 node->light = loadResult.data->lights[extensionData->lightIndex].get();
2174 } else {
2175 SetError(loadResult, "Node refers to invalid light index");
2176 result = false;
2177 }
2178 }
2179 #endif
2180 } else {
2181 result = false;
2182 }
2183
2184 if (!ParseOptionalNumber(loadResult, node->tmpSkin, jsonData, "skin", GLTF_INVALID_INDEX)) {
2185 result = false;
2186 }
2187 if (!NodeChildren(loadResult, jsonData, *node)) {
2188 result = false;
2189 }
2190
2191 if (!NodeTransform(loadResult, jsonData, *node)) {
2192 result = false;
2193 }
2194
2195 if (!NodeExtras(loadResult, jsonData, *node)) {
2196 result = false;
2197 }
2198
2199 loadResult.data->nodes.push_back(move(node));
2200 return result;
2201 }
2202
2203 bool FinalizeNodes(LoadResult& loadResult)
2204 {
2205 bool result = true;
2206 // resolve indices to direct pointers
2207 auto& nodes = loadResult.data->nodes;
2208
2209 for (const auto& node : nodes) {
2210 for (auto index : node->tmpChildren) {
2211 if (index >= nodes.size()) {
2212 SetError(loadResult, "Invalid node index");
2213 result = false;
2214 continue;
2215 }
2216 auto childNode = nodes[index].get();
2217 if (childNode->parent) {
2218 SetError(loadResult, "Node has multiple parents");
2219 result = false;
2220 continue;
2221 }
2222 // since parent owns childs, and we don't want ref-loops, pass a raw pointer instead
2223 childNode->parent = node.get();
2224 node->children.push_back(childNode);
2225 }
2226
2227 if (node->tmpSkin != GLTF_INVALID_INDEX && node->tmpSkin < loadResult.data->skins.size()) {
2228 node->skin = loadResult.data->skins[node->tmpSkin].get();
2229 }
2230 }
2231
2232 return result;
2233 }
2234
2235 bool SceneExtensions(LoadResult& loadResult, const json::value& jsonData, Scene& scene)
2236 {
2237 const auto parseExtensions = [&scene](LoadResult& loadResult, const json::value& extensions) -> bool {
2238 size_t lightIndex = GLTF_INVALID_INDEX;
2239 #if defined(GLTF2_EXTENSION_KHR_LIGHTS)
2240 if (!ParseObject(loadResult, extensions, "KHR_lights",
2241 [&lightIndex](LoadResult& loadResult, const json::value& light) -> bool {
2242 return ParseOptionalNumber<size_t>(loadResult, lightIndex, light, "light", GLTF_INVALID_INDEX);
2243 })) {
2244 return false;
2245 }
2246 #endif
2247 #if defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
2248 if (lightIndex == GLTF_INVALID_INDEX) {
2249 if (!ParseObject(loadResult, extensions, "KHR_lights_pbr",
2250 [&lightIndex](LoadResult& loadResult, const json::value& light) -> bool {
2251 if (!ParseOptionalNumber<size_t>(loadResult, lightIndex, light, "light", GLTF_INVALID_INDEX)) {
2252 return false;
2253 } else if (lightIndex != GLTF_INVALID_INDEX) {
2254 lightIndex += loadResult.data->pbrLightOffset;
2255 }
2256 return true;
2257 })) {
2258 return false;
2259 }
2260 }
2261 #endif
2262
2263 #if defined(GLTF2_EXTENSION_EXT_LIGHTS_IMAGE_BASED)
2264 if (!ParseObject(loadResult, extensions, "EXT_lights_image_based",
2265 [&scene](LoadResult& loadResult, const json::value& light) -> bool {
2266 return ParseOptionalNumber<size_t>(
2267 loadResult, scene.imageBasedLightIndex, light, "light", GLTF_INVALID_INDEX);
2268 })) {
2269 return false;
2270 }
2271 #endif
2272 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
2273 if (lightIndex != GLTF_INVALID_INDEX) {
2274 if (lightIndex < loadResult.data->lights.size()) {
2275 // Only Ambient light could be set for scene
2276 scene.light = loadResult.data->lights[lightIndex].get();
2277 } else {
2278 SetError(loadResult, "Scene refers to invalid light index");
2279 return false;
2280 }
2281 }
2282 #endif
2283 return true;
2284 };
2285
2286 return ParseObject(loadResult, jsonData, "extensions", parseExtensions);
2287 }
2288
2289 bool ParseScene(LoadResult& loadResult, const json::value& jsonData)
2290 {
2291 bool result = true;
2292
2293 auto scene = make_unique<Scene>();
2294 if (!ParseOptionalString(loadResult, scene->name, jsonData, "name", "")) {
2295 return false;
2296 }
2297
2298 const auto parseNodes = [&scene](LoadResult& loadResult, const json::value& nodeIndex) -> bool {
2299 if (!nodeIndex.is_number()) {
2300 RETURN_WITH_ERROR(loadResult, "Invalid node index (not a number)");
2301 }
2302
2303 const size_t index = nodeIndex.as_number<size_t>();
2304 if (index >= loadResult.data->nodes.size()) {
2305 RETURN_WITH_ERROR(loadResult, "Invalid node index");
2306 }
2307
2308 auto const equalsNodeToAdd = [nodeToAdd = loadResult.data->nodes[index].get()](
2309 auto const& node) { return node == nodeToAdd; };
2310
2311 if (std::any_of(scene->nodes.begin(), scene->nodes.end(), equalsNodeToAdd)) {
2312 RETURN_WITH_ERROR(loadResult, "Non-unique node index");
2313 }
2314
2315 scene->nodes.push_back(loadResult.data->nodes[index].get());
2316
2317 return true;
2318 };
2319
2320 if (!ForEachInArray(loadResult, jsonData, "nodes", parseNodes)) {
2321 result = false;
2322 }
2323
2324 if (!SceneExtensions(loadResult, jsonData, *scene)) {
2325 result = false;
2326 }
2327
2328 loadResult.data->scenes.push_back(move(scene));
2329
2330 return result;
2331 }
2332
2333 bool SceneContainsNode(Scene const& scene, Node const& node)
2334 {
2335 return std::any_of(
2336 scene.nodes.begin(), scene.nodes.end(), [nodePtr = &node](auto const node) { return nodePtr == node; });
2337 }
2338
2339 Scene* SceneContainingNode(vector<unique_ptr<Scene>> const& scenes, Node const& node)
2340 {
2341 auto const pos = std::find_if(scenes.begin(), scenes.end(),
2342 [nodePtr = &node](auto const& scene) { return SceneContainsNode(*scene, *nodePtr); });
2343 if (pos != scenes.end()) {
2344 return pos->get();
2345 }
2346 return nullptr;
2347 }
2348
2349 bool JointsInSameScene(Skin const& skin, LoadResult& loadResult)
2350 {
2351 Scene const* scene = nullptr;
2352 return std::all_of(skin.joints.begin(), skin.joints.end(), [&loadResult, &scene](auto const joint) {
2353 Node const* hierarchyRoot = joint;
2354 while (hierarchyRoot->parent) {
2355 hierarchyRoot = hierarchyRoot->parent;
2356 }
2357
2358 if (!scene) {
2359 scene = SceneContainingNode(loadResult.data->scenes, *hierarchyRoot);
2360 if (!scene) {
2361 RETURN_WITH_ERROR(loadResult, "Joint must belong to a scene");
2362 }
2363 } else if (!SceneContainsNode(*scene, *hierarchyRoot)) {
2364 RETURN_WITH_ERROR(loadResult, "Skin joints must belong to the same scene");
2365 }
2366 return true;
2367 });
2368 }
2369
2370 bool ParseSkin(LoadResult& loadResult, const json::value& jsonData)
2371 {
2372 auto skin = make_unique<Skin>();
2373
2374 size_t matrices;
2375 if (!ParseOptionalNumber<size_t>(loadResult, matrices, jsonData, "inverseBindMatrices", GLTF_INVALID_INDEX)) {
2376 return false;
2377 }
2378
2379 if (matrices != GLTF_INVALID_INDEX && matrices < loadResult.data->accessors.size()) {
2380 skin->inverseBindMatrices = loadResult.data->accessors[matrices].get();
2381 }
2382
2383 size_t skeleton;
2384 if (!ParseOptionalNumber<size_t>(loadResult, skeleton, jsonData, "skeleton", GLTF_INVALID_INDEX)) {
2385 return false;
2386 }
2387
2388 if (skeleton != GLTF_INVALID_INDEX && skeleton < loadResult.data->nodes.size()) {
2389 skin->skeleton = loadResult.data->nodes[skeleton].get();
2390 }
2391
2392 vector<size_t> joints;
2393 if (!ParseOptionalNumberArray(loadResult, joints, jsonData, "joints", vector<size_t>())) {
2394 return false;
2395 }
2396
2397 if (joints.size() > CORE_DEFAULT_MATERIAL_MAX_JOINT_COUNT) {
2398 CORE_LOG_W("Number of joints (%zu) more than current limit (%u)", joints.size(),
2399 CORE_DEFAULT_MATERIAL_MAX_JOINT_COUNT);
2400 }
2401
2402 skin->joints.resize(joints.size());
2403
2404 for (size_t i = 0; i < joints.size(); i++) {
2405 if (joints[i] >= loadResult.data->nodes.size()) {
2406 RETURN_WITH_ERROR(loadResult, "Invalid node index");
2407 }
2408 auto joint = loadResult.data->nodes[joints[i]].get();
2409 joint->isJoint = true;
2410 skin->joints[i] = joint;
2411 }
2412
2413 loadResult.data->skins.push_back(move(skin));
2414
2415 return true;
2416 }
2417
2418 void FinalizeGltfContent(LoadResult& loadResult)
2419 {
2420 using ImageContainer = vector<unique_ptr<Image>>;
2421 using TextureContainer = vector<unique_ptr<Texture>>;
2422
2423 // See if there are duplicate images with the same uri.
2424 for (size_t imageIndex = 0; imageIndex < loadResult.data->images.size(); ++imageIndex) {
2425 if (loadResult.data->images[imageIndex]->uri.empty()) {
2426 continue;
2427 }
2428
2429 bool hasDuplicate = false;
2430 for (size_t lookupImageIndex = imageIndex + 1; lookupImageIndex < loadResult.data->images.size();) {
2431 // Two images are the same?
2432 if (loadResult.data->images[imageIndex]->uri == loadResult.data->images[lookupImageIndex]->uri) {
2433 hasDuplicate = true;
2434
2435 // Fix all textures to reference the first image.
2436 for (TextureContainer::iterator textureIt = loadResult.data->textures.begin();
2437 textureIt != loadResult.data->textures.end(); ++textureIt) {
2438 if ((*textureIt)->image == loadResult.data->images[lookupImageIndex].get()) {
2439 (*textureIt)->image = loadResult.data->images[imageIndex].get();
2440 }
2441 }
2442
2443 // Two images are the same and the other one can be removed.
2444 const auto indexOffset = static_cast<typename ImageContainer::difference_type>(lookupImageIndex);
2445 loadResult.data->images.erase(loadResult.data->images.begin() + indexOffset);
2446 } else {
2447 ++lookupImageIndex;
2448 }
2449 }
2450
2451 if (hasDuplicate) {
2452 CORE_LOG_D("Optimizing out duplicate image from glTF: %s/images/%zu",
2453 loadResult.data->defaultResources.c_str(), imageIndex);
2454 }
2455 }
2456 }
2457
2458 bool AnimationSamplers(LoadResult& loadResult, const json::value& jsonData, Animation& animation)
2459 {
2460 const auto parseSamplers = [&animation](LoadResult& loadResult, const json::value& samplerJson) -> bool {
2461 auto sampler = make_unique<AnimationSampler>();
2462
2463 // parse sampler
2464 size_t accessor;
2465 if (!ParseOptionalNumber<size_t>(loadResult, accessor, samplerJson, "input", GLTF_INVALID_INDEX)) {
2466 return false;
2467 }
2468
2469 if (accessor != GLTF_INVALID_INDEX && accessor < loadResult.data->accessors.size()) {
2470 sampler->input = loadResult.data->accessors[accessor].get();
2471 }
2472
2473 if (!ParseOptionalNumber<size_t>(loadResult, accessor, samplerJson, "output", GLTF_INVALID_INDEX)) {
2474 return false;
2475 }
2476
2477 if (accessor != GLTF_INVALID_INDEX && accessor < loadResult.data->accessors.size()) {
2478 sampler->output = loadResult.data->accessors[accessor].get();
2479 }
2480
2481 string interpolation;
2482 if (!ParseOptionalString(loadResult, interpolation, samplerJson, "interpolation", string())) {
2483 return false;
2484 }
2485
2486 // This attribute is not required, defaults to linear.
2487 GetAnimationInterpolation(interpolation, sampler->interpolation);
2488
2489 animation.samplers.push_back(move(sampler));
2490
2491 return true;
2492 };
2493
2494 if (!ForEachInArray(loadResult, jsonData, "samplers", parseSamplers)) {
2495 return false;
2496 }
2497 return true;
2498 }
2499
2500 bool AnimationChannels(LoadResult& loadResult, const json::value& jsonData, Animation& animation)
2501 {
2502 const auto channelsParser = [&animation](LoadResult& loadResult, const json::value& channelJson) -> bool {
2503 AnimationTrack animationTrack;
2504
2505 // parse sampler
2506 size_t sampler;
2507 if (!ParseOptionalNumber<size_t>(loadResult, sampler, channelJson, "sampler", GLTF_INVALID_INDEX)) {
2508 return false;
2509 }
2510
2511 if (sampler != GLTF_INVALID_INDEX && sampler < animation.samplers.size()) {
2512 animationTrack.sampler = animation.samplers[sampler].get();
2513 }
2514
2515 const auto targetParser = [&animationTrack](LoadResult& loadResult, const json::value& targetJson) -> bool {
2516 {
2517 string path;
2518 if (!ParseOptionalString(loadResult, path, targetJson, "path", string())) {
2519 return false;
2520 }
2521
2522 if (path.empty()) {
2523 RETURN_WITH_ERROR(loadResult, "Path is required");
2524 }
2525
2526 if (!GetAnimationPath(path, animationTrack.channel.path)) {
2527 CORE_LOG_W("Skipping unsupported animation path: %s", path.c_str());
2528 return false;
2529 }
2530 }
2531
2532 size_t node;
2533 if (!ParseOptionalNumber<size_t>(loadResult, node, targetJson, "node", GLTF_INVALID_INDEX)) {
2534 return false;
2535 }
2536
2537 if (node != GLTF_INVALID_INDEX && node < loadResult.data->nodes.size()) {
2538 animationTrack.channel.node = loadResult.data->nodes[node].get();
2539 } else {
2540 // this channel will be ignored
2541 }
2542
2543 return true;
2544 };
2545
2546 if (!ParseObject(loadResult, channelJson, "target", targetParser)) {
2547 return false;
2548 }
2549
2550 animation.tracks.push_back(move(animationTrack));
2551 return true;
2552 };
2553
2554 if (!ForEachInArray(loadResult, jsonData, "channels", channelsParser)) {
2555 return false;
2556 }
2557 return true;
2558 }
2559
2560 bool ParseAnimation(LoadResult& loadResult, const json::value& jsonData)
2561 {
2562 auto animation = make_unique<Animation>();
2563 if (!ParseOptionalString(loadResult, animation->name, jsonData, "name",
2564 "animation_" + to_string(loadResult.data->animations.size()))) {
2565 return false;
2566 }
2567
2568 if (!AnimationSamplers(loadResult, jsonData, *animation)) {
2569 return false;
2570 }
2571
2572 if (!AnimationChannels(loadResult, jsonData, *animation)) {
2573 return false;
2574 }
2575
2576 if (!animation->tracks.empty() && !animation->samplers.empty()) {
2577 loadResult.data->animations.push_back(move(animation));
2578 } else {
2579 // RsdzExporter produces empty animations so just adding an error message.
2580 loadResult.error += "Skipped empty animation. Animation should have at least one channel and sampler.\n";
2581 }
2582 return true;
2583 }
2584
2585 bool GltfAsset(LoadResult& loadResult, const json::value& jsonData)
2586 {
2587 if (auto const& assetJson = jsonData.find("asset"); assetJson) {
2588 // Client implementations should first check whether a minVersion property is specified and ensure both
2589 // major and minor versions can be supported.
2590 string version;
2591 ParseOptionalString(loadResult, version, *assetJson, "minVersion", "");
2592 if (!version.empty()) {
2593 if (const auto minVersion = ParseVersion(version); minVersion) {
2594 if ((minVersion->first > 2u) || (minVersion->second > 0u)) {
2595 RETURN_WITH_ERROR(loadResult, "Required glTF minVersion not supported");
2596 }
2597 } else {
2598 RETURN_WITH_ERROR(loadResult, "Invalid minVersion");
2599 }
2600 } else {
2601 // If no minVersion is specified, then clients should check the version property and ensure the major
2602 // version is supported.
2603 ParseOptionalString(loadResult, version, *assetJson, "version", "");
2604 if (const auto minVersion = ParseVersion(version); minVersion) {
2605 if ((minVersion->first > 2u)) {
2606 RETURN_WITH_ERROR(loadResult, "Required glTF version not supported");
2607 }
2608 } else {
2609 RETURN_WITH_ERROR(loadResult, "Invalid version");
2610 }
2611 }
2612 return true;
2613 } else {
2614 RETURN_WITH_ERROR(loadResult, "Missing asset metadata");
2615 }
2616 }
2617
2618 bool GltfRequiredExtension(LoadResult& loadResult, const json::value& jsonData)
2619 {
2620 const auto parseRequiredExtensions = [](LoadResult& loadResult, const json::value& extension) {
2621 if (extension.is_string()) {
2622 const auto& val = extension.string_;
2623 if (std::find(std::begin(SUPPORTED_EXTENSIONS), std::end(SUPPORTED_EXTENSIONS), val) ==
2624 std::end(SUPPORTED_EXTENSIONS)) {
2625 SetError(loadResult, "glTF requires unsupported extension: " + val);
2626 return false;
2627 }
2628 #if defined(GLTF2_EXTENSION_KHR_MESH_QUANTIZATION)
2629 if (val == "KHR_mesh_quantization") {
2630 loadResult.data->quantization = true;
2631 }
2632 #endif
2633 }
2634
2635 return true;
2636 };
2637 return ForEachInArray(loadResult, jsonData, "extensionsRequired", parseRequiredExtensions);
2638 }
2639
2640 bool GltfUsedExtension(LoadResult& loadResult, const json::value& jsonData)
2641 {
2642 const auto parseUsedExtensions = [](LoadResult& loadResult, const json::value& extension) {
2643 if (extension.is_string()) {
2644 const auto& val = extension.string_;
2645 if (std::find(std::begin(SUPPORTED_EXTENSIONS), std::end(SUPPORTED_EXTENSIONS), val) ==
2646 std::end(SUPPORTED_EXTENSIONS)) {
2647 CORE_LOG_W("glTF uses unsupported extension: %s", string(val).c_str());
2648 }
2649 }
2650
2651 return true;
2652 };
2653 return ForEachInArray(loadResult, jsonData, "extensionsUsed", parseUsedExtensions);
2654 }
2655
2656 bool GltfExtension(LoadResult& loadResult, const json::value& jsonData)
2657 {
2658 return ParseObject(loadResult, jsonData, "extensions", [](LoadResult& loadResult, const json::value& extensions) {
2659 bool result = true;
2660
2661 #if defined(GLTF2_EXTENSION_KHR_LIGHTS)
2662 if (!ParseObject(loadResult, extensions, "KHR_lights_punctual",
2663 [](LoadResult& loadResult, const json::value& khrLights) {
2664 return ForEachObjectInArray(loadResult, khrLights, "lights", ParseKHRLight);
2665 })) {
2666 result = false;
2667 }
2668 #endif
2669 #if defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
2670 loadResult.data->pbrLightOffset = static_cast<uint32_t>(loadResult.data->lights.size());
2671
2672 if (!ParseObject(loadResult, extensions, "KHR_lights_pbr",
2673 [](LoadResult& loadResult, const json::value& khrLights) -> bool {
2674 return ForEachObjectInArray(loadResult, khrLights, "lights", ParseKHRLight);
2675 })) {
2676 result = false;
2677 }
2678 #endif
2679
2680 #if defined(GLTF2_EXTENSION_HW_XR_EXT)
2681 if (!ParseObject(
2682 loadResult, extensions, "HW_XR_EXT", [](LoadResult& loadResult, const json::value& jsonData) -> bool {
2683 string thumbnailUri;
2684 if (ParseOptionalString(loadResult, thumbnailUri, jsonData, "sceneThumbnail", "")) {
2685 DecodeUri(thumbnailUri);
2686 loadResult.data->thumbnails.push_back(Assets::Thumbnail { move(thumbnailUri), {}, {} });
2687 } else {
2688 return false;
2689 }
2690 return true;
2691 })) {
2692 result = false;
2693 }
2694 #endif
2695
2696 #if defined(GLTF2_EXTENSION_EXT_LIGHTS_IMAGE_BASED)
2697 if (!ParseObject(loadResult, extensions, "EXT_lights_image_based",
2698 [](LoadResult& loadResult, const json::value& imageBasedLights) -> bool {
2699 return ForEachObjectInArray(loadResult, imageBasedLights, "lights", ParseImageBasedLight);
2700 })) {
2701 result = false;
2702 }
2703 #endif
2704 return result;
2705 });
2706 }
2707
2708 bool GltfExtras(LoadResult& loadResult, const json::value& jsonData)
2709 {
2710 #if defined(GLTF2_EXTRAS_RSDZ)
2711 const auto parseExtras = [](LoadResult& loadResult, const json::value& extras) -> bool {
2712 return ForEachObjectInArray(loadResult, extras, "rsdzAnimations", ParseAnimation);
2713 };
2714 return ParseObject(loadResult, jsonData, "extras", parseExtras);
2715 #else
2716 return true;
2717 #endif
2718 }
2719
2720 bool ParseGLTF(LoadResult& loadResult, const json::value& jsonData)
2721 {
2722 if (!GltfAsset(loadResult, jsonData) || !GltfRequiredExtension(loadResult, jsonData)) {
2723 return false;
2724 }
2725 // parse through all types of objects regardles of errors. result will be false is any of the steps failed.
2726 bool result = GltfUsedExtension(loadResult, jsonData);
2727 result = ForEachObjectInArray(loadResult, jsonData, "buffers", ParseBuffer) && result;
2728 result = ForEachObjectInArray(loadResult, jsonData, "bufferViews", ParseBufferView) && result;
2729 result = ForEachObjectInArray(loadResult, jsonData, "accessors", ParseAccessor) && result;
2730 result = ForEachObjectInArray(loadResult, jsonData, "images", ParseImage) && result;
2731 result = ForEachObjectInArray(loadResult, jsonData, "samplers", ParseSampler) && result;
2732 result = ForEachObjectInArray(loadResult, jsonData, "textures", ParseTexture) && result;
2733 result = ForEachObjectInArray(loadResult, jsonData, "materials", ParseMaterial) && result;
2734 result = ForEachObjectInArray(loadResult, jsonData, "meshes", ParseMesh) && result;
2735 result = ForEachObjectInArray(loadResult, jsonData, "cameras", ParseCamera) && result;
2736 result = GltfExtension(loadResult, jsonData) && result;
2737 result = ForEachObjectInArray(loadResult, jsonData, "nodes", ParseNode) && result;
2738 result = ForEachObjectInArray(loadResult, jsonData, "skins", ParseSkin) && result;
2739 result = ForEachObjectInArray(loadResult, jsonData, "animations", ParseAnimation) && result;
2740 result = GltfExtras(loadResult, jsonData) && result;
2741 result = FinalizeNodes(loadResult) && result;
2742 result = ForEachObjectInArray(loadResult, jsonData, "scenes", ParseScene) && result;
2743
2744 if (!std::all_of(loadResult.data->skins.begin(), loadResult.data->skins.end(),
2745 [&loadResult](auto const& skin) { return JointsInSameScene(*skin, loadResult); })) {
2746 return false;
2747 }
2748
2749 int defaultSceneIndex;
2750 if (!ParseOptionalNumber<int>(loadResult, defaultSceneIndex, jsonData, "scene", -1)) {
2751 result = false;
2752 } else if (defaultSceneIndex != -1) {
2753 if (defaultSceneIndex < 0 || size_t(defaultSceneIndex) >= loadResult.data->scenes.size()) {
2754 loadResult.error += "Invalid default scene index\n";
2755 loadResult.success = false;
2756 } else {
2757 loadResult.data->defaultScene = loadResult.data->scenes[static_cast<size_t>(defaultSceneIndex)].get();
2758 }
2759 }
2760
2761 FinalizeGltfContent(loadResult);
2762
2763 return result;
2764 }
2765
2766 void LoadGLTF(LoadResult& loadResult, IFile& file)
2767 {
2768 const uint64_t byteLength = file.GetLength();
2769
2770 string raw;
2771 raw.resize(static_cast<size_t>(byteLength));
2772
2773 if (file.Read(raw.data(), byteLength) != byteLength) {
2774 return;
2775 }
2776 CORE_CPU_PERF_BEGIN(jkson, "CORE3D", "LoadGLTF", "json::parse", CORE3D_PROFILER_DEFAULT_COLOR);
2777 json::value jsonObject = json::parse(raw.data());
2778 CORE_CPU_PERF_END(jkson);
2779 if (!jsonObject) {
2780 SetError(loadResult, "Parsing GLTF failed: invalid JSON");
2781 return;
2782 }
2783
2784 ParseGLTF(loadResult, jsonObject);
2785 }
2786
2787 bool LoadGLB(LoadResult& loadResult, IFile& file)
2788 {
2789 GLBHeader header;
2790 uint64_t bytes = file.Read(&header, sizeof(GLBHeader));
2791
2792 if (bytes < sizeof(GLBHeader)) {
2793 // cannot read header
2794 RETURN_WITH_ERROR(loadResult, "Parsing GLTF failed: expected GLB object");
2795 }
2796
2797 if (header.magic != GLTF_MAGIC) {
2798 // 0x46546C67 >> "glTF"
2799 RETURN_WITH_ERROR(loadResult, "Parsing GLTF failed: expected GLB header");
2800 }
2801
2802 if (header.length > loadResult.data->size) {
2803 RETURN_WITH_ERROR(loadResult, "Parsing GLTF failed: GLB header definition for size is larger than file size");
2804 } else {
2805 loadResult.data->size = header.length;
2806 }
2807
2808 if (header.version != 2) {
2809 RETURN_WITH_ERROR(loadResult, "Parsing GLTF failed: expected GLB version 2");
2810 }
2811
2812 GLBChunk chunkJson;
2813 bytes = file.Read(&chunkJson, sizeof(GLBChunk));
2814
2815 if (bytes < sizeof(GLBChunk)) {
2816 // cannot read chunk data
2817 RETURN_WITH_ERROR(loadResult, "Parsing GLTF failed: expected GLB chunk");
2818 }
2819
2820 if (chunkJson.chunkType != static_cast<uint32_t>(ChunkType::JSON) || chunkJson.chunkLength == 0 ||
2821 (chunkJson.chunkLength % 4) || chunkJson.chunkLength > (header.length - sizeof(header) - sizeof(chunkJson))) {
2822 // first chunk have to be JSON
2823 RETURN_WITH_ERROR(loadResult, "Parsing GLTF failed: expected JSON chunk");
2824 }
2825
2826 const size_t dataOffset = chunkJson.chunkLength + sizeof(GLBHeader) + 2 * sizeof(GLBChunk);
2827
2828 if (dataOffset > loadResult.data->size) {
2829 RETURN_WITH_ERROR(loadResult, "Parsing GLTF failed: data part offset is out of file");
2830 }
2831
2832 loadResult.data->defaultResourcesOffset = static_cast<int32_t>(dataOffset);
2833
2834 string jsonString;
2835 jsonString.resize(chunkJson.chunkLength);
2836
2837 if (jsonString.size() != chunkJson.chunkLength) {
2838 RETURN_WITH_ERROR(loadResult, "Parsing GLTF failed: allocation for JSON data failed");
2839 }
2840
2841 bytes = file.Read(reinterpret_cast<void*>(jsonString.data()), chunkJson.chunkLength);
2842
2843 if (bytes < chunkJson.chunkLength) {
2844 // cannot read chunk data
2845 RETURN_WITH_ERROR(loadResult, "Parsing GLTF failed: JSON chunk size not match");
2846 }
2847
2848 json::value o = json::parse(jsonString.data());
2849 if (!o) {
2850 RETURN_WITH_ERROR(loadResult, "Parsing GLTF failed: invalid JSON");
2851 }
2852
2853 return ParseGLTF(loadResult, o);
2854 }
2855 } // namespace
2856
2857 // Internal loading function.
2858 LoadResult LoadGLTF(IFileManager& fileManager, const string_view uri)
2859 {
2860 LoadResult result;
2861
2862 CORE_CPU_PERF_SCOPE("CORE3D", "LoadGLTF()", uri, CORE3D_PROFILER_DEFAULT_COLOR);
2863
2864 IFile::Ptr file = fileManager.OpenFile(uri);
2865 if (!file) {
2866 CORE_LOG_D("Error loading '%s'", string(uri).data());
2867 return LoadResult("Failed to open file.");
2868 }
2869
2870 const uint64_t fileLength = file->GetLength();
2871 if (fileLength > SIZE_MAX) {
2872 CORE_LOG_D("Error loading '%s'", string(uri).data());
2873 return LoadResult("Failed to open file, file size larger than SIZE_MAX");
2874 }
2875
2876 string_view baseName;
2877 string_view path;
2878 SplitFilename(uri, baseName, path);
2879
2880 result.data = make_unique<Data>(fileManager);
2881 result.data->filepath = path;
2882 result.data->defaultResources = baseName;
2883 result.data->size = static_cast<size_t>(fileLength);
2884
2885 string_view baseNameNoExt;
2886 string_view extensionView;
2887 SplitBaseFilename(baseName, baseNameNoExt, extensionView);
2888
2889 string extension(extensionView.size(), '\0');
2890 std::transform(extensionView.begin(), extensionView.end(), extension.begin(),
2891 [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
2892
2893 if (extension == "gltf" || extension == "glt") {
2894 LoadGLTF(result, *file);
2895 } else if (extension == "glb") {
2896 LoadGLB(result, *file);
2897 } else {
2898 LoadGLB(result, *file);
2899 }
2900 return result;
2901 }
2902
2903 LoadResult LoadGLTF(IFileManager& fileManager, array_view<uint8_t const> data)
2904 {
2905 LoadResult result;
2906
2907 // if the buffer starts with a GLB header assume GLB, otherwise glTF with embedded data.
2908 char const* ext = ".gltf";
2909 if (data.size() >= (sizeof(GLBHeader) + sizeof(GLBChunk))) {
2910 GLBHeader const& header = *reinterpret_cast<GLBHeader const*>(data.data());
2911 if (header.magic == GLTF_MAGIC) {
2912 ext = ".glb";
2913 }
2914 }
2915
2916 // wrap the buffer in a temporary file
2917 auto const tmpFileName = "memory://" + to_string((uintptr_t)data.data()) + ext;
2918 {
2919 auto tmpFile = fileManager.CreateFile(tmpFileName);
2920 if (!tmpFile) {
2921 result.success = false;
2922 return result;
2923 }
2924 // NOTE: not ideal as this actually copies the data
2925 // alternative would be to cast to MemoryFile and give the array_view to the file
2926 tmpFile->Write(data.data(), data.size());
2927
2928 result = GLTF2::LoadGLTF(fileManager, tmpFileName);
2929 if (result.success) {
2930 // File is stored here so it can be deleted from the file manager. loadBuffers is the only thing at
2931 // the moment which will need the file after parsing and that will check whether to use the
2932 // mMemoryFile or the URI related to the buffer.
2933 (*result.data).memoryFile_ = move(tmpFile);
2934 }
2935 }
2936 fileManager.DeleteFile(tmpFileName);
2937 return result;
2938 }
2939 } // namespace GLTF2
2940 CORE3D_END_NAMESPACE()
2941