54#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
55#include <android/asset_manager.h>
61#define TINYGLTF_MODE_POINTS (0)
62#define TINYGLTF_MODE_LINE (1)
63#define TINYGLTF_MODE_LINE_LOOP (2)
64#define TINYGLTF_MODE_LINE_STRIP (3)
65#define TINYGLTF_MODE_TRIANGLES (4)
66#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
67#define TINYGLTF_MODE_TRIANGLE_FAN (6)
69#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
70#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
71#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
72#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
73#define TINYGLTF_COMPONENT_TYPE_INT (5124)
74#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
75#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
76#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130)
78#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
79#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
80#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
81#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
82#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
83#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
85#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
86#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
87#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
90#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
91#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
92#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
93#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
94#define TINYGLTF_PARAMETER_TYPE_INT (5124)
95#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
96#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
98#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
99#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
100#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
102#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
103#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
104#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
106#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
107#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
108#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
109#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
111#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
112#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
113#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
115#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
119#define TINYGLTF_TYPE_VEC2 (2)
120#define TINYGLTF_TYPE_VEC3 (3)
121#define TINYGLTF_TYPE_VEC4 (4)
122#define TINYGLTF_TYPE_MAT2 (32 + 2)
123#define TINYGLTF_TYPE_MAT3 (32 + 3)
124#define TINYGLTF_TYPE_MAT4 (32 + 4)
125#define TINYGLTF_TYPE_SCALAR (64 + 1)
126#define TINYGLTF_TYPE_VECTOR (64 + 4)
127#define TINYGLTF_TYPE_MATRIX (64 + 16)
129#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
130#define TINYGLTF_IMAGE_FORMAT_PNG (1)
131#define TINYGLTF_IMAGE_FORMAT_BMP (2)
132#define TINYGLTF_IMAGE_FORMAT_GIF (3)
134#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
135#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
136#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
137#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
138#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
140#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
141#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
143#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
144#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
146#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
147#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
149#define TINYGLTF_DOUBLE_EPS (1.e-12)
150#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
153#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
154AAssetManager *asset_manager =
nullptr;
169static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
170 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
172 }
else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
174 }
else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
176 }
else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
178 }
else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
180 }
else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
182 }
else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
184 }
else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
192static inline int32_t GetTypeSizeInBytes(uint32_t ty) {
193 if (ty == TINYGLTF_TYPE_SCALAR) {
195 }
else if (ty == TINYGLTF_TYPE_VEC2) {
197 }
else if (ty == TINYGLTF_TYPE_VEC3) {
199 }
else if (ty == TINYGLTF_TYPE_VEC4) {
201 }
else if (ty == TINYGLTF_TYPE_MAT2) {
203 }
else if (ty == TINYGLTF_TYPE_MAT3) {
205 }
else if (ty == TINYGLTF_TYPE_MAT4) {
213bool IsDataURI(
const std::string &in);
214bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
215 const std::string &in,
size_t reqBytes,
bool checkSize);
218#pragma clang diagnostic push
221#pragma clang diagnostic ignored "-Wexit-time-destructors"
222#pragma clang diagnostic ignored "-Wpadded"
228 typedef std::vector<Value> Array;
229 typedef std::map<std::string, Value> Object;
231 Value() : type_(NULL_TYPE) {}
233 explicit Value(
bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
234 explicit Value(
int i) : type_(INT_TYPE) { int_value_ = i; }
235 explicit Value(
double n) : type_(NUMBER_TYPE) { number_value_ = n; }
236 explicit Value(
const std::string &s) : type_(STRING_TYPE) {
239 explicit Value(
const unsigned char *p,
size_t n) : type_(BINARY_TYPE) {
240 binary_value_.resize(n);
241 memcpy(binary_value_.data(), p, n);
243 explicit Value(
const Array &a) : type_(ARRAY_TYPE) {
244 array_value_ = Array(a);
246 explicit Value(
const Object &o) : type_(OBJECT_TYPE) {
247 object_value_ = Object(o);
250 char Type()
const {
return static_cast<const char>(type_); }
252 bool IsBool()
const {
return (type_ == BOOL_TYPE); }
254 bool IsInt()
const {
return (type_ == INT_TYPE); }
256 bool IsNumber()
const {
return (type_ == NUMBER_TYPE); }
258 bool IsString()
const {
return (type_ == STRING_TYPE); }
260 bool IsBinary()
const {
return (type_ == BINARY_TYPE); }
262 bool IsArray()
const {
return (type_ == ARRAY_TYPE); }
264 bool IsObject()
const {
return (type_ == OBJECT_TYPE); }
267 template <
typename T>
268 const T &Get()
const;
269 template <
typename T>
273 const Value &Get(
int idx)
const {
274 static Value null_value;
277 return (
static_cast<size_t>(idx) < array_value_.size())
278 ? array_value_[
static_cast<size_t>(idx)]
283 const Value &Get(
const std::string &key)
const {
284 static Value null_value;
286 Object::const_iterator it = object_value_.find(key);
287 return (it != object_value_.end()) ? it->second : null_value;
290 size_t ArrayLen()
const {
291 if (!IsArray())
return 0;
292 return array_value_.size();
296 bool Has(
const std::string &key)
const {
297 if (!IsObject())
return false;
298 Object::const_iterator it = object_value_.find(key);
299 return (it != object_value_.end()) ? true :
false;
303 std::vector<std::string> Keys()
const {
304 std::vector<std::string> keys;
305 if (!IsObject())
return keys;
307 for (Object::const_iterator it = object_value_.begin();
308 it != object_value_.end(); ++it) {
309 keys.push_back(it->first);
315 size_t Size()
const {
return (IsArray() ? ArrayLen() : Keys().size()); }
323 double number_value_;
324 std::string string_value_;
325 std::vector<unsigned char> binary_value_;
327 Object object_value_;
332#pragma clang diagnostic pop
335#define TINYGLTF_VALUE_GET(ctype, var) \
337 inline const ctype &Value::Get<ctype>() const { \
341 inline ctype &Value::Get<ctype>() { \
344TINYGLTF_VALUE_GET(
bool, boolean_value_)
345TINYGLTF_VALUE_GET(
double, number_value_)
346TINYGLTF_VALUE_GET(
int, int_value_)
347TINYGLTF_VALUE_GET(std::string, string_value_)
348TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
349TINYGLTF_VALUE_GET(Value::Array, array_value_)
350TINYGLTF_VALUE_GET(Value::Object, object_value_)
351#undef TINYGLTF_VALUE_GET
354#pragma clang diagnostic push
355#pragma clang diagnostic ignored "-Wc++98-compat"
356#pragma clang diagnostic ignored "-Wpadded"
360using ColorValue = std::array<double, 4>;
363 bool bool_value =
false;
364 bool has_number_value =
false;
365 std::string string_value;
366 std::vector<double> number_array;
367 std::map<std::string, double> json_double_value;
368 double number_value = 0.0;
378 const auto it = json_double_value.find(
"index");
379 if (it != std::end(json_double_value)) {
380 return int(it->second);
389 const auto it = json_double_value.find(
"texCoord");
390 if (it != std::end(json_double_value)) {
391 return int(it->second);
399 double Factor()
const {
return number_value; }
407 number_array[0], number_array[1], number_array[2],
408 (number_array.size() > 3 ? number_array[3] : 1.0)}};
411 bool operator==(
const Parameter &)
const;
415#pragma clang diagnostic pop
419#pragma clang diagnostic push
420#pragma clang diagnostic ignored "-Wpadded"
423typedef std::map<std::string, Parameter> ParameterMap;
424typedef std::map<std::string, Value> ExtensionMap;
429 std::string target_path;
440 std::string interpolation;
450 std::vector<AnimationChannel> channels;
451 std::vector<AnimationSampler> samplers;
454 bool operator==(
const Animation &)
const;
459 int inverseBindMatrices;
461 std::vector<int> joints;
464 inverseBindMatrices = -1;
467 bool operator==(
const Skin &)
const;
484 : minFilter(TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR),
485 magFilter(TINYGLTF_TEXTURE_FILTER_LINEAR),
486 wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
487 wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT),
488 wrapR(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
489 bool operator==(
const Sampler &)
const;
500 std::vector<unsigned char> image;
502 std::string mimeType;
506 ExtensionMap extensions;
516 Image() : as_is(
false) {
522 bool operator==(
const Image &)
const;
531 ExtensionMap extensions;
533 Texture() : sampler(-1), source(-1) {}
534 bool operator==(
const Texture &)
const;
544 ParameterMap additionalValues;
546 ExtensionMap extensions;
549 bool operator==(
const Material &)
const;
563 BufferView() : byteOffset(0), byteStride(0), dracoDecoded(
false) {}
578 std::vector<double> minValues;
579 std::vector<double> maxValues;
600 if (bufferViewObject.byteStride == 0) {
602 int componentSizeInBytes =
603 GetComponentSizeInBytes(
static_cast<uint32_t
>(componentType));
604 if (componentSizeInBytes <= 0) {
608 int typeSizeInBytes = GetTypeSizeInBytes(
static_cast<uint32_t
>(type));
609 if (typeSizeInBytes <= 0) {
613 return componentSizeInBytes * typeSizeInBytes;
617 int componentSizeInBytes =
618 GetComponentSizeInBytes(
static_cast<uint32_t
>(componentType));
619 if (componentSizeInBytes <= 0) {
623 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
626 return static_cast<int>(bufferViewObject.byteStride);
634 sparse.isSparse =
false;
653 ExtensionMap extensions;
666 ExtensionMap extensions;
678 bool operator==(
const Camera &)
const;
680 ExtensionMap extensions;
685 std::map<std::string, int> attributes;
693 std::vector<std::map<std::string, int> > targets;
697 ExtensionMap extensions;
704 bool operator==(
const Primitive &)
const;
709 std::vector<Primitive> primitives;
710 std::vector<double> weights;
711 std::vector<std::map<std::string, int> > targets;
712 ExtensionMap extensions;
715 bool operator==(
const Mesh &)
const;
720 Node() : camera(-1), skin(-1), mesh(-1) {}
728 children = rhs.children;
729 rotation = rhs.rotation;
731 translation = rhs.translation;
733 weights = rhs.weights;
735 extensions = rhs.extensions;
739 bool operator==(
const Node &)
const;
746 std::vector<int> children;
747 std::vector<double> rotation;
748 std::vector<double> scale;
749 std::vector<double> translation;
750 std::vector<double> matrix;
751 std::vector<double> weights;
753 ExtensionMap extensions;
759 std::vector<unsigned char> data;
764 bool operator==(
const Buffer &)
const;
769 std::string generator;
770 std::string minVersion;
771 std::string copyright;
772 ExtensionMap extensions;
775 bool operator==(
const Asset &)
const;
780 std::vector<int> nodes;
782 ExtensionMap extensions;
785 bool operator==(
const Scene &)
const;
790 std::vector<double> color;
793 bool operator==(
const Light &)
const;
800 bool operator==(
const Model &)
const;
802 std::vector<Accessor> accessors;
803 std::vector<Animation> animations;
804 std::vector<Buffer> buffers;
805 std::vector<BufferView> bufferViews;
806 std::vector<Material> materials;
807 std::vector<Mesh> meshes;
808 std::vector<Node> nodes;
809 std::vector<Texture> textures;
810 std::vector<Image> images;
811 std::vector<Skin> skins;
812 std::vector<Sampler> samplers;
813 std::vector<Camera> cameras;
814 std::vector<Scene> scenes;
815 std::vector<Light> lights;
816 ExtensionMap extensions;
819 std::vector<std::string> extensionsUsed;
820 std::vector<std::string> extensionsRequired;
829 REQUIRE_SCENE = 0x01,
830 REQUIRE_SCENES = 0x02,
831 REQUIRE_NODES = 0x04,
832 REQUIRE_ACCESSORS = 0x08,
833 REQUIRE_BUFFERS = 0x10,
834 REQUIRE_BUFFER_VIEWS = 0x20,
841typedef bool (*LoadImageDataFunction)(Image *,
const int, std::string *,
842 std::string *, int, int,
843 const unsigned char *, int,
void *);
848typedef bool (*WriteImageDataFunction)(
const std::string *,
const std::string *,
849 Image *, bool,
void *);
851#ifndef TINYGLTF_NO_STB_IMAGE
853bool LoadImageData(Image *image,
const int image_idx, std::string *err,
854 std::string *warn,
int req_width,
int req_height,
855 const unsigned char *
bytes,
int size,
void *);
858#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
860bool WriteImageData(
const std::string *basepath,
const std::string *filename,
861 Image *image,
bool embedImages,
void *);
867typedef bool (*FileExistsFunction)(
const std::string &abs_filename,
void *);
872typedef std::string (*ExpandFilePathFunction)(
const std::string &,
void *);
877typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
878 std::string *,
const std::string &,
884typedef bool (*WriteWholeFileFunction)(std::string *,
const std::string &,
885 const std::vector<unsigned char> &,
893 FileExistsFunction FileExists;
894 ExpandFilePathFunction ExpandFilePath;
895 ReadWholeFileFunction ReadWholeFile;
896 WriteWholeFileFunction WriteWholeFile;
901#ifndef TINYGLTF_NO_FS
904bool FileExists(
const std::string &abs_filename,
void *);
906std::string ExpandFilePath(
const std::string &filepath,
void *);
908bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
909 const std::string &filepath,
void *);
911bool WriteWholeFile(std::string *err,
const std::string &filepath,
912 const std::vector<unsigned char> &contents,
void *);
918#pragma clang diagnostic push
919#pragma clang diagnostic ignored "-Wc++98-compat"
922 TinyGLTF() : bin_data_(
nullptr), bin_size_(0), is_binary_(
false) {}
925#pragma clang diagnostic pop
936 const std::string &filename,
937 unsigned int check_sections = REQUIRE_ALL);
946 const char *str,
const unsigned int length,
947 const std::string &base_dir,
948 unsigned int check_sections = REQUIRE_ALL);
956 const std::string &filename,
957 unsigned int check_sections = REQUIRE_ALL);
966 const unsigned char *
bytes,
967 const unsigned int length,
968 const std::string &base_dir =
"",
969 unsigned int check_sections = REQUIRE_ALL);
975 bool embedImages,
bool embedBuffers,
976 bool prettyPrint,
bool writeBinary);
1000 bool LoadFromString(
Model *model, std::string *err, std::string *warn,
1001 const char *str,
const unsigned int length,
1002 const std::string &base_dir,
unsigned int check_sections);
1004 const unsigned char *bin_data_;
1009#ifndef TINYGLTF_NO_FS
1010 &tinygltf::FileExists, &tinygltf::ExpandFilePath,
1011 &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile,
1015 nullptr,
nullptr,
nullptr,
nullptr,
1021 LoadImageDataFunction LoadImageData =
1022#ifndef TINYGLTF_NO_STB_IMAGE
1023 &tinygltf::LoadImageData;
1027 void *load_image_user_data_ =
reinterpret_cast<void *
>(&fs);
1029 WriteImageDataFunction WriteImageData =
1030#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1031 &tinygltf::WriteImageData;
1035 void *write_image_user_data_ =
reinterpret_cast<void *
>(&fs);
1039#pragma clang diagnostic pop
1046#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
1049#ifndef TINYGLTF_NO_FS
1056#pragma clang diagnostic push
1057#pragma clang diagnostic ignored "-Wfloat-equal"
1058#pragma clang diagnostic ignored "-Wexit-time-destructors"
1059#pragma clang diagnostic ignored "-Wconversion"
1060#pragma clang diagnostic ignored "-Wold-style-cast"
1061#pragma clang diagnostic ignored "-Wglobal-constructors"
1062#pragma clang diagnostic ignored "-Wreserved-id-macro"
1063#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1064#pragma clang diagnostic ignored "-Wpadded"
1065#pragma clang diagnostic ignored "-Wc++98-compat"
1066#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
1067#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1068#pragma clang diagnostic ignored "-Wswitch-enum"
1069#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
1070#pragma clang diagnostic ignored "-Wweak-vtables"
1071#pragma clang diagnostic ignored "-Wcovered-switch-default"
1072#if __has_warning("-Wdouble-promotion")
1073#pragma clang diagnostic ignored "-Wdouble-promotion"
1075#if __has_warning("-Wcomma")
1076#pragma clang diagnostic ignored "-Wcomma"
1078#if __has_warning("-Wzero-as-null-pointer-constant")
1079#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1081#if __has_warning("-Wcast-qual")
1082#pragma clang diagnostic ignored "-Wcast-qual"
1084#if __has_warning("-Wmissing-variable-declarations")
1085#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1087#if __has_warning("-Wmissing-prototypes")
1088#pragma clang diagnostic ignored "-Wmissing-prototypes"
1090#if __has_warning("-Wcast-align")
1091#pragma clang diagnostic ignored "-Wcast-align"
1093#if __has_warning("-Wnewline-eof")
1094#pragma clang diagnostic ignored "-Wnewline-eof"
1096#if __has_warning("-Wunused-parameter")
1097#pragma clang diagnostic ignored "-Wunused-parameter"
1099#if __has_warning("-Wmismatched-tags")
1100#pragma clang diagnostic ignored "-Wmismatched-tags"
1106#pragma GCC diagnostic push
1107#pragma GCC diagnostic ignored "-Wtype-limits"
1110#ifndef TINYGLTF_NO_INCLUDE_JSON
1114#ifdef TINYGLTF_ENABLE_DRACO
1115#include "draco/compression/decode.h"
1116#include "draco/core/decoder_buffer.h"
1119#ifndef TINYGLTF_NO_STB_IMAGE
1120#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
1121#include "stb_image.h"
1125#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1126#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
1127#include "stb_image_write.h"
1132#pragma clang diagnostic pop
1136#pragma GCC diagnostic pop
1145#define TINYGLTF_INTERNAL_NOMINMAX
1149#ifndef WIN32_LEAN_AND_MEAN
1150#define WIN32_LEAN_AND_MEAN
1151#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1155#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1156#undef WIN32_LEAN_AND_MEAN
1159#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1163#elif !defined(__ANDROID__)
1167#if defined(__sparcv9)
1170#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1171#define TINYGLTF_LITTLE_ENDIAN 1
1178#include "TargetConditionals.h"
1182#pragma clang diagnostic push
1183#pragma clang diagnostic ignored "-Wc++98-compat"
1190 if (one.Type() != other.Type())
return false;
1192 switch (one.Type()) {
1196 return one.Get<
bool>() == other.Get<
bool>();
1198 return TINYGLTF_DOUBLE_EQUAL(one.Get<
double>(), other.Get<
double>());
1200 return one.Get<
int>() == other.Get<
int>();
1202 auto oneObj = one.Get<tinygltf::Value::Object>();
1203 auto otherObj = other.Get<tinygltf::Value::Object>();
1204 if (oneObj.size() != otherObj.size())
return false;
1205 for (
auto &it : oneObj) {
1206 auto otherIt = otherObj.find(it.first);
1207 if (otherIt == otherObj.end())
return false;
1209 if (!Equals(it.second, otherIt->second))
return false;
1214 if (one.Size() != other.Size())
return false;
1215 for (
int i = 0; i < int(one.Size()); ++i)
1216 if (!Equals(one.Get(i), other.Get(i)))
return false;
1220 return one.Get<std::string>() == other.Get<std::string>();
1222 return one.Get<std::vector<unsigned char> >() ==
1223 other.Get<std::vector<unsigned char> >();
1232static bool Equals(
const std::vector<double> &one,
1233 const std::vector<double> &other) {
1234 if (one.size() != other.size())
return false;
1235 for (
int i = 0; i < int(one.size()); ++i) {
1236 if (!TINYGLTF_DOUBLE_EQUAL(one[
size_t(i)], other[
size_t(i)]))
return false;
1241bool Accessor::operator==(
const Accessor &other)
const {
1242 return this->bufferView == other.bufferView &&
1243 this->byteOffset == other.byteOffset &&
1244 this->componentType == other.componentType &&
1245 this->count == other.count && this->extras == other.extras &&
1246 Equals(this->maxValues, other.maxValues) &&
1247 Equals(this->minValues, other.minValues) && this->name == other.name &&
1248 this->normalized == other.normalized && this->type == other.type;
1250bool Animation::operator==(
const Animation &other)
const {
1251 return this->channels == other.channels && this->extras == other.extras &&
1252 this->name == other.name && this->samplers == other.samplers;
1254bool AnimationChannel::operator==(
const AnimationChannel &other)
const {
1255 return this->extras == other.extras &&
1256 this->target_node == other.target_node &&
1257 this->target_path == other.target_path &&
1258 this->sampler == other.sampler;
1260bool AnimationSampler::operator==(
const AnimationSampler &other)
const {
1261 return this->extras == other.extras && this->input == other.input &&
1262 this->interpolation == other.interpolation &&
1263 this->output == other.output;
1265bool Asset::operator==(
const Asset &other)
const {
1266 return this->copyright == other.copyright &&
1267 this->extensions == other.extensions && this->extras == other.extras &&
1268 this->generator == other.generator &&
1269 this->minVersion == other.minVersion && this->version == other.version;
1271bool Buffer::operator==(
const Buffer &other)
const {
1272 return this->data == other.data && this->extras == other.extras &&
1273 this->name == other.name && this->uri == other.uri;
1275bool BufferView::operator==(
const BufferView &other)
const {
1276 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1277 this->byteOffset == other.byteOffset &&
1278 this->byteStride == other.byteStride && this->name == other.name &&
1279 this->target == other.target && this->extras == other.extras &&
1280 this->dracoDecoded == other.dracoDecoded;
1282bool Camera::operator==(
const Camera &other)
const {
1283 return this->name == other.name && this->extensions == other.extensions &&
1284 this->extras == other.extras &&
1285 this->orthographic == other.orthographic &&
1286 this->perspective == other.perspective && this->type == other.type;
1288bool Image::operator==(
const Image &other)
const {
1289 return this->bufferView == other.bufferView &&
1290 this->component == other.component && this->extras == other.extras &&
1291 this->height == other.height && this->image == other.image &&
1292 this->mimeType == other.mimeType && this->name == other.name &&
1293 this->uri == other.uri && this->width == other.width;
1295bool Light::operator==(
const Light &other)
const {
1296 return Equals(this->color, other.color) && this->name == other.name &&
1297 this->type == other.type;
1299bool Material::operator==(
const Material &other)
const {
1300 return this->additionalValues == other.additionalValues &&
1301 this->extensions == other.extensions && this->extras == other.extras &&
1302 this->name == other.name && this->values == other.values;
1304bool Mesh::operator==(
const Mesh &other)
const {
1305 return this->extensions == other.extensions && this->extras == other.extras &&
1306 this->name == other.name && this->primitives == other.primitives &&
1307 this->targets == other.targets && Equals(this->weights, other.weights);
1309bool Model::operator==(
const Model &other)
const {
1310 return this->accessors == other.accessors &&
1311 this->animations == other.animations && this->asset == other.asset &&
1312 this->buffers == other.buffers &&
1313 this->bufferViews == other.bufferViews &&
1314 this->cameras == other.cameras &&
1315 this->defaultScene == other.defaultScene &&
1316 this->extensions == other.extensions &&
1317 this->extensionsRequired == other.extensionsRequired &&
1318 this->extensionsUsed == other.extensionsUsed &&
1319 this->extras == other.extras && this->images == other.images &&
1320 this->lights == other.lights && this->materials == other.materials &&
1321 this->meshes == other.meshes && this->nodes == other.nodes &&
1322 this->samplers == other.samplers && this->scenes == other.scenes &&
1323 this->skins == other.skins && this->textures == other.textures;
1325bool Node::operator==(
const Node &other)
const {
1326 return this->camera == other.camera && this->children == other.children &&
1327 this->extensions == other.extensions && this->extras == other.extras &&
1328 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
1329 this->name == other.name && Equals(this->rotation, other.rotation) &&
1330 Equals(this->scale, other.scale) && this->skin == other.skin &&
1331 Equals(this->translation, other.translation) &&
1332 Equals(this->weights, other.weights);
1334bool OrthographicCamera::operator==(
const OrthographicCamera &other)
const {
1335 return this->extensions == other.extensions && this->extras == other.extras &&
1336 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
1337 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
1338 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1339 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
1341bool Parameter::operator==(
const Parameter &other)
const {
1342 if (this->bool_value != other.bool_value ||
1343 this->has_number_value != other.has_number_value)
1346 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
1349 if (this->json_double_value.size() != other.json_double_value.size())
1351 for (
auto &it : this->json_double_value) {
1352 auto otherIt = other.json_double_value.find(it.first);
1353 if (otherIt == other.json_double_value.end())
return false;
1355 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second))
return false;
1358 if (!Equals(this->number_array, other.number_array))
return false;
1360 if (this->string_value != other.string_value)
return false;
1364bool PerspectiveCamera::operator==(
const PerspectiveCamera &other)
const {
1365 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
1366 this->extensions == other.extensions && this->extras == other.extras &&
1367 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
1368 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1369 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
1371bool Primitive::operator==(
const Primitive &other)
const {
1372 return this->attributes == other.attributes && this->extras == other.extras &&
1373 this->indices == other.indices && this->material == other.material &&
1374 this->mode == other.mode && this->targets == other.targets;
1376bool Sampler::operator==(
const Sampler &other)
const {
1377 return this->extras == other.extras && this->magFilter == other.magFilter &&
1378 this->minFilter == other.minFilter && this->name == other.name &&
1379 this->wrapR == other.wrapR && this->wrapS == other.wrapS &&
1380 this->wrapT == other.wrapT;
1382bool Scene::operator==(
const Scene &other)
const {
1383 return this->extensions == other.extensions && this->extras == other.extras &&
1384 this->name == other.name && this->nodes == other.nodes;
1387bool Skin::operator==(
const Skin &other)
const {
1388 return this->inverseBindMatrices == other.inverseBindMatrices &&
1389 this->joints == other.joints && this->name == other.name &&
1390 this->skeleton == other.skeleton;
1392bool Texture::operator==(
const Texture &other)
const {
1393 return this->extensions == other.extensions && this->extras == other.extras &&
1394 this->name == other.name && this->sampler == other.sampler &&
1395 this->source == other.source;
1397bool Value::operator==(
const Value &other)
const {
1398 return Equals(*
this, other);
1401static void swap4(
unsigned int *val) {
1402#ifdef TINYGLTF_LITTLE_ENDIAN
1405 unsigned int tmp = *val;
1406 unsigned char *dst =
reinterpret_cast<unsigned char *
>(val);
1407 unsigned char *src =
reinterpret_cast<unsigned char *
>(&tmp);
1416static std::string JoinPath(
const std::string &path0,
1417 const std::string &path1) {
1418 if (path0.empty()) {
1422 char lastChar = *path0.rbegin();
1423 if (lastChar !=
'/') {
1424 return path0 + std::string(
"/") + path1;
1426 return path0 + path1;
1431static std::string FindFile(
const std::vector<std::string> &paths,
1432 const std::string &filepath, FsCallbacks *fs) {
1433 if (fs ==
nullptr || fs->ExpandFilePath ==
nullptr ||
1434 fs->FileExists ==
nullptr) {
1436 return std::string();
1439 for (
size_t i = 0; i < paths.size(); i++) {
1440 std::string absPath =
1441 fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data);
1442 if (fs->FileExists(absPath, fs->user_data)) {
1447 return std::string();
1450static std::string GetFilePathExtension(
const std::string &FileName) {
1451 if (FileName.find_last_of(
".") != std::string::npos)
1452 return FileName.substr(FileName.find_last_of(
".") + 1);
1456static std::string GetBaseDir(
const std::string &filepath) {
1457 if (filepath.find_last_of(
"/\\") != std::string::npos)
1458 return filepath.substr(0, filepath.find_last_of(
"/\\"));
1463static std::string GetBaseFilename(
const std::string &filepath) {
1464 return filepath.substr(filepath.find_last_of(
"/\\") + 1);
1467std::string base64_encode(
unsigned char const *,
unsigned int len);
1468std::string base64_decode(std::string
const &s);
1498#pragma clang diagnostic push
1499#pragma clang diagnostic ignored "-Wexit-time-destructors"
1500#pragma clang diagnostic ignored "-Wglobal-constructors"
1501#pragma clang diagnostic ignored "-Wsign-conversion"
1502#pragma clang diagnostic ignored "-Wconversion"
1504static const std::string base64_chars =
1505 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1506 "abcdefghijklmnopqrstuvwxyz"
1509static inline bool is_base64(
unsigned char c) {
1510 return (isalnum(c) || (c ==
'+') || (c ==
'/'));
1513std::string base64_encode(
unsigned char const *bytes_to_encode,
1514 unsigned int in_len) {
1518 unsigned char char_array_3[3];
1519 unsigned char char_array_4[4];
1522 char_array_3[i++] = *(bytes_to_encode++);
1524 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
1526 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
1528 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
1529 char_array_4[3] = char_array_3[2] & 0x3f;
1531 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
1537 for (j = i; j < 3; j++) char_array_3[j] =
'\0';
1539 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
1541 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
1543 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
1545 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
1547 while ((i++ < 3)) ret +=
'=';
1553std::string base64_decode(std::string
const &encoded_string) {
1554 int in_len =
static_cast<int>(encoded_string.size());
1558 unsigned char char_array_4[4], char_array_3[3];
1561 while (in_len-- && (encoded_string[in_] !=
'=') &&
1562 is_base64(encoded_string[in_])) {
1563 char_array_4[i++] = encoded_string[in_];
1566 for (i = 0; i < 4; i++)
1568 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
1571 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
1573 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
1574 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
1576 for (i = 0; (i < 3); i++) ret += char_array_3[i];
1582 for (j = i; j < 4; j++) char_array_4[j] = 0;
1584 for (j = 0; j < 4; j++)
1586 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
1588 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
1590 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
1591 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
1593 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
1599#pragma clang diagnostic pop
1602static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
1603 std::string *warn,
const std::string &filename,
1604 const std::string &basedir,
bool required,
1605 size_t reqBytes,
bool checkSize, FsCallbacks *fs) {
1606 if (fs ==
nullptr || fs->FileExists ==
nullptr ||
1607 fs->ExpandFilePath ==
nullptr || fs->ReadWholeFile ==
nullptr) {
1610 (*err) +=
"FS callback[s] not set\n";
1615 std::string *failMsgOut = required ? err : warn;
1619 std::vector<std::string> paths;
1620 paths.push_back(basedir);
1621 paths.push_back(
".");
1623 std::string filepath = FindFile(paths, filename, fs);
1624 if (filepath.empty() || filename.empty()) {
1626 (*failMsgOut) +=
"File not found : " + filename +
"\n";
1631 std::vector<unsigned char> buf;
1632 std::string fileReadErr;
1634 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
1638 "File read error : " + filepath +
" : " + fileReadErr +
"\n";
1643 size_t sz = buf.size();
1646 (*failMsgOut) +=
"File is empty : " + filepath +
"\n";
1652 if (reqBytes == sz) {
1656 std::stringstream ss;
1657 ss <<
"File size mismatch : " << filepath <<
", requestedBytes "
1658 << reqBytes <<
", but got " << sz << std::endl;
1660 (*failMsgOut) += ss.str();
1671 LoadImageData = func;
1672 load_image_user_data_ = user_data;
1675#ifndef TINYGLTF_NO_STB_IMAGE
1676bool LoadImageData(Image *image,
const int image_idx, std::string *err,
1677 std::string *warn,
int req_width,
int req_height,
1678 const unsigned char *
bytes,
int size,
void *user_data) {
1682 int w, h, comp, req_comp;
1684 unsigned char *data =
nullptr;
1690 int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
1698 if (stbi_is_16_bit_from_memory(
bytes, size)) {
1699 data = (
unsigned char *)stbi_load_16_from_memory(
bytes, size, &w, &h, &comp,
1703 pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
1716 if (!data) data = stbi_load_from_memory(
bytes, size, &w, &h, &comp, req_comp);
1721 "Unknown image format. STB cannot decode image data for image[" +
1722 std::to_string(image_idx) +
"] name = \"" + image->name +
"\".\n";
1727 if (w < 1 || h < 1) {
1728 stbi_image_free(data);
1730 (*err) +=
"Invalid image data for image[" + std::to_string(image_idx) +
1731 "] name = \"" + image->name +
"\"\n";
1736 if (req_width > 0) {
1737 if (req_width != w) {
1738 stbi_image_free(data);
1740 (*err) +=
"Image width mismatch for image[" +
1741 std::to_string(image_idx) +
"] name = \"" + image->name +
1748 if (req_height > 0) {
1749 if (req_height != h) {
1750 stbi_image_free(data);
1752 (*err) +=
"Image height mismatch. for image[" +
1753 std::to_string(image_idx) +
"] name = \"" + image->name +
1762 image->component = req_comp;
1764 image->pixel_type = pixel_type;
1765 image->image.resize(
static_cast<size_t>(w * h * req_comp) * (bits / 8));
1766 std::copy(data, data + w * h * req_comp * (bits / 8), image->image.begin());
1767 stbi_image_free(data);
1774 WriteImageData = func;
1775 write_image_user_data_ = user_data;
1778#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1779static void WriteToMemory_stbi(
void *
context,
void *data,
int size) {
1780 std::vector<unsigned char> *buffer =
1781 reinterpret_cast<std::vector<unsigned char> *
>(
context);
1783 unsigned char *pData =
reinterpret_cast<unsigned char *
>(data);
1785 buffer->insert(buffer->end(), pData, pData + size);
1788bool WriteImageData(
const std::string *basepath,
const std::string *filename,
1789 Image *image,
bool embedImages,
void *fsPtr) {
1790 const std::string ext = GetFilePathExtension(*filename);
1794 std::vector<unsigned char> data;
1797 if ((image->bits != 8) ||
1798 (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
1803 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
1804 image->height, image->component,
1805 &image->image[0], 0)) {
1808 header =
"data:image/png;base64,";
1809 }
else if (ext ==
"jpg") {
1810 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
1811 image->height, image->component,
1812 &image->image[0], 100)) {
1815 header =
"data:image/jpeg;base64,";
1816 }
else if (ext ==
"bmp") {
1817 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
1818 image->height, image->component,
1819 &image->image[0])) {
1822 header =
"data:image/bmp;base64,";
1823 }
else if (!embedImages) {
1833 base64_encode(&data[0],
static_cast<unsigned int>(data.size()));
1839 FsCallbacks *fs =
reinterpret_cast<FsCallbacks *
>(fsPtr);
1840 if ((fs !=
nullptr) && (fs->WriteWholeFile !=
nullptr)) {
1841 const std::string imagefilepath = JoinPath(*basepath, *filename);
1842 std::string writeError;
1843 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
1851 image->uri = *filename;
1860#ifndef TINYGLTF_NO_FS
1863bool FileExists(
const std::string &abs_filename,
void *) {
1865#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
1866 if (asset_manager) {
1867 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
1868 AASSET_MODE_STREAMING);
1872 AAsset_close(asset);
1880 errno_t err = fopen_s(&fp, abs_filename.c_str(),
"rb");
1885 FILE *fp = fopen(abs_filename.c_str(),
"rb");
1898std::string ExpandFilePath(
const std::string &filepath,
void *) {
1900 DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
1901 char *str =
new char[len];
1902 ExpandEnvironmentStringsA(filepath.c_str(), str, len);
1911#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
1912 defined(__ANDROID__) || defined(__EMSCRIPTEN__)
1914 std::string s = filepath;
1919 if (filepath.empty()) {
1924 int ret = wordexp(filepath.c_str(), &p, 0);
1933 s = std::string(p.we_wordv[0]);
1945bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
1946 const std::string &filepath,
void *) {
1947#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
1948 if (asset_manager) {
1949 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
1950 AASSET_MODE_STREAMING);
1953 (*err) +=
"File open error : " + filepath +
"\n";
1957 size_t size = AAsset_getLength(asset);
1960 (*err) +=
"Invalid file size : " + filepath +
1961 " (does the path point to a directory?)";
1965 AAsset_read(asset,
reinterpret_cast<char *
>(&out->at(0)), size);
1966 AAsset_close(asset);
1970 (*err) +=
"No asset manager specified : " + filepath +
"\n";
1975 std::ifstream f(filepath.c_str(), std::ifstream::binary);
1978 (*err) +=
"File open error : " + filepath +
"\n";
1984 size_t sz =
static_cast<size_t>(f.tellg());
1989 (*err) +=
"Invalid file size : " + filepath +
1990 " (does the path point to a directory?)";
1993 }
else if (sz == 0) {
1995 (*err) +=
"File is empty : " + filepath +
"\n";
2001 f.read(
reinterpret_cast<char *
>(&out->at(0)),
2002 static_cast<std::streamsize
>(sz));
2009bool WriteWholeFile(std::string *err,
const std::string &filepath,
2010 const std::vector<unsigned char> &contents,
void *) {
2011 std::ofstream f(filepath.c_str(), std::ofstream::binary);
2014 (*err) +=
"File open error for writing : " + filepath +
"\n";
2019 f.write(
reinterpret_cast<const char *
>(&contents.at(0)),
2020 static_cast<std::streamsize
>(contents.size()));
2023 (*err) +=
"File write error: " + filepath +
"\n";
2034static std::string MimeToExt(
const std::string &mimeType) {
2035 if (mimeType ==
"image/jpeg") {
2037 }
else if (mimeType ==
"image/png") {
2039 }
else if (mimeType ==
"image/bmp") {
2041 }
else if (mimeType ==
"image/gif") {
2048static void UpdateImageObject(Image &image, std::string &baseDir,
int index,
2050 WriteImageDataFunction *WriteImageData =
nullptr,
2051 void *user_data =
nullptr) {
2052 std::string filename;
2056 if (image.uri.size()) {
2057 filename = GetBaseFilename(image.uri);
2058 ext = GetFilePathExtension(filename);
2060 }
else if (image.name.size()) {
2061 ext = MimeToExt(image.mimeType);
2063 filename = image.name +
"." + ext;
2065 ext = MimeToExt(image.mimeType);
2067 filename = std::to_string(index) +
"." + ext;
2071 if (*WriteImageData !=
nullptr) {
2073 (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
2077bool IsDataURI(
const std::string &in) {
2078 std::string header =
"data:application/octet-stream;base64,";
2079 if (in.find(header) == 0) {
2083 header =
"data:image/jpeg;base64,";
2084 if (in.find(header) == 0) {
2088 header =
"data:image/png;base64,";
2089 if (in.find(header) == 0) {
2093 header =
"data:image/bmp;base64,";
2094 if (in.find(header) == 0) {
2098 header =
"data:image/gif;base64,";
2099 if (in.find(header) == 0) {
2103 header =
"data:text/plain;base64,";
2104 if (in.find(header) == 0) {
2108 header =
"data:application/gltf-buffer;base64,";
2109 if (in.find(header) == 0) {
2116bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
2117 const std::string &in,
size_t reqBytes,
bool checkSize) {
2118 std::string header =
"data:application/octet-stream;base64,";
2120 if (in.find(header) == 0) {
2121 data = base64_decode(in.substr(header.size()));
2125 header =
"data:image/jpeg;base64,";
2126 if (in.find(header) == 0) {
2127 mime_type =
"image/jpeg";
2128 data = base64_decode(in.substr(header.size()));
2133 header =
"data:image/png;base64,";
2134 if (in.find(header) == 0) {
2135 mime_type =
"image/png";
2136 data = base64_decode(in.substr(header.size()));
2141 header =
"data:image/bmp;base64,";
2142 if (in.find(header) == 0) {
2143 mime_type =
"image/bmp";
2144 data = base64_decode(in.substr(header.size()));
2149 header =
"data:image/gif;base64,";
2150 if (in.find(header) == 0) {
2151 mime_type =
"image/gif";
2152 data = base64_decode(in.substr(header.size()));
2157 header =
"data:text/plain;base64,";
2158 if (in.find(header) == 0) {
2159 mime_type =
"text/plain";
2160 data = base64_decode(in.substr(header.size()));
2165 header =
"data:application/gltf-buffer;base64,";
2166 if (in.find(header) == 0) {
2167 data = base64_decode(in.substr(header.size()));
2176 if (data.size() != reqBytes) {
2179 out->resize(reqBytes);
2181 out->resize(data.size());
2183 std::copy(data.begin(), data.end(), out->begin());
2187static bool ParseJsonAsValue(Value *ret,
const json &o) {
2190 case json::value_t::object: {
2191 Value::Object value_object;
2192 for (
auto it = o.begin(); it != o.end(); it++) {
2194 ParseJsonAsValue(&entry, it.value());
2195 if (entry.Type() != NULL_TYPE) value_object[it.key()] = entry;
2197 if (value_object.size() > 0) val = Value(value_object);
2199 case json::value_t::array: {
2200 Value::Array value_array;
2201 for (
auto it = o.begin(); it != o.end(); it++) {
2203 ParseJsonAsValue(&entry, it.value());
2204 if (entry.Type() != NULL_TYPE) value_array.push_back(entry);
2206 if (value_array.size() > 0) val = Value(value_array);
2208 case json::value_t::string:
2209 val = Value(o.get<std::string>());
2211 case json::value_t::boolean:
2212 val = Value(o.get<
bool>());
2214 case json::value_t::number_integer:
2215 case json::value_t::number_unsigned:
2216 val = Value(
static_cast<int>(o.get<int64_t>()));
2218 case json::value_t::number_float:
2219 val = Value(o.get<
double>());
2221 case json::value_t::null:
2222 case json::value_t::discarded:
2226 if (ret) *ret = val;
2228 return val.Type() != NULL_TYPE;
2231static bool ParseExtrasProperty(Value *ret,
const json &o) {
2232 json::const_iterator it = o.find(
"extras");
2233 if (it == o.end()) {
2237 return ParseJsonAsValue(ret, it.value());
2240static bool ParseBooleanProperty(
bool *ret, std::string *err,
const json &o,
2241 const std::string &property,
2242 const bool required,
2243 const std::string &parent_node =
"") {
2244 json::const_iterator it = o.find(property);
2245 if (it == o.end()) {
2248 (*err) +=
"'" +
property +
"' property is missing";
2249 if (!parent_node.empty()) {
2250 (*err) +=
" in " + parent_node;
2258 if (!it.value().is_boolean()) {
2261 (*err) +=
"'" +
property +
"' property is not a bool type.\n";
2268 (*ret) = it.value().get<
bool>();
2274static bool ParseIntegerProperty(
int *ret, std::string *err,
const json &o,
2275 const std::string &property,
2276 const bool required,
2277 const std::string &parent_node =
"") {
2278 json::const_iterator it = o.find(property);
2279 if (it == o.end()) {
2282 (*err) +=
"'" +
property +
"' property is missing";
2283 if (!parent_node.empty()) {
2284 (*err) +=
" in " + parent_node;
2292 if (!it.value().is_number_integer()) {
2295 (*err) +=
"'" +
property +
"' property is not an integer type.\n";
2302 (*ret) = it.value().get<
int>();
2308static bool ParseUnsignedProperty(
size_t *ret, std::string *err,
const json &o,
2309 const std::string &property,
2310 const bool required,
2311 const std::string &parent_node =
"") {
2312 json::const_iterator it = o.find(property);
2313 if (it == o.end()) {
2316 (*err) +=
"'" +
property +
"' property is missing";
2317 if (!parent_node.empty()) {
2318 (*err) +=
" in " + parent_node;
2326 if (!it.value().is_number_unsigned()) {
2329 (*err) +=
"'" +
property +
"' property is not a positive integer.\n";
2336 (*ret) = it.value().get<
size_t>();
2342static bool ParseNumberProperty(
double *ret, std::string *err,
const json &o,
2343 const std::string &property,
2344 const bool required,
2345 const std::string &parent_node =
"") {
2346 json::const_iterator it = o.find(property);
2347 if (it == o.end()) {
2350 (*err) +=
"'" +
property +
"' property is missing";
2351 if (!parent_node.empty()) {
2352 (*err) +=
" in " + parent_node;
2360 if (!it.value().is_number()) {
2363 (*err) +=
"'" +
property +
"' property is not a number type.\n";
2370 (*ret) = it.value().get<
double>();
2376static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
2377 const json &o,
const std::string &property,
2379 const std::string &parent_node =
"") {
2380 json::const_iterator it = o.find(property);
2381 if (it == o.end()) {
2384 (*err) +=
"'" +
property +
"' property is missing";
2385 if (!parent_node.empty()) {
2386 (*err) +=
" in " + parent_node;
2394 if (!it.value().is_array()) {
2397 (*err) +=
"'" +
property +
"' property is not an array";
2398 if (!parent_node.empty()) {
2399 (*err) +=
" in " + parent_node;
2408 for (json::const_iterator i = it.value().begin(); i != it.value().end();
2410 if (!i.value().is_number()) {
2413 (*err) +=
"'" +
property +
"' property is not a number.\n";
2414 if (!parent_node.empty()) {
2415 (*err) +=
" in " + parent_node;
2422 ret->push_back(i.value());
2428static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
2430 const std::string &property,
2432 const std::string &parent_node =
"") {
2433 json::const_iterator it = o.find(property);
2434 if (it == o.end()) {
2437 (*err) +=
"'" +
property +
"' property is missing";
2438 if (!parent_node.empty()) {
2439 (*err) +=
" in " + parent_node;
2447 if (!it.value().is_array()) {
2450 (*err) +=
"'" +
property +
"' property is not an array";
2451 if (!parent_node.empty()) {
2452 (*err) +=
" in " + parent_node;
2461 for (json::const_iterator i = it.value().begin(); i != it.value().end();
2463 if (!i.value().is_number_integer()) {
2466 (*err) +=
"'" +
property +
"' property is not an integer type.\n";
2467 if (!parent_node.empty()) {
2468 (*err) +=
" in " + parent_node;
2475 ret->push_back(i.value());
2481static bool ParseStringProperty(
2482 std::string *ret, std::string *err,
const json &o,
2483 const std::string &property,
bool required,
2484 const std::string &parent_node = std::string()) {
2485 json::const_iterator it = o.find(property);
2486 if (it == o.end()) {
2489 (*err) +=
"'" +
property +
"' property is missing";
2490 if (parent_node.empty()) {
2493 (*err) +=
" in `" + parent_node +
"'.\n";
2500 if (!it.value().is_string()) {
2503 (*err) +=
"'" +
property +
"' property is not a string type.\n";
2510 (*ret) = it.value().get<std::string>();
2516static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
2517 std::string *err,
const json &o,
2518 const std::string &property,
2520 const std::string &parent =
"") {
2521 json::const_iterator it = o.find(property);
2522 if (it == o.end()) {
2525 if (!parent.empty()) {
2527 "'" +
property +
"' property is missing in " + parent +
".\n";
2529 (*err) +=
"'" +
property +
"' property is missing.\n";
2537 if (!it.value().is_object()) {
2540 (*err) +=
"'" +
property +
"' property is not an object.\n";
2547 const json &dict = it.value();
2549 json::const_iterator dictIt(dict.begin());
2550 json::const_iterator dictItEnd(dict.end());
2552 for (; dictIt != dictItEnd; ++dictIt) {
2553 if (!dictIt.value().is_number_integer()) {
2556 (*err) +=
"'" +
property +
"' value is not an integer type.\n";
2563 (*ret)[dictIt.key()] = dictIt.value();
2568static bool ParseJSONProperty(std::map<std::string, double> *ret,
2569 std::string *err,
const json &o,
2570 const std::string &property,
bool required) {
2571 json::const_iterator it = o.find(property);
2572 if (it == o.end()) {
2575 (*err) +=
"'" +
property +
"' property is missing. \n'";
2581 if (!it.value().is_object()) {
2584 (*err) +=
"'" +
property +
"' property is not a JSON object.\n";
2591 const json &obj = it.value();
2592 json::const_iterator it2(obj.begin());
2593 json::const_iterator itEnd(obj.end());
2594 for (; it2 != itEnd; it2++) {
2595 if (it2.value().is_number())
2596 ret->insert(std::pair<std::string, double>(it2.key(), it2.value()));
2602static bool ParseParameterProperty(Parameter *param, std::string *err,
2603 const json &o,
const std::string &prop,
2610 if (ParseStringProperty(¶m->string_value, err, o, prop,
false)) {
2613 }
else if (ParseNumberArrayProperty(¶m->number_array, err, o, prop,
2617 }
else if (ParseNumberProperty(¶m->number_value, err, o, prop,
false)) {
2618 return param->has_number_value =
true;
2619 }
else if (ParseJSONProperty(¶m->json_double_value, err, o, prop,
2622 }
else if (ParseBooleanProperty(¶m->bool_value, err, o, prop,
false)) {
2627 (*err) +=
"parameter must be a string or number / number array.\n";
2634static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
2638 json::const_iterator it = o.find(
"extensions");
2639 if (it == o.end()) {
2642 if (!it.value().is_object()) {
2645 ExtensionMap extensions;
2646 json::const_iterator extIt = it.value().begin();
2647 for (; extIt != it.value().end(); extIt++) {
2648 if (!extIt.value().is_object())
continue;
2649 if (!ParseJsonAsValue(&extensions[extIt.key()], extIt.value())) {
2650 if (!extIt.key().empty()) {
2653 extensions[extIt.key()] = Value{Value::Object{}};
2658 (*ret) = extensions;
2663static bool ParseAsset(Asset *asset, std::string *err,
const json &o) {
2664 ParseStringProperty(&asset->version, err, o,
"version",
true,
"Asset");
2665 ParseStringProperty(&asset->generator, err, o,
"generator",
false,
"Asset");
2666 ParseStringProperty(&asset->minVersion, err, o,
"minVersion",
false,
"Asset");
2668 ParseExtensionsProperty(&asset->extensions, err, o);
2671 ParseExtrasProperty(&(asset->extras), o);
2676static bool ParseImage(Image *image,
const int image_idx, std::string *err,
2677 std::string *warn,
const json &o,
2678 const std::string &basedir, FsCallbacks *fs,
2679 LoadImageDataFunction *LoadImageData =
nullptr,
2680 void *load_image_user_data =
nullptr) {
2685 bool hasBufferView = (o.find(
"bufferView") != o.end());
2686 bool hasURI = (o.find(
"uri") != o.end());
2688 ParseStringProperty(&image->name, err, o,
"name",
false);
2690 if (hasBufferView && hasURI) {
2694 "Only one of `bufferView` or `uri` should be defined, but both are "
2695 "defined for image[" +
2696 std::to_string(image_idx) +
"] name = \"" + image->name +
"\"\n";
2701 if (!hasBufferView && !hasURI) {
2703 (*err) +=
"Neither required `bufferView` nor `uri` defined for image[" +
2704 std::to_string(image_idx) +
"] name = \"" + image->name +
2710 ParseExtensionsProperty(&image->extensions, err, o);
2711 ParseExtrasProperty(&image->extras, o);
2713 if (hasBufferView) {
2714 int bufferView = -1;
2715 if (!ParseIntegerProperty(&bufferView, err, o,
"bufferView",
true)) {
2717 (*err) +=
"Failed to parse `bufferView` for image[" +
2718 std::to_string(image_idx) +
"] name = \"" + image->name +
2724 std::string mime_type;
2725 ParseStringProperty(&mime_type, err, o,
"mimeType",
false);
2728 ParseIntegerProperty(&width, err, o,
"width",
false);
2731 ParseIntegerProperty(&height, err, o,
"height",
false);
2735 image->bufferView = bufferView;
2736 image->mimeType = mime_type;
2737 image->width = width;
2738 image->height = height;
2746 std::string tmp_err;
2747 if (!ParseStringProperty(&uri, &tmp_err, o,
"uri",
true)) {
2749 (*err) +=
"Failed to parse `uri` for image[" + std::to_string(image_idx) +
2750 "] name = \"" + image->name +
"\".\n";
2755 std::vector<unsigned char> img;
2757 if (IsDataURI(uri)) {
2758 if (!DecodeDataURI(&img, image->mimeType, uri, 0,
false)) {
2760 (*err) +=
"Failed to decode 'uri' for image[" +
2761 std::to_string(image_idx) +
"] name = [" + image->name +
2770#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
2773 if (!LoadExternalFile(&img, err, warn, uri, basedir,
false, 0,
false, fs)) {
2775 (*warn) +=
"Failed to load external 'uri' for image[" +
2776 std::to_string(image_idx) +
"] name = [" + image->name +
2785 (*warn) +=
"Image data is empty for image[" +
2786 std::to_string(image_idx) +
"] name = [" + image->name +
2793 if (*LoadImageData ==
nullptr) {
2795 (*err) +=
"No LoadImageData callback specified.\n";
2799 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
2800 static_cast<int>(img.size()), load_image_user_data);
2803static bool ParseTexture(Texture *texture, std::string *err,
const json &o,
2804 const std::string &basedir) {
2808 ParseIntegerProperty(&sampler, err, o,
"sampler",
false);
2810 ParseIntegerProperty(&source, err, o,
"source",
false);
2812 texture->sampler = sampler;
2813 texture->source = source;
2815 ParseExtensionsProperty(&texture->extensions, err, o);
2816 ParseExtrasProperty(&texture->extras, o);
2818 ParseStringProperty(&texture->name, err, o,
"name",
false);
2823static bool ParseBuffer(Buffer *buffer, std::string *err,
const json &o,
2824 FsCallbacks *fs,
const std::string &basedir,
2825 bool is_binary =
false,
2826 const unsigned char *bin_data =
nullptr,
2827 size_t bin_size = 0) {
2829 if (!ParseUnsignedProperty(&byteLength, err, o,
"byteLength",
true,
2835 buffer->uri.clear();
2836 ParseStringProperty(&buffer->uri, err, o,
"uri",
false,
"Buffer");
2839 if (!is_binary && buffer->uri.empty()) {
2841 (*err) +=
"'uri' is missing from non binary glTF file buffer.\n";
2845 json::const_iterator type = o.find(
"type");
2846 if (type != o.end()) {
2847 if (type.value().is_string()) {
2848 const std::string &ty = type.value();
2849 if (ty.compare(
"arraybuffer") == 0) {
2857 if (!buffer->uri.empty()) {
2859 if (IsDataURI(buffer->uri)) {
2860 std::string mime_type;
2861 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
2865 "Failed to decode 'uri' : " + buffer->uri +
" in Buffer\n";
2871 if (!LoadExternalFile(&buffer->data, err,
nullptr,
2872 buffer->uri, basedir,
true, byteLength,
true,
2880 if ((bin_size == 0) || (bin_data ==
nullptr)) {
2882 (*err) +=
"Invalid binary data in `Buffer'.\n";
2887 if (byteLength > bin_size) {
2889 std::stringstream ss;
2890 ss <<
"Invalid `byteLength'. Must be equal or less than binary size: "
2892 << byteLength <<
", binary size = " << bin_size << std::endl;
2899 buffer->data.resize(
static_cast<size_t>(byteLength));
2900 memcpy(&(buffer->data.at(0)), bin_data,
static_cast<size_t>(byteLength));
2904 if (IsDataURI(buffer->uri)) {
2905 std::string mime_type;
2906 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
2909 (*err) +=
"Failed to decode 'uri' : " + buffer->uri +
" in Buffer\n";
2915 if (!LoadExternalFile(&buffer->data, err,
nullptr, buffer->uri,
2916 basedir,
true, byteLength,
true, fs)) {
2922 ParseStringProperty(&buffer->name, err, o,
"name",
false);
2927static bool ParseBufferView(BufferView *bufferView, std::string *err,
2930 if (!ParseIntegerProperty(&buffer, err, o,
"buffer",
true,
"BufferView")) {
2934 size_t byteOffset = 0;
2935 ParseUnsignedProperty(&byteOffset, err, o,
"byteOffset",
false);
2937 size_t byteLength = 1;
2938 if (!ParseUnsignedProperty(&byteLength, err, o,
"byteLength",
true,
2943 size_t byteStride = 0;
2944 if (!ParseUnsignedProperty(&byteStride, err, o,
"byteStride",
false)) {
2953 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
2955 std::stringstream ss;
2956 ss <<
"Invalid `byteStride' value. `byteStride' must be the multiple of "
2958 << byteStride << std::endl;
2966 ParseIntegerProperty(&target, err, o,
"target",
false);
2967 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
2968 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
2973 bufferView->target = target;
2975 ParseStringProperty(&bufferView->name, err, o,
"name",
false);
2977 bufferView->buffer = buffer;
2978 bufferView->byteOffset = byteOffset;
2979 bufferView->byteLength = byteLength;
2980 bufferView->byteStride = byteStride;
2984static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
2986 accessor->sparse.isSparse =
true;
2989 ParseIntegerProperty(&count, err, o,
"count",
true);
2991 const auto indices_iterator = o.find(
"indices");
2992 const auto values_iterator = o.find(
"values");
2993 if (indices_iterator == o.end()) {
2994 (*err) =
"the sparse object of this accessor doesn't have indices";
2998 if (values_iterator == o.end()) {
2999 (*err) =
"the sparse object ob ths accessor doesn't have values";
3003 const json &indices_obj = *indices_iterator;
3004 const json &values_obj = *values_iterator;
3006 int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
3007 ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
"bufferView",
3009 ParseIntegerProperty(&indices_byte_offset, err, indices_obj,
"byteOffset",
3011 ParseIntegerProperty(&component_type, err, indices_obj,
"componentType",
3014 int values_buffer_view = 0, values_byte_offset = 0;
3015 ParseIntegerProperty(&values_buffer_view, err, values_obj,
"bufferView",
3017 ParseIntegerProperty(&values_byte_offset, err, values_obj,
"byteOffset",
3020 accessor->sparse.count = count;
3021 accessor->sparse.indices.bufferView = indices_buffer_view;
3022 accessor->sparse.indices.byteOffset = indices_byte_offset;
3023 accessor->sparse.indices.componentType = component_type;
3024 accessor->sparse.values.bufferView = values_buffer_view;
3025 accessor->sparse.values.byteOffset = values_byte_offset;
3032static bool ParseAccessor(Accessor *accessor, std::string *err,
const json &o) {
3033 int bufferView = -1;
3034 ParseIntegerProperty(&bufferView, err, o,
"bufferView",
false,
"Accessor");
3036 size_t byteOffset = 0;
3037 ParseUnsignedProperty(&byteOffset, err, o,
"byteOffset",
false,
"Accessor");
3039 bool normalized =
false;
3040 ParseBooleanProperty(&normalized, err, o,
"normalized",
false,
"Accessor");
3042 size_t componentType = 0;
3043 if (!ParseUnsignedProperty(&componentType, err, o,
"componentType",
true,
3049 if (!ParseUnsignedProperty(&count, err, o,
"count",
true,
"Accessor")) {
3054 if (!ParseStringProperty(&type, err, o,
"type",
true,
"Accessor")) {
3058 if (type.compare(
"SCALAR") == 0) {
3059 accessor->type = TINYGLTF_TYPE_SCALAR;
3060 }
else if (type.compare(
"VEC2") == 0) {
3061 accessor->type = TINYGLTF_TYPE_VEC2;
3062 }
else if (type.compare(
"VEC3") == 0) {
3063 accessor->type = TINYGLTF_TYPE_VEC3;
3064 }
else if (type.compare(
"VEC4") == 0) {
3065 accessor->type = TINYGLTF_TYPE_VEC4;
3066 }
else if (type.compare(
"MAT2") == 0) {
3067 accessor->type = TINYGLTF_TYPE_MAT2;
3068 }
else if (type.compare(
"MAT3") == 0) {
3069 accessor->type = TINYGLTF_TYPE_MAT3;
3070 }
else if (type.compare(
"MAT4") == 0) {
3071 accessor->type = TINYGLTF_TYPE_MAT4;
3073 std::stringstream ss;
3074 ss <<
"Unsupported `type` for accessor object. Got \"" << type <<
"\"\n";
3081 ParseStringProperty(&accessor->name, err, o,
"name",
false);
3083 accessor->minValues.clear();
3084 accessor->maxValues.clear();
3085 ParseNumberArrayProperty(&accessor->minValues, err, o,
"min",
false,
3088 ParseNumberArrayProperty(&accessor->maxValues, err, o,
"max",
false,
3091 accessor->count = count;
3092 accessor->bufferView = bufferView;
3093 accessor->byteOffset = byteOffset;
3094 accessor->normalized = normalized;
3096 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
3097 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
3099 accessor->componentType = componentType;
3101 std::stringstream ss;
3102 ss <<
"Invalid `componentType` in accessor. Got " << componentType
3111 ParseExtrasProperty(&(accessor->extras), o);
3114 const auto iterator = o.find(
"sparse");
3115 if (iterator != o.end()) {
3117 return ParseSparseAccessor(accessor, err, *iterator);
3123#ifdef TINYGLTF_ENABLE_DRACO
3125static void DecodeIndexBuffer(draco::Mesh *mesh,
size_t componentSize,
3126 std::vector<uint8_t> &outBuffer) {
3127 if (componentSize == 4) {
3128 assert(
sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
3129 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
3132 size_t faceStride = componentSize * 3;
3133 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
3134 const draco::Mesh::Face &face = mesh->face(f);
3135 if (componentSize == 2) {
3139 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
3145 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
3152template <
typename T>
3153static bool GetAttributeForAllPoints(draco::Mesh *mesh,
3154 const draco::PointAttribute *pAttribute,
3155 std::vector<uint8_t> &outBuffer) {
3156 size_t byteOffset = 0;
3157 T values[4] = {0, 0, 0, 0};
3158 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
3159 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
3160 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
3164 memcpy(outBuffer.data() + byteOffset, &values[0],
3165 sizeof(T) * pAttribute->num_components());
3166 byteOffset +=
sizeof(T) * pAttribute->num_components();
3172static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
3173 const draco::PointAttribute *pAttribute,
3174 std::vector<uint8_t> &outBuffer) {
3175 bool decodeResult =
false;
3176 switch (componentType) {
3177 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
3179 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
3181 case TINYGLTF_COMPONENT_TYPE_BYTE:
3183 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
3185 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
3187 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
3189 case TINYGLTF_COMPONENT_TYPE_SHORT:
3191 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
3193 case TINYGLTF_COMPONENT_TYPE_INT:
3195 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
3197 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
3199 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
3201 case TINYGLTF_COMPONENT_TYPE_FLOAT:
3203 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
3205 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
3207 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
3213 return decodeResult;
3216static bool ParseDracoExtension(Primitive *primitive, Model *model,
3218 const Value &dracoExtensionValue) {
3219 auto bufferViewValue = dracoExtensionValue.Get(
"bufferView");
3220 if (!bufferViewValue.IsInt())
return false;
3221 auto attributesValue = dracoExtensionValue.Get(
"attributes");
3222 if (!attributesValue.IsObject())
return false;
3224 auto attributesObject = attributesValue.Get<Value::Object>();
3225 int bufferView = bufferViewValue.Get<
int>();
3227 BufferView &view = model->bufferViews[bufferView];
3228 Buffer &buffer = model->buffers[view.buffer];
3230 if (view.dracoDecoded)
return true;
3231 view.dracoDecoded =
true;
3233 const char *bufferViewData =
3234 reinterpret_cast<const char *
>(buffer.data.data() + view.byteOffset);
3235 size_t bufferViewSize = view.byteLength;
3238 draco::DecoderBuffer decoderBuffer;
3239 decoderBuffer.Init(bufferViewData, bufferViewSize);
3240 draco::Decoder decoder;
3241 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
3242 if (!decodeResult.ok()) {
3245 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
3248 if (primitive->indices >= 0) {
3249 int32_t componentSize = GetComponentSizeInBytes(
3250 model->accessors[primitive->indices].componentType);
3251 Buffer decodedIndexBuffer;
3252 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
3254 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
3256 model->buffers.emplace_back(std::move(decodedIndexBuffer));
3258 BufferView decodedIndexBufferView;
3259 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
3260 decodedIndexBufferView.byteLength =
3261 int(mesh->num_faces() * 3 * componentSize);
3262 decodedIndexBufferView.byteOffset = 0;
3263 decodedIndexBufferView.byteStride = 0;
3264 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
3265 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
3267 model->accessors[primitive->indices].bufferView =
3268 int(model->bufferViews.size() - 1);
3269 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
3272 for (
const auto &attribute : attributesObject) {
3273 if (!attribute.second.IsInt())
return false;
3274 auto primitiveAttribute = primitive->attributes.find(attribute.first);
3275 if (primitiveAttribute == primitive->attributes.end())
return false;
3277 int dracoAttributeIndex = attribute.second.Get<
int>();
3278 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
3279 const auto pBuffer = pAttribute->buffer();
3280 const auto componentType =
3281 model->accessors[primitiveAttribute->second].componentType;
3284 Buffer decodedBuffer;
3285 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
3286 GetComponentSizeInBytes(componentType);
3287 decodedBuffer.data.resize(bufferSize);
3289 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
3290 decodedBuffer.data))
3293 model->buffers.emplace_back(std::move(decodedBuffer));
3295 BufferView decodedBufferView;
3296 decodedBufferView.buffer = int(model->buffers.size() - 1);
3297 decodedBufferView.byteLength = bufferSize;
3298 decodedBufferView.byteOffset = pAttribute->byte_offset();
3299 decodedBufferView.byteStride = pAttribute->byte_stride();
3300 decodedBufferView.target = primitive->indices >= 0
3301 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
3302 : TINYGLTF_TARGET_ARRAY_BUFFER;
3303 model->bufferViews.emplace_back(std::move(decodedBufferView));
3305 model->accessors[primitiveAttribute->second].bufferView =
3306 int(model->bufferViews.size() - 1);
3307 model->accessors[primitiveAttribute->second].count =
3308 int(mesh->num_points());
3315static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
3318 ParseIntegerProperty(&material, err, o,
"material",
false);
3319 primitive->material = material;
3321 int mode = TINYGLTF_MODE_TRIANGLES;
3322 ParseIntegerProperty(&mode, err, o,
"mode",
false);
3323 primitive->mode = mode;
3326 ParseIntegerProperty(&indices, err, o,
"indices",
false);
3327 primitive->indices = indices;
3328 if (!ParseStringIntegerProperty(&primitive->attributes, err, o,
"attributes",
3329 true,
"Primitive")) {
3334 json::const_iterator targetsObject = o.find(
"targets");
3335 if ((targetsObject != o.end()) && targetsObject.value().is_array()) {
3336 for (json::const_iterator i = targetsObject.value().begin();
3337 i != targetsObject.value().end(); i++) {
3338 std::map<std::string, int> targetAttribues;
3340 const json &dict = i.value();
3341 json::const_iterator dictIt(dict.begin());
3342 json::const_iterator dictItEnd(dict.end());
3344 for (; dictIt != dictItEnd; ++dictIt) {
3345 targetAttribues[dictIt.key()] =
static_cast<int>(dictIt.value());
3347 primitive->targets.push_back(targetAttribues);
3351 ParseExtrasProperty(&(primitive->extras), o);
3353 ParseExtensionsProperty(&primitive->extensions, err, o);
3355#ifdef TINYGLTF_ENABLE_DRACO
3356 auto dracoExtension =
3357 primitive->extensions.find(
"KHR_draco_mesh_compression");
3358 if (dracoExtension != primitive->extensions.end()) {
3359 ParseDracoExtension(primitive, model, err, dracoExtension->second);
3368static bool ParseMesh(Mesh *mesh, Model *model, std::string *err,
3370 ParseStringProperty(&mesh->name, err, o,
"name",
false);
3372 mesh->primitives.clear();
3373 json::const_iterator primObject = o.find(
"primitives");
3374 if ((primObject != o.end()) && primObject.value().is_array()) {
3375 for (json::const_iterator i = primObject.value().begin();
3376 i != primObject.value().end(); i++) {
3377 Primitive primitive;
3378 if (ParsePrimitive(&primitive, model, err, i.value())) {
3380 mesh->primitives.push_back(primitive);
3386 json::const_iterator targetsObject = o.find(
"targets");
3387 if ((targetsObject != o.end()) && targetsObject.value().is_array()) {
3388 for (json::const_iterator i = targetsObject.value().begin();
3389 i != targetsObject.value().end(); i++) {
3390 std::map<std::string, int> targetAttribues;
3392 const json &dict = i.value();
3393 json::const_iterator dictIt(dict.begin());
3394 json::const_iterator dictItEnd(dict.end());
3396 for (; dictIt != dictItEnd; ++dictIt) {
3397 targetAttribues[dictIt.key()] =
static_cast<int>(dictIt.value());
3399 mesh->targets.push_back(targetAttribues);
3404 ParseNumberArrayProperty(&mesh->weights, err, o,
"weights",
false);
3406 ParseExtensionsProperty(&mesh->extensions, err, o);
3407 ParseExtrasProperty(&(mesh->extras), o);
3412static bool ParseLight(Light *light, std::string *err,
const json &o) {
3413 ParseStringProperty(&light->name, err, o,
"name",
false);
3414 ParseNumberArrayProperty(&light->color, err, o,
"color",
false);
3415 ParseStringProperty(&light->type, err, o,
"type",
false);
3419static bool ParseNode(Node *node, std::string *err,
const json &o) {
3420 ParseStringProperty(&node->name, err, o,
"name",
false);
3423 ParseIntegerProperty(&skin, err, o,
"skin",
false);
3427 if (!ParseNumberArrayProperty(&node->matrix, err, o,
"matrix",
false)) {
3428 ParseNumberArrayProperty(&node->rotation, err, o,
"rotation",
false);
3429 ParseNumberArrayProperty(&node->scale, err, o,
"scale",
false);
3430 ParseNumberArrayProperty(&node->translation, err, o,
"translation",
false);
3434 ParseIntegerProperty(&camera, err, o,
"camera",
false);
3435 node->camera = camera;
3438 ParseIntegerProperty(&mesh, err, o,
"mesh",
false);
3441 node->children.clear();
3442 ParseIntegerArrayProperty(&node->children, err, o,
"children",
false);
3444 ParseExtensionsProperty(&node->extensions, err, o);
3445 ParseExtrasProperty(&(node->extras), o);
3450static bool ParseMaterial(Material *material, std::string *err,
const json &o) {
3451 material->values.clear();
3452 material->extensions.clear();
3453 material->additionalValues.clear();
3455 json::const_iterator it(o.begin());
3456 json::const_iterator itEnd(o.end());
3458 for (; it != itEnd; it++) {
3459 if (it.key() ==
"pbrMetallicRoughness") {
3460 if (it.value().is_object()) {
3461 const json &values_object = it.value();
3463 json::const_iterator itVal(values_object.begin());
3464 json::const_iterator itValEnd(values_object.end());
3466 for (; itVal != itValEnd; itVal++) {
3468 if (ParseParameterProperty(¶m, err, values_object, itVal.key(),
3470 material->values[itVal.key()] = param;
3474 }
else if (it.key() ==
"extensions" || it.key() ==
"extras") {
3479 if (ParseParameterProperty(¶m, err, o, it.key(),
false)) {
3480 material->additionalValues[it.key()] = param;
3485 ParseExtensionsProperty(&material->extensions, err, o);
3486 ParseExtrasProperty(&(material->extras), o);
3491static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err,
3493 int samplerIndex = -1;
3494 int targetIndex = -1;
3495 if (!ParseIntegerProperty(&samplerIndex, err, o,
"sampler",
true,
3496 "AnimationChannel")) {
3498 (*err) +=
"`sampler` field is missing in animation channels\n";
3503 json::const_iterator targetIt = o.find(
"target");
3504 if ((targetIt != o.end()) && targetIt.value().is_object()) {
3505 const json &target_object = targetIt.value();
3507 if (!ParseIntegerProperty(&targetIndex, err, target_object,
"node",
true)) {
3509 (*err) +=
"`node` field is missing in animation.channels.target\n";
3514 if (!ParseStringProperty(&channel->target_path, err, target_object,
"path",
3517 (*err) +=
"`path` field is missing in animation.channels.target\n";
3523 channel->sampler = samplerIndex;
3524 channel->target_node = targetIndex;
3526 ParseExtrasProperty(&(channel->extras), o);
3531static bool ParseAnimation(Animation *animation, std::string *err,
3534 json::const_iterator channelsIt = o.find(
"channels");
3535 if ((channelsIt != o.end()) && channelsIt.value().is_array()) {
3536 for (json::const_iterator i = channelsIt.value().begin();
3537 i != channelsIt.value().end(); i++) {
3538 AnimationChannel channel;
3539 if (ParseAnimationChannel(&channel, err, i.value())) {
3541 animation->channels.push_back(channel);
3548 json::const_iterator samplerIt = o.find(
"samplers");
3549 if ((samplerIt != o.end()) && samplerIt.value().is_array()) {
3550 const json &sampler_array = samplerIt.value();
3552 json::const_iterator it = sampler_array.begin();
3553 json::const_iterator itEnd = sampler_array.end();
3555 for (; it != itEnd; it++) {
3558 AnimationSampler sampler;
3559 int inputIndex = -1;
3560 int outputIndex = -1;
3561 if (!ParseIntegerProperty(&inputIndex, err, s,
"input",
true)) {
3563 (*err) +=
"`input` field is missing in animation.sampler\n";
3567 ParseStringProperty(&sampler.interpolation, err, s,
"interpolation",
3569 if (!ParseIntegerProperty(&outputIndex, err, s,
"output",
true)) {
3571 (*err) +=
"`output` field is missing in animation.sampler\n";
3575 sampler.input = inputIndex;
3576 sampler.output = outputIndex;
3577 ParseExtrasProperty(&(sampler.extras), s);
3578 animation->samplers.push_back(sampler);
3583 ParseStringProperty(&animation->name, err, o,
"name",
false);
3585 ParseExtrasProperty(&(animation->extras), o);
3590static bool ParseSampler(Sampler *sampler, std::string *err,
const json &o) {
3591 ParseStringProperty(&sampler->name, err, o,
"name",
false);
3593 int minFilter = TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR;
3594 int magFilter = TINYGLTF_TEXTURE_FILTER_LINEAR;
3595 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
3596 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
3597 ParseIntegerProperty(&minFilter, err, o,
"minFilter",
false);
3598 ParseIntegerProperty(&magFilter, err, o,
"magFilter",
false);
3599 ParseIntegerProperty(&wrapS, err, o,
"wrapS",
false);
3600 ParseIntegerProperty(&wrapT, err, o,
"wrapT",
false);
3602 sampler->minFilter = minFilter;
3603 sampler->magFilter = magFilter;
3604 sampler->wrapS = wrapS;
3605 sampler->wrapT = wrapT;
3607 ParseExtrasProperty(&(sampler->extras), o);
3612static bool ParseSkin(Skin *skin, std::string *err,
const json &o) {
3613 ParseStringProperty(&skin->name, err, o,
"name",
false,
"Skin");
3615 std::vector<int> joints;
3616 if (!ParseIntegerArrayProperty(&joints, err, o,
"joints",
false,
"Skin")) {
3619 skin->joints = std::move(joints);
3622 ParseIntegerProperty(&skeleton, err, o,
"skeleton",
false,
"Skin");
3623 skin->skeleton = skeleton;
3626 ParseIntegerProperty(&invBind, err, o,
"inverseBindMatrices",
true,
"Skin");
3627 skin->inverseBindMatrices = invBind;
3632static bool ParsePerspectiveCamera(PerspectiveCamera *camera, std::string *err,
3635 if (!ParseNumberProperty(&yfov, err, o,
"yfov",
true,
"OrthographicCamera")) {
3640 if (!ParseNumberProperty(&znear, err, o,
"znear",
true,
3641 "PerspectiveCamera")) {
3645 double aspectRatio = 0.0;
3646 ParseNumberProperty(&aspectRatio, err, o,
"aspectRatio",
false,
3647 "PerspectiveCamera");
3650 ParseNumberProperty(&zfar, err, o,
"zfar",
false,
"PerspectiveCamera");
3652 camera->aspectRatio = aspectRatio;
3653 camera->zfar = zfar;
3654 camera->yfov = yfov;
3655 camera->znear = znear;
3657 ParseExtensionsProperty(&camera->extensions, err, o);
3658 ParseExtrasProperty(&(camera->extras), o);
3665static bool ParseOrthographicCamera(OrthographicCamera *camera,
3666 std::string *err,
const json &o) {
3668 if (!ParseNumberProperty(&xmag, err, o,
"xmag",
true,
"OrthographicCamera")) {
3673 if (!ParseNumberProperty(&ymag, err, o,
"ymag",
true,
"OrthographicCamera")) {
3678 if (!ParseNumberProperty(&zfar, err, o,
"zfar",
true,
"OrthographicCamera")) {
3683 if (!ParseNumberProperty(&znear, err, o,
"znear",
true,
3684 "OrthographicCamera")) {
3688 ParseExtensionsProperty(&camera->extensions, err, o);
3689 ParseExtrasProperty(&(camera->extras), o);
3691 camera->xmag = xmag;
3692 camera->ymag = ymag;
3693 camera->zfar = zfar;
3694 camera->znear = znear;
3701static bool ParseCamera(
Camera *camera, std::string *err,
const json &o) {
3702 if (!ParseStringProperty(&camera->type, err, o,
"type",
true,
"Camera")) {
3706 if (camera->type.compare(
"orthographic") == 0) {
3707 if (o.find(
"orthographic") == o.end()) {
3709 std::stringstream ss;
3710 ss <<
"Orhographic camera description not found." << std::endl;
3716 const json &v = o.find(
"orthographic").value();
3717 if (!v.is_object()) {
3719 std::stringstream ss;
3720 ss <<
"\"orthographic\" is not a JSON object." << std::endl;
3726 if (!ParseOrthographicCamera(&camera->orthographic, err, v.get<json>())) {
3729 }
else if (camera->type.compare(
"perspective") == 0) {
3730 if (o.find(
"perspective") == o.end()) {
3732 std::stringstream ss;
3733 ss <<
"Perspective camera description not found." << std::endl;
3739 const json &v = o.find(
"perspective").value();
3740 if (!v.is_object()) {
3742 std::stringstream ss;
3743 ss <<
"\"perspective\" is not a JSON object." << std::endl;
3749 if (!ParsePerspectiveCamera(&camera->perspective, err, v.get<json>())) {
3754 std::stringstream ss;
3755 ss <<
"Invalid camera type: \"" << camera->type
3756 <<
"\". Must be \"perspective\" or \"orthographic\"" << std::endl;
3762 ParseStringProperty(&camera->name, err, o,
"name",
false);
3764 ParseExtensionsProperty(&camera->extensions, err, o);
3765 ParseExtrasProperty(&(camera->extras), o);
3770bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
3771 const char *str,
unsigned int length,
3772 const std::string &base_dir,
3773 unsigned int check_sections) {
3776 (*err) =
"JSON string too short.\n";
3783#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
3784 defined(_CPPUNWIND)) && \
3785 not defined(TINYGLTF_NOEXCEPTION)
3787 v = json::parse(str, str + length);
3789 }
catch (
const std::exception &e) {
3797 v = json::parse(str, str + length,
nullptr,
false);
3799 if (!v.is_object()) {
3802 (*err) =
"Failed to parse JSON object\n";
3809 if (!v.is_object()) {
3812 (*err) =
"Root element is not a JSON object\n";
3821 json::const_iterator it = v.find(
"scenes");
3822 if ((it != v.end()) && it.value().is_array()) {
3824 }
else if (check_sections & REQUIRE_SCENES) {
3826 (*err) +=
"\"scenes\" object not found in .gltf or not an array type\n";
3833 json::const_iterator it = v.find(
"nodes");
3834 if ((it != v.end()) && it.value().is_array()) {
3836 }
else if (check_sections & REQUIRE_NODES) {
3838 (*err) +=
"\"nodes\" object not found in .gltf\n";
3845 json::const_iterator it = v.find(
"accessors");
3846 if ((it != v.end()) && it.value().is_array()) {
3848 }
else if (check_sections & REQUIRE_ACCESSORS) {
3850 (*err) +=
"\"accessors\" object not found in .gltf\n";
3857 json::const_iterator it = v.find(
"buffers");
3858 if ((it != v.end()) && it.value().is_array()) {
3860 }
else if (check_sections & REQUIRE_BUFFERS) {
3862 (*err) +=
"\"buffers\" object not found in .gltf\n";
3869 json::const_iterator it = v.find(
"bufferViews");
3870 if ((it != v.end()) && it.value().is_array()) {
3872 }
else if (check_sections & REQUIRE_BUFFER_VIEWS) {
3874 (*err) +=
"\"bufferViews\" object not found in .gltf\n";
3880 model->buffers.clear();
3881 model->bufferViews.clear();
3882 model->accessors.clear();
3883 model->meshes.clear();
3884 model->cameras.clear();
3885 model->nodes.clear();
3886 model->extensionsUsed.clear();
3887 model->extensionsRequired.clear();
3888 model->extensions.clear();
3889 model->defaultScene = -1;
3893 json::const_iterator it = v.find(
"asset");
3894 if ((it != v.end()) && it.value().is_object()) {
3895 const json &root = it.value();
3897 ParseAsset(&model->asset, err, root);
3903 json::const_iterator it = v.find(
"extensionsUsed");
3904 if ((it != v.end()) && it.value().is_array()) {
3905 const json &root = it.value();
3906 for (
unsigned int i = 0; i < root.size(); ++i) {
3907 model->extensionsUsed.push_back(root[i].get<std::string>());
3913 json::const_iterator it = v.find(
"extensionsRequired");
3914 if ((it != v.end()) && it.value().is_array()) {
3915 const json &root = it.value();
3916 for (
unsigned int i = 0; i < root.size(); ++i) {
3917 model->extensionsRequired.push_back(root[i].get<std::string>());
3924 json::const_iterator rootIt = v.find(
"buffers");
3925 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3926 const json &root = rootIt.value();
3928 json::const_iterator it(root.begin());
3929 json::const_iterator itEnd(root.end());
3930 for (; it != itEnd; it++) {
3931 if (!it.value().is_object()) {
3933 (*err) +=
"`buffers' does not contain an JSON object.";
3938 if (!ParseBuffer(&buffer, err, it->get<json>(), &fs, base_dir,
3939 is_binary_, bin_data_, bin_size_)) {
3943 model->buffers.push_back(buffer);
3950 json::const_iterator rootIt = v.find(
"bufferViews");
3951 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3952 const json &root = rootIt.value();
3954 json::const_iterator it(root.begin());
3955 json::const_iterator itEnd(root.end());
3956 for (; it != itEnd; it++) {
3957 if (!it.value().is_object()) {
3959 (*err) +=
"`bufferViews' does not contain an JSON object.";
3963 BufferView bufferView;
3964 if (!ParseBufferView(&bufferView, err, it->get<json>())) {
3968 model->bufferViews.push_back(bufferView);
3975 json::const_iterator rootIt = v.find(
"accessors");
3976 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3977 const json &root = rootIt.value();
3979 json::const_iterator it(root.begin());
3980 json::const_iterator itEnd(root.end());
3981 for (; it != itEnd; it++) {
3982 if (!it.value().is_object()) {
3984 (*err) +=
"`accessors' does not contain an JSON object.";
3989 if (!ParseAccessor(&accessor, err, it->get<json>())) {
3993 model->accessors.push_back(accessor);
4000 json::const_iterator rootIt = v.find(
"meshes");
4001 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4002 const json &root = rootIt.value();
4004 json::const_iterator it(root.begin());
4005 json::const_iterator itEnd(root.end());
4006 for (; it != itEnd; it++) {
4007 if (!it.value().is_object()) {
4009 (*err) +=
"`meshes' does not contain an JSON object.";
4014 if (!ParseMesh(&mesh, model, err, it->get<json>())) {
4018 model->meshes.push_back(mesh);
4026 for (
auto &mesh : model->meshes) {
4027 for (
auto &primitive : mesh.primitives) {
4028 if (primitive.indices >
4031 if (
size_t(primitive.indices) >= model->accessors.size()) {
4033 (*err) +=
"primitive indices accessor out of bounds";
4038 auto bufferView = model->accessors[primitive.indices].bufferView;
4039 if (bufferView < 0 ||
size_t(bufferView) >= model->bufferViews.size()) {
4041 (*err) +=
"accessor[" + std::to_string(primitive.indices) +
4042 "] invalid bufferView";
4047 model->bufferViews[bufferView].target =
4048 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
4056 for (
auto &bufferView : model->bufferViews) {
4057 if (bufferView.target == 0)
4059 bufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4065 json::const_iterator rootIt = v.find(
"nodes");
4066 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4067 const json &root = rootIt.value();
4069 json::const_iterator it(root.begin());
4070 json::const_iterator itEnd(root.end());
4071 for (; it != itEnd; it++) {
4072 if (!it.value().is_object()) {
4074 (*err) +=
"`nodes' does not contain an JSON object.";
4079 if (!ParseNode(&node, err, it->get<json>())) {
4083 model->nodes.push_back(node);
4090 json::const_iterator rootIt = v.find(
"scenes");
4091 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4092 const json &root = rootIt.value();
4094 json::const_iterator it(root.begin());
4095 json::const_iterator itEnd(root.end());
4096 for (; it != itEnd; it++) {
4097 if (!(it.value().is_object())) {
4099 (*err) +=
"`scenes' does not contain an JSON object.";
4104 std::vector<int> nodes;
4105 if (!ParseIntegerArrayProperty(&nodes, err, o,
"nodes",
false)) {
4110 scene.nodes = std::move(nodes);
4112 ParseStringProperty(&scene.name, err, o,
"name",
false);
4114 ParseExtensionsProperty(&scene.extensions, err, o);
4115 ParseExtrasProperty(&scene.extras, o);
4117 model->scenes.push_back(scene);
4124 json::const_iterator rootIt = v.find(
"scene");
4125 if ((rootIt != v.end()) && rootIt.value().is_number_integer()) {
4126 const int defaultScene = rootIt.value();
4128 model->defaultScene = defaultScene;
4134 json::const_iterator rootIt = v.find(
"materials");
4135 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4136 const json &root = rootIt.value();
4138 json::const_iterator it(root.begin());
4139 json::const_iterator itEnd(root.end());
4140 for (; it != itEnd; it++) {
4141 if (!it.value().is_object()) {
4143 (*err) +=
"`materials' does not contain an JSON object.";
4147 json jsonMaterial = it->get<
json>();
4150 ParseStringProperty(&material.name, err, jsonMaterial,
"name",
false);
4152 if (!ParseMaterial(&material, err, jsonMaterial)) {
4156 model->materials.push_back(material);
4163 json::const_iterator rootIt = v.find(
"images");
4164 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4165 const json &root = rootIt.value();
4167 json::const_iterator it(root.begin());
4168 json::const_iterator itEnd(root.end());
4170 for (; it != itEnd; it++, idx++) {
4171 if (!it.value().is_object()) {
4174 "image[" + std::to_string(idx) +
"] is not a JSON object.";
4179 if (!ParseImage(&image, idx, err, warn, it.value(), base_dir, &fs,
4180 &this->LoadImageData, load_image_user_data_)) {
4184 if (image.bufferView != -1) {
4186 if (
size_t(image.bufferView) >= model->bufferViews.size()) {
4188 std::stringstream ss;
4189 ss <<
"image[" << idx <<
"] bufferView \"" << image.bufferView
4190 <<
"\" not found in the scene." << std::endl;
4196 const BufferView &bufferView =
4197 model->bufferViews[size_t(image.bufferView)];
4198 if (
size_t(bufferView.buffer) >= model->buffers.size()) {
4200 std::stringstream ss;
4201 ss <<
"image[" << idx <<
"] buffer \"" << bufferView.buffer
4202 <<
"\" not found in the scene." << std::endl;
4207 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
4209 if (*LoadImageData ==
nullptr) {
4211 (*err) +=
"No LoadImageData callback specified.\n";
4215 bool ret = LoadImageData(
4216 &image, idx, err, warn, image.width, image.height,
4217 &buffer.data[bufferView.byteOffset],
4218 static_cast<int>(bufferView.byteLength), load_image_user_data_);
4224 model->images.push_back(image);
4231 json::const_iterator rootIt = v.find(
"textures");
4232 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4233 const json &root = rootIt.value();
4235 json::const_iterator it(root.begin());
4236 json::const_iterator itEnd(root.end());
4237 for (; it != itEnd; it++) {
4238 if (!it.value().is_object()) {
4240 (*err) +=
"`textures' does not contain an JSON object.";
4245 if (!ParseTexture(&texture, err, it->get<json>(), base_dir)) {
4249 model->textures.push_back(texture);
4256 json::const_iterator rootIt = v.find(
"animations");
4257 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4258 const json &root = rootIt.value();
4260 json::const_iterator it(root.begin());
4261 json::const_iterator itEnd(root.end());
4262 for (; it != itEnd; ++it) {
4263 if (!it.value().is_object()) {
4265 (*err) +=
"`animations' does not contain an JSON object.";
4269 Animation animation;
4270 if (!ParseAnimation(&animation, err, it->get<json>())) {
4274 model->animations.push_back(animation);
4281 json::const_iterator rootIt = v.find(
"skins");
4282 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4283 const json &root = rootIt.value();
4285 json::const_iterator it(root.begin());
4286 json::const_iterator itEnd(root.end());
4287 for (; it != itEnd; ++it) {
4288 if (!it.value().is_object()) {
4290 (*err) +=
"`skins' does not contain an JSON object.";
4295 if (!ParseSkin(&skin, err, it->get<json>())) {
4299 model->skins.push_back(skin);
4306 json::const_iterator rootIt = v.find(
"samplers");
4307 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4308 const json &root = rootIt.value();
4310 json::const_iterator it(root.begin());
4311 json::const_iterator itEnd(root.end());
4312 for (; it != itEnd; ++it) {
4313 if (!it.value().is_object()) {
4315 (*err) +=
"`samplers' does not contain an JSON object.";
4320 if (!ParseSampler(&sampler, err, it->get<json>())) {
4324 model->samplers.push_back(sampler);
4331 json::const_iterator rootIt = v.find(
"cameras");
4332 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4333 const json &root = rootIt.value();
4335 json::const_iterator it(root.begin());
4336 json::const_iterator itEnd(root.end());
4337 for (; it != itEnd; ++it) {
4338 if (!it.value().is_object()) {
4340 (*err) +=
"`cameras' does not contain an JSON object.";
4345 if (!ParseCamera(&camera, err, it->get<json>())) {
4349 model->cameras.push_back(camera);
4355 ParseExtensionsProperty(&model->extensions, err, v);
4359 json::const_iterator rootIt = v.find(
"extensions");
4360 if ((rootIt != v.end()) && rootIt.value().is_object()) {
4361 const json &root = rootIt.value();
4363 json::const_iterator it(root.begin());
4364 json::const_iterator itEnd(root.end());
4365 for (; it != itEnd; ++it) {
4367 if ((it.key().compare(
"KHR_lights_cmn") == 0) &&
4368 it.value().is_object()) {
4369 const json &
object = it.value();
4370 json::const_iterator itLight(
object.find(
"lights"));
4371 json::const_iterator itLightEnd(
object.end());
4372 if (itLight == itLightEnd) {
4376 if (!itLight.value().is_array()) {
4380 const json &lights = itLight.value();
4381 json::const_iterator arrayIt(lights.begin());
4382 json::const_iterator arrayItEnd(lights.end());
4383 for (; arrayIt != arrayItEnd; ++arrayIt) {
4385 if (!ParseLight(&light, err, arrayIt.value())) {
4388 model->lights.push_back(light);
4396 ParseExtrasProperty(&model->extras, v);
4402 std::string *warn,
const char *str,
4403 unsigned int length,
4404 const std::string &base_dir,
4405 unsigned int check_sections) {
4407 bin_data_ =
nullptr;
4410 return LoadFromString(model, err, warn, str, length, base_dir,
4415 std::string *warn,
const std::string &filename,
4416 unsigned int check_sections) {
4417 std::stringstream ss;
4419 if (fs.ReadWholeFile ==
nullptr) {
4421 ss <<
"Failed to read file: " << filename
4422 <<
": one or more FS callback not set" << std::endl;
4429 std::vector<unsigned char> data;
4430 std::string fileerr;
4431 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
4433 ss <<
"Failed to read file: " << filename <<
": " << fileerr << std::endl;
4440 size_t sz = data.size();
4443 (*err) =
"Empty file.";
4448 std::string basedir = GetBaseDir(filename);
4451 model, err, warn,
reinterpret_cast<const char *
>(&data.at(0)),
4452 static_cast<unsigned int>(data.size()), basedir, check_sections);
4459 const unsigned char *
bytes,
4461 const std::string &base_dir,
4462 unsigned int check_sections) {
4465 (*err) =
"Too short data size for glTF Binary.";
4475 (*err) =
"Invalid magic.";
4480 unsigned int version;
4481 unsigned int length;
4482 unsigned int model_length;
4483 unsigned int model_format;
4486 memcpy(&version,
bytes + 4, 4);
4488 memcpy(&length,
bytes + 8, 4);
4490 memcpy(&model_length,
bytes + 12, 4);
4491 swap4(&model_length);
4492 memcpy(&model_format,
bytes + 16, 4);
4493 swap4(&model_format);
4498 if ((20 + model_length > size) || (model_length < 1) || (length > size) ||
4499 (model_format != 0x4E4F534A)) {
4501 (*err) =
"Invalid glTF binary.";
4507 std::string jsonString(
reinterpret_cast<const char *
>(&
bytes[20]),
4511 bin_data_ =
bytes + 20 + model_length +
4514 length - (20 + model_length);
4516 bool ret = LoadFromString(model, err, warn,
4517 reinterpret_cast<const char *
>(&
bytes[20]),
4518 model_length, base_dir, check_sections);
4528 const std::string &filename,
4529 unsigned int check_sections) {
4530 std::stringstream ss;
4532 if (fs.ReadWholeFile ==
nullptr) {
4534 ss <<
"Failed to read file: " << filename
4535 <<
": one or more FS callback not set" << std::endl;
4542 std::vector<unsigned char> data;
4543 std::string fileerr;
4544 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
4546 ss <<
"Failed to read file: " << filename <<
": " << fileerr << std::endl;
4553 std::string basedir = GetBaseDir(filename);
4556 static_cast<unsigned int>(data.size()),
4557 basedir, check_sections);
4568template <
typename T>
4569static void SerializeNumberProperty(
const std::string &key, T number,
4577template <
typename T>
4578static void SerializeNumberArrayProperty(
const std::string &key,
4579 const std::vector<T> &value,
4584 for (
unsigned int i = 0; i < value.size(); ++i) {
4585 vals.push_back(
static_cast<T
>(value[i]));
4587 if (!vals.is_null()) {
4592static void SerializeStringProperty(
const std::string &key,
4593 const std::string &value, json &obj) {
4597static void SerializeStringArrayProperty(
const std::string &key,
4598 const std::vector<std::string> &value,
4603 for (
unsigned int i = 0; i < value.size(); ++i) {
4604 vals.push_back(value[i]);
4610static bool ValueToJson(
const Value &value, json *ret) {
4612 switch (value.Type()) {
4614 obj =
json(value.Get<
double>());
4617 obj =
json(value.Get<
int>());
4620 obj =
json(value.Get<
bool>());
4623 obj =
json(value.Get<std::string>());
4626 for (
unsigned int i = 0; i < value.ArrayLen(); ++i) {
4627 Value elementValue = value.Get(
int(i));
4629 if (ValueToJson(value.Get(
int(i)), &elementJson))
4630 obj.push_back(elementJson);
4640 Value::Object objMap = value.Get<Value::Object>();
4641 for (
auto &it : objMap) {
4643 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
4651 if (ret) *ret = obj;
4655static void SerializeValue(
const std::string &key,
const Value &value,
4658 if (ValueToJson(value, &ret)) obj[key] = ret;
4661static void SerializeGltfBufferData(
const std::vector<unsigned char> &data,
4663 std::string header =
"data:application/octet-stream;base64,";
4664 std::string encodedData =
4665 base64_encode(&data[0],
static_cast<unsigned int>(data.size()));
4666 SerializeStringProperty(
"uri", header + encodedData, o);
4669static bool SerializeGltfBufferData(
const std::vector<unsigned char> &data,
4670 const std::string &binFilename) {
4671 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
4672 if (!output.is_open())
return false;
4673 output.write(
reinterpret_cast<const char *
>(&data[0]),
4674 std::streamsize(data.size()));
4679static void SerializeParameterMap(ParameterMap ¶m, json &o) {
4680 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
4682 if (paramIt->second.number_array.size()) {
4683 SerializeNumberArrayProperty<double>(paramIt->first,
4684 paramIt->second.number_array, o);
4685 }
else if (paramIt->second.json_double_value.size()) {
4686 json json_double_value;
4687 for (std::map<std::string, double>::iterator it =
4688 paramIt->second.json_double_value.begin();
4689 it != paramIt->second.json_double_value.end(); ++it) {
4690 if (it->first ==
"index") {
4691 json_double_value[it->first] = paramIt->second.TextureIndex();
4693 json_double_value[it->first] = it->second;
4697 o[paramIt->first] = json_double_value;
4698 }
else if (!paramIt->second.string_value.empty()) {
4699 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
4700 }
else if (paramIt->second.has_number_value) {
4701 o[paramIt->first] = paramIt->second.number_value;
4703 o[paramIt->first] = paramIt->second.bool_value;
4708static void SerializeExtensionMap(ExtensionMap &extensions, json &o) {
4709 if (!extensions.size())
return;
4712 for (ExtensionMap::iterator extIt = extensions.begin();
4713 extIt != extensions.end(); ++extIt) {
4714 json extension_values;
4718 if (ValueToJson(extIt->second, &ret)) {
4719 extMap[extIt->first] = ret;
4721 if (ret.is_null()) {
4722 if (!(extIt->first.empty())) {
4725 extMap[extIt->first] =
json({});
4729 o[
"extensions"] = extMap;
4732static void SerializeGltfAccessor(Accessor &accessor, json &o) {
4733 SerializeNumberProperty<int>(
"bufferView", accessor.bufferView, o);
4735 if (accessor.byteOffset != 0.0)
4736 SerializeNumberProperty<int>(
"byteOffset",
int(accessor.byteOffset), o);
4738 SerializeNumberProperty<int>(
"componentType", accessor.componentType, o);
4739 SerializeNumberProperty<size_t>(
"count", accessor.count, o);
4740 SerializeNumberArrayProperty<double>(
"min", accessor.minValues, o);
4741 SerializeNumberArrayProperty<double>(
"max", accessor.maxValues, o);
4742 SerializeValue(
"normalized", Value(accessor.normalized), o);
4744 switch (accessor.type) {
4745 case TINYGLTF_TYPE_SCALAR:
4748 case TINYGLTF_TYPE_VEC2:
4751 case TINYGLTF_TYPE_VEC3:
4754 case TINYGLTF_TYPE_VEC4:
4757 case TINYGLTF_TYPE_MAT2:
4760 case TINYGLTF_TYPE_MAT3:
4763 case TINYGLTF_TYPE_MAT4:
4768 SerializeStringProperty(
"type", type, o);
4769 if (!accessor.name.empty()) SerializeStringProperty(
"name", accessor.name, o);
4771 if (accessor.extras.Type() != NULL_TYPE) {
4772 SerializeValue(
"extras", accessor.extras, o);
4776static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
4777 SerializeNumberProperty(
"sampler", channel.sampler, o);
4779 SerializeNumberProperty(
"node", channel.target_node, target);
4780 SerializeStringProperty(
"path", channel.target_path, target);
4782 o[
"target"] = target;
4784 if (channel.extras.Type() != NULL_TYPE) {
4785 SerializeValue(
"extras", channel.extras, o);
4789static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
4790 SerializeNumberProperty(
"input", sampler.input, o);
4791 SerializeNumberProperty(
"output", sampler.output, o);
4792 SerializeStringProperty(
"interpolation", sampler.interpolation, o);
4794 if (sampler.extras.Type() != NULL_TYPE) {
4795 SerializeValue(
"extras", sampler.extras, o);
4799static void SerializeGltfAnimation(Animation &animation, json &o) {
4800 if (!animation.name.empty())
4801 SerializeStringProperty(
"name", animation.name, o);
4803 for (
unsigned int i = 0; i < animation.channels.size(); ++i) {
4805 AnimationChannel gltfChannel = animation.channels[i];
4806 SerializeGltfAnimationChannel(gltfChannel, channel);
4807 channels.push_back(channel);
4809 o[
"channels"] = channels;
4812 for (
unsigned int i = 0; i < animation.samplers.size(); ++i) {
4814 AnimationSampler gltfSampler = animation.samplers[i];
4815 SerializeGltfAnimationSampler(gltfSampler, sampler);
4816 samplers.push_back(sampler);
4819 o[
"samplers"] = samplers;
4821 if (animation.extras.Type() != NULL_TYPE) {
4822 SerializeValue(
"extras", animation.extras, o);
4826static void SerializeGltfAsset(Asset &asset, json &o) {
4827 if (!asset.generator.empty()) {
4828 SerializeStringProperty(
"generator", asset.generator, o);
4831 if (!asset.version.empty()) {
4832 SerializeStringProperty(
"version", asset.version, o);
4835 if (asset.extras.Keys().size()) {
4836 SerializeValue(
"extras", asset.extras, o);
4839 SerializeExtensionMap(asset.extensions, o);
4842static void SerializeGltfBuffer(Buffer &buffer, json &o) {
4843 SerializeNumberProperty(
"byteLength", buffer.data.size(), o);
4844 SerializeGltfBufferData(buffer.data, o);
4846 if (buffer.name.size()) SerializeStringProperty(
"name", buffer.name, o);
4848 if (buffer.extras.Type() != NULL_TYPE) {
4849 SerializeValue(
"extras", buffer.extras, o);
4853static bool SerializeGltfBuffer(Buffer &buffer, json &o,
4854 const std::string &binFilename,
4855 const std::string &binBaseFilename) {
4856 if (!SerializeGltfBufferData(buffer.data, binFilename))
return false;
4857 SerializeNumberProperty(
"byteLength", buffer.data.size(), o);
4858 SerializeStringProperty(
"uri", binBaseFilename, o);
4860 if (buffer.name.size()) SerializeStringProperty(
"name", buffer.name, o);
4862 if (buffer.extras.Type() != NULL_TYPE) {
4863 SerializeValue(
"extras", buffer.extras, o);
4868static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
4869 SerializeNumberProperty(
"buffer", bufferView.buffer, o);
4870 SerializeNumberProperty<size_t>(
"byteLength", bufferView.byteLength, o);
4873 if (bufferView.byteStride >= 4) {
4874 SerializeNumberProperty<size_t>(
"byteStride", bufferView.byteStride, o);
4877 if (bufferView.byteOffset > 0) {
4878 SerializeNumberProperty<size_t>(
"byteOffset", bufferView.byteOffset, o);
4881 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
4882 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
4883 SerializeNumberProperty(
"target", bufferView.target, o);
4885 if (bufferView.name.size()) {
4886 SerializeStringProperty(
"name", bufferView.name, o);
4889 if (bufferView.extras.Type() != NULL_TYPE) {
4890 SerializeValue(
"extras", bufferView.extras, o);
4894static void SerializeGltfImage(Image &image, json &o) {
4895 SerializeStringProperty(
"uri", image.uri, o);
4897 if (image.name.size()) {
4898 SerializeStringProperty(
"name", image.name, o);
4901 if (image.extras.Type() != NULL_TYPE) {
4902 SerializeValue(
"extras", image.extras, o);
4905 SerializeExtensionMap(image.extensions, o);
4908static void SerializeGltfMaterial(Material &material, json &o) {
4909 if (material.extras.Size()) SerializeValue(
"extras", material.extras, o);
4910 SerializeExtensionMap(material.extensions, o);
4912 if (material.values.size()) {
4913 json pbrMetallicRoughness;
4914 SerializeParameterMap(material.values, pbrMetallicRoughness);
4915 o[
"pbrMetallicRoughness"] = pbrMetallicRoughness;
4918 SerializeParameterMap(material.additionalValues, o);
4920 if (material.name.size()) {
4921 SerializeStringProperty(
"name", material.name, o);
4924 if (material.extras.Type() != NULL_TYPE) {
4925 SerializeValue(
"extras", material.extras, o);
4929static void SerializeGltfMesh(Mesh &mesh, json &o) {
4931 for (
unsigned int i = 0; i < mesh.primitives.size(); ++i) {
4934 Primitive gltfPrimitive = mesh.primitives[i];
4935 for (std::map<std::string, int>::iterator attrIt =
4936 gltfPrimitive.attributes.begin();
4937 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
4938 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
4941 primitive[
"attributes"] = attributes;
4944 if (gltfPrimitive.indices > -1) {
4945 SerializeNumberProperty<int>(
"indices", gltfPrimitive.indices, primitive);
4948 if (gltfPrimitive.material > -1) {
4949 SerializeNumberProperty<int>(
"material", gltfPrimitive.material,
4952 SerializeNumberProperty<int>(
"mode", gltfPrimitive.mode, primitive);
4955 if (gltfPrimitive.targets.size()) {
4957 for (
unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
4958 json targetAttributes;
4959 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
4960 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
4961 attrIt != targetData.end(); ++attrIt) {
4962 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
4966 targets.push_back(targetAttributes);
4968 primitive[
"targets"] = targets;
4971 if (gltfPrimitive.extras.Type() != NULL_TYPE) {
4972 SerializeValue(
"extras", gltfPrimitive.extras, primitive);
4975 primitives.push_back(primitive);
4978 o[
"primitives"] = primitives;
4979 if (mesh.weights.size()) {
4980 SerializeNumberArrayProperty<double>(
"weights", mesh.weights, o);
4983 if (mesh.name.size()) {
4984 SerializeStringProperty(
"name", mesh.name, o);
4987 if (mesh.extras.Type() != NULL_TYPE) {
4988 SerializeValue(
"extras", mesh.extras, o);
4992static void SerializeGltfLight(Light &light, json &o) {
4993 if (!light.name.empty()) SerializeStringProperty(
"name", light.name, o);
4994 SerializeNumberArrayProperty(
"color", light.color, o);
4995 SerializeStringProperty(
"type", light.type, o);
4998static void SerializeGltfNode(Node &node, json &o) {
4999 if (node.translation.size() > 0) {
5000 SerializeNumberArrayProperty<double>(
"translation", node.translation, o);
5002 if (node.rotation.size() > 0) {
5003 SerializeNumberArrayProperty<double>(
"rotation", node.rotation, o);
5005 if (node.scale.size() > 0) {
5006 SerializeNumberArrayProperty<double>(
"scale", node.scale, o);
5008 if (node.matrix.size() > 0) {
5009 SerializeNumberArrayProperty<double>(
"matrix", node.matrix, o);
5011 if (node.mesh != -1) {
5012 SerializeNumberProperty<int>(
"mesh", node.mesh, o);
5015 if (node.skin != -1) {
5016 SerializeNumberProperty<int>(
"skin", node.skin, o);
5019 if (node.camera != -1) {
5020 SerializeNumberProperty<int>(
"camera", node.camera, o);
5023 if (node.extras.Type() != NULL_TYPE) {
5024 SerializeValue(
"extras", node.extras, o);
5027 SerializeExtensionMap(node.extensions, o);
5028 if (!node.name.empty()) SerializeStringProperty(
"name", node.name, o);
5029 SerializeNumberArrayProperty<int>(
"children", node.children, o);
5032static void SerializeGltfSampler(Sampler &sampler, json &o) {
5033 SerializeNumberProperty(
"magFilter", sampler.magFilter, o);
5034 SerializeNumberProperty(
"minFilter", sampler.minFilter, o);
5035 SerializeNumberProperty(
"wrapR", sampler.wrapR, o);
5036 SerializeNumberProperty(
"wrapS", sampler.wrapS, o);
5037 SerializeNumberProperty(
"wrapT", sampler.wrapT, o);
5039 if (sampler.extras.Type() != NULL_TYPE) {
5040 SerializeValue(
"extras", sampler.extras, o);
5044static void SerializeGltfOrthographicCamera(
const OrthographicCamera &camera,
5046 SerializeNumberProperty(
"zfar", camera.zfar, o);
5047 SerializeNumberProperty(
"znear", camera.znear, o);
5048 SerializeNumberProperty(
"xmag", camera.xmag, o);
5049 SerializeNumberProperty(
"ymag", camera.ymag, o);
5051 if (camera.extras.Type() != NULL_TYPE) {
5052 SerializeValue(
"extras", camera.extras, o);
5056static void SerializeGltfPerspectiveCamera(
const PerspectiveCamera &camera,
5058 SerializeNumberProperty(
"zfar", camera.zfar, o);
5059 SerializeNumberProperty(
"znear", camera.znear, o);
5060 if (camera.aspectRatio > 0) {
5061 SerializeNumberProperty(
"aspectRatio", camera.aspectRatio, o);
5064 if (camera.yfov > 0) {
5065 SerializeNumberProperty(
"yfov", camera.yfov, o);
5068 if (camera.extras.Type() != NULL_TYPE) {
5069 SerializeValue(
"extras", camera.extras, o);
5073static void SerializeGltfCamera(
const Camera &camera, json &o) {
5074 SerializeStringProperty(
"type", camera.type, o);
5075 if (!camera.name.empty()) {
5076 SerializeStringProperty(
"name", camera.name, o);
5079 if (camera.type.compare(
"orthographic") == 0) {
5081 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
5082 o[
"orthographic"] = orthographic;
5083 }
else if (camera.type.compare(
"perspective") == 0) {
5085 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
5092static void SerializeGltfScene(Scene &scene, json &o) {
5093 SerializeNumberArrayProperty<int>(
"nodes", scene.nodes, o);
5095 if (scene.name.size()) {
5096 SerializeStringProperty(
"name", scene.name, o);
5098 if (scene.extras.Type() != NULL_TYPE) {
5099 SerializeValue(
"extras", scene.extras, o);
5101 SerializeExtensionMap(scene.extensions, o);
5104static void SerializeGltfSkin(Skin &skin, json &o) {
5105 if (skin.inverseBindMatrices != -1)
5106 SerializeNumberProperty(
"inverseBindMatrices", skin.inverseBindMatrices, o);
5108 SerializeNumberArrayProperty<int>(
"joints", skin.joints, o);
5109 SerializeNumberProperty(
"skeleton", skin.skeleton, o);
5110 if (skin.name.size()) {
5111 SerializeStringProperty(
"name", skin.name, o);
5115static void SerializeGltfTexture(Texture &texture, json &o) {
5116 if (texture.sampler > -1) {
5117 SerializeNumberProperty(
"sampler", texture.sampler, o);
5119 if (texture.source > -1) {
5120 SerializeNumberProperty(
"source", texture.source, o);
5122 if (texture.extras.Type() != NULL_TYPE) {
5123 SerializeValue(
"extras", texture.extras, o);
5125 SerializeExtensionMap(texture.extensions, o);
5128static bool WriteGltfFile(
const std::string &output,
5129 const std::string &content) {
5130 std::ofstream gltfFile(output.c_str());
5131 if (!gltfFile.is_open())
return false;
5132 gltfFile << content << std::endl;
5136static void WriteBinaryGltfFile(
const std::string &output,
5137 const std::string &content) {
5138 std::ofstream gltfFile(output.c_str(), std::ios::binary);
5140 const std::string header =
"glTF";
5141 const int version = 2;
5142 const int padding_size = content.size() % 4;
5146 const int length = 12 + 8 + int(content.size()) + padding_size;
5148 gltfFile.write(header.c_str(), header.size());
5149 gltfFile.write(
reinterpret_cast<const char *
>(&version),
sizeof(version));
5150 gltfFile.write(
reinterpret_cast<const char *
>(&length),
sizeof(length));
5153 const int model_length = int(content.size()) + padding_size;
5154 const int model_format = 0x4E4F534A;
5155 gltfFile.write(
reinterpret_cast<const char *
>(&model_length),
5156 sizeof(model_length));
5157 gltfFile.write(
reinterpret_cast<const char *
>(&model_format),
5158 sizeof(model_format));
5159 gltfFile.write(content.c_str(), content.size());
5162 if (padding_size > 0) {
5163 const std::string padding = std::string(padding_size,
' ');
5164 gltfFile.write(padding.c_str(), padding.size());
5169 bool embedImages =
false,
5170 bool embedBuffers =
false,
5171 bool prettyPrint =
true,
5172 bool writeBinary =
false) {
5177 for (
unsigned int i = 0; i < model->accessors.size(); ++i) {
5179 SerializeGltfAccessor(model->accessors[i], accessor);
5180 accessors.push_back(accessor);
5182 output[
"accessors"] = accessors;
5185 if (model->animations.size()) {
5187 for (
unsigned int i = 0; i < model->animations.size(); ++i) {
5188 if (model->animations[i].channels.size()) {
5190 SerializeGltfAnimation(model->animations[i], animation);
5191 animations.push_back(animation);
5194 output[
"animations"] = animations;
5199 SerializeGltfAsset(model->asset, asset);
5200 output[
"asset"] = asset;
5202 std::string defaultBinFilename = GetBaseFilename(filename);
5203 std::string defaultBinFileExt =
".bin";
5204 std::string::size_type pos =
5205 defaultBinFilename.rfind(
'.', defaultBinFilename.length());
5207 if (pos != std::string::npos) {
5208 defaultBinFilename = defaultBinFilename.substr(0, pos);
5210 std::string baseDir = GetBaseDir(filename);
5211 if (baseDir.empty()) {
5216 std::vector<std::string> usedUris;
5218 for (
unsigned int i = 0; i < model->buffers.size(); ++i) {
5221 SerializeGltfBuffer(model->buffers[i], buffer);
5223 std::string binSavePath;
5225 if (!model->buffers[i].uri.empty() && !IsDataURI(model->buffers[i].uri)) {
5226 binUri = model->buffers[i].uri;
5228 binUri = defaultBinFilename + defaultBinFileExt;
5233 for (
const std::string &usedName : usedUris) {
5234 if (binUri.compare(usedName) != 0)
continue;
5236 binUri = defaultBinFilename + std::to_string(numUsed++) +
5242 usedUris.push_back(binUri);
5243 binSavePath = JoinPath(baseDir, binUri);
5244 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
5249 buffers.push_back(buffer);
5251 output[
"buffers"] = buffers;
5255 for (
unsigned int i = 0; i < model->bufferViews.size(); ++i) {
5257 SerializeGltfBufferView(model->bufferViews[i], bufferView);
5258 bufferViews.push_back(bufferView);
5260 output[
"bufferViews"] = bufferViews;
5263 if (model->extensionsUsed.size()) {
5264 SerializeStringArrayProperty(
"extensionsUsed", model->extensionsUsed,
5269 if (model->extensionsRequired.size()) {
5270 SerializeStringArrayProperty(
"extensionsRequired",
5271 model->extensionsRequired, output);
5275 if (model->images.size()) {
5277 for (
unsigned int i = 0; i < model->images.size(); ++i) {
5280 UpdateImageObject(model->images[i], baseDir,
int(i), embedImages,
5281 &this->WriteImageData, this->write_image_user_data_);
5282 SerializeGltfImage(model->images[i], image);
5283 images.push_back(image);
5285 output[
"images"] = images;
5289 if (model->materials.size()) {
5291 for (
unsigned int i = 0; i < model->materials.size(); ++i) {
5293 SerializeGltfMaterial(model->materials[i], material);
5294 materials.push_back(material);
5296 output[
"materials"] = materials;
5300 if (model->meshes.size()) {
5302 for (
unsigned int i = 0; i < model->meshes.size(); ++i) {
5304 SerializeGltfMesh(model->meshes[i], mesh);
5305 meshes.push_back(mesh);
5307 output[
"meshes"] = meshes;
5311 if (model->nodes.size()) {
5313 for (
unsigned int i = 0; i < model->nodes.size(); ++i) {
5315 SerializeGltfNode(model->nodes[i], node);
5316 nodes.push_back(node);
5318 output[
"nodes"] = nodes;
5322 if (model->defaultScene > -1) {
5323 SerializeNumberProperty<int>(
"scene", model->defaultScene, output);
5327 if (model->scenes.size()) {
5329 for (
unsigned int i = 0; i < model->scenes.size(); ++i) {
5331 SerializeGltfScene(model->scenes[i], currentScene);
5332 scenes.push_back(currentScene);
5334 output[
"scenes"] = scenes;
5338 if (model->skins.size()) {
5340 for (
unsigned int i = 0; i < model->skins.size(); ++i) {
5342 SerializeGltfSkin(model->skins[i], skin);
5343 skins.push_back(skin);
5345 output[
"skins"] = skins;
5349 if (model->textures.size()) {
5351 for (
unsigned int i = 0; i < model->textures.size(); ++i) {
5353 SerializeGltfTexture(model->textures[i], texture);
5354 textures.push_back(texture);
5356 output[
"textures"] = textures;
5360 if (model->samplers.size()) {
5362 for (
unsigned int i = 0; i < model->samplers.size(); ++i) {
5364 SerializeGltfSampler(model->samplers[i], sampler);
5365 samplers.push_back(sampler);
5367 output[
"samplers"] = samplers;
5371 if (model->cameras.size()) {
5373 for (
unsigned int i = 0; i < model->cameras.size(); ++i) {
5375 SerializeGltfCamera(model->cameras[i], camera);
5376 cameras.push_back(camera);
5378 output[
"cameras"] = cameras;
5382 SerializeExtensionMap(model->extensions, output);
5385 if (model->lights.size()) {
5387 for (
unsigned int i = 0; i < model->lights.size(); ++i) {
5389 SerializeGltfLight(model->lights[i], light);
5390 lights.push_back(light);
5392 json khr_lights_cmn;
5393 khr_lights_cmn[
"lights"] = lights;
5396 if (output.find(
"extensions") != output.end()) {
5397 ext_j = output[
"extensions"];
5400 ext_j[
"KHR_lights_cmn"] = khr_lights_cmn;
5402 output[
"extensions"] = ext_j;
5406 if (model->extras.Type() != NULL_TYPE) {
5407 SerializeValue(
"extras", model->extras, output);
5411 WriteBinaryGltfFile(filename, output.dump());
5413 WriteGltfFile(filename, output.dump(prettyPrint ? 2 : -1));
5422#pragma clang diagnostic pop
a class to store JSON values
Definition json.hpp:12544
Definition tiny_gltf.h:796
Definition tiny_gltf.h:718
Definition tiny_gltf.h:915
bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn, const std::string &filename, unsigned int check_sections=REQUIRE_ALL)
void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data)
bool WriteGltfSceneToFile(Model *model, const std::string &filename, bool embedImages, bool embedBuffers, bool prettyPrint, bool writeBinary)
bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn, const char *str, const unsigned int length, const std::string &base_dir, unsigned int check_sections=REQUIRE_ALL)
void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data)
bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn, const std::string &filename, unsigned int check_sections=REQUIRE_ALL)
void SetFsCallbacks(FsCallbacks callbacks)
bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn, const unsigned char *bytes, const unsigned int length, const std::string &base_dir="", unsigned int check_sections=REQUIRE_ALL)
Definition tiny_gltf.h:226
GLM_FUNC_DECL mat< 4, 4, T, defaultp > perspective(T fovy, T aspect, T near, T far)
Definition matrix_clip_space.inl:338
uint8 uint8_t
Definition fwd.hpp:103
int32 int32_t
Definition fwd.hpp:71
uint16 uint16_t
Definition fwd.hpp:117
basic_json<> json
default JSON class
Definition json.hpp:110
Definition tiny_gltf.h:567
int ByteStride(const BufferView &bufferViewObject) const
Definition tiny_gltf.h:599
Definition tiny_gltf.h:426
Definition tiny_gltf.h:437
Definition tiny_gltf.h:448
Definition tiny_gltf.h:767
Definition tiny_gltf.h:552
Definition tiny_gltf.h:757
Definition tiny_gltf.h:670
Definition tiny_gltf.h:892
Definition tiny_gltf.h:492
Definition tiny_gltf.h:788
Definition tiny_gltf.h:540
Definition tiny_gltf.h:707
Definition tiny_gltf.h:657
Definition tiny_gltf.h:362
int TextureIndex() const
Definition tiny_gltf.h:377
ColorValue ColorFactor() const
Definition tiny_gltf.h:404
int TextureTexCoord() const
Definition tiny_gltf.h:388
double Factor() const
Definition tiny_gltf.h:399
Definition tiny_gltf.h:639
Definition tiny_gltf.h:684
Definition tiny_gltf.h:470
Definition tiny_gltf.h:778
Definition tiny_gltf.h:457
Definition tiny_gltf.h:525