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