tiny_gltf 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,7 @@
7
7
  #endif
8
8
 
9
9
  #include <time.h> // work around C++/C linkage error on some platforms
10
+ #include "ruby.h"
10
11
 
11
12
  #if __cplusplus
12
13
  #include <algorithm>
@@ -15,8 +16,6 @@
15
16
  extern "C" {
16
17
  #endif
17
18
 
18
- #include "ruby.h"
19
-
20
19
  void Init_tiny_gltf(void);
21
20
  VALUE rb_tgltf_load(int argc, VALUE *argv, VALUE self);
22
21
 
@@ -4,7 +4,7 @@
4
4
  //
5
5
  // The MIT License (MIT)
6
6
  //
7
- // Copyright (c) 2015 - 2019 Syoyo Fujita, Aurélien Chatelain and many
7
+ // Copyright (c) 2015 - 2020 Syoyo Fujita, Aurélien Chatelain and many
8
8
  // contributors.
9
9
  //
10
10
  // Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -26,6 +26,7 @@
26
26
  // THE SOFTWARE.
27
27
 
28
28
  // Version:
29
+ // - v2.4.2 Decode percent-encoded URI.
29
30
  // - v2.4.1 Fix some glTF object class does not have `extensions` and/or
30
31
  // `extras` property.
31
32
  // - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone).
@@ -51,6 +52,7 @@
51
52
 
52
53
  #include <array>
53
54
  #include <cassert>
55
+ #include <cmath> // std::fabs
54
56
  #include <cstdint>
55
57
  #include <cstdlib>
56
58
  #include <cstring>
@@ -321,7 +323,8 @@ class Value {
321
323
  }
322
324
 
323
325
  // Use this function if you want to have number value as int.
324
- double GetNumberAsInt() const {
326
+ // TODO(syoyo): Support int value larger than 32 bits
327
+ int GetNumberAsInt() const {
325
328
  if (type_ == REAL_TYPE) {
326
329
  return int(real_value_);
327
330
  } else {
@@ -526,10 +529,12 @@ struct AnimationChannel {
526
529
  // "weights"]
527
530
  Value extras;
528
531
  ExtensionMap extensions;
532
+ ExtensionMap target_extensions;
529
533
 
530
534
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
531
535
  std::string extras_json_string;
532
536
  std::string extensions_json_string;
537
+ std::string target_extensions_json_string;
533
538
 
534
539
  AnimationChannel() : sampler(-1), target_node(-1) {}
535
540
  DEFAULT_METHODS(AnimationChannel)
@@ -637,7 +642,8 @@ struct Image {
637
642
  int bufferView; // (required if no uri)
638
643
  std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
639
644
  // "image/bmp", "image/gif"]
640
- std::string uri; // (required if no mimeType)
645
+ std::string uri; // (required if no mimeType) uri is not decoded(e.g.
646
+ // whitespace may be represented as %20)
641
647
  Value extras;
642
648
  ExtensionMap extensions;
643
649
 
@@ -658,6 +664,8 @@ struct Image {
658
664
  width = -1;
659
665
  height = -1;
660
666
  component = -1;
667
+ bits = -1;
668
+ pixel_type = -1;
661
669
  }
662
670
  DEFAULT_METHODS(Image)
663
671
 
@@ -797,12 +805,13 @@ struct Material {
797
805
 
798
806
  struct BufferView {
799
807
  std::string name;
800
- int buffer; // Required
801
- size_t byteOffset; // minimum 0, default 0
802
- size_t byteLength; // required, minimum 1
803
- size_t byteStride; // minimum 4, maximum 252 (multiple of 4), default 0 =
804
- // understood to be tightly packed
805
- int target; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"]
808
+ int buffer{-1}; // Required
809
+ size_t byteOffset{0}; // minimum 0, default 0
810
+ size_t byteLength{0}; // required, minimum 1. 0 = invalid
811
+ size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
812
+ // understood to be tightly packed
813
+ int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
814
+ // or atttribs. Could be 0 for other data
806
815
  Value extras;
807
816
  ExtensionMap extensions;
808
817
 
@@ -810,9 +819,15 @@ struct BufferView {
810
819
  std::string extras_json_string;
811
820
  std::string extensions_json_string;
812
821
 
813
- bool dracoDecoded; // Flag indicating this has been draco decoded
822
+ bool dracoDecoded{false}; // Flag indicating this has been draco decoded
814
823
 
815
- BufferView() : byteOffset(0), byteStride(0), dracoDecoded(false) {}
824
+ BufferView()
825
+ : buffer(-1),
826
+ byteOffset(0),
827
+ byteLength(0),
828
+ byteStride(0),
829
+ target(0),
830
+ dracoDecoded(false) {}
816
831
  DEFAULT_METHODS(BufferView)
817
832
  bool operator==(const BufferView &) const;
818
833
  };
@@ -887,13 +902,13 @@ struct Accessor {
887
902
  // unreachable return 0;
888
903
  }
889
904
 
890
- Accessor() :
891
- bufferView(-1),
892
- byteOffset(0),
893
- normalized(false),
894
- componentType(-1),
895
- count(0),
896
- type(-1){
905
+ Accessor()
906
+ : bufferView(-1),
907
+ byteOffset(0),
908
+ normalized(false),
909
+ componentType(-1),
910
+ count(0),
911
+ type(-1) {
897
912
  sparse.isSparse = false;
898
913
  }
899
914
  DEFAULT_METHODS(Accessor)
@@ -983,6 +998,7 @@ struct Primitive {
983
998
  Primitive() {
984
999
  material = -1;
985
1000
  indices = -1;
1001
+ mode = -1;
986
1002
  }
987
1003
  DEFAULT_METHODS(Primitive)
988
1004
  bool operator==(const Primitive &) const;
@@ -1037,6 +1053,7 @@ struct Buffer {
1037
1053
  std::vector<unsigned char> data;
1038
1054
  std::string
1039
1055
  uri; // considered as required here but not in the spec (need to clarify)
1056
+ // uri is not decoded(e.g. whitespace may be represented as %20)
1040
1057
  Value extras;
1041
1058
  ExtensionMap extensions;
1042
1059
 
@@ -1101,9 +1118,9 @@ struct SpotLight {
1101
1118
  struct Light {
1102
1119
  std::string name;
1103
1120
  std::vector<double> color;
1104
- double intensity;
1121
+ double intensity{1.0};
1105
1122
  std::string type;
1106
- double range;
1123
+ double range{0.0}; // 0.0 = inifinite
1107
1124
  SpotLight spot;
1108
1125
 
1109
1126
  Light() : intensity(1.0), range(0.0) {}
@@ -1141,7 +1158,7 @@ class Model {
1141
1158
  std::vector<Scene> scenes;
1142
1159
  std::vector<Light> lights;
1143
1160
 
1144
- int defaultScene;
1161
+ int defaultScene = -1;
1145
1162
  std::vector<std::string> extensionsUsed;
1146
1163
  std::vector<std::string> extensionsRequired;
1147
1164
 
@@ -1235,7 +1252,14 @@ struct FsCallbacks {
1235
1252
 
1236
1253
  bool FileExists(const std::string &abs_filename, void *);
1237
1254
 
1238
- std::string ExpandFilePath(const std::string &filepath, void *);
1255
+ ///
1256
+ /// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
1257
+ /// `C:\Users\tinygltf\AppData`)
1258
+ ///
1259
+ /// @param[in] filepath File path string. Assume UTF-8
1260
+ /// @param[in] userdata User data. Set to `nullptr` if you don't need it.
1261
+ ///
1262
+ std::string ExpandFilePath(const std::string &filepath, void *userdata);
1239
1263
 
1240
1264
  bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
1241
1265
  const std::string &filepath, void *);
@@ -1547,11 +1571,12 @@ class TinyGLTF {
1547
1571
  #undef NOMINMAX
1548
1572
  #endif
1549
1573
 
1550
- #if defined(__GLIBCXX__) // mingw
1574
+ #if defined(__GLIBCXX__) // mingw
1551
1575
 
1552
- #include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
1553
1576
  #include <fcntl.h> // _O_RDONLY
1554
1577
 
1578
+ #include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
1579
+
1555
1580
  #endif
1556
1581
 
1557
1582
  #elif !defined(__ANDROID__)
@@ -2119,6 +2144,88 @@ std::string base64_decode(std::string const &encoded_string) {
2119
2144
  #pragma clang diagnostic pop
2120
2145
  #endif
2121
2146
 
2147
+ // https://github.com/syoyo/tinygltf/issues/228
2148
+ // TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
2149
+ // decoding?
2150
+ //
2151
+ // https://stackoverflow.com/questions/18307429/encode-decode-url-in-c
2152
+ // http://dlib.net/dlib/server/server_http.cpp.html
2153
+
2154
+ // --- dlib beign ------------------------------------------------------------
2155
+ // Copyright (C) 2003 Davis E. King (davis@dlib.net)
2156
+ // License: Boost Software License See LICENSE.txt for the full license.
2157
+
2158
+ namespace dlib {
2159
+
2160
+ #if 0
2161
+ inline unsigned char to_hex( unsigned char x )
2162
+ {
2163
+ return x + (x > 9 ? ('A'-10) : '0');
2164
+ }
2165
+
2166
+ const std::string urlencode( const std::string& s )
2167
+ {
2168
+ std::ostringstream os;
2169
+
2170
+ for ( std::string::const_iterator ci = s.begin(); ci != s.end(); ++ci )
2171
+ {
2172
+ if ( (*ci >= 'a' && *ci <= 'z') ||
2173
+ (*ci >= 'A' && *ci <= 'Z') ||
2174
+ (*ci >= '0' && *ci <= '9') )
2175
+ { // allowed
2176
+ os << *ci;
2177
+ }
2178
+ else if ( *ci == ' ')
2179
+ {
2180
+ os << '+';
2181
+ }
2182
+ else
2183
+ {
2184
+ os << '%' << to_hex(static_cast<unsigned char>(*ci >> 4)) << to_hex(static_cast<unsigned char>(*ci % 16));
2185
+ }
2186
+ }
2187
+
2188
+ return os.str();
2189
+ }
2190
+ #endif
2191
+
2192
+ inline unsigned char from_hex(unsigned char ch) {
2193
+ if (ch <= '9' && ch >= '0')
2194
+ ch -= '0';
2195
+ else if (ch <= 'f' && ch >= 'a')
2196
+ ch -= 'a' - 10;
2197
+ else if (ch <= 'F' && ch >= 'A')
2198
+ ch -= 'A' - 10;
2199
+ else
2200
+ ch = 0;
2201
+ return ch;
2202
+ }
2203
+
2204
+ static const std::string urldecode(const std::string &str) {
2205
+ using namespace std;
2206
+ string result;
2207
+ string::size_type i;
2208
+ for (i = 0; i < str.size(); ++i) {
2209
+ if (str[i] == '+') {
2210
+ result += ' ';
2211
+ } else if (str[i] == '%' && str.size() > i + 2) {
2212
+ const unsigned char ch1 =
2213
+ from_hex(static_cast<unsigned char>(str[i + 1]));
2214
+ const unsigned char ch2 =
2215
+ from_hex(static_cast<unsigned char>(str[i + 2]));
2216
+ const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
2217
+ result += static_cast<char>(ch);
2218
+ i += 2;
2219
+ } else {
2220
+ result += str[i];
2221
+ }
2222
+ }
2223
+ return result;
2224
+ }
2225
+
2226
+ } // namespace dlib
2227
+ // --- dlib end --------------------------------------------------------------
2228
+
2122
2229
  static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
2123
2230
  std::string *warn, const std::string &filename,
2124
2231
  const std::string &basedir, bool required,
@@ -2379,11 +2486,22 @@ void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
2379
2486
 
2380
2487
  #ifdef _WIN32
2381
2488
  static inline std::wstring UTF8ToWchar(const std::string &str) {
2382
- int wstr_size = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
2489
+ int wstr_size =
2490
+ MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
2383
2491
  std::wstring wstr(wstr_size, 0);
2384
- MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0], (int)wstr.size());
2492
+ MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
2493
+ (int)wstr.size());
2385
2494
  return wstr;
2386
2495
  }
2496
+
2497
+ static inline std::string WcharToUTF8(const std::wstring &wstr) {
2498
+ int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
2499
+ nullptr, 0, NULL, NULL);
2500
+ std::string str(str_size, 0);
2501
+ WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
2502
+ (int)str.size(), NULL, NULL);
2503
+ return str;
2504
+ }
2387
2505
  #endif
2388
2506
 
2389
2507
  #ifndef TINYGLTF_NO_FS
@@ -2435,15 +2553,16 @@ bool FileExists(const std::string &abs_filename, void *) {
2435
2553
 
2436
2554
  std::string ExpandFilePath(const std::string &filepath, void *) {
2437
2555
  #ifdef _WIN32
2438
- DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
2439
- char *str = new char[len];
2440
- ExpandEnvironmentStringsA(filepath.c_str(), str, len);
2556
+ // Assume input `filepath` is encoded in UTF-8
2557
+ std::wstring wfilepath = UTF8ToWchar(filepath);
2558
+ DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
2559
+ wchar_t *wstr = new wchar_t[wlen];
2560
+ ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
2441
2561
 
2442
- std::string s(str);
2562
+ std::wstring ws(wstr);
2563
+ delete[] wstr;
2564
+ return WcharToUTF8(ws);
2443
2565
 
2444
- delete[] str;
2445
-
2446
- return s;
2447
2566
  #else
2448
2567
 
2449
2568
  #if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
@@ -2458,8 +2577,10 @@ std::string ExpandFilePath(const std::string &filepath, void *) {
2458
2577
  return "";
2459
2578
  }
2460
2579
 
2580
+ // Quote the string to keep any spaces in filepath intact.
2581
+ std::string quoted_path = "\"" + filepath + "\"";
2461
2582
  // char** w;
2462
- int ret = wordexp(filepath.c_str(), &p, 0);
2583
+ int ret = wordexp(quoted_path.c_str(), &p, 0);
2463
2584
  if (ret) {
2464
2585
  // err
2465
2586
  s = filepath;
@@ -2493,11 +2614,12 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
2493
2614
  return false;
2494
2615
  }
2495
2616
  size_t size = AAsset_getLength(asset);
2496
- if (size <= 0) {
2617
+ if (size == 0) {
2497
2618
  if (err) {
2498
2619
  (*err) += "Invalid file size : " + filepath +
2499
2620
  " (does the path point to a directory?)";
2500
2621
  }
2622
+ return false;
2501
2623
  }
2502
2624
  out->resize(size);
2503
2625
  AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
@@ -2511,13 +2633,17 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
2511
2633
  }
2512
2634
  #else
2513
2635
  #ifdef _WIN32
2514
- #if defined(__GLIBCXX__) // mingw
2515
- int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
2636
+ #if defined(__GLIBCXX__) // mingw
2637
+ int file_descriptor =
2638
+ _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
2516
2639
  __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2517
2640
  std::istream f(&wfile_buf);
2518
- #elif defined(_MSC_VER)
2641
+ #elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
2642
+ // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
2643
+ // `wchar_t *`
2519
2644
  std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
2520
- #else // clang?
2645
+ #else
2646
+ // Unknown compiler/runtime
2521
2647
  std::ifstream f(filepath.c_str(), std::ifstream::binary);
2522
2648
  #endif
2523
2649
  #else
@@ -2558,13 +2684,15 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
2558
2684
  bool WriteWholeFile(std::string *err, const std::string &filepath,
2559
2685
  const std::vector<unsigned char> &contents, void *) {
2560
2686
  #ifdef _WIN32
2561
- #if defined(__GLIBCXX__) // mingw
2562
- int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), _O_WRONLY | _O_BINARY);
2563
- __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2687
+ #if defined(__GLIBCXX__) // mingw
2688
+ int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
2689
+ _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
2690
+ __gnu_cxx::stdio_filebuf<char> wfile_buf(
2691
+ file_descriptor, std::ios_base::out | std::ios_base::binary);
2564
2692
  std::ostream f(&wfile_buf);
2565
2693
  #elif defined(_MSC_VER)
2566
2694
  std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
2567
- #else // clang?
2695
+ #else // clang?
2568
2696
  std::ofstream f(filepath.c_str(), std::ofstream::binary);
2569
2697
  #endif
2570
2698
  #else
@@ -2611,12 +2739,13 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index,
2611
2739
  void *user_data = nullptr) {
2612
2740
  std::string filename;
2613
2741
  std::string ext;
2614
-
2615
- // If image have uri. Use it it as a filename
2742
+ // If image has uri, use it it as a filename
2616
2743
  if (image.uri.size()) {
2617
2744
  filename = GetBaseFilename(image.uri);
2618
2745
  ext = GetFilePathExtension(filename);
2619
-
2746
+ } else if (image.bufferView != -1) {
2747
+ // If there's no URI and the data exists in a buffer,
2748
+ // don't change properties or write images
2620
2749
  } else if (image.name.size()) {
2621
2750
  ext = MimeToExt(image.mimeType);
2622
2751
  // Otherwise use name as filename
@@ -2628,7 +2757,7 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index,
2628
2757
  }
2629
2758
 
2630
2759
  // If callback is set, modify image data object
2631
- if (*WriteImageData != nullptr) {
2760
+ if (*WriteImageData != nullptr && !filename.empty()) {
2632
2761
  std::string uri;
2633
2762
  (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
2634
2763
  }
@@ -2728,6 +2857,7 @@ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
2728
2857
  }
2729
2858
  }
2730
2859
 
2860
+ // TODO(syoyo): Allow empty buffer? #229
2731
2861
  if (data.empty()) {
2732
2862
  return false;
2733
2863
  }
@@ -2872,7 +3002,9 @@ json_const_iterator ObjectEnd(const json &o) {
2872
3002
  #endif
2873
3003
  }
2874
3004
 
2875
- const char *GetKey(json_const_iterator &it) {
3005
+ // Making this a const char* results in a pointer to a temporary when
3006
+ // TINYGLTF_USE_RAPIDJSON is off.
3007
+ std::string GetKey(json_const_iterator &it) {
2876
3008
  #ifdef TINYGLTF_USE_RAPIDJSON
2877
3009
  return it->name.GetString();
2878
3010
  #else
@@ -3508,6 +3640,7 @@ static bool ParseAsset(Asset *asset, std::string *err, const json &o,
3508
3640
  ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
3509
3641
  ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
3510
3642
  ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
3643
+ ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
3511
3644
 
3512
3645
  ParseExtensionsProperty(&asset->extensions, err, o);
3513
3646
 
@@ -3646,7 +3779,10 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
3646
3779
  #ifdef TINYGLTF_NO_EXTERNAL_IMAGE
3647
3780
  return true;
3648
3781
  #endif
3649
- if (!LoadExternalFile(&img, err, warn, uri, basedir, false, 0, false, fs)) {
3782
+ std::string decoded_uri = dlib::urldecode(uri);
3783
+ if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
3784
+ /* required */ false, /* required bytes */ 0,
3785
+ /* checksize */ false, fs)) {
3650
3786
  if (warn) {
3651
3787
  (*warn) += "Failed to load external 'uri' for image[" +
3652
3788
  std::to_string(image_idx) + "] name = [" + image->name +
@@ -3868,9 +4004,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
3868
4004
  }
3869
4005
  } else {
3870
4006
  // External .bin file.
4007
+ std::string decoded_uri = dlib::urldecode(buffer->uri);
3871
4008
  if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
3872
- buffer->uri, basedir, true, byteLength, true,
3873
- fs)) {
4009
+ decoded_uri, basedir, /* required */ true,
4010
+ byteLength, /* checkSize */ true, fs)) {
3874
4011
  return false;
3875
4012
  }
3876
4013
  }
@@ -3912,8 +4049,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
3912
4049
  }
3913
4050
  } else {
3914
4051
  // Assume external .bin file.
3915
- if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, buffer->uri,
3916
- basedir, true, byteLength, true, fs)) {
4052
+ std::string decoded_uri = dlib::urldecode(buffer->uri);
4053
+ if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
4054
+ basedir, /* required */ true, byteLength,
4055
+ /* checkSize */ true, fs)) {
3917
4056
  return false;
3918
4057
  }
3919
4058
  }
@@ -4751,6 +4890,13 @@ static bool ParseAnimationChannel(
4751
4890
  }
4752
4891
  return false;
4753
4892
  }
4893
+ ParseExtensionsProperty(&channel->target_extensions, err, target_object);
4894
+ if (store_original_json_for_extras_and_extensions) {
4895
+ json_const_iterator it;
4896
+ if (FindMember(target_object, "extensions", it)) {
4897
+ channel->target_extensions_json_string = JsonToString(GetValue(it));
4898
+ }
4899
+ }
4754
4900
  }
4755
4901
 
4756
4902
  channel->sampler = samplerIndex;
@@ -5252,7 +5398,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
5252
5398
 
5253
5399
  #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5254
5400
  defined(_CPPUNWIND)) && \
5255
- !defined(TINYGLTF_NOEXCEPTION)
5401
+ !defined(TINYGLTF_NOEXCEPTION)
5256
5402
  try {
5257
5403
  JsonParse(v, json_str, json_str_length, true);
5258
5404
 
@@ -5525,7 +5671,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
5525
5671
 
5526
5672
  // Assign missing bufferView target types
5527
5673
  // - Look for missing Mesh indices
5528
- // - Look for missing bufferView targets
5674
+ // - Look for missing Mesh attributes
5529
5675
  for (auto &mesh : model->meshes) {
5530
5676
  for (auto &primitive : mesh.primitives) {
5531
5677
  if (primitive.indices >
@@ -5553,14 +5699,25 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
5553
5699
  // we could optionally check if acessors' bufferView type is Scalar, as
5554
5700
  // it should be
5555
5701
  }
5556
- }
5557
- }
5558
- // find any missing targets, must be an array buffer type if not fulfilled
5559
- // from previous check
5560
- for (auto &bufferView : model->bufferViews) {
5561
- if (bufferView.target == 0) // missing target type
5562
- {
5563
- bufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
5702
+
5703
+ for (auto &attribute : primitive.attributes) {
5704
+ model
5705
+ ->bufferViews[size_t(
5706
+ model->accessors[size_t(attribute.second)].bufferView)]
5707
+ .target = TINYGLTF_TARGET_ARRAY_BUFFER;
5708
+ }
5709
+
5710
+ for (auto &target : primitive.targets) {
5711
+ for (auto &attribute : target) {
5712
+ auto bufferView =
5713
+ model->accessors[size_t(attribute.second)].bufferView;
5714
+ // bufferView could be null(-1) for sparse morph target
5715
+ if (bufferView >= 0) {
5716
+ model->bufferViews[size_t(bufferView)].target =
5717
+ TINYGLTF_TARGET_ARRAY_BUFFER;
5718
+ }
5719
+ }
5720
+ }
5564
5721
  }
5565
5722
  }
5566
5723
 
@@ -6143,6 +6300,13 @@ static void SerializeNumberProperty(const std::string &key, T number,
6143
6300
  JsonAddMember(obj, key.c_str(), json(number));
6144
6301
  }
6145
6302
 
6303
+ #ifdef TINYGLTF_USE_RAPIDJSON
6304
+ template <>
6305
+ void SerializeNumberProperty(const std::string &key, size_t number, json &obj) {
6306
+ JsonAddMember(obj, key.c_str(), json(static_cast<uint64_t>(number)));
6307
+ }
6308
+ #endif
6309
+
6146
6310
  template <typename T>
6147
6311
  static void SerializeNumberArrayProperty(const std::string &key,
6148
6312
  const std::vector<T> &value,
@@ -6278,17 +6442,25 @@ static void SerializeValue(const std::string &key, const Value &value,
6278
6442
  static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
6279
6443
  json &o) {
6280
6444
  std::string header = "data:application/octet-stream;base64,";
6281
- std::string encodedData =
6282
- base64_encode(&data[0], static_cast<unsigned int>(data.size()));
6283
- SerializeStringProperty("uri", header + encodedData, o);
6445
+ if (data.size() > 0) {
6446
+ std::string encodedData =
6447
+ base64_encode(&data[0], static_cast<unsigned int>(data.size()));
6448
+ SerializeStringProperty("uri", header + encodedData, o);
6449
+ } else {
6450
+ // Issue #229
6451
+ // size 0 is allowd. Just emit mime header.
6452
+ SerializeStringProperty("uri", header, o);
6453
+ }
6284
6454
  }
6285
6455
 
6286
6456
  static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
6287
6457
  const std::string &binFilename) {
6288
6458
  #ifdef _WIN32
6289
- #if defined(__GLIBCXX__) // mingw
6290
- int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(), _O_WRONLY | _O_BINARY);
6291
- __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
6459
+ #if defined(__GLIBCXX__) // mingw
6460
+ int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
6461
+ _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
6462
+ __gnu_cxx::stdio_filebuf<char> wfile_buf(
6463
+ file_descriptor, std::ios_base::out | std::ios_base::binary);
6292
6464
  std::ostream output(&wfile_buf);
6293
6465
  if (!wfile_buf.is_open()) return false;
6294
6466
  #elif defined(_MSC_VER)
@@ -6302,8 +6474,14 @@ static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
6302
6474
  std::ofstream output(binFilename.c_str(), std::ofstream::binary);
6303
6475
  if (!output.is_open()) return false;
6304
6476
  #endif
6305
- output.write(reinterpret_cast<const char *>(&data[0]),
6306
- std::streamsize(data.size()));
6477
+ if (data.size() > 0) {
6478
+ output.write(reinterpret_cast<const char *>(&data[0]),
6479
+ std::streamsize(data.size()));
6480
+ } else {
6481
+ // Issue #229
6482
+ // size 0 will be still valid buffer data.
6483
+ // write empty file.
6484
+ }
6307
6485
  return true;
6308
6486
  }
6309
6487
 
@@ -6365,9 +6543,10 @@ static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
6365
6543
  }
6366
6544
 
6367
6545
  static void SerializeGltfAccessor(Accessor &accessor, json &o) {
6368
- SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
6546
+ if (accessor.bufferView >= 0)
6547
+ SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
6369
6548
 
6370
- if (accessor.byteOffset != 0.0)
6549
+ if (accessor.byteOffset != 0)
6371
6550
  SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
6372
6551
 
6373
6552
  SerializeNumberProperty<int>("componentType", accessor.componentType, o);
@@ -6416,6 +6595,8 @@ static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
6416
6595
  SerializeNumberProperty("node", channel.target_node, target);
6417
6596
  SerializeStringProperty("path", channel.target_path, target);
6418
6597
 
6598
+ SerializeExtensionMap(channel.target_extensions, target);
6599
+
6419
6600
  JsonAddMember(o, "target", std::move(target));
6420
6601
  }
6421
6602
 
@@ -6455,6 +6636,7 @@ static void SerializeGltfAnimation(Animation &animation, json &o) {
6455
6636
 
6456
6637
  {
6457
6638
  json samplers;
6639
+ JsonReserveArray(samplers, animation.samplers.size());
6458
6640
  for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
6459
6641
  json sampler;
6460
6642
  AnimationSampler gltfSampler = animation.samplers[i];
@@ -6491,10 +6673,10 @@ static void SerializeGltfAsset(Asset &asset, json &o) {
6491
6673
  SerializeExtensionMap(asset.extensions, o);
6492
6674
  }
6493
6675
 
6494
- static void SerializeGltfBufferBin(Buffer &buffer, json &o,
6495
- std::vector<unsigned char> &binBuffer) {
6676
+ static void SerializeGltfBufferBin(Buffer &buffer, json &o,
6677
+ std::vector<unsigned char> &binBuffer) {
6496
6678
  SerializeNumberProperty("byteLength", buffer.data.size(), o);
6497
- binBuffer=buffer.data;
6679
+ binBuffer = buffer.data;
6498
6680
 
6499
6681
  if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
6500
6682
 
@@ -6561,6 +6743,7 @@ static void SerializeGltfImage(Image &image, json &o) {
6561
6743
  SerializeStringProperty("mimeType", image.mimeType, o);
6562
6744
  SerializeNumberProperty<int>("bufferView", image.bufferView, o);
6563
6745
  } else {
6746
+ // TODO(syoyo): dlib::urilencode?
6564
6747
  SerializeStringProperty("uri", image.uri, o);
6565
6748
  }
6566
6749
 
@@ -6677,8 +6860,8 @@ static void SerializeGltfMaterial(Material &material, json &o) {
6677
6860
  SerializeStringProperty("alphaMode", material.alphaMode, o);
6678
6861
  }
6679
6862
 
6680
- if(material.doubleSided != false)
6681
- JsonAddMember(o, "doubleSided", json(material.doubleSided));
6863
+ if (material.doubleSided != false)
6864
+ JsonAddMember(o, "doubleSided", json(material.doubleSided));
6682
6865
 
6683
6866
  if (material.normalTexture.index > -1) {
6684
6867
  json texinfo;
@@ -6782,7 +6965,7 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) {
6782
6965
  JsonAddMember(primitive, "targets", std::move(targets));
6783
6966
  }
6784
6967
 
6785
- SerializeExtensionMap(gltfPrimitive.extensions, o);
6968
+ SerializeExtensionMap(gltfPrimitive.extensions, primitive);
6786
6969
 
6787
6970
  if (gltfPrimitive.extras.Type() != NULL_TYPE) {
6788
6971
  SerializeValue("extras", gltfPrimitive.extras, primitive);
@@ -6819,7 +7002,9 @@ static void SerializeSpotLight(SpotLight &spot, json &o) {
6819
7002
  static void SerializeGltfLight(Light &light, json &o) {
6820
7003
  if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
6821
7004
  SerializeNumberProperty("intensity", light.intensity, o);
6822
- SerializeNumberProperty("range", light.range, o);
7005
+ if (light.range > 0.0) {
7006
+ SerializeNumberProperty("range", light.range, o);
7007
+ }
6823
7008
  SerializeNumberArrayProperty("color", light.color, o);
6824
7009
  SerializeStringProperty("type", light.type, o);
6825
7010
  if (light.type == "spot") {
@@ -6933,6 +7118,11 @@ static void SerializeGltfCamera(const Camera &camera, json &o) {
6933
7118
  } else {
6934
7119
  // ???
6935
7120
  }
7121
+
7122
+ if (camera.extras.Type() != NULL_TYPE) {
7123
+ SerializeValue("extras", camera.extras, o);
7124
+ }
7125
+ SerializeExtensionMap(camera.extensions, o);
6936
7126
  }
6937
7127
 
6938
7128
  static void SerializeGltfScene(Scene &scene, json &o) {
@@ -6979,14 +7169,16 @@ static void SerializeGltfTexture(Texture &texture, json &o) {
6979
7169
  ///
6980
7170
  static void SerializeGltfModel(Model *model, json &o) {
6981
7171
  // ACCESSORS
6982
- json accessors;
6983
- JsonReserveArray(accessors, model->accessors.size());
6984
- for (unsigned int i = 0; i < model->accessors.size(); ++i) {
6985
- json accessor;
6986
- SerializeGltfAccessor(model->accessors[i], accessor);
6987
- JsonPushBack(accessors, std::move(accessor));
7172
+ if (model->accessors.size()) {
7173
+ json accessors;
7174
+ JsonReserveArray(accessors, model->accessors.size());
7175
+ for (unsigned int i = 0; i < model->accessors.size(); ++i) {
7176
+ json accessor;
7177
+ SerializeGltfAccessor(model->accessors[i], accessor);
7178
+ JsonPushBack(accessors, std::move(accessor));
7179
+ }
7180
+ JsonAddMember(o, "accessors", std::move(accessors));
6988
7181
  }
6989
- JsonAddMember(o, "accessors", std::move(accessors));
6990
7182
 
6991
7183
  // ANIMATIONS
6992
7184
  if (model->animations.size()) {
@@ -7009,18 +7201,15 @@ static void SerializeGltfModel(Model *model, json &o) {
7009
7201
  JsonAddMember(o, "asset", std::move(asset));
7010
7202
 
7011
7203
  // BUFFERVIEWS
7012
- json bufferViews;
7013
- JsonReserveArray(bufferViews, model->bufferViews.size());
7014
- for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
7015
- json bufferView;
7016
- SerializeGltfBufferView(model->bufferViews[i], bufferView);
7017
- JsonPushBack(bufferViews, std::move(bufferView));
7018
- }
7019
- JsonAddMember(o, "bufferViews", std::move(bufferViews));
7020
-
7021
- // Extensions used
7022
- if (model->extensionsUsed.size()) {
7023
- SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed, o);
7204
+ if (model->bufferViews.size()) {
7205
+ json bufferViews;
7206
+ JsonReserveArray(bufferViews, model->bufferViews.size());
7207
+ for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
7208
+ json bufferView;
7209
+ SerializeGltfBufferView(model->bufferViews[i], bufferView);
7210
+ JsonPushBack(bufferViews, std::move(bufferView));
7211
+ }
7212
+ JsonAddMember(o, "bufferViews", std::move(bufferViews));
7024
7213
  }
7025
7214
 
7026
7215
  // Extensions required
@@ -7133,7 +7322,9 @@ static void SerializeGltfModel(Model *model, json &o) {
7133
7322
  // EXTENSIONS
7134
7323
  SerializeExtensionMap(model->extensions, o);
7135
7324
 
7136
- // LIGHTS as KHR_lights_cmn
7325
+ auto extensionsUsed = model->extensionsUsed;
7326
+
7327
+ // LIGHTS as KHR_lights_punctual
7137
7328
  if (model->lights.size()) {
7138
7329
  json lights;
7139
7330
  JsonReserveArray(lights, model->lights.size());
@@ -7148,7 +7339,7 @@ static void SerializeGltfModel(Model *model, json &o) {
7148
7339
 
7149
7340
  {
7150
7341
  json_const_iterator it;
7151
- if (!FindMember(o, "extensions", it)) {
7342
+ if (FindMember(o, "extensions", it)) {
7152
7343
  JsonAssign(ext_j, GetValue(it));
7153
7344
  }
7154
7345
  }
@@ -7156,6 +7347,24 @@ static void SerializeGltfModel(Model *model, json &o) {
7156
7347
  JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
7157
7348
 
7158
7349
  JsonAddMember(o, "extensions", std::move(ext_j));
7350
+
7351
+ // Also add "KHR_lights_punctual" to `extensionsUsed`
7352
+ {
7353
+ auto has_khr_lights_punctual =
7354
+ std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
7355
+ [](const std::string &s) {
7356
+ return (s.compare("KHR_lights_punctual") == 0);
7357
+ });
7358
+
7359
+ if (has_khr_lights_punctual == extensionsUsed.end()) {
7360
+ extensionsUsed.push_back("KHR_lights_punctual");
7361
+ }
7362
+ }
7363
+ }
7364
+
7365
+ // Extensions used
7366
+ if (model->extensionsUsed.size()) {
7367
+ SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
7159
7368
  }
7160
7369
 
7161
7370
  // EXTRAS
@@ -7175,8 +7384,10 @@ static bool WriteGltfFile(const std::string &output,
7175
7384
  #if defined(_MSC_VER)
7176
7385
  std::ofstream gltfFile(UTF8ToWchar(output).c_str());
7177
7386
  #elif defined(__GLIBCXX__)
7178
- int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), _O_WRONLY | _O_BINARY);
7179
- __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
7387
+ int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7388
+ _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7389
+ __gnu_cxx::stdio_filebuf<char> wfile_buf(
7390
+ file_descriptor, std::ios_base::out | std::ios_base::binary);
7180
7391
  std::ostream gltfFile(&wfile_buf);
7181
7392
  if (!wfile_buf.is_open()) return false;
7182
7393
  #else
@@ -7197,32 +7408,31 @@ static void WriteBinaryGltfStream(std::ostream &stream,
7197
7408
  const int version = 2;
7198
7409
 
7199
7410
  // https://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number
7200
- auto roundUp = [](uint32_t numToRound, uint32_t multiple)
7201
- {
7202
- if (multiple == 0)
7203
- return numToRound;
7411
+ auto roundUp = [](uint32_t numToRound, uint32_t multiple) {
7412
+ if (multiple == 0) return numToRound;
7204
7413
 
7205
- uint32_t remainder = numToRound % multiple;
7206
- if (remainder == 0)
7207
- return numToRound;
7414
+ uint32_t remainder = numToRound % multiple;
7415
+ if (remainder == 0) return numToRound;
7208
7416
 
7209
- return numToRound + multiple - remainder;
7417
+ return numToRound + multiple - remainder;
7210
7418
  };
7211
7419
 
7212
- const uint32_t padding_size = roundUp(content.size(), 4) - content.size();
7420
+ const uint32_t padding_size =
7421
+ roundUp(uint32_t(content.size()), 4) - uint32_t(content.size());
7213
7422
 
7214
7423
  // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
7215
7424
  // Chunk data must be located at 4-byte boundary.
7216
- const int length = 12 + 8 + roundUp(content.size(), 4)+
7217
- (binBuffer.size()?(8+roundUp(binBuffer.size(),4)) : 0);
7425
+ const uint32_t length =
7426
+ 12 + 8 + roundUp(uint32_t(content.size()), 4) +
7427
+ (binBuffer.size() ? (8 + roundUp(uint32_t(binBuffer.size()), 4)) : 0);
7218
7428
 
7219
7429
  stream.write(header.c_str(), std::streamsize(header.size()));
7220
7430
  stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
7221
7431
  stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
7222
7432
 
7223
7433
  // JSON chunk info, then JSON data
7224
- const int model_length = int(content.size()) + padding_size;
7225
- const int model_format = 0x4E4F534A;
7434
+ const uint32_t model_length = uint32_t(content.size()) + padding_size;
7435
+ const uint32_t model_format = 0x4E4F534A;
7226
7436
  stream.write(reinterpret_cast<const char *>(&model_length),
7227
7437
  sizeof(model_length));
7228
7438
  stream.write(reinterpret_cast<const char *>(&model_format),
@@ -7234,20 +7444,24 @@ static void WriteBinaryGltfStream(std::ostream &stream,
7234
7444
  const std::string padding = std::string(size_t(padding_size), ' ');
7235
7445
  stream.write(padding.c_str(), std::streamsize(padding.size()));
7236
7446
  }
7237
- if (binBuffer.size() > 0){
7238
- const uint32_t bin_padding_size = roundUp(binBuffer.size(), 4) - binBuffer.size();
7447
+ if (binBuffer.size() > 0) {
7448
+ const uint32_t bin_padding_size =
7449
+ roundUp(uint32_t(binBuffer.size()), 4) - uint32_t(binBuffer.size());
7239
7450
  // BIN chunk info, then BIN data
7240
- const int bin_length = int(binBuffer.size()) + bin_padding_size;
7241
- const int bin_format = 0x004e4942;
7451
+ const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
7452
+ const uint32_t bin_format = 0x004e4942;
7242
7453
  stream.write(reinterpret_cast<const char *>(&bin_length),
7243
- sizeof(bin_length));
7454
+ sizeof(bin_length));
7244
7455
  stream.write(reinterpret_cast<const char *>(&bin_format),
7245
- sizeof(bin_format));
7246
- stream.write((const char *)binBuffer.data(), std::streamsize(binBuffer.size()));
7456
+ sizeof(bin_format));
7457
+ stream.write(reinterpret_cast<const char *>(binBuffer.data()),
7458
+ std::streamsize(binBuffer.size()));
7247
7459
  // Chunksize must be multiplies of 4, so pad with zeroes
7248
7460
  if (bin_padding_size > 0) {
7249
- const std::vector<unsigned char> padding = std::vector<unsigned char>(size_t(bin_padding_size), 0);
7250
- stream.write((const char *)padding.data(), std::streamsize(padding.size()));
7461
+ const std::vector<unsigned char> padding =
7462
+ std::vector<unsigned char>(size_t(bin_padding_size), 0);
7463
+ stream.write(reinterpret_cast<const char *>(padding.data()),
7464
+ std::streamsize(padding.size()));
7251
7465
  }
7252
7466
  }
7253
7467
  }
@@ -7259,8 +7473,10 @@ static void WriteBinaryGltfFile(const std::string &output,
7259
7473
  #if defined(_MSC_VER)
7260
7474
  std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
7261
7475
  #elif defined(__GLIBCXX__)
7262
- int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), _O_WRONLY | _O_BINARY);
7263
- __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
7476
+ int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7477
+ _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7478
+ __gnu_cxx::stdio_filebuf<char> wfile_buf(
7479
+ file_descriptor, std::ios_base::out | std::ios_base::binary);
7264
7480
  std::ostream gltfFile(&wfile_buf);
7265
7481
  #else
7266
7482
  std::ofstream gltfFile(output.c_str(), std::ios::binary);
@@ -7268,7 +7484,7 @@ static void WriteBinaryGltfFile(const std::string &output,
7268
7484
  #else
7269
7485
  std::ofstream gltfFile(output.c_str(), std::ios::binary);
7270
7486
  #endif
7271
- WriteBinaryGltfStream(gltfFile, content,binBuffer);
7487
+ WriteBinaryGltfStream(gltfFile, content, binBuffer);
7272
7488
  }
7273
7489
 
7274
7490
  bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
@@ -7280,20 +7496,21 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
7280
7496
  SerializeGltfModel(model, output);
7281
7497
 
7282
7498
  // BUFFERS
7283
- std::vector<std::string> usedUris;
7284
7499
  std::vector<unsigned char> binBuffer;
7285
- json buffers;
7286
- JsonReserveArray(buffers, model->buffers.size());
7287
- for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7288
- json buffer;
7289
- if (writeBinary && i==0 && model->buffers[i].uri.empty()){
7290
- SerializeGltfBufferBin(model->buffers[i], buffer,binBuffer);
7291
- } else {
7292
- SerializeGltfBuffer(model->buffers[i], buffer);
7500
+ if (model->buffers.size()) {
7501
+ json buffers;
7502
+ JsonReserveArray(buffers, model->buffers.size());
7503
+ for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7504
+ json buffer;
7505
+ if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7506
+ SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
7507
+ } else {
7508
+ SerializeGltfBuffer(model->buffers[i], buffer);
7509
+ }
7510
+ JsonPushBack(buffers, std::move(buffer));
7293
7511
  }
7294
- JsonPushBack(buffers, std::move(buffer));
7512
+ JsonAddMember(output, "buffers", std::move(buffers));
7295
7513
  }
7296
- JsonAddMember(output, "buffers", std::move(buffers));
7297
7514
 
7298
7515
  // IMAGES
7299
7516
  if (model->images.size()) {
@@ -7303,9 +7520,9 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
7303
7520
  json image;
7304
7521
 
7305
7522
  std::string dummystring = "";
7306
- // UpdateImageObject need baseDir but only uses it if embededImages is
7307
- // enable, since we won't write separte images when writing to a stream we
7308
- // use a dummystring
7523
+ // UpdateImageObject need baseDir but only uses it if embeddedImages is
7524
+ // enabled, since we won't write separate images when writing to a stream
7525
+ // we
7309
7526
  UpdateImageObject(model->images[i], dummystring, int(i), false,
7310
7527
  &this->WriteImageData, this->write_image_user_data_);
7311
7528
  SerializeGltfImage(model->images[i], image);
@@ -7315,7 +7532,7 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
7315
7532
  }
7316
7533
 
7317
7534
  if (writeBinary) {
7318
- WriteBinaryGltfStream(stream, JsonToString(output),binBuffer);
7535
+ WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
7319
7536
  } else {
7320
7537
  WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
7321
7538
  }
@@ -7347,44 +7564,47 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
7347
7564
  // BUFFERS
7348
7565
  std::vector<std::string> usedUris;
7349
7566
  std::vector<unsigned char> binBuffer;
7350
- json buffers;
7351
- JsonReserveArray(buffers, model->buffers.size());
7352
- for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7353
- json buffer;
7354
- if (writeBinary && i==0 && model->buffers[i].uri.empty()){
7355
- SerializeGltfBufferBin(model->buffers[i], buffer,binBuffer);
7356
- } else if (embedBuffers) {
7357
- SerializeGltfBuffer(model->buffers[i], buffer);
7358
- } else {
7359
- std::string binSavePath;
7360
- std::string binUri;
7361
- if (!model->buffers[i].uri.empty() && !IsDataURI(model->buffers[i].uri)) {
7362
- binUri = model->buffers[i].uri;
7567
+ if (model->buffers.size()) {
7568
+ json buffers;
7569
+ JsonReserveArray(buffers, model->buffers.size());
7570
+ for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7571
+ json buffer;
7572
+ if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7573
+ SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
7574
+ } else if (embedBuffers) {
7575
+ SerializeGltfBuffer(model->buffers[i], buffer);
7363
7576
  } else {
7364
- binUri = defaultBinFilename + defaultBinFileExt;
7365
- bool inUse = true;
7366
- int numUsed = 0;
7367
- while (inUse) {
7368
- inUse = false;
7369
- for (const std::string &usedName : usedUris) {
7370
- if (binUri.compare(usedName) != 0) continue;
7371
- inUse = true;
7372
- binUri = defaultBinFilename + std::to_string(numUsed++) +
7373
- defaultBinFileExt;
7374
- break;
7577
+ std::string binSavePath;
7578
+ std::string binUri;
7579
+ if (!model->buffers[i].uri.empty() &&
7580
+ !IsDataURI(model->buffers[i].uri)) {
7581
+ binUri = model->buffers[i].uri;
7582
+ } else {
7583
+ binUri = defaultBinFilename + defaultBinFileExt;
7584
+ bool inUse = true;
7585
+ int numUsed = 0;
7586
+ while (inUse) {
7587
+ inUse = false;
7588
+ for (const std::string &usedName : usedUris) {
7589
+ if (binUri.compare(usedName) != 0) continue;
7590
+ inUse = true;
7591
+ binUri = defaultBinFilename + std::to_string(numUsed++) +
7592
+ defaultBinFileExt;
7593
+ break;
7594
+ }
7375
7595
  }
7376
7596
  }
7597
+ usedUris.push_back(binUri);
7598
+ binSavePath = JoinPath(baseDir, binUri);
7599
+ if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
7600
+ binUri)) {
7601
+ return false;
7602
+ }
7377
7603
  }
7378
- usedUris.push_back(binUri);
7379
- binSavePath = JoinPath(baseDir, binUri);
7380
- if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
7381
- binUri)) {
7382
- return false;
7383
- }
7604
+ JsonPushBack(buffers, std::move(buffer));
7384
7605
  }
7385
- JsonPushBack(buffers, std::move(buffer));
7606
+ JsonAddMember(output, "buffers", std::move(buffers));
7386
7607
  }
7387
- JsonAddMember(output, "buffers", std::move(buffers));
7388
7608
 
7389
7609
  // IMAGES
7390
7610
  if (model->images.size()) {
@@ -7402,7 +7622,7 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
7402
7622
  }
7403
7623
 
7404
7624
  if (writeBinary) {
7405
- WriteBinaryGltfFile(filename, JsonToString(output),binBuffer);
7625
+ WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
7406
7626
  } else {
7407
7627
  WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
7408
7628
  }