15#include <system_error>
16#include <unordered_map>
19#include <nlohmann/json.hpp>
21#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L) && (_MSC_VER >= 1911))
22#define FX_GLTF_HAS_CPP_17
33 constexpr std::array<char, 64> EncodeMap =
35 'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
36 'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z',
'a',
'b',
'c',
'd',
'e',
'f',
37 'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
38 'w',
'x',
'y',
'z',
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'+',
'/'
41 constexpr std::array<char, 256> DecodeMap =
43 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
44 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
45 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
46 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
47 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
48 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
49 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
50 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
51 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
52 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
53 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
54 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
55 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
56 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
57 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
58 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
63 inline std::string Encode(std::vector<uint8_t>
const & bytes)
65 const std::size_t length = bytes.size();
72 out.reserve(((length * 4 / 3) + 3) & (~3u));
75 int32_t bitCount = -6;
76 for (
const uint8_t c : bytes)
78 value = (value << 8u) + c;
82 const uint32_t shiftOperand = bitCount;
83 out.push_back(detail::EncodeMap.at((value >> shiftOperand) & 0x3fu));
90 const uint32_t shiftOperand = bitCount + 8;
91 out.push_back(detail::EncodeMap.at(((value << 8u) >> shiftOperand) & 0x3fu));
94 while (out.size() % 4 != 0)
102#if defined(FX_GLTF_HAS_CPP_17)
103 inline bool TryDecode(std::string_view in, std::vector<uint8_t> & out)
105 inline bool TryDecode(std::string
const & in, std::vector<uint8_t> & out)
110 const std::size_t length = in.length();
121 out.reserve((length / 4) * 3);
123 bool invalid =
false;
125 int32_t bitCount = -8;
126 for (std::size_t i = 0; i < length; i++)
128 const uint8_t c =
static_cast<uint8_t
>(in[i]);
129 const char map = detail::DecodeMap.at(c);
139 const std::size_t remaining = length - i - 1;
140 if (remaining > 1 || (remaining == 1 ? in[i + 1] !=
'=' : false))
149 value = (value << 6u) + map;
153 const uint32_t shiftOperand = bitCount;
154 out.push_back(
static_cast<uint8_t
>(value >> shiftOperand));
174 : std::runtime_error(message)
179 : std::runtime_error(CreateMessage(message, extra).c_str())
184 std::string CreateMessage(
char const * message, std::string
const & extra)
186 return std::string(message).append(
" : ").append(extra);
192#if defined(FX_GLTF_HAS_CPP_17)
193 template <
typename TTarget>
194 inline void ReadRequiredField(std::string_view key, nlohmann::json
const & json, TTarget & target)
196 template <
typename TKey,
typename TTarget>
197 inline void ReadRequiredField(TKey && key, nlohmann::json
const & json, TTarget & target)
200 const nlohmann::json::const_iterator iter = json.find(key);
201 if (iter == json.end())
206 target = iter->get<TTarget>();
209#if defined(FX_GLTF_HAS_CPP_17)
210 template <
typename TTarget>
211 inline void ReadOptionalField(std::string_view key, nlohmann::json
const & json, TTarget & target)
213 template <
typename TKey,
typename TTarget>
214 inline void ReadOptionalField(TKey && key, nlohmann::json
const & json, TTarget & target)
217 const nlohmann::json::const_iterator iter = json.find(key);
218 if (iter != json.end())
220 target = iter->get<TTarget>();
224 inline void ReadExtensionsAndExtras(nlohmann::json
const & json, nlohmann::json & extensionsAndExtras)
226 const nlohmann::json::const_iterator iterExtensions = json.find(
"extensions");
227 const nlohmann::json::const_iterator iterExtras = json.find(
"extras");
228 if (iterExtensions != json.end())
230 extensionsAndExtras[
"extensions"] = *iterExtensions;
233 if (iterExtras != json.end())
235 extensionsAndExtras[
"extras"] = *iterExtras;
239 template <
typename TValue>
240 inline void WriteField(std::string
const & key, nlohmann::json & json, TValue
const & value)
248 template <
typename TValue>
249 inline void WriteField(std::string
const & key, nlohmann::json & json, TValue
const & value, TValue
const & defaultValue)
251 if (value != defaultValue)
257 inline void WriteExtensions(nlohmann::json & json, nlohmann::json
const & extensionsAndExtras)
259 if (!extensionsAndExtras.empty())
261 for (nlohmann::json::const_iterator it = extensionsAndExtras.begin(); it != extensionsAndExtras.end(); ++it)
263 json[it.key()] = it.value();
268 inline std::string GetDocumentRootPath(std::string
const & documentFilePath)
270 const std::size_t pos = documentFilePath.find_last_of(
"/\\");
271 if (pos != std::string::npos)
273 return documentFilePath.substr(0, pos);
279 inline std::string CreateBufferUriPath(std::string
const & documentRootPath, std::string
const & bufferUri)
282 if (bufferUri.empty() || bufferUri.find(
"..") != std::string::npos || bufferUri.front() ==
'/' || bufferUri.front() ==
'\\')
284 throw invalid_gltf_document(
"Invalid buffer.uri value", bufferUri);
287 std::string documentRoot = documentRootPath;
288 if (documentRoot.length() > 0)
290 if (documentRoot.back() !=
'/')
292 documentRoot.push_back(
'/');
296 return documentRoot + bufferUri;
301 uint32_t chunkLength{};
302 uint32_t chunkType{};
311 ChunkHeader jsonHeader{};
314 constexpr uint32_t DefaultMaxBufferCount = 8;
315 constexpr uint32_t DefaultMaxMemoryAllocation = 32 * 1024 * 1024;
316 constexpr std::size_t HeaderSize{
sizeof(GLBHeader) };
317 constexpr std::size_t ChunkHeaderSize{
sizeof(ChunkHeader) };
318 constexpr uint32_t GLBHeaderMagic = 0x46546c67u;
319 constexpr uint32_t GLBChunkJSON = 0x4e4f534au;
320 constexpr uint32_t GLBChunkBIN = 0x004e4942u;
322 constexpr char const *
const MimetypeApplicationOctet =
"data:application/octet-stream;base64";
323 constexpr char const *
const MimetypeImagePNG =
"data:image/png;base64";
324 constexpr char const *
const MimetypeImageJPG =
"data:image/jpeg;base64";
329 constexpr std::array<float, 16> IdentityMatrix{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
330 constexpr std::array<float, 4> IdentityRotation{ 0, 0, 0, 1 };
331 constexpr std::array<float, 4> IdentityVec4{ 1, 1, 1, 1 };
332 constexpr std::array<float, 3> IdentityVec3{ 1, 1, 1 };
333 constexpr std::array<float, 3> NullVec3{ 0, 0, 0 };
334 constexpr float IdentityScalar = 1;
335 constexpr float FloatSentinel = 10000;
337 constexpr bool AccessorNormalized =
false;
339 constexpr float MaterialAlphaCutoff = 0.5f;
340 constexpr bool MaterialDoubleSided =
false;
343 using Attributes = std::unordered_map<std::string, uint32_t>;
347 bool empty()
const noexcept
355 enum class ComponentType : uint16_t
361 UnsignedShort = 5123,
366 enum class Type : uint8_t
382 uint32_t bufferView{};
383 uint32_t byteOffset{};
384 ComponentType componentType{ ComponentType::None };
386 nlohmann::json extensionsAndExtras{};
391 uint32_t bufferView{};
392 uint32_t byteOffset{};
394 nlohmann::json extensionsAndExtras{};
401 nlohmann::json extensionsAndExtras{};
403 bool empty() const noexcept
409 int32_t bufferView{ -1 };
410 uint32_t byteOffset{};
412 bool normalized{ defaults::AccessorNormalized };
414 ComponentType componentType{ ComponentType::None };
415 Type type{ Type::None };
419 std::vector<float> max{};
420 std::vector<float> min{};
422 nlohmann::json extensionsAndExtras{};
434 nlohmann::json extensionsAndExtras{};
437 int32_t sampler{ -1 };
440 nlohmann::json extensionsAndExtras{};
453 int32_t output{ -1 };
455 Type interpolation{ Sampler::Type::Linear };
457 nlohmann::json extensionsAndExtras{};
461 std::vector<Channel> channels{};
462 std::vector<Sampler> samplers{};
464 nlohmann::json extensionsAndExtras{};
469 std::string copyright{};
470 std::string generator{};
471 std::string minVersion{};
472 std::string version{
"2.0" };
474 nlohmann::json extensionsAndExtras{};
479 uint32_t byteLength{};
484 nlohmann::json extensionsAndExtras{};
486 std::vector<uint8_t> data{};
488 bool IsEmbeddedResource()
const noexcept
490 return uri.find(detail::MimetypeApplicationOctet) == 0;
493 void SetEmbeddedResource()
495 uri = std::string(detail::MimetypeApplicationOctet).append(
",").append(base64::Encode(data));
501 enum class TargetType : uint16_t
505 ElementArrayBuffer = 34963
510 int32_t buffer{ -1 };
511 uint32_t byteOffset{};
512 uint32_t byteLength{};
513 uint32_t byteStride{};
515 TargetType target{ TargetType::None };
517 nlohmann::json extensionsAndExtras{};
531 float xmag{ defaults::FloatSentinel };
532 float ymag{ defaults::FloatSentinel };
533 float zfar{ -defaults::FloatSentinel };
534 float znear{ -defaults::FloatSentinel };
536 nlohmann::json extensionsAndExtras{};
546 nlohmann::json extensionsAndExtras{};
550 Type type{ Type::None };
552 Orthographic orthographic;
553 Perspective perspective;
555 nlohmann::json extensionsAndExtras{};
560 int32_t bufferView{};
564 std::string mimeType;
566 nlohmann::json extensionsAndExtras{};
568 bool IsEmbeddedResource()
const noexcept
570 return uri.find(detail::MimetypeImagePNG) == 0 || uri.find(detail::MimetypeImageJPG) == 0;
573 void MaterializeData(std::vector<uint8_t> & data)
const
575 char const *
const mimetype = uri.find(detail::MimetypeImagePNG) == 0 ? detail::MimetypeImagePNG : detail::MimetypeImageJPG;
576 const std::size_t startPos = std::char_traits<char>::length(mimetype) + 1;
578#if defined(FX_GLTF_HAS_CPP_17)
579 const std::size_t base64Length = uri.length() - startPos;
580 const bool success = base64::TryDecode({ &uri[startPos], base64Length }, data);
582 const bool success = base64::TryDecode(uri.substr(startPos), data);
593 enum class AlphaMode : uint8_t
605 nlohmann::json extensionsAndExtras{};
607 bool empty()
const noexcept
615 float scale{ defaults::IdentityScalar };
620 float strength{ defaults::IdentityScalar };
625 std::array<float, 4> baseColorFactor = { defaults::IdentityVec4 };
628 float roughnessFactor{ defaults::IdentityScalar };
629 float metallicFactor{ defaults::IdentityScalar };
630 Texture metallicRoughnessTexture;
632 nlohmann::json extensionsAndExtras{};
636 return baseColorTexture.empty() && metallicRoughnessTexture.empty() && metallicFactor == 1.0f && roughnessFactor == 1.0f && baseColorFactor == defaults::IdentityVec4;
640 float alphaCutoff{ defaults::MaterialAlphaCutoff };
641 AlphaMode alphaMode{ AlphaMode::Opaque };
643 bool doubleSided{ defaults::MaterialDoubleSided };
645 NormalTexture normalTexture;
646 OcclusionTexture occlusionTexture;
647 PBRMetallicRoughness pbrMetallicRoughness;
649 Texture emissiveTexture;
650 std::array<float, 3> emissiveFactor = { defaults::NullVec3 };
653 nlohmann::json extensionsAndExtras{};
658 enum class Mode : uint8_t
669 int32_t indices{ -1 };
670 int32_t material{ -1 };
672 Mode mode{ Mode::Triangles };
674 Attributes attributes{};
675 std::vector<Attributes> targets{};
677 nlohmann::json extensionsAndExtras{};
684 std::vector<float> weights{};
685 std::vector<Primitive> primitives{};
687 nlohmann::json extensionsAndExtras{};
694 int32_t camera{ -1 };
698 std::array<float, 16> matrix{ defaults::IdentityMatrix };
699 std::array<float, 4> rotation{ defaults::IdentityRotation };
700 std::array<float, 3> scale{ defaults::IdentityVec3 };
701 std::array<float, 3> translation{ defaults::NullVec3 };
703 std::vector<int32_t> children{};
704 std::vector<float> weights{};
706 nlohmann::json extensionsAndExtras{};
711 enum class MagFilter : uint16_t
718 enum class MinFilter : uint16_t
723 NearestMipMapNearest = 9984,
724 LinearMipMapNearest = 9985,
725 NearestMipMapLinear = 9986,
726 LinearMipMapLinear = 9987
729 enum class WrappingMode : uint16_t
732 MirroredRepeat = 33648,
738 MagFilter magFilter{ MagFilter::None };
739 MinFilter minFilter{ MinFilter::None };
741 WrappingMode wrapS{ WrappingMode::Repeat };
742 WrappingMode wrapT{ WrappingMode::Repeat };
744 nlohmann::json extensionsAndExtras{};
746 bool empty()
const noexcept
748 return name.empty() && magFilter == MagFilter::None && minFilter == MinFilter::None && wrapS == WrappingMode::Repeat && wrapT == WrappingMode::Repeat && extensionsAndExtras.empty();
756 std::vector<uint32_t> nodes{};
758 nlohmann::json extensionsAndExtras{};
763 int32_t inverseBindMatrices{ -1 };
764 int32_t skeleton{ -1 };
767 std::vector<uint32_t> joints{};
769 nlohmann::json extensionsAndExtras{};
776 int32_t sampler{ -1 };
777 int32_t source{ -1 };
779 nlohmann::json extensionsAndExtras{};
786 std::vector<Accessor> accessors{};
787 std::vector<Animation> animations{};
788 std::vector<Buffer> buffers{};
789 std::vector<BufferView> bufferViews{};
790 std::vector<Camera> cameras{};
791 std::vector<Image> images{};
792 std::vector<Material> materials{};
793 std::vector<Mesh> meshes{};
794 std::vector<Node> nodes{};
795 std::vector<Sampler> samplers{};
796 std::vector<Scene> scenes{};
797 std::vector<Skin> skins{};
798 std::vector<Texture> textures{};
801 std::vector<std::string> extensionsUsed{};
802 std::vector<std::string> extensionsRequired{};
804 nlohmann::json extensionsAndExtras{};
809 uint32_t MaxBufferCount{ detail::DefaultMaxBufferCount };
810 uint32_t MaxFileSize{ detail::DefaultMaxMemoryAllocation };
811 uint32_t MaxBufferByteLength{ detail::DefaultMaxMemoryAllocation };
814 inline void from_json(nlohmann::json
const & json, Accessor::Type & accessorType)
816 std::string type = json.get<std::string>();
817 if (type ==
"SCALAR")
819 accessorType = Accessor::Type::Scalar;
821 else if (type ==
"VEC2")
823 accessorType = Accessor::Type::Vec2;
825 else if (type ==
"VEC3")
827 accessorType = Accessor::Type::Vec3;
829 else if (type ==
"VEC4")
831 accessorType = Accessor::Type::Vec4;
833 else if (type ==
"MAT2")
835 accessorType = Accessor::Type::Mat2;
837 else if (type ==
"MAT3")
839 accessorType = Accessor::Type::Mat3;
841 else if (type ==
"MAT4")
843 accessorType = Accessor::Type::Mat4;
847 throw invalid_gltf_document(
"Unknown accessor.type value", type);
851 inline void from_json(nlohmann::json
const & json, Accessor::Sparse::Values & values)
853 detail::ReadRequiredField(
"bufferView", json, values.bufferView);
855 detail::ReadOptionalField(
"byteOffset", json, values.byteOffset);
857 detail::ReadExtensionsAndExtras(json, values.extensionsAndExtras);
860 inline void from_json(nlohmann::json
const & json, Accessor::Sparse::Indices & indices)
862 detail::ReadRequiredField(
"bufferView", json, indices.bufferView);
863 detail::ReadRequiredField(
"componentType", json, indices.componentType);
865 detail::ReadOptionalField(
"byteOffset", json, indices.byteOffset);
867 detail::ReadExtensionsAndExtras(json, indices.extensionsAndExtras);
870 inline void from_json(nlohmann::json
const & json, Accessor::Sparse & sparse)
872 detail::ReadRequiredField(
"count", json, sparse.count);
873 detail::ReadRequiredField(
"indices", json, sparse.indices);
874 detail::ReadRequiredField(
"values", json, sparse.values);
876 detail::ReadExtensionsAndExtras(json, sparse.extensionsAndExtras);
879 inline void from_json(nlohmann::json
const & json, Accessor & accessor)
881 detail::ReadRequiredField(
"componentType", json, accessor.componentType);
882 detail::ReadRequiredField(
"count", json, accessor.count);
883 detail::ReadRequiredField(
"type", json, accessor.type);
885 detail::ReadOptionalField(
"bufferView", json, accessor.bufferView);
886 detail::ReadOptionalField(
"byteOffset", json, accessor.byteOffset);
887 detail::ReadOptionalField(
"max", json, accessor.max);
888 detail::ReadOptionalField(
"min", json, accessor.min);
889 detail::ReadOptionalField(
"name", json, accessor.name);
890 detail::ReadOptionalField(
"normalized", json, accessor.normalized);
891 detail::ReadOptionalField(
"sparse", json, accessor.sparse);
893 detail::ReadExtensionsAndExtras(json, accessor.extensionsAndExtras);
896 inline void from_json(nlohmann::json
const & json, Animation::Channel::Target & animationChannelTarget)
898 detail::ReadRequiredField(
"path", json, animationChannelTarget.path);
900 detail::ReadOptionalField(
"node", json, animationChannelTarget.node);
902 detail::ReadExtensionsAndExtras(json, animationChannelTarget.extensionsAndExtras);
905 inline void from_json(nlohmann::json
const & json, Animation::Channel & animationChannel)
907 detail::ReadRequiredField(
"sampler", json, animationChannel.sampler);
908 detail::ReadRequiredField(
"target", json, animationChannel.target);
910 detail::ReadExtensionsAndExtras(json, animationChannel.extensionsAndExtras);
913 inline void from_json(nlohmann::json
const & json, Animation::Sampler::Type & animationSamplerType)
915 std::string type = json.get<std::string>();
916 if (type ==
"LINEAR")
918 animationSamplerType = Animation::Sampler::Type::Linear;
920 else if (type ==
"STEP")
922 animationSamplerType = Animation::Sampler::Type::Step;
924 else if (type ==
"CUBICSPLINE")
926 animationSamplerType = Animation::Sampler::Type::CubicSpline;
930 throw invalid_gltf_document(
"Unknown animation.sampler.interpolation value", type);
934 inline void from_json(nlohmann::json
const & json, Animation::Sampler & animationSampler)
936 detail::ReadRequiredField(
"input", json, animationSampler.input);
937 detail::ReadRequiredField(
"output", json, animationSampler.output);
939 detail::ReadOptionalField(
"interpolation", json, animationSampler.interpolation);
941 detail::ReadExtensionsAndExtras(json, animationSampler.extensionsAndExtras);
944 inline void from_json(nlohmann::json
const & json, Animation & animation)
946 detail::ReadRequiredField(
"channels", json, animation.channels);
947 detail::ReadRequiredField(
"samplers", json, animation.samplers);
949 detail::ReadOptionalField(
"name", json, animation.name);
951 detail::ReadExtensionsAndExtras(json, animation.extensionsAndExtras);
954 inline void from_json(nlohmann::json
const & json, Asset & asset)
956 detail::ReadRequiredField(
"version", json, asset.version);
957 detail::ReadOptionalField(
"copyright", json, asset.copyright);
958 detail::ReadOptionalField(
"generator", json, asset.generator);
959 detail::ReadOptionalField(
"minVersion", json, asset.minVersion);
961 detail::ReadExtensionsAndExtras(json, asset.extensionsAndExtras);
964 inline void from_json(nlohmann::json
const & json, Buffer & buffer)
966 detail::ReadRequiredField(
"byteLength", json, buffer.byteLength);
968 detail::ReadOptionalField(
"name", json, buffer.name);
969 detail::ReadOptionalField(
"uri", json, buffer.uri);
971 detail::ReadExtensionsAndExtras(json, buffer.extensionsAndExtras);
974 inline void from_json(nlohmann::json
const & json, BufferView & bufferView)
976 detail::ReadRequiredField(
"buffer", json, bufferView.buffer);
977 detail::ReadRequiredField(
"byteLength", json, bufferView.byteLength);
979 detail::ReadOptionalField(
"byteOffset", json, bufferView.byteOffset);
980 detail::ReadOptionalField(
"byteStride", json, bufferView.byteStride);
981 detail::ReadOptionalField(
"name", json, bufferView.name);
982 detail::ReadOptionalField(
"target", json, bufferView.target);
984 detail::ReadExtensionsAndExtras(json, bufferView.extensionsAndExtras);
987 inline void from_json(nlohmann::json
const & json, Camera::Type & cameraType)
989 std::string type = json.get<std::string>();
990 if (type ==
"orthographic")
992 cameraType = Camera::Type::Orthographic;
994 else if (type ==
"perspective")
996 cameraType = Camera::Type::Perspective;
1000 throw invalid_gltf_document(
"Unknown camera.type value", type);
1004 inline void from_json(nlohmann::json
const & json, Camera::Orthographic & camera)
1006 detail::ReadRequiredField(
"xmag", json, camera.xmag);
1007 detail::ReadRequiredField(
"ymag", json, camera.ymag);
1008 detail::ReadRequiredField(
"zfar", json, camera.zfar);
1009 detail::ReadRequiredField(
"znear", json, camera.znear);
1011 detail::ReadExtensionsAndExtras(json, camera.extensionsAndExtras);
1014 inline void from_json(nlohmann::json
const & json, Camera::Perspective & camera)
1016 detail::ReadRequiredField(
"yfov", json, camera.yfov);
1017 detail::ReadRequiredField(
"znear", json, camera.znear);
1019 detail::ReadOptionalField(
"aspectRatio", json, camera.aspectRatio);
1020 detail::ReadOptionalField(
"zfar", json, camera.zfar);
1022 detail::ReadExtensionsAndExtras(json, camera.extensionsAndExtras);
1025 inline void from_json(nlohmann::json
const & json, Camera & camera)
1027 detail::ReadRequiredField(
"type", json, camera.type);
1029 detail::ReadOptionalField(
"name", json, camera.name);
1031 detail::ReadExtensionsAndExtras(json, camera.extensionsAndExtras);
1033 if (camera.type == Camera::Type::Perspective)
1035 detail::ReadRequiredField(
"perspective", json, camera.perspective);
1037 else if (camera.type == Camera::Type::Orthographic)
1039 detail::ReadRequiredField(
"orthographic", json, camera.orthographic);
1043 inline void from_json(nlohmann::json
const & json, Image & image)
1045 detail::ReadOptionalField(
"bufferView", json, image.bufferView);
1046 detail::ReadOptionalField(
"mimeType", json, image.mimeType);
1047 detail::ReadOptionalField(
"name", json, image.name);
1048 detail::ReadOptionalField(
"uri", json, image.uri);
1050 detail::ReadExtensionsAndExtras(json, image.extensionsAndExtras);
1053 inline void from_json(nlohmann::json
const & json, Material::AlphaMode & materialAlphaMode)
1055 std::string alphaMode = json.get<std::string>();
1056 if (alphaMode ==
"OPAQUE")
1058 materialAlphaMode = Material::AlphaMode::Opaque;
1060 else if (alphaMode ==
"MASK")
1062 materialAlphaMode = Material::AlphaMode::Mask;
1064 else if (alphaMode ==
"BLEND")
1066 materialAlphaMode = Material::AlphaMode::Blend;
1070 throw invalid_gltf_document(
"Unknown material.alphaMode value", alphaMode);
1074 inline void from_json(nlohmann::json
const & json, Material::Texture & materialTexture)
1076 detail::ReadRequiredField(
"index", json, materialTexture.index);
1077 detail::ReadOptionalField(
"texCoord", json, materialTexture.texCoord);
1079 detail::ReadExtensionsAndExtras(json, materialTexture.extensionsAndExtras);
1082 inline void from_json(nlohmann::json
const & json, Material::NormalTexture & materialTexture)
1084 from_json(json,
static_cast<Material::Texture &
>(materialTexture));
1085 detail::ReadOptionalField(
"scale", json, materialTexture.scale);
1087 detail::ReadExtensionsAndExtras(json, materialTexture.extensionsAndExtras);
1090 inline void from_json(nlohmann::json
const & json, Material::OcclusionTexture & materialTexture)
1092 from_json(json,
static_cast<Material::Texture &
>(materialTexture));
1093 detail::ReadOptionalField(
"strength", json, materialTexture.strength);
1095 detail::ReadExtensionsAndExtras(json, materialTexture.extensionsAndExtras);
1098 inline void from_json(nlohmann::json
const & json, Material::PBRMetallicRoughness & pbrMetallicRoughness)
1100 detail::ReadOptionalField(
"baseColorFactor", json, pbrMetallicRoughness.baseColorFactor);
1101 detail::ReadOptionalField(
"baseColorTexture", json, pbrMetallicRoughness.baseColorTexture);
1102 detail::ReadOptionalField(
"metallicFactor", json, pbrMetallicRoughness.metallicFactor);
1103 detail::ReadOptionalField(
"metallicRoughnessTexture", json, pbrMetallicRoughness.metallicRoughnessTexture);
1104 detail::ReadOptionalField(
"roughnessFactor", json, pbrMetallicRoughness.roughnessFactor);
1106 detail::ReadExtensionsAndExtras(json, pbrMetallicRoughness.extensionsAndExtras);
1109 inline void from_json(nlohmann::json
const & json, Material & material)
1111 detail::ReadOptionalField(
"alphaMode", json, material.alphaMode);
1112 detail::ReadOptionalField(
"alphaCutoff", json, material.alphaCutoff);
1113 detail::ReadOptionalField(
"doubleSided", json, material.doubleSided);
1114 detail::ReadOptionalField(
"emissiveFactor", json, material.emissiveFactor);
1115 detail::ReadOptionalField(
"emissiveTexture", json, material.emissiveTexture);
1116 detail::ReadOptionalField(
"name", json, material.name);
1117 detail::ReadOptionalField(
"normalTexture", json, material.normalTexture);
1118 detail::ReadOptionalField(
"occlusionTexture", json, material.occlusionTexture);
1119 detail::ReadOptionalField(
"pbrMetallicRoughness", json, material.pbrMetallicRoughness);
1121 detail::ReadExtensionsAndExtras(json, material.extensionsAndExtras);
1124 inline void from_json(nlohmann::json
const & json, Mesh & mesh)
1126 detail::ReadRequiredField(
"primitives", json, mesh.primitives);
1128 detail::ReadOptionalField(
"name", json, mesh.name);
1129 detail::ReadOptionalField(
"weights", json, mesh.weights);
1131 detail::ReadExtensionsAndExtras(json, mesh.extensionsAndExtras);
1134 inline void from_json(nlohmann::json
const & json, Node & node)
1136 detail::ReadOptionalField(
"camera", json, node.camera);
1137 detail::ReadOptionalField(
"children", json, node.children);
1138 detail::ReadOptionalField(
"matrix", json, node.matrix);
1139 detail::ReadOptionalField(
"mesh", json, node.mesh);
1140 detail::ReadOptionalField(
"name", json, node.name);
1141 detail::ReadOptionalField(
"rotation", json, node.rotation);
1142 detail::ReadOptionalField(
"scale", json, node.scale);
1143 detail::ReadOptionalField(
"skin", json, node.skin);
1144 detail::ReadOptionalField(
"translation", json, node.translation);
1146 detail::ReadExtensionsAndExtras(json, node.extensionsAndExtras);
1149 inline void from_json(nlohmann::json
const & json, Primitive & primitive)
1151 detail::ReadRequiredField(
"attributes", json, primitive.attributes);
1153 detail::ReadOptionalField(
"indices", json, primitive.indices);
1154 detail::ReadOptionalField(
"material", json, primitive.material);
1155 detail::ReadOptionalField(
"mode", json, primitive.mode);
1156 detail::ReadOptionalField(
"targets", json, primitive.targets);
1158 detail::ReadExtensionsAndExtras(json, primitive.extensionsAndExtras);
1161 inline void from_json(nlohmann::json
const & json, Sampler & sampler)
1163 detail::ReadOptionalField(
"magFilter", json, sampler.magFilter);
1164 detail::ReadOptionalField(
"minFilter", json, sampler.minFilter);
1165 detail::ReadOptionalField(
"name", json, sampler.name);
1166 detail::ReadOptionalField(
"wrapS", json, sampler.wrapS);
1167 detail::ReadOptionalField(
"wrapT", json, sampler.wrapT);
1169 detail::ReadExtensionsAndExtras(json, sampler.extensionsAndExtras);
1172 inline void from_json(nlohmann::json
const & json, Scene & scene)
1174 detail::ReadOptionalField(
"name", json, scene.name);
1175 detail::ReadOptionalField(
"nodes", json, scene.nodes);
1177 detail::ReadExtensionsAndExtras(json, scene.extensionsAndExtras);
1180 inline void from_json(nlohmann::json
const & json, Skin & skin)
1182 detail::ReadRequiredField(
"joints", json, skin.joints);
1184 detail::ReadOptionalField(
"inverseBindMatrices", json, skin.inverseBindMatrices);
1185 detail::ReadOptionalField(
"name", json, skin.name);
1186 detail::ReadOptionalField(
"skeleton", json, skin.skeleton);
1188 detail::ReadExtensionsAndExtras(json, skin.extensionsAndExtras);
1191 inline void from_json(nlohmann::json
const & json, Texture & texture)
1193 detail::ReadOptionalField(
"name", json, texture.name);
1194 detail::ReadOptionalField(
"sampler", json, texture.sampler);
1195 detail::ReadOptionalField(
"source", json, texture.source);
1197 detail::ReadExtensionsAndExtras(json, texture.extensionsAndExtras);
1200 inline void from_json(nlohmann::json
const & json, Document & document)
1202 detail::ReadRequiredField(
"asset", json, document.asset);
1204 detail::ReadOptionalField(
"accessors", json, document.accessors);
1205 detail::ReadOptionalField(
"animations", json, document.animations);
1206 detail::ReadOptionalField(
"buffers", json, document.buffers);
1207 detail::ReadOptionalField(
"bufferViews", json, document.bufferViews);
1208 detail::ReadOptionalField(
"cameras", json, document.cameras);
1209 detail::ReadOptionalField(
"materials", json, document.materials);
1210 detail::ReadOptionalField(
"meshes", json, document.meshes);
1211 detail::ReadOptionalField(
"nodes", json, document.nodes);
1212 detail::ReadOptionalField(
"images", json, document.images);
1213 detail::ReadOptionalField(
"samplers", json, document.samplers);
1214 detail::ReadOptionalField(
"scene", json, document.scene);
1215 detail::ReadOptionalField(
"scenes", json, document.scenes);
1216 detail::ReadOptionalField(
"skins", json, document.skins);
1217 detail::ReadOptionalField(
"textures", json, document.textures);
1219 detail::ReadOptionalField(
"extensionsUsed", json, document.extensionsUsed);
1220 detail::ReadOptionalField(
"extensionsRequired", json, document.extensionsRequired);
1221 detail::ReadExtensionsAndExtras(json, document.extensionsAndExtras);
1224 inline void to_json(nlohmann::json & json, Accessor::ComponentType
const & accessorComponentType)
1226 if (accessorComponentType == Accessor::ComponentType::None)
1228 throw invalid_gltf_document(
"Unknown accessor.componentType value");
1231 json =
static_cast<uint16_t
>(accessorComponentType);
1234 inline void to_json(nlohmann::json & json, Accessor::Type
const & accessorType)
1236 switch (accessorType)
1238 case Accessor::Type::Scalar:
1241 case Accessor::Type::Vec2:
1244 case Accessor::Type::Vec3:
1247 case Accessor::Type::Vec4:
1250 case Accessor::Type::Mat2:
1253 case Accessor::Type::Mat3:
1256 case Accessor::Type::Mat4:
1260 throw invalid_gltf_document(
"Unknown accessor.type value");
1264 inline void to_json(nlohmann::json & json, Accessor::Sparse::Values
const & values)
1266 detail::WriteField(
"bufferView", json, values.bufferView,
static_cast<uint32_t
>(-1));
1267 detail::WriteField(
"byteOffset", json, values.byteOffset, {});
1268 detail::WriteExtensions(json, values.extensionsAndExtras);
1271 inline void to_json(nlohmann::json & json, Accessor::Sparse::Indices
const & indices)
1273 detail::WriteField(
"componentType", json, indices.componentType, Accessor::ComponentType::None);
1274 detail::WriteField(
"bufferView", json, indices.bufferView,
static_cast<uint32_t
>(-1));
1275 detail::WriteField(
"byteOffset", json, indices.byteOffset, {});
1276 detail::WriteExtensions(json, indices.extensionsAndExtras);
1279 inline void to_json(nlohmann::json & json, Accessor::Sparse
const & sparse)
1281 detail::WriteField(
"count", json, sparse.count, -1);
1282 detail::WriteField(
"indices", json, sparse.indices);
1283 detail::WriteField(
"values", json, sparse.values);
1284 detail::WriteExtensions(json, sparse.extensionsAndExtras);
1287 inline void to_json(nlohmann::json & json, Accessor
const & accessor)
1289 detail::WriteField(
"bufferView", json, accessor.bufferView, -1);
1290 detail::WriteField(
"byteOffset", json, accessor.byteOffset, {});
1291 detail::WriteField(
"componentType", json, accessor.componentType, Accessor::ComponentType::None);
1292 detail::WriteField(
"count", json, accessor.count, {});
1293 detail::WriteField(
"max", json, accessor.max);
1294 detail::WriteField(
"min", json, accessor.min);
1295 detail::WriteField(
"name", json, accessor.name);
1296 detail::WriteField(
"normalized", json, accessor.normalized,
false);
1297 detail::WriteField(
"sparse", json, accessor.sparse);
1298 detail::WriteField(
"type", json, accessor.type, Accessor::Type::None);
1299 detail::WriteExtensions(json, accessor.extensionsAndExtras);
1302 inline void to_json(nlohmann::json & json, Animation::Channel::Target
const & animationChannelTarget)
1304 detail::WriteField(
"node", json, animationChannelTarget.node, -1);
1305 detail::WriteField(
"path", json, animationChannelTarget.path);
1306 detail::WriteExtensions(json, animationChannelTarget.extensionsAndExtras);
1309 inline void to_json(nlohmann::json & json, Animation::Channel
const & animationChannel)
1311 detail::WriteField(
"sampler", json, animationChannel.sampler, -1);
1312 detail::WriteField(
"target", json, animationChannel.target);
1313 detail::WriteExtensions(json, animationChannel.extensionsAndExtras);
1316 inline void to_json(nlohmann::json & json, Animation::Sampler::Type
const & animationSamplerType)
1318 switch (animationSamplerType)
1320 case Animation::Sampler::Type::Linear:
1323 case Animation::Sampler::Type::Step:
1326 case Animation::Sampler::Type::CubicSpline:
1327 json =
"CUBICSPLINE";
1332 inline void to_json(nlohmann::json & json, Animation::Sampler
const & animationSampler)
1334 detail::WriteField(
"input", json, animationSampler.input, -1);
1335 detail::WriteField(
"interpolation", json, animationSampler.interpolation, Animation::Sampler::Type::Linear);
1336 detail::WriteField(
"output", json, animationSampler.output, -1);
1337 detail::WriteExtensions(json, animationSampler.extensionsAndExtras);
1340 inline void to_json(nlohmann::json & json, Animation
const & animation)
1342 detail::WriteField(
"channels", json, animation.channels);
1343 detail::WriteField(
"name", json, animation.name);
1344 detail::WriteField(
"samplers", json, animation.samplers);
1345 detail::WriteExtensions(json, animation.extensionsAndExtras);
1348 inline void to_json(nlohmann::json & json, Asset
const & asset)
1350 detail::WriteField(
"copyright", json, asset.copyright);
1351 detail::WriteField(
"generator", json, asset.generator);
1352 detail::WriteField(
"minVersion", json, asset.minVersion);
1353 detail::WriteField(
"version", json, asset.version);
1354 detail::WriteExtensions(json, asset.extensionsAndExtras);
1357 inline void to_json(nlohmann::json & json, Buffer
const & buffer)
1359 detail::WriteField(
"byteLength", json, buffer.byteLength, {});
1360 detail::WriteField(
"name", json, buffer.name);
1361 detail::WriteField(
"uri", json, buffer.uri);
1362 detail::WriteExtensions(json, buffer.extensionsAndExtras);
1365 inline void to_json(nlohmann::json & json, BufferView
const & bufferView)
1367 detail::WriteField(
"buffer", json, bufferView.buffer, -1);
1368 detail::WriteField(
"byteLength", json, bufferView.byteLength, {});
1369 detail::WriteField(
"byteOffset", json, bufferView.byteOffset, {});
1370 detail::WriteField(
"byteStride", json, bufferView.byteStride, {});
1371 detail::WriteField(
"name", json, bufferView.name);
1372 detail::WriteField(
"target", json, bufferView.target, BufferView::TargetType::None);
1373 detail::WriteExtensions(json, bufferView.extensionsAndExtras);
1376 inline void to_json(nlohmann::json & json, Camera::Type
const & cameraType)
1380 case Camera::Type::Orthographic:
1381 json =
"orthographic";
1383 case Camera::Type::Perspective:
1384 json =
"perspective";
1387 throw invalid_gltf_document(
"Unknown camera.type value");
1391 inline void to_json(nlohmann::json & json, Camera::Orthographic
const & camera)
1393 detail::WriteField(
"xmag", json, camera.xmag, defaults::FloatSentinel);
1394 detail::WriteField(
"ymag", json, camera.ymag, defaults::FloatSentinel);
1395 detail::WriteField(
"zfar", json, camera.zfar, -defaults::FloatSentinel);
1396 detail::WriteField(
"znear", json, camera.znear, -defaults::FloatSentinel);
1397 detail::WriteExtensions(json, camera.extensionsAndExtras);
1400 inline void to_json(nlohmann::json & json, Camera::Perspective
const & camera)
1402 detail::WriteField(
"aspectRatio", json, camera.aspectRatio, {});
1403 detail::WriteField(
"yfov", json, camera.yfov, {});
1404 detail::WriteField(
"zfar", json, camera.zfar, {});
1405 detail::WriteField(
"znear", json, camera.znear, {});
1406 detail::WriteExtensions(json, camera.extensionsAndExtras);
1409 inline void to_json(nlohmann::json & json, Camera
const & camera)
1411 detail::WriteField(
"name", json, camera.name);
1412 detail::WriteField(
"type", json, camera.type, Camera::Type::None);
1413 detail::WriteExtensions(json, camera.extensionsAndExtras);
1415 if (camera.type == Camera::Type::Perspective)
1417 detail::WriteField(
"perspective", json, camera.perspective);
1419 else if (camera.type == Camera::Type::Orthographic)
1421 detail::WriteField(
"orthographic", json, camera.orthographic);
1425 inline void to_json(nlohmann::json & json, Image
const & image)
1427 detail::WriteField(
"bufferView", json, image.bufferView, image.uri.empty() ? -1 : 0);
1428 detail::WriteField(
"mimeType", json, image.mimeType);
1429 detail::WriteField(
"name", json, image.name);
1430 detail::WriteField(
"uri", json, image.uri);
1431 detail::WriteExtensions(json, image.extensionsAndExtras);
1434 inline void to_json(nlohmann::json & json, Material::AlphaMode
const & materialAlphaMode)
1436 switch (materialAlphaMode)
1438 case Material::AlphaMode::Opaque:
1441 case Material::AlphaMode::Mask:
1444 case Material::AlphaMode::Blend:
1450 inline void to_json(nlohmann::json & json, Material::Texture
const & materialTexture)
1452 detail::WriteField(
"index", json, materialTexture.index, -1);
1453 detail::WriteField(
"texCoord", json, materialTexture.texCoord, 0);
1454 detail::WriteExtensions(json, materialTexture.extensionsAndExtras);
1457 inline void to_json(nlohmann::json & json, Material::NormalTexture
const & materialTexture)
1459 to_json(json,
static_cast<Material::Texture
const &
>(materialTexture));
1460 detail::WriteField(
"scale", json, materialTexture.scale, defaults::IdentityScalar);
1461 detail::WriteExtensions(json, materialTexture.extensionsAndExtras);
1464 inline void to_json(nlohmann::json & json, Material::OcclusionTexture
const & materialTexture)
1466 to_json(json,
static_cast<Material::Texture
const &
>(materialTexture));
1467 detail::WriteField(
"strength", json, materialTexture.strength, defaults::IdentityScalar);
1468 detail::WriteExtensions(json, materialTexture.extensionsAndExtras);
1471 inline void to_json(nlohmann::json & json, Material::PBRMetallicRoughness
const & pbrMetallicRoughness)
1473 detail::WriteField(
"baseColorFactor", json, pbrMetallicRoughness.baseColorFactor, defaults::IdentityVec4);
1474 detail::WriteField(
"baseColorTexture", json, pbrMetallicRoughness.baseColorTexture);
1475 detail::WriteField(
"metallicFactor", json, pbrMetallicRoughness.metallicFactor, defaults::IdentityScalar);
1476 detail::WriteField(
"metallicRoughnessTexture", json, pbrMetallicRoughness.metallicRoughnessTexture);
1477 detail::WriteField(
"roughnessFactor", json, pbrMetallicRoughness.roughnessFactor, defaults::IdentityScalar);
1478 detail::WriteExtensions(json, pbrMetallicRoughness.extensionsAndExtras);
1481 inline void to_json(nlohmann::json & json, Material
const & material)
1483 detail::WriteField(
"alphaCutoff", json, material.alphaCutoff, defaults::MaterialAlphaCutoff);
1484 detail::WriteField(
"alphaMode", json, material.alphaMode, Material::AlphaMode::Opaque);
1485 detail::WriteField(
"doubleSided", json, material.doubleSided, defaults::MaterialDoubleSided);
1486 detail::WriteField(
"emissiveTexture", json, material.emissiveTexture);
1487 detail::WriteField(
"emissiveFactor", json, material.emissiveFactor, defaults::NullVec3);
1488 detail::WriteField(
"name", json, material.name);
1489 detail::WriteField(
"normalTexture", json, material.normalTexture);
1490 detail::WriteField(
"occlusionTexture", json, material.occlusionTexture);
1491 detail::WriteField(
"pbrMetallicRoughness", json, material.pbrMetallicRoughness);
1493 detail::WriteExtensions(json, material.extensionsAndExtras);
1496 inline void to_json(nlohmann::json & json, Mesh
const & mesh)
1498 detail::WriteField(
"name", json, mesh.name);
1499 detail::WriteField(
"primitives", json, mesh.primitives);
1500 detail::WriteField(
"weights", json, mesh.weights);
1501 detail::WriteExtensions(json, mesh.extensionsAndExtras);
1504 inline void to_json(nlohmann::json & json, Node
const & node)
1506 detail::WriteField(
"camera", json, node.camera, -1);
1507 detail::WriteField(
"children", json, node.children);
1508 detail::WriteField(
"matrix", json, node.matrix, defaults::IdentityMatrix);
1509 detail::WriteField(
"mesh", json, node.mesh, -1);
1510 detail::WriteField(
"name", json, node.name);
1511 detail::WriteField(
"rotation", json, node.rotation, defaults::IdentityRotation);
1512 detail::WriteField(
"scale", json, node.scale, defaults::IdentityVec3);
1513 detail::WriteField(
"skin", json, node.skin, -1);
1514 detail::WriteField(
"translation", json, node.translation, defaults::NullVec3);
1515 detail::WriteField(
"weights", json, node.weights);
1516 detail::WriteExtensions(json, node.extensionsAndExtras);
1519 inline void to_json(nlohmann::json & json, Primitive
const & primitive)
1521 detail::WriteField(
"attributes", json, primitive.attributes);
1522 detail::WriteField(
"indices", json, primitive.indices, -1);
1523 detail::WriteField(
"material", json, primitive.material, -1);
1524 detail::WriteField(
"mode", json, primitive.mode, Primitive::Mode::Triangles);
1525 detail::WriteField(
"targets", json, primitive.targets);
1526 detail::WriteExtensions(json, primitive.extensionsAndExtras);
1529 inline void to_json(nlohmann::json & json, Sampler
const & sampler)
1531 if (!sampler.empty())
1533 detail::WriteField(
"name", json, sampler.name);
1534 detail::WriteField(
"magFilter", json, sampler.magFilter, Sampler::MagFilter::None);
1535 detail::WriteField(
"minFilter", json, sampler.minFilter, Sampler::MinFilter::None);
1536 detail::WriteField(
"wrapS", json, sampler.wrapS, Sampler::WrappingMode::Repeat);
1537 detail::WriteField(
"wrapT", json, sampler.wrapT, Sampler::WrappingMode::Repeat);
1538 detail::WriteExtensions(json, sampler.extensionsAndExtras);
1543 json = nlohmann::json::object();
1547 inline void to_json(nlohmann::json & json, Scene
const & scene)
1549 detail::WriteField(
"name", json, scene.name);
1550 detail::WriteField(
"nodes", json, scene.nodes);
1551 detail::WriteExtensions(json, scene.extensionsAndExtras);
1554 inline void to_json(nlohmann::json & json, Skin
const & skin)
1556 detail::WriteField(
"inverseBindMatrices", json, skin.inverseBindMatrices, -1);
1557 detail::WriteField(
"name", json, skin.name);
1558 detail::WriteField(
"skeleton", json, skin.skeleton, -1);
1559 detail::WriteField(
"joints", json, skin.joints);
1560 detail::WriteExtensions(json, skin.extensionsAndExtras);
1563 inline void to_json(nlohmann::json & json, Texture
const & texture)
1565 detail::WriteField(
"name", json, texture.name);
1566 detail::WriteField(
"sampler", json, texture.sampler, -1);
1567 detail::WriteField(
"source", json, texture.source, -1);
1568 detail::WriteExtensions(json, texture.extensionsAndExtras);
1571 inline void to_json(nlohmann::json & json, Document
const & document)
1573 detail::WriteField(
"accessors", json, document.accessors);
1574 detail::WriteField(
"animations", json, document.animations);
1575 detail::WriteField(
"asset", json, document.asset);
1576 detail::WriteField(
"buffers", json, document.buffers);
1577 detail::WriteField(
"bufferViews", json, document.bufferViews);
1578 detail::WriteField(
"cameras", json, document.cameras);
1579 detail::WriteField(
"images", json, document.images);
1580 detail::WriteField(
"materials", json, document.materials);
1581 detail::WriteField(
"meshes", json, document.meshes);
1582 detail::WriteField(
"nodes", json, document.nodes);
1583 detail::WriteField(
"samplers", json, document.samplers);
1584 detail::WriteField(
"scene", json, document.scene, -1);
1585 detail::WriteField(
"scenes", json, document.scenes);
1586 detail::WriteField(
"skins", json, document.skins);
1587 detail::WriteField(
"textures", json, document.textures);
1589 detail::WriteField(
"extensionsUsed", json, document.extensionsUsed);
1590 detail::WriteField(
"extensionsRequired", json, document.extensionsRequired);
1591 detail::WriteExtensions(json, document.extensionsAndExtras);
1598 std::string bufferRootPath{};
1601 std::vector<uint8_t> * binaryData{};
1604 inline void ThrowIfBad(std::ios
const & io)
1608 throw std::system_error(std::make_error_code(std::errc::io_error));
1612 inline void MaterializeData(Buffer & buffer)
1614 const std::size_t startPos = std::char_traits<char>::length(detail::MimetypeApplicationOctet) + 1;
1615 const std::size_t base64Length = buffer.uri.length() - startPos;
1616 const std::size_t decodedEstimate = base64Length / 4 * 3;
1617 if ((decodedEstimate - 2) > buffer.byteLength)
1619 throw invalid_gltf_document(
"Invalid buffer.uri value",
"malformed base64");
1622#if defined(FX_GLTF_HAS_CPP_17)
1623 const bool success = base64::TryDecode({ &buffer.uri[startPos], base64Length }, buffer.data);
1625 const bool success = base64::TryDecode(buffer.uri.substr(startPos), buffer.data);
1629 throw invalid_gltf_document(
"Invalid buffer.uri value",
"malformed base64");
1633 inline Document Create(nlohmann::json
const & json, DataContext
const & dataContext)
1635 Document document = json;
1637 if (document.buffers.size() > dataContext.readQuotas.MaxBufferCount)
1639 throw invalid_gltf_document(
"Quota exceeded : number of buffers > MaxBufferCount");
1642 for (
auto & buffer : document.buffers)
1644 if (buffer.byteLength == 0)
1646 throw invalid_gltf_document(
"Invalid buffer.byteLength value : 0");
1649 if (buffer.byteLength > dataContext.readQuotas.MaxBufferByteLength)
1651 throw invalid_gltf_document(
"Quota exceeded : buffer.byteLength > MaxBufferByteLength");
1654 if (!buffer.uri.empty())
1656 if (buffer.IsEmbeddedResource())
1658 detail::MaterializeData(buffer);
1662 std::ifstream fileData(detail::CreateBufferUriPath(dataContext.bufferRootPath, buffer.uri), std::ios::binary);
1663 if (!fileData.good())
1665 throw invalid_gltf_document(
"Invalid buffer.uri value", buffer.uri);
1668 buffer.data.resize(buffer.byteLength);
1669 fileData.read(
reinterpret_cast<char *
>(&buffer.data[0]), buffer.byteLength);
1672 else if (dataContext.binaryData !=
nullptr)
1674 std::vector<uint8_t> & binary = *dataContext.binaryData;
1675 if (binary.size() < buffer.byteLength)
1677 throw invalid_gltf_document(
"Invalid GLB buffer data");
1680 buffer.data.resize(buffer.byteLength);
1681 std::memcpy(&buffer.data[0], &binary[0], buffer.byteLength);
1688 inline void ValidateBuffers(Document
const & document,
bool useBinaryFormat)
1690 if (document.buffers.empty())
1692 throw invalid_gltf_document(
"Invalid glTF document. A document must have at least 1 buffer.");
1695 bool foundBinaryBuffer =
false;
1696 for (std::size_t bufferIndex = 0; bufferIndex < document.buffers.size(); bufferIndex++)
1698 Buffer
const & buffer = document.buffers[bufferIndex];
1699 if (buffer.byteLength == 0)
1701 throw invalid_gltf_document(
"Invalid buffer.byteLength value : 0");
1704 if (buffer.byteLength != buffer.data.size())
1706 throw invalid_gltf_document(
"Invalid buffer.byteLength value : does not match buffer.data size");
1709 if (buffer.uri.empty())
1711 foundBinaryBuffer =
true;
1712 if (bufferIndex != 0)
1714 throw invalid_gltf_document(
"Invalid glTF document. Only 1 buffer, the very first, is allowed to have an empty buffer.uri field.");
1719 if (useBinaryFormat && !foundBinaryBuffer)
1721 throw invalid_gltf_document(
"Invalid glTF document. No buffer found which can meet the criteria for saving to a .glb file.");
1725 inline void Save(Document
const & document, std::ostream & output, std::string
const & documentRootPath,
bool useBinaryFormat)
1729 detail::ThrowIfBad(output);
1731 nlohmann::json json = document;
1733 std::size_t externalBufferIndex = 0;
1734 if (useBinaryFormat)
1736 detail::GLBHeader header{ detail::GLBHeaderMagic, 2, 0, { 0, detail::GLBChunkJSON } };
1737 detail::ChunkHeader binHeader{ 0, detail::GLBChunkBIN };
1739 std::string jsonText = json.dump();
1741 Buffer
const & binBuffer = document.buffers.front();
1742 const uint32_t binPaddedLength = ((binBuffer.byteLength + 3) & (~3u));
1743 const uint32_t binPadding = binPaddedLength - binBuffer.byteLength;
1744 binHeader.chunkLength = binPaddedLength;
1746 header.jsonHeader.chunkLength = ((jsonText.length() + 3) & (~3u));
1747 const uint32_t headerPadding =
static_cast<uint32_t
>(header.jsonHeader.chunkLength - jsonText.length());
1748 header.length = detail::HeaderSize + header.jsonHeader.chunkLength + detail::ChunkHeaderSize + binHeader.chunkLength;
1750 const char spaces[3] = {
' ',
' ',
' ' };
1751 const char nulls[3] = { 0, 0, 0 };
1753 output.write(
reinterpret_cast<char *
>(&header), detail::HeaderSize);
1754 output.write(jsonText.c_str(), jsonText.length());
1755 output.write(&spaces[0], headerPadding);
1756 output.write(
reinterpret_cast<char *
>(&binHeader), detail::ChunkHeaderSize);
1757 output.write(
reinterpret_cast<char const *
>(&binBuffer.data[0]), binBuffer.byteLength);
1758 output.write(&nulls[0], binPadding);
1760 externalBufferIndex = 1;
1764 output << json.dump(2);
1769 for (; externalBufferIndex < document.buffers.size(); externalBufferIndex++)
1771 Buffer
const & buffer = document.buffers[externalBufferIndex];
1772 if (!buffer.IsEmbeddedResource())
1774 std::ofstream fileData(detail::CreateBufferUriPath(documentRootPath, buffer.uri), std::ios::binary);
1775 if (!fileData.good())
1777 throw invalid_gltf_document(
"Invalid buffer.uri value", buffer.uri);
1780 fileData.write(
reinterpret_cast<char const *
>(&buffer.data[0]), buffer.byteLength);
1786 inline Document LoadFromText(std::istream & input, std::string
const & documentRootPath, ReadQuotas
const & readQuotas = {})
1790 detail::ThrowIfBad(input);
1792 nlohmann::json json;
1795 return detail::Create(json, { documentRootPath, readQuotas });
1797 catch (invalid_gltf_document &)
1801 catch (std::system_error &)
1807 std::throw_with_nested(invalid_gltf_document(
"Invalid glTF document. See nested exception for details."));
1811 inline Document LoadFromText(std::string
const & documentFilePath, ReadQuotas
const & readQuotas = {})
1813 std::ifstream input(documentFilePath);
1814 if (!input.is_open())
1816 throw std::system_error(std::make_error_code(std::errc::no_such_file_or_directory));
1819 return LoadFromText(input, detail::GetDocumentRootPath(documentFilePath), readQuotas);
1822 inline Document LoadFromBinary(std::istream & input, std::string
const & documentRootPath, ReadQuotas
const & readQuotas = {})
1826 detail::GLBHeader header{};
1827 detail::ThrowIfBad(input.read(
reinterpret_cast<char *
>(&header), detail::HeaderSize));
1828 if (header.magic != detail::GLBHeaderMagic ||
1829 header.jsonHeader.chunkType != detail::GLBChunkJSON ||
1830 header.jsonHeader.chunkLength + detail::HeaderSize > header.length)
1832 throw invalid_gltf_document(
"Invalid GLB header");
1835 std::vector<uint8_t> json{};
1836 json.resize(header.jsonHeader.chunkLength);
1837 detail::ThrowIfBad(input.read(
reinterpret_cast<char *
>(&json[0]), header.jsonHeader.chunkLength));
1839 std::size_t totalSize = detail::HeaderSize + header.jsonHeader.chunkLength;
1840 if (totalSize > readQuotas.MaxFileSize)
1842 throw invalid_gltf_document(
"Quota exceeded : file size > MaxFileSize");
1845 detail::ChunkHeader binHeader{};
1846 detail::ThrowIfBad(input.read(
reinterpret_cast<char *
>(&binHeader), detail::ChunkHeaderSize));
1847 if (binHeader.chunkType != detail::GLBChunkBIN)
1849 throw invalid_gltf_document(
"Invalid GLB header");
1852 totalSize += detail::ChunkHeaderSize + binHeader.chunkLength;
1853 if (totalSize > readQuotas.MaxFileSize)
1855 throw invalid_gltf_document(
"Quota exceeded : file size > MaxFileSize");
1858 std::vector<uint8_t> binary{};
1859 binary.resize(binHeader.chunkLength);
1860 detail::ThrowIfBad(input.read(
reinterpret_cast<char *
>(&binary[0]), binHeader.chunkLength));
1862 return detail::Create(
1863 nlohmann::json::parse({ &json[0], header.jsonHeader.chunkLength }),
1864 { documentRootPath, readQuotas, &binary });
1866 catch (invalid_gltf_document &)
1870 catch (std::system_error &)
1876 std::throw_with_nested(invalid_gltf_document(
"Invalid glTF document. See nested exception for details."));
1880 inline Document LoadFromBinary(std::string
const & documentFilePath, ReadQuotas
const & readQuotas = {})
1882 std::ifstream input(documentFilePath, std::ios::binary);
1883 if (!input.is_open())
1885 throw std::system_error(std::make_error_code(std::errc::no_such_file_or_directory));
1888 return LoadFromBinary(input, detail::GetDocumentRootPath(documentFilePath), readQuotas);
1891 inline void Save(Document
const & document, std::ostream & output, std::string
const & documentRootPath,
bool useBinaryFormat)
1895 detail::ValidateBuffers(document, useBinaryFormat);
1897 detail::Save(document, output, documentRootPath, useBinaryFormat);
1899 catch (invalid_gltf_document &)
1903 catch (std::system_error &)
1909 std::throw_with_nested(invalid_gltf_document(
"Invalid glTF document. See nested exception for details."));
1913 inline void Save(Document
const & document, std::string
const & documentFilePath,
bool useBinaryFormat)
1915 std::ofstream output(documentFilePath, useBinaryFormat ? std::ios::binary : std::ios::out);
1916 Save(document, output, detail::GetDocumentRootPath(documentFilePath), useBinaryFormat);
1921inline void FormatException(std::string & output, std::exception
const & ex,
int level = 0)
1923 output.append(std::string(level,
' ')).append(ex.what());
1926 std::rethrow_if_nested(ex);
1928 catch (std::exception
const & e)
1930 FormatException(output.append(
"\n"), e, level + 2);
1936#undef FX_GLTF_HAS_CPP_17