tiny_gltf 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  }