tiny_gltf 1.0.2 → 2.5.0.1

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.
@@ -4,7 +4,7 @@
4
4
  //
5
5
  // The MIT License (MIT)
6
6
  //
7
- // Copyright (c) 2015 - 2020 Syoyo Fujita, Aurélien Chatelain and many
7
+ // Copyright (c) 2015 - Present 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,9 @@
26
26
  // THE SOFTWARE.
27
27
 
28
28
  // Version:
29
+ // - v2.5.0 Add SetPreserveImageChannels() option to load image data as is.
30
+ // - v2.4.3 Fix null object output when when material has all default
31
+ // parameters.
29
32
  // - v2.4.2 Decode percent-encoded URI.
30
33
  // - v2.4.1 Fix some glTF object class does not have `extensions` and/or
31
34
  // `extras` property.
@@ -105,7 +108,7 @@ namespace tinygltf {
105
108
  #define TINYGLTF_COMPONENT_TYPE_INT (5124)
106
109
  #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
107
110
  #define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
108
- #define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130)
111
+ #define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not support double type even the schema seems allow any value of integer: https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22
109
112
 
110
113
  #define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
111
114
  #define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
@@ -188,14 +191,14 @@ AAssetManager *asset_manager = nullptr;
188
191
  #endif
189
192
 
190
193
  typedef enum {
191
- NULL_TYPE = 0,
192
- REAL_TYPE = 1,
193
- INT_TYPE = 2,
194
- BOOL_TYPE = 3,
195
- STRING_TYPE = 4,
196
- ARRAY_TYPE = 5,
197
- BINARY_TYPE = 6,
198
- OBJECT_TYPE = 7
194
+ NULL_TYPE,
195
+ REAL_TYPE,
196
+ INT_TYPE,
197
+ BOOL_TYPE,
198
+ STRING_TYPE,
199
+ ARRAY_TYPE,
200
+ BINARY_TYPE,
201
+ OBJECT_TYPE
199
202
  } Type;
200
203
 
201
204
  static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
@@ -250,7 +253,6 @@ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
250
253
  #ifdef __clang__
251
254
  #pragma clang diagnostic push
252
255
  // Suppress warning for : static Value null_value
253
- // https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang
254
256
  #pragma clang diagnostic ignored "-Wexit-time-destructors"
255
257
  #pragma clang diagnostic ignored "-Wpadded"
256
258
  #endif
@@ -295,7 +297,7 @@ class Value {
295
297
 
296
298
  DEFAULT_METHODS(Value)
297
299
 
298
- char Type() const { return static_cast<const char>(type_); }
300
+ char Type() const { return static_cast<char>(type_); }
299
301
 
300
302
  bool IsBool() const { return (type_ == BOOL_TYPE); }
301
303
 
@@ -601,7 +603,7 @@ struct Sampler {
601
603
  // `magFilter`. Set -1 in TinyGLTF(issue #186)
602
604
  int minFilter =
603
605
  -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
604
- // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_NEAREST",
606
+ // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
605
607
  // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
606
608
  int magFilter =
607
609
  -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
@@ -611,7 +613,7 @@ struct Sampler {
611
613
  int wrapT =
612
614
  TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
613
615
  // "REPEAT"], default "REPEAT"
614
- int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension
616
+ //int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently not used.
615
617
 
616
618
  Value extras;
617
619
  ExtensionMap extensions;
@@ -624,8 +626,7 @@ struct Sampler {
624
626
  : minFilter(-1),
625
627
  magFilter(-1),
626
628
  wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
627
- wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT),
628
- wrapR(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
629
+ wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
629
630
  DEFAULT_METHODS(Sampler)
630
631
  bool operator==(const Sampler &) const;
631
632
  };
@@ -848,8 +849,10 @@ struct Accessor {
848
849
  std::string extras_json_string;
849
850
  std::string extensions_json_string;
850
851
 
851
- std::vector<double> minValues; // optional
852
- std::vector<double> maxValues; // optional
852
+ std::vector<double>
853
+ minValues; // optional. integer value is promoted to double
854
+ std::vector<double>
855
+ maxValues; // optional. integer value is promoted to double
853
856
 
854
857
  struct {
855
858
  int count;
@@ -1067,7 +1070,7 @@ struct Buffer {
1067
1070
  };
1068
1071
 
1069
1072
  struct Asset {
1070
- std::string version; // required
1073
+ std::string version = "2.0"; // required
1071
1074
  std::string generator;
1072
1075
  std::string minVersion;
1073
1076
  std::string copyright;
@@ -1189,7 +1192,8 @@ enum SectionCheck {
1189
1192
  ///
1190
1193
  typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1191
1194
  std::string *, int, int,
1192
- const unsigned char *, int, void *);
1195
+ const unsigned char *, int,
1196
+ void *user_pointer);
1193
1197
 
1194
1198
  ///
1195
1199
  /// WriteImageDataFunction type. Signature for custom image writing callbacks.
@@ -1254,7 +1258,7 @@ bool FileExists(const std::string &abs_filename, void *);
1254
1258
 
1255
1259
  ///
1256
1260
  /// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
1257
- /// `C:\Users\tinygltf\AppData`)
1261
+ /// `C:\\Users\\tinygltf\\AppData`)
1258
1262
  ///
1259
1263
  /// @param[in] filepath File path string. Assume UTF-8
1260
1264
  /// @param[in] userdata User data. Set to `nullptr` if you don't need it.
@@ -1345,6 +1349,11 @@ class TinyGLTF {
1345
1349
  ///
1346
1350
  void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1347
1351
 
1352
+ ///
1353
+ /// Unset(remove) callback of loading image data
1354
+ ///
1355
+ void RemoveImageLoader();
1356
+
1348
1357
  ///
1349
1358
  /// Set callback to use for writing image data
1350
1359
  ///
@@ -1383,6 +1392,16 @@ class TinyGLTF {
1383
1392
  return store_original_json_for_extras_and_extensions_;
1384
1393
  }
1385
1394
 
1395
+ ///
1396
+ /// Specify whether preserve image channales when loading images or not.
1397
+ /// (Not effective when the user suppy their own LoadImageData callbacks)
1398
+ ///
1399
+ void SetPreserveImageChannels(bool onoff) {
1400
+ preserve_image_channels_ = onoff;
1401
+ }
1402
+
1403
+ bool GetPreserveImageChannels() const { return preserve_image_channels_; }
1404
+
1386
1405
  private:
1387
1406
  ///
1388
1407
  /// Loads glTF asset from string(memory).
@@ -1402,6 +1421,9 @@ class TinyGLTF {
1402
1421
 
1403
1422
  bool store_original_json_for_extras_and_extensions_ = false;
1404
1423
 
1424
+ bool preserve_image_channels_ = false; /// Default false(expand channels to
1425
+ /// RGBA) for backward compatibility.
1426
+
1405
1427
  FsCallbacks fs = {
1406
1428
  #ifndef TINYGLTF_NO_FS
1407
1429
  &tinygltf::FileExists, &tinygltf::ExpandFilePath,
@@ -1421,7 +1443,8 @@ class TinyGLTF {
1421
1443
  #else
1422
1444
  nullptr;
1423
1445
  #endif
1424
- void *load_image_user_data_ = reinterpret_cast<void *>(&fs);
1446
+ void *load_image_user_data_{nullptr};
1447
+ bool user_image_loader_{false};
1425
1448
 
1426
1449
  WriteImageDataFunction WriteImageData =
1427
1450
  #ifndef TINYGLTF_NO_STB_IMAGE_WRITE
@@ -1429,7 +1452,7 @@ class TinyGLTF {
1429
1452
  #else
1430
1453
  nullptr;
1431
1454
  #endif
1432
- void *write_image_user_data_ = reinterpret_cast<void *>(&fs);
1455
+ void *write_image_user_data_{nullptr};
1433
1456
  };
1434
1457
 
1435
1458
  #ifdef __clang__
@@ -1514,6 +1537,7 @@ class TinyGLTF {
1514
1537
  #ifndef TINYGLTF_USE_RAPIDJSON
1515
1538
  #include "json.hpp"
1516
1539
  #else
1540
+ #ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
1517
1541
  #include "document.h"
1518
1542
  #include "prettywriter.h"
1519
1543
  #include "rapidjson.h"
@@ -1521,6 +1545,7 @@ class TinyGLTF {
1521
1545
  #include "writer.h"
1522
1546
  #endif
1523
1547
  #endif
1548
+ #endif
1524
1549
 
1525
1550
  #ifdef TINYGLTF_ENABLE_DRACO
1526
1551
  #include "draco/compression/decode.h"
@@ -1579,7 +1604,7 @@ class TinyGLTF {
1579
1604
 
1580
1605
  #endif
1581
1606
 
1582
- #elif !defined(__ANDROID__)
1607
+ #elif !defined(__ANDROID__) && !defined(__OpenBSD__)
1583
1608
  #include <wordexp.h>
1584
1609
  #endif
1585
1610
 
@@ -1683,6 +1708,19 @@ void JsonParse(JsonDocument &doc, const char *str, size_t length,
1683
1708
 
1684
1709
  namespace tinygltf {
1685
1710
 
1711
+ ///
1712
+ /// Internal LoadImageDataOption struct.
1713
+ /// This struct is passed through `user_pointer` in LoadImageData.
1714
+ /// The struct is not passed when the user supply their own LoadImageData
1715
+ /// callbacks.
1716
+ ///
1717
+ struct LoadImageDataOption {
1718
+ // true: preserve image channels(e.g. load as RGB image if the image has RGB
1719
+ // channels) default `false`(channels are expanded to RGBA for backward
1720
+ // compatiblity).
1721
+ bool preserve_channels{false};
1722
+ };
1723
+
1686
1724
  // Equals function for Value, for recursivity
1687
1725
  static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1688
1726
  if (one.Type() != other.Type()) return false;
@@ -1895,8 +1933,10 @@ bool Sampler::operator==(const Sampler &other) const {
1895
1933
  return this->extensions == other.extensions && this->extras == other.extras &&
1896
1934
  this->magFilter == other.magFilter &&
1897
1935
  this->minFilter == other.minFilter && this->name == other.name &&
1898
- this->wrapR == other.wrapR && this->wrapS == other.wrapS &&
1936
+ this->wrapS == other.wrapS &&
1899
1937
  this->wrapT == other.wrapT;
1938
+
1939
+ //this->wrapR == other.wrapR
1900
1940
  }
1901
1941
  bool Scene::operator==(const Scene &other) const {
1902
1942
  return this->extensions == other.extensions && this->extras == other.extras &&
@@ -2000,9 +2040,11 @@ static std::string GetBaseDir(const std::string &filepath) {
2000
2040
  return "";
2001
2041
  }
2002
2042
 
2003
- // https://stackoverflow.com/questions/8520560/get-a-file-name-from-a-path
2004
2043
  static std::string GetBaseFilename(const std::string &filepath) {
2005
- return filepath.substr(filepath.find_last_of("/\\") + 1);
2044
+ auto idx = filepath.find_last_of("/\\");
2045
+ if (idx != std::string::npos)
2046
+ return filepath.substr(idx + 1);
2047
+ return filepath;
2006
2048
  }
2007
2049
 
2008
2050
  std::string base64_encode(unsigned char const *, unsigned int len);
@@ -2148,47 +2190,35 @@ std::string base64_decode(std::string const &encoded_string) {
2148
2190
  // TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
2149
2191
  // decoding?
2150
2192
  //
2151
- // https://stackoverflow.com/questions/18307429/encode-decode-url-in-c
2193
+ // Uri Decoding from DLIB
2152
2194
  // http://dlib.net/dlib/server/server_http.cpp.html
2153
-
2154
- // --- dlib beign ------------------------------------------------------------
2195
+ // --- dlib begin ------------------------------------------------------------
2155
2196
  // Copyright (C) 2003 Davis E. King (davis@dlib.net)
2156
- // License: Boost Software License See LICENSE.txt for the full license.
2157
-
2197
+ // License: Boost Software License
2198
+ // Boost Software License - Version 1.0 - August 17th, 2003
2199
+
2200
+ // Permission is hereby granted, free of charge, to any person or organization
2201
+ // obtaining a copy of the software and accompanying documentation covered by
2202
+ // this license (the "Software") to use, reproduce, display, distribute,
2203
+ // execute, and transmit the Software, and to prepare derivative works of the
2204
+ // Software, and to permit third-parties to whom the Software is furnished to
2205
+ // do so, all subject to the following:
2206
+ // The copyright notices in the Software and this entire statement, including
2207
+ // the above license grant, this restriction and the following disclaimer,
2208
+ // must be included in all copies of the Software, in whole or in part, and
2209
+ // all derivative works of the Software, unless such copies or derivative
2210
+ // works are solely in the form of machine-executable object code generated by
2211
+ // a source language processor.
2212
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2213
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2214
+ // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
2215
+ // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
2216
+ // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
2217
+ // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2218
+ // DEALINGS IN THE SOFTWARE.
2219
+ //
2158
2220
  namespace dlib {
2159
2221
 
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
2222
  inline unsigned char from_hex(unsigned char ch) {
2193
2223
  if (ch <= '9' && ch >= '0')
2194
2224
  ch -= '0';
@@ -2297,22 +2327,40 @@ static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
2297
2327
  void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
2298
2328
  LoadImageData = func;
2299
2329
  load_image_user_data_ = user_data;
2330
+ user_image_loader_ = true;
2331
+ }
2332
+
2333
+ void TinyGLTF::RemoveImageLoader() {
2334
+ LoadImageData =
2335
+ #ifndef TINYGLTF_NO_STB_IMAGE
2336
+ &tinygltf::LoadImageData;
2337
+ #else
2338
+ nullptr;
2339
+ #endif
2340
+
2341
+ load_image_user_data_ = nullptr;
2342
+ user_image_loader_ = false;
2300
2343
  }
2301
2344
 
2302
2345
  #ifndef TINYGLTF_NO_STB_IMAGE
2303
2346
  bool LoadImageData(Image *image, const int image_idx, std::string *err,
2304
2347
  std::string *warn, int req_width, int req_height,
2305
2348
  const unsigned char *bytes, int size, void *user_data) {
2306
- (void)user_data;
2307
2349
  (void)warn;
2308
2350
 
2351
+ LoadImageDataOption option;
2352
+ if (user_data) {
2353
+ option = *reinterpret_cast<LoadImageDataOption *>(user_data);
2354
+ }
2355
+
2309
2356
  int w = 0, h = 0, comp = 0, req_comp = 0;
2310
2357
 
2311
2358
  unsigned char *data = nullptr;
2312
2359
 
2313
- // force 32-bit textures for common Vulkan compatibility. It appears that
2314
- // some GPU drivers do not support 24-bit images for Vulkan
2315
- req_comp = 4;
2360
+ // preserve_channels true: Use channels stored in the image file.
2361
+ // false: force 32-bit textures for common Vulkan compatibility. It appears
2362
+ // that some GPU drivers do not support 24-bit images for Vulkan
2363
+ req_comp = option.preserve_channels ? 0 : 4;
2316
2364
  int bits = 8;
2317
2365
  int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
2318
2366
 
@@ -2384,13 +2432,18 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err,
2384
2432
  }
2385
2433
  }
2386
2434
 
2435
+ if (req_comp != 0) {
2436
+ // loaded data has `req_comp` channels(components)
2437
+ comp = req_comp;
2438
+ }
2439
+
2387
2440
  image->width = w;
2388
2441
  image->height = h;
2389
- image->component = req_comp;
2442
+ image->component = comp;
2390
2443
  image->bits = bits;
2391
2444
  image->pixel_type = pixel_type;
2392
- image->image.resize(static_cast<size_t>(w * h * req_comp) * size_t(bits / 8));
2393
- std::copy(data, data + w * h * req_comp * (bits / 8), image->image.begin());
2445
+ image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
2446
+ std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
2394
2447
  stbi_image_free(data);
2395
2448
 
2396
2449
  return true;
@@ -2566,7 +2619,7 @@ std::string ExpandFilePath(const std::string &filepath, void *) {
2566
2619
  #else
2567
2620
 
2568
2621
  #if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
2569
- defined(__ANDROID__) || defined(__EMSCRIPTEN__)
2622
+ defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
2570
2623
  // no expansion
2571
2624
  std::string s = filepath;
2572
2625
  #else
@@ -3139,6 +3192,7 @@ static bool ParseJsonAsValue(Value *ret, const json &o) {
3139
3192
  break;
3140
3193
  case json::value_t::null:
3141
3194
  case json::value_t::discarded:
3195
+ case json::value_t::binary:
3142
3196
  // default:
3143
3197
  break;
3144
3198
  }
@@ -4162,7 +4216,9 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
4162
4216
  accessor->sparse.isSparse = true;
4163
4217
 
4164
4218
  int count = 0;
4165
- ParseIntegerProperty(&count, err, o, "count", true);
4219
+ if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
4220
+ return false;
4221
+ }
4166
4222
 
4167
4223
  json_const_iterator indices_iterator;
4168
4224
  json_const_iterator values_iterator;
@@ -4180,18 +4236,24 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
4180
4236
  const json &values_obj = GetValue(values_iterator);
4181
4237
 
4182
4238
  int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
4183
- ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView",
4184
- true);
4239
+ if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView",
4240
+ true, "SparseAccessor")) {
4241
+ return false;
4242
+ }
4185
4243
  ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
4186
- true);
4187
- ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
4188
- true);
4244
+ false);
4245
+ if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
4246
+ true, "SparseAccessor")) {
4247
+ return false;
4248
+ }
4189
4249
 
4190
4250
  int values_buffer_view = 0, values_byte_offset = 0;
4191
- ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
4192
- true);
4251
+ if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
4252
+ true, "SparseAccessor")) {
4253
+ return false;
4254
+ }
4193
4255
  ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
4194
- true);
4256
+ false);
4195
4257
 
4196
4258
  accessor->sparse.count = count;
4197
4259
  accessor->sparse.indices.bufferView = indices_buffer_view;
@@ -4200,8 +4262,6 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
4200
4262
  accessor->sparse.values.bufferView = values_buffer_view;
4201
4263
  accessor->sparse.values.byteOffset = values_byte_offset;
4202
4264
 
4203
- // todo check theses values
4204
-
4205
4265
  return true;
4206
4266
  }
4207
4267
 
@@ -4409,6 +4469,7 @@ static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4409
4469
  static bool ParseDracoExtension(Primitive *primitive, Model *model,
4410
4470
  std::string *err,
4411
4471
  const Value &dracoExtensionValue) {
4472
+ (void)err;
4412
4473
  auto bufferViewValue = dracoExtensionValue.Get("bufferView");
4413
4474
  if (!bufferViewValue.IsInt()) return false;
4414
4475
  auto attributesValue = dracoExtensionValue.Get("attributes");
@@ -4469,7 +4530,6 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model,
4469
4530
 
4470
4531
  int dracoAttributeIndex = attribute.second.Get<int>();
4471
4532
  const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
4472
- const auto pBuffer = pAttribute->buffer();
4473
4533
  const auto componentType =
4474
4534
  model->accessors[primitiveAttribute->second].componentType;
4475
4535
 
@@ -5028,12 +5088,12 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
5028
5088
  int magFilter = -1;
5029
5089
  int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5030
5090
  int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
5031
- int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
5091
+ //int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
5032
5092
  ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5033
5093
  ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5034
5094
  ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5035
5095
  ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
5036
- ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf extension
5096
+ //ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf extension
5037
5097
 
5038
5098
  // TODO(syoyo): Check the value is alloed one.
5039
5099
  // (e.g. we allow 9728(NEAREST), but don't allow 9727)
@@ -5042,7 +5102,7 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
5042
5102
  sampler->magFilter = magFilter;
5043
5103
  sampler->wrapS = wrapS;
5044
5104
  sampler->wrapT = wrapT;
5045
- sampler->wrapR = wrapR;
5105
+ //sampler->wrapR = wrapR;
5046
5106
 
5047
5107
  ParseExtensionsProperty(&(sampler->extensions), err, o);
5048
5108
  ParseExtrasProperty(&(sampler->extras), o);
@@ -5769,13 +5829,13 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
5769
5829
  {
5770
5830
  json_const_iterator it;
5771
5831
  if (FindMember(o, "extensions", it)) {
5772
- model->extensions_json_string = JsonToString(GetValue(it));
5832
+ scene.extensions_json_string = JsonToString(GetValue(it));
5773
5833
  }
5774
5834
  }
5775
5835
  {
5776
5836
  json_const_iterator it;
5777
5837
  if (FindMember(o, "extras", it)) {
5778
- model->extras_json_string = JsonToString(GetValue(it));
5838
+ scene.extras_json_string = JsonToString(GetValue(it));
5779
5839
  }
5780
5840
  }
5781
5841
  }
@@ -5825,6 +5885,18 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
5825
5885
  }
5826
5886
 
5827
5887
  // 11. Parse Image
5888
+ void *load_image_user_data{nullptr};
5889
+
5890
+ LoadImageDataOption load_image_option;
5891
+
5892
+ if (user_image_loader_) {
5893
+ // Use user supplied pointer
5894
+ load_image_user_data = load_image_user_data_;
5895
+ } else {
5896
+ load_image_option.preserve_channels = preserve_image_channels_;
5897
+ load_image_user_data = reinterpret_cast<void *>(&load_image_option);
5898
+ }
5899
+
5828
5900
  {
5829
5901
  int idx = 0;
5830
5902
  bool success = ForEachInArray(v, "images", [&](const json &o) {
@@ -5837,7 +5909,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
5837
5909
  Image image;
5838
5910
  if (!ParseImage(&image, idx, err, warn, o,
5839
5911
  store_original_json_for_extras_and_extensions_, base_dir,
5840
- &fs, &this->LoadImageData, load_image_user_data_)) {
5912
+ &fs, &this->LoadImageData, load_image_user_data)) {
5841
5913
  return false;
5842
5914
  }
5843
5915
 
@@ -5875,7 +5947,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
5875
5947
  bool ret = LoadImageData(
5876
5948
  &image, idx, err, warn, image.width, image.height,
5877
5949
  &buffer.data[bufferView.byteOffset],
5878
- static_cast<int>(bufferView.byteLength), load_image_user_data_);
5950
+ static_cast<int>(bufferView.byteLength), load_image_user_data);
5879
5951
  if (!ret) {
5880
5952
  return false;
5881
5953
  }
@@ -6551,8 +6623,33 @@ static void SerializeGltfAccessor(Accessor &accessor, json &o) {
6551
6623
 
6552
6624
  SerializeNumberProperty<int>("componentType", accessor.componentType, o);
6553
6625
  SerializeNumberProperty<size_t>("count", accessor.count, o);
6554
- SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
6555
- SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
6626
+
6627
+ if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
6628
+ (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
6629
+ SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
6630
+ SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
6631
+ } else {
6632
+ // Issue #301. Serialize as integer.
6633
+ // Assume int value is within [-2**31-1, 2**31-1]
6634
+ {
6635
+ std::vector<int> values;
6636
+ std::transform(accessor.minValues.begin(), accessor.minValues.end(),
6637
+ std::back_inserter(values),
6638
+ [](double v) { return static_cast<int>(v); });
6639
+
6640
+ SerializeNumberArrayProperty<int>("min", values, o);
6641
+ }
6642
+
6643
+ {
6644
+ std::vector<int> values;
6645
+ std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
6646
+ std::back_inserter(values),
6647
+ [](double v) { return static_cast<int>(v); });
6648
+
6649
+ SerializeNumberArrayProperty<int>("max", values, o);
6650
+ }
6651
+ }
6652
+
6556
6653
  if (accessor.normalized)
6557
6654
  SerializeValue("normalized", Value(accessor.normalized), o);
6558
6655
  std::string type;
@@ -6662,10 +6759,15 @@ static void SerializeGltfAsset(Asset &asset, json &o) {
6662
6759
  SerializeStringProperty("copyright", asset.copyright, o);
6663
6760
  }
6664
6761
 
6665
- if (!asset.version.empty()) {
6666
- SerializeStringProperty("version", asset.version, o);
6762
+ if (asset.version.empty()) {
6763
+ // Just in case
6764
+ // `version` must be defined
6765
+ asset.version = "2.0";
6667
6766
  }
6668
6767
 
6768
+ // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
6769
+ SerializeStringProperty("version", asset.version, o);
6770
+
6669
6771
  if (asset.extras.Keys().size()) {
6670
6772
  SerializeValue("extras", asset.extras, o);
6671
6773
  }
@@ -7063,7 +7165,7 @@ static void SerializeGltfSampler(Sampler &sampler, json &o) {
7063
7165
  if (sampler.minFilter != -1) {
7064
7166
  SerializeNumberProperty("minFilter", sampler.minFilter, o);
7065
7167
  }
7066
- SerializeNumberProperty("wrapR", sampler.wrapR, o);
7168
+ //SerializeNumberProperty("wrapR", sampler.wrapR, o);
7067
7169
  SerializeNumberProperty("wrapS", sampler.wrapS, o);
7068
7170
  SerializeNumberProperty("wrapT", sampler.wrapT, o);
7069
7171
 
@@ -7138,11 +7240,17 @@ static void SerializeGltfScene(Scene &scene, json &o) {
7138
7240
  }
7139
7241
 
7140
7242
  static void SerializeGltfSkin(Skin &skin, json &o) {
7141
- if (skin.inverseBindMatrices != -1)
7243
+ // required
7244
+ SerializeNumberArrayProperty<int>("joints", skin.joints, o);
7245
+
7246
+ if (skin.inverseBindMatrices >= 0) {
7142
7247
  SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
7248
+ }
7249
+
7250
+ if (skin.skeleton >= 0) {
7251
+ SerializeNumberProperty("skeleton", skin.skeleton, o);
7252
+ }
7143
7253
 
7144
- SerializeNumberArrayProperty<int>("joints", skin.joints, o);
7145
- SerializeNumberProperty("skeleton", skin.skeleton, o);
7146
7254
  if (skin.name.size()) {
7147
7255
  SerializeStringProperty("name", skin.name, o);
7148
7256
  }
@@ -7225,6 +7333,16 @@ static void SerializeGltfModel(Model *model, json &o) {
7225
7333
  for (unsigned int i = 0; i < model->materials.size(); ++i) {
7226
7334
  json material;
7227
7335
  SerializeGltfMaterial(model->materials[i], material);
7336
+
7337
+ if (JsonIsNull(material)) {
7338
+ // Issue 294.
7339
+ // `material` does not have any required parameters
7340
+ // so the result may be null(unmodified) when all material parameters
7341
+ // have default value.
7342
+ //
7343
+ // null is not allowed thus we create an empty JSON object.
7344
+ JsonSetObject(material);
7345
+ }
7228
7346
  JsonPushBack(materials, std::move(material));
7229
7347
  }
7230
7348
  JsonAddMember(o, "materials", std::move(materials));
@@ -7363,7 +7481,7 @@ static void SerializeGltfModel(Model *model, json &o) {
7363
7481
  }
7364
7482
 
7365
7483
  // Extensions used
7366
- if (model->extensionsUsed.size()) {
7484
+ if (extensionsUsed.size()) {
7367
7485
  SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
7368
7486
  }
7369
7487
 
@@ -7407,31 +7525,24 @@ static void WriteBinaryGltfStream(std::ostream &stream,
7407
7525
  const std::string header = "glTF";
7408
7526
  const int version = 2;
7409
7527
 
7410
- // https://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number
7411
- auto roundUp = [](uint32_t numToRound, uint32_t multiple) {
7412
- if (multiple == 0) return numToRound;
7413
-
7414
- uint32_t remainder = numToRound % multiple;
7415
- if (remainder == 0) return numToRound;
7416
-
7417
- return numToRound + multiple - remainder;
7418
- };
7419
-
7420
- const uint32_t padding_size =
7421
- roundUp(uint32_t(content.size()), 4) - uint32_t(content.size());
7528
+ const uint32_t content_size = uint32_t(content.size());
7529
+ const uint32_t binBuffer_size = uint32_t(binBuffer.size());
7530
+ // determine number of padding bytes required to ensure 4 byte alignment
7531
+ const uint32_t content_padding_size = content_size % 4 == 0 ? 0 : 4 - content_size % 4;
7532
+ const uint32_t bin_padding_size = binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
7422
7533
 
7423
7534
  // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
7424
- // Chunk data must be located at 4-byte boundary.
7535
+ // Chunk data must be located at 4-byte boundary, which may require padding
7425
7536
  const uint32_t length =
7426
- 12 + 8 + roundUp(uint32_t(content.size()), 4) +
7427
- (binBuffer.size() ? (8 + roundUp(uint32_t(binBuffer.size()), 4)) : 0);
7537
+ 12 + 8 + content_size + content_padding_size +
7538
+ (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
7428
7539
 
7429
7540
  stream.write(header.c_str(), std::streamsize(header.size()));
7430
7541
  stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
7431
7542
  stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
7432
7543
 
7433
7544
  // JSON chunk info, then JSON data
7434
- const uint32_t model_length = uint32_t(content.size()) + padding_size;
7545
+ const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
7435
7546
  const uint32_t model_format = 0x4E4F534A;
7436
7547
  stream.write(reinterpret_cast<const char *>(&model_length),
7437
7548
  sizeof(model_length));
@@ -7440,13 +7551,11 @@ static void WriteBinaryGltfStream(std::ostream &stream,
7440
7551
  stream.write(content.c_str(), std::streamsize(content.size()));
7441
7552
 
7442
7553
  // Chunk must be multiplies of 4, so pad with spaces
7443
- if (padding_size > 0) {
7444
- const std::string padding = std::string(size_t(padding_size), ' ');
7554
+ if (content_padding_size > 0) {
7555
+ const std::string padding = std::string(size_t(content_padding_size), ' ');
7445
7556
  stream.write(padding.c_str(), std::streamsize(padding.size()));
7446
7557
  }
7447
7558
  if (binBuffer.size() > 0) {
7448
- const uint32_t bin_padding_size =
7449
- roundUp(uint32_t(binBuffer.size()), 4) - uint32_t(binBuffer.size());
7450
7559
  // BIN chunk info, then BIN data
7451
7560
  const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
7452
7561
  const uint32_t bin_format = 0x004e4942;