blob: d1d22e78195256ce256305ab440bed4415f8096b [file] [log] [blame]
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001//
Syoyo Fujita9a1ea7e2017-06-20 02:17:28 +09002// Header-only tiny glTF 2.0 loader and serializer.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003//
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005// The MIT License (MIT)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006//
Syoyo Fujitaca56f722019-03-07 21:04:25 +09007// Copyright (c) 2015 - 2019 Syoyo Fujita, Aurélien Chatelain and many
Syoyo Fujita5b407452017-06-04 17:42:41 +09008// contributors.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09009//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090010// Permission is hereby granted, free of charge, to any person obtaining a copy
11// of this software and associated documentation files (the "Software"), to deal
12// in the Software without restriction, including without limitation the rights
13// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14// copies of the Software, and to permit persons to whom the Software is
15// furnished to do so, subject to the following conditions:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090016//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090017// The above copyright notice and this permission notice shall be included in
18// all copies or substantial portions of the Software.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090019//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090020// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26// THE SOFTWARE.
27
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090028// Version:
Syoyo Fujitaca56f722019-03-07 21:04:25 +090029// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks
30// to @Ybalrid)
Syoyo Fujita7ae71102019-01-19 03:03:22 +090031// - v2.1.0 Add draco compression.
Syoyo Fujita0820d832018-10-04 15:45:13 +090032// - v2.0.1 Add comparsion feature(Thanks to @Selmar).
Syoyo Fujitaf6120152017-05-27 23:51:23 +090033// - v2.0.0 glTF 2.0!.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090034//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090035// Tiny glTF loader is using following third party libraries:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090036//
Syoyo Fujita2e21be72017-11-05 17:13:01 +090037// - jsonhpp: C++ JSON library.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090038// - base64: base64 decode/encode library.
39// - stb_image: Image loading library.
40//
Syoyo Fujita2840a0e2017-06-21 02:52:11 +090041#ifndef TINY_GLTF_H_
42#define TINY_GLTF_H_
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090043
Syoyo Fujitad42767e2018-03-15 21:52:00 -050044#include <array>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090045#include <cassert>
Syoyo Fujitad42767e2018-03-15 21:52:00 -050046#include <cstdint>
Selmar Kok31cb7f92018-10-03 15:39:05 +020047#include <cstdlib>
Syoyo Fujita641b3cc2018-10-04 15:43:33 +090048#include <cstring>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090049#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090050#include <string>
51#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090052
Sascha Willems5f9cb242018-12-28 20:53:41 +010053#ifdef __ANDROID__
54#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
55#include <android/asset_manager.h>
56#endif
57#endif
58
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090059namespace tinygltf {
60
61#define TINYGLTF_MODE_POINTS (0)
62#define TINYGLTF_MODE_LINE (1)
63#define TINYGLTF_MODE_LINE_LOOP (2)
Thomas Tissot6c4a0062019-01-30 13:10:51 +010064#define TINYGLTF_MODE_LINE_STRIP (3)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090065#define TINYGLTF_MODE_TRIANGLES (4)
66#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
67#define TINYGLTF_MODE_TRIANGLE_FAN (6)
68
69#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
70#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
71#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
72#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
73#define TINYGLTF_COMPONENT_TYPE_INT (5124)
74#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
75#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
Syoyo Fujita476a8b22018-01-21 12:19:01 +090076#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090077
Syoyo Fujitac2615632016-06-19 21:56:06 +090078#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
79#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
80#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
81#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
82#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
83#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
84
Cemalettin Dervis246d8662017-12-07 20:29:51 +010085#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
Syoyo Fujitac2615632016-06-19 21:56:06 +090086#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -040087#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +090088
Luke San Antoniocdf4cb72016-06-14 21:32:11 -040089// Redeclarations of the above for technique.parameters.
90#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
91#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
92#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
93#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
94#define TINYGLTF_PARAMETER_TYPE_INT (5124)
95#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
96#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
97
98#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
99#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
100#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
101
102#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
103#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
104#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
105
106#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
107#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
108#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
109#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
110
111#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
112#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
113#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
114
115#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
116
117// End parameter types
118
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900119#define TINYGLTF_TYPE_VEC2 (2)
120#define TINYGLTF_TYPE_VEC3 (3)
121#define TINYGLTF_TYPE_VEC4 (4)
122#define TINYGLTF_TYPE_MAT2 (32 + 2)
123#define TINYGLTF_TYPE_MAT3 (32 + 3)
124#define TINYGLTF_TYPE_MAT4 (32 + 4)
125#define TINYGLTF_TYPE_SCALAR (64 + 1)
126#define TINYGLTF_TYPE_VECTOR (64 + 4)
127#define TINYGLTF_TYPE_MATRIX (64 + 16)
128
129#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
130#define TINYGLTF_IMAGE_FORMAT_PNG (1)
131#define TINYGLTF_IMAGE_FORMAT_BMP (2)
132#define TINYGLTF_IMAGE_FORMAT_GIF (3)
133
Luke San Antonio6d616f52016-06-23 14:09:23 -0400134#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
135#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900136#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400137#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
138#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
139
Syoyo Fujitabde70212016-02-07 17:38:17 +0900140#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
141#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
142
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900143#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
144#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
145
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400146#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
147#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
148
Selmar Kok31cb7f92018-10-03 15:39:05 +0200149#define TINYGLTF_DOUBLE_EPS (1.e-12)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900150#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
Selmar Kok31cb7f92018-10-03 15:39:05 +0200151
Sascha Willems5f9cb242018-12-28 20:53:41 +0100152#ifdef __ANDROID__
153#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000154AAssetManager *asset_manager = nullptr;
Sascha Willems5f9cb242018-12-28 20:53:41 +0100155#endif
156#endif
157
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900158typedef enum {
159 NULL_TYPE = 0,
160 NUMBER_TYPE = 1,
161 INT_TYPE = 2,
162 BOOL_TYPE = 3,
163 STRING_TYPE = 4,
164 ARRAY_TYPE = 5,
165 BINARY_TYPE = 6,
166 OBJECT_TYPE = 7
167} Type;
168
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500169static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900170 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
171 return 1;
172 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
173 return 1;
174 } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
175 return 2;
176 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
177 return 2;
178 } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
179 return 4;
180 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
181 return 4;
182 } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
183 return 4;
184 } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
185 return 8;
186 } else {
187 // Unknown componenty type
188 return -1;
189 }
190}
191
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500192static inline int32_t GetTypeSizeInBytes(uint32_t ty) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900193 if (ty == TINYGLTF_TYPE_SCALAR) {
194 return 1;
195 } else if (ty == TINYGLTF_TYPE_VEC2) {
196 return 2;
197 } else if (ty == TINYGLTF_TYPE_VEC3) {
198 return 3;
199 } else if (ty == TINYGLTF_TYPE_VEC4) {
200 return 4;
201 } else if (ty == TINYGLTF_TYPE_MAT2) {
202 return 4;
203 } else if (ty == TINYGLTF_TYPE_MAT3) {
204 return 9;
205 } else if (ty == TINYGLTF_TYPE_MAT4) {
206 return 16;
207 } else {
208 // Unknown componenty type
209 return -1;
210 }
211}
212
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200213bool IsDataURI(const std::string &in);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900214bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
215 const std::string &in, size_t reqBytes, bool checkSize);
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200216
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900217#ifdef __clang__
218#pragma clang diagnostic push
219// Suppress warning for : static Value null_value
220// https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang
221#pragma clang diagnostic ignored "-Wexit-time-destructors"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900222#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900223#endif
224
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900225// Simple class to represent JSON object
226class Value {
227 public:
228 typedef std::vector<Value> Array;
229 typedef std::map<std::string, Value> Object;
230
231 Value() : type_(NULL_TYPE) {}
232
233 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900234 explicit Value(int i) : type_(INT_TYPE) { int_value_ = i; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900235 explicit Value(double n) : type_(NUMBER_TYPE) { number_value_ = n; }
236 explicit Value(const std::string &s) : type_(STRING_TYPE) {
237 string_value_ = s;
238 }
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900239 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900240 binary_value_.resize(n);
241 memcpy(binary_value_.data(), p, n);
242 }
243 explicit Value(const Array &a) : type_(ARRAY_TYPE) {
244 array_value_ = Array(a);
245 }
246 explicit Value(const Object &o) : type_(OBJECT_TYPE) {
247 object_value_ = Object(o);
248 }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900249
250 char Type() const { return static_cast<const char>(type_); }
251
252 bool IsBool() const { return (type_ == BOOL_TYPE); }
253
254 bool IsInt() const { return (type_ == INT_TYPE); }
255
256 bool IsNumber() const { return (type_ == NUMBER_TYPE); }
257
258 bool IsString() const { return (type_ == STRING_TYPE); }
259
260 bool IsBinary() const { return (type_ == BINARY_TYPE); }
261
262 bool IsArray() const { return (type_ == ARRAY_TYPE); }
263
264 bool IsObject() const { return (type_ == OBJECT_TYPE); }
265
266 // Accessor
267 template <typename T>
268 const T &Get() const;
269 template <typename T>
270 T &Get();
271
272 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900273 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900274 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900275 assert(IsArray());
276 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900277 return (static_cast<size_t>(idx) < array_value_.size())
278 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900279 : null_value;
280 }
281
282 // Lookup value from a key-value pair
283 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900284 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900285 assert(IsObject());
286 Object::const_iterator it = object_value_.find(key);
287 return (it != object_value_.end()) ? it->second : null_value;
288 }
289
290 size_t ArrayLen() const {
291 if (!IsArray()) return 0;
292 return array_value_.size();
293 }
294
295 // Valid only for object type.
296 bool Has(const std::string &key) const {
297 if (!IsObject()) return false;
298 Object::const_iterator it = object_value_.find(key);
299 return (it != object_value_.end()) ? true : false;
300 }
301
302 // List keys
303 std::vector<std::string> Keys() const {
304 std::vector<std::string> keys;
305 if (!IsObject()) return keys; // empty
306
307 for (Object::const_iterator it = object_value_.begin();
308 it != object_value_.end(); ++it) {
309 keys.push_back(it->first);
310 }
311
312 return keys;
313 }
314
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900315 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900316
317 bool operator==(const tinygltf::Value &other) const;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000318
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900319 protected:
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900320 int type_;
321
322 int int_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900323 double number_value_;
324 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900325 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900326 Array array_value_;
327 Object object_value_;
328 bool boolean_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900329};
330
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900331#ifdef __clang__
332#pragma clang diagnostic pop
333#endif
334
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900335#define TINYGLTF_VALUE_GET(ctype, var) \
336 template <> \
337 inline const ctype &Value::Get<ctype>() const { \
338 return var; \
339 } \
340 template <> \
341 inline ctype &Value::Get<ctype>() { \
342 return var; \
343 }
344TINYGLTF_VALUE_GET(bool, boolean_value_)
345TINYGLTF_VALUE_GET(double, number_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900346TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900347TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900348TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900349TINYGLTF_VALUE_GET(Value::Array, array_value_)
350TINYGLTF_VALUE_GET(Value::Object, object_value_)
351#undef TINYGLTF_VALUE_GET
352
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900353#ifdef __clang__
354#pragma clang diagnostic push
355#pragma clang diagnostic ignored "-Wc++98-compat"
356#pragma clang diagnostic ignored "-Wpadded"
357#endif
358
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500359/// Agregate object for representing a color
Arthur Brainville58453192018-01-08 18:25:52 +0100360using ColorValue = std::array<double, 4>;
361
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500362struct Parameter {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200363 bool bool_value = false;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700364 bool has_number_value = false;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900365 std::string string_value;
366 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000367 std::map<std::string, double> json_double_value;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200368 double number_value = 0.0;
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500369 // context sensitive methods. depending the type of the Parameter you are
370 // accessing, these are either valid or not
371 // If this parameter represent a texture map in a material, will return the
372 // texture index
Arthur Brainville58453192018-01-08 18:25:52 +0100373
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500374 /// Return the index of a texture if this Parameter is a texture map.
375 /// Returned value is only valid if the parameter represent a texture from a
376 /// material
Arthur Brainville41fe7722018-01-08 18:32:48 +0100377 int TextureIndex() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100378 const auto it = json_double_value.find("index");
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500379 if (it != std::end(json_double_value)) {
Arthur Brainville58453192018-01-08 18:25:52 +0100380 return int(it->second);
381 }
382 return -1;
383 }
384
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000385 /// Return the index of a texture coordinate set if this Parameter is a
386 /// texture map. Returned value is only valid if the parameter represent a
387 /// texture from a material
Sascha Willemseb011062019-02-23 21:15:45 +0100388 int TextureTexCoord() const {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000389 const auto it = json_double_value.find("texCoord");
390 if (it != std::end(json_double_value)) {
391 return int(it->second);
392 }
393 return 0;
Sascha Willemseb011062019-02-23 21:15:45 +0100394 }
395
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500396 /// Material factor, like the roughness or metalness of a material
397 /// Returned value is only valid if the parameter represent a texture from a
398 /// material
Ben Buzbeef6af2242018-04-25 15:13:05 -0700399 double Factor() const { return number_value; }
Arthur Brainville58453192018-01-08 18:25:52 +0100400
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500401 /// Return the color of a material
402 /// Returned value is only valid if the parameter represent a texture from a
403 /// material
Arthur Brainville95853912018-01-08 18:37:44 +0100404 ColorValue ColorFactor() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100405 return {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500406 {// this agregate intialize the std::array object, and uses C++11 RVO.
407 number_array[0], number_array[1], number_array[2],
408 (number_array.size() > 3 ? number_array[3] : 1.0)}};
Arthur Brainville58453192018-01-08 18:25:52 +0100409 }
Selmar Kok31cb7f92018-10-03 15:39:05 +0200410
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900411 bool operator==(const Parameter &) const;
Arthur Brainville58453192018-01-08 18:25:52 +0100412};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900413
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900414#ifdef __clang__
415#pragma clang diagnostic pop
416#endif
417
418#ifdef __clang__
419#pragma clang diagnostic push
420#pragma clang diagnostic ignored "-Wpadded"
421#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900422
Syoyo Fujitabde70212016-02-07 17:38:17 +0900423typedef std::map<std::string, Parameter> ParameterMap;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200424typedef std::map<std::string, Value> ExtensionMap;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900425
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000426struct AnimationChannel {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900427 int sampler; // required
428 int target_node; // required (index of the node to target)
429 std::string target_path; // required in ["translation", "rotation", "scale",
430 // "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900431 Value extras;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900432
Syoyo Fujita5b407452017-06-04 17:42:41 +0900433 AnimationChannel() : sampler(-1), target_node(-1) {}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900434 bool operator==(const AnimationChannel &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000435};
436
437struct AnimationSampler {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900438 int input; // required
439 int output; // required
Syoyo Fujitafbc42952019-05-16 16:54:28 +0900440 std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined string. default "LINEAR"
Jens Olssonb3af2f12018-06-04 10:17:49 +0200441 Value extras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000442
Syoyo Fujita5b407452017-06-04 17:42:41 +0900443 AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900444 bool operator==(const AnimationSampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000445};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900446
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900447struct Animation {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900448 std::string name;
449 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000450 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900451 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200452
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900453 bool operator==(const Animation &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900454};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900455
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000456struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900457 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900458 int inverseBindMatrices; // required here but not in the spec
459 int skeleton; // The index of the node used as a skeleton root
460 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000461
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900462 Skin() {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000463 inverseBindMatrices = -1;
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +0000464 skeleton = -1;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000465 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900466 bool operator==(const Skin &) const;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000467};
468
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000469struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900470 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900471 int minFilter; // ["NEAREST", "LINEAR", "NEAREST_MIPMAP_LINEAR",
472 // "LINEAR_MIPMAP_NEAREST", "NEAREST_MIPMAP_LINEAR",
473 // "LINEAR_MIPMAP_LINEAR"]
474 int magFilter; // ["NEAREST", "LINEAR"]
475 int wrapS; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default
476 // "REPEAT"
477 int wrapT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default
478 // "REPEAT"
479 int wrapR; // TinyGLTF extension
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900480 Value extras;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900481
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000482 Sampler()
timmmeh62a72c42019-01-31 11:46:19 -0800483 : minFilter(TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR),
484 magFilter(TINYGLTF_TEXTURE_FILTER_LINEAR),
timmmeh73584ba2019-01-30 18:38:46 -0800485 wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
Selmar Kok5892d3e2018-12-04 19:55:56 +0100486 wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT),
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000487 wrapR(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900488 bool operator==(const Sampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000489};
490
Syoyo Fujita5b407452017-06-04 17:42:41 +0900491struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900492 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900493 int width;
494 int height;
495 int component;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000496 int bits; // bit depth per channel. 8(byte), 16 or 32.
497 int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
498 // UBYTE(bits = 8) or USHORT(bits = 16)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900499 std::vector<unsigned char> image;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900500 int bufferView; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500501 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
502 // "image/bmp", "image/gif"]
Squareys188965b2018-03-13 22:20:01 +0100503 std::string uri; // (required if no mimeType)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900504 Value extras;
Syoyo Fujita3e53feb2018-09-02 15:36:17 +0900505 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900506
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900507 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
508 // compressed for "image/jpeg" mime) This feature is good if you use custom
509 // image loader function. (e.g. delayed decoding of images for faster glTF
510 // parsing) Default parser for Image does not provide as-is loading feature at
511 // the moment. (You can manipulate this by providing your own LoadImageData
512 // function)
Selmar Kokfa0a9982018-10-03 15:46:23 +0200513 bool as_is;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900514
515 Image() : as_is(false) {
516 bufferView = -1;
517 width = -1;
518 height = -1;
519 component = -1;
520 }
521 bool operator==(const Image &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000522};
523
524struct Texture {
Vladimír Vondruš239be2c2018-07-24 23:23:56 +0200525 std::string name;
526
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000527 int sampler;
Selmar Kok8eb39042018-10-05 14:29:35 +0200528 int source;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900529 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200530 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900531
Syoyo Fujita5b407452017-06-04 17:42:41 +0900532 Texture() : sampler(-1), source(-1) {}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900533 bool operator==(const Texture &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000534};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900535
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000536// Each extension should be stored in a ParameterMap.
537// members not in the values could be included in the ParameterMap
538// to keep a single material model
539struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900540 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900541
Syoyo Fujita5b407452017-06-04 17:42:41 +0900542 ParameterMap values; // PBR metal/roughness workflow
543 ParameterMap additionalValues; // normal/occlusion/emissive values
Selmar09d2ff12018-03-15 17:30:42 +0100544
545 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900546 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200547
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900548 bool operator==(const Material &) const;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000549};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900550
Syoyo Fujita5b407452017-06-04 17:42:41 +0900551struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900552 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900553 int buffer; // Required
554 size_t byteOffset; // minimum 0, default 0
555 size_t byteLength; // required, minimum 1
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900556 size_t byteStride; // minimum 4, maximum 252 (multiple of 4), default 0 =
557 // understood to be tightly packed
Syoyo Fujita5b407452017-06-04 17:42:41 +0900558 int target; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900559 Value extras;
Alex Wood7319db72019-01-24 15:38:16 -0500560 bool dracoDecoded; // Flag indicating this has been draco decoded
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900561
Alex Wood7319db72019-01-24 15:38:16 -0500562 BufferView() : byteOffset(0), byteStride(0), dracoDecoded(false) {}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900563 bool operator==(const BufferView &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000564};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900565
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000566struct Accessor {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900567 int bufferView; // optional in spec but required here since sparse accessor
568 // are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900569 std::string name;
570 size_t byteOffset;
Fabien Freling9056aee2019-03-21 17:03:18 +0100571 bool normalized; // optional.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000572 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900573 size_t count; // required
574 int type; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900575 Value extras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000576
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900577 std::vector<double> minValues; // optional
578 std::vector<double> maxValues; // optional
579
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100580 struct {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000581 int count;
582 bool isSparse;
583 struct {
584 int byteOffset;
585 int bufferView;
586 int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
587 } indices;
588 struct {
589 int bufferView;
590 int byteOffset;
591 } values;
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100592 } sparse;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000593
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900594 ///
595 /// Utility function to compute byteStride for a given bufferView object.
596 /// Returns -1 upon invalid glTF value or parameter configuration.
597 ///
598 int ByteStride(const BufferView &bufferViewObject) const {
599 if (bufferViewObject.byteStride == 0) {
600 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500601 int componentSizeInBytes =
602 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900603 if (componentSizeInBytes <= 0) {
604 return -1;
605 }
606
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900607 int typeSizeInBytes = GetTypeSizeInBytes(static_cast<uint32_t>(type));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900608 if (typeSizeInBytes <= 0) {
609 return -1;
610 }
611
612 return componentSizeInBytes * typeSizeInBytes;
613 } else {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500614 // Check if byteStride is a mulple of the size of the accessor's component
615 // type.
616 int componentSizeInBytes =
617 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900618 if (componentSizeInBytes <= 0) {
619 return -1;
620 }
621
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900622 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900623 return -1;
624 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100625 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900626 }
627
628 return 0;
629 }
630
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000631 Accessor() {
632 bufferView = -1;
633 sparse.isSparse = false;
634 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900635 bool operator==(const tinygltf::Accessor &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000636};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900637
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900638struct PerspectiveCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200639 double aspectRatio; // min > 0
640 double yfov; // required. min > 0
641 double zfar; // min > 0
642 double znear; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900643
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900644 PerspectiveCamera()
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900645 : aspectRatio(0.0),
646 yfov(0.0),
647 zfar(0.0) // 0 = use infinite projecton matrix
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900648 ,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900649 znear(0.0) {}
650 bool operator==(const PerspectiveCamera &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900651
Selmar09d2ff12018-03-15 17:30:42 +0100652 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900653 Value extras;
654};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000655
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900656struct OrthographicCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200657 double xmag; // required. must not be zero.
658 double ymag; // required. must not be zero.
659 double zfar; // required. `zfar` must be greater than `znear`.
660 double znear; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000661
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900662 OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {}
663 bool operator==(const OrthographicCamera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900664
Selmar09d2ff12018-03-15 17:30:42 +0100665 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900666 Value extras;
667};
668
669struct Camera {
670 std::string type; // required. "perspective" or "orthographic"
671 std::string name;
672
673 PerspectiveCamera perspective;
674 OrthographicCamera orthographic;
675
676 Camera() {}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900677 bool operator==(const Camera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900678
Selmar09d2ff12018-03-15 17:30:42 +0100679 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000680 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900681};
682
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000683struct Primitive {
684 std::map<std::string, int> attributes; // (required) A dictionary object of
685 // integer, where each integer
686 // is the index of the accessor
687 // containing an attribute.
688 int material; // The index of the material to apply to this primitive
Syoyo Fujita5b407452017-06-04 17:42:41 +0900689 // when rendering.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000690 int indices; // The index of the accessor that contains the indices.
691 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900692 std::vector<std::map<std::string, int> > targets; // array of morph targets,
Syoyo Fujita5b407452017-06-04 17:42:41 +0900693 // where each target is a dict with attribues in ["POSITION, "NORMAL",
694 // "TANGENT"] pointing
695 // to their corresponding accessors
Alex Wood7319db72019-01-24 15:38:16 -0500696 ExtensionMap extensions;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000697 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900698
Syoyo Fujita5b407452017-06-04 17:42:41 +0900699 Primitive() {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000700 material = -1;
701 indices = -1;
702 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900703 bool operator==(const Primitive &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000704};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900705
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900706struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900707 std::string name;
708 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900709 std::vector<double> weights; // weights to be applied to the Morph Targets
Selmar09d2ff12018-03-15 17:30:42 +0100710 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900711 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200712
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900713 bool operator==(const Mesh &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900714};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900715
716class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900717 public:
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900718 Node() : camera(-1), skin(-1), mesh(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000719
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900720 Node(const Node &rhs) {
721 camera = rhs.camera;
722
723 name = rhs.name;
724 skin = rhs.skin;
725 mesh = rhs.mesh;
726 children = rhs.children;
727 rotation = rhs.rotation;
728 scale = rhs.scale;
729 translation = rhs.translation;
730 matrix = rhs.matrix;
731 weights = rhs.weights;
732
Selmar09d2ff12018-03-15 17:30:42 +0100733 extensions = rhs.extensions;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900734 extras = rhs.extras;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900735 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900736 ~Node() {}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900737 bool operator==(const Node &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900738
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000739 int camera; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900740
741 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000742 int skin;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000743 int mesh;
744 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900745 std::vector<double> rotation; // length must be 0 or 4
746 std::vector<double> scale; // length must be 0 or 3
747 std::vector<double> translation; // length must be 0 or 3
748 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +0900749 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -0700750
Selmar09d2ff12018-03-15 17:30:42 +0100751 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900752 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900753};
754
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900755struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900756 std::string name;
757 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900758 std::string
759 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900760 Value extras;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900761
762 bool operator==(const Buffer &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900763};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900764
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900765struct Asset {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900766 std::string version; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900767 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +0000768 std::string minVersion;
769 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +0100770 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900771 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200772
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900773 bool operator==(const Asset &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900774};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900775
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000776struct Scene {
777 std::string name;
778 std::vector<int> nodes;
779
Selmar09d2ff12018-03-15 17:30:42 +0100780 ExtensionMap extensions;
781 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200782
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900783 bool operator==(const Scene &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000784};
785
Emanuel Schrade186322b2017-11-06 11:14:41 +0100786struct Light {
787 std::string name;
788 std::vector<double> color;
789 std::string type;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200790
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900791 bool operator==(const Light &) const;
Emanuel Schrade186322b2017-11-06 11:14:41 +0100792};
793
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000794class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900795 public:
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000796 Model() {}
797 ~Model() {}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900798 bool operator==(const Model &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900799
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000800 std::vector<Accessor> accessors;
801 std::vector<Animation> animations;
802 std::vector<Buffer> buffers;
803 std::vector<BufferView> bufferViews;
804 std::vector<Material> materials;
805 std::vector<Mesh> meshes;
806 std::vector<Node> nodes;
807 std::vector<Texture> textures;
808 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000809 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000810 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900811 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000812 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +0100813 std::vector<Light> lights;
Selmar09d2ff12018-03-15 17:30:42 +0100814 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900815
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000816 int defaultScene;
817 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +0000818 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900819
820 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900821
822 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900823};
824
Syoyo Fujita0614eb82016-10-14 18:50:14 +0900825enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -0400826 NO_REQUIRE = 0x00,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +0200827 REQUIRE_VERSION = 0x01,
828 REQUIRE_SCENE = 0x02,
829 REQUIRE_SCENES = 0x04,
830 REQUIRE_NODES = 0x08,
831 REQUIRE_ACCESSORS = 0x10,
832 REQUIRE_BUFFERS = 0x20,
833 REQUIRE_BUFFER_VIEWS = 0x40,
834 REQUIRE_ALL = 0x7f
Luke San Antonio9e36b612016-06-23 14:10:51 -0400835};
836
Squareysff644d82018-03-13 22:36:18 +0100837///
838/// LoadImageDataFunction type. Signature for custom image loading callbacks.
839///
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000840typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
841 std::string *, int, int,
842 const unsigned char *, int, void *);
Squareysff644d82018-03-13 22:36:18 +0100843
johan bowald642a3432018-04-01 12:37:18 +0200844///
845/// WriteImageDataFunction type. Signature for custom image writing callbacks.
846///
847typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
848 Image *, bool, void *);
849
Squareys2d3594d2018-03-13 22:40:53 +0100850#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +0100851// Declaration of default image loader callback
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000852bool LoadImageData(Image *image, const int image_idx, std::string *err,
853 std::string *warn, int req_width, int req_height,
854 const unsigned char *bytes, int size, void *);
Squareys2d3594d2018-03-13 22:40:53 +0100855#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900856
johan bowald642a3432018-04-01 12:37:18 +0200857#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
858// Declaration of default image writer callback
859bool WriteImageData(const std::string *basepath, const std::string *filename,
860 Image *image, bool embedImages, void *);
861#endif
862
Paolo Jovone6601bf2018-07-07 20:43:33 +0200863///
864/// FilExistsFunction type. Signature for custom filesystem callbacks.
865///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900866typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +0200867
868///
869/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
870///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900871typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +0200872
873///
874/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
875///
876typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900877 std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +0200878 void *);
879
880///
881/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
882///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900883typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +0200884 const std::vector<unsigned char> &,
885 void *);
886
887///
888/// A structure containing all required filesystem callbacks and a pointer to
889/// their user data.
890///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900891struct FsCallbacks {
892 FileExistsFunction FileExists;
893 ExpandFilePathFunction ExpandFilePath;
894 ReadWholeFileFunction ReadWholeFile;
895 WriteWholeFileFunction WriteWholeFile;
Paolo Jovone6601bf2018-07-07 20:43:33 +0200896
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900897 void *user_data; // An argument that is passed to all fs callbacks
Paolo Jovone6601bf2018-07-07 20:43:33 +0200898};
899
900#ifndef TINYGLTF_NO_FS
901// Declaration of default filesystem callbacks
902
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900903bool FileExists(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +0200904
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900905std::string ExpandFilePath(const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +0200906
907bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900908 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +0200909
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900910bool WriteWholeFile(std::string *err, const std::string &filepath,
911 const std::vector<unsigned char> &contents, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +0200912#endif
913
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900914class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900915 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900916#ifdef __clang__
917#pragma clang diagnostic push
918#pragma clang diagnostic ignored "-Wc++98-compat"
919#endif
920
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500921 TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {}
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900922
923#ifdef __clang__
924#pragma clang diagnostic pop
925#endif
926
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900927 ~TinyGLTF() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900928
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900929 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900930 /// Loads glTF ASCII asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900931 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900932 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900933 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900934 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400935 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +0200936 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900937
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900938 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900939 /// Loads glTF ASCII asset from string(memory).
940 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900941 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900942 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900943 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900944 bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
945 const char *str, const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400946 const std::string &base_dir,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +0200947 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900948
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900949 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900950 /// Loads glTF binary asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900951 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900952 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900953 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900954 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400955 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +0200956 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900957
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900958 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900959 /// Loads glTF binary asset from memory.
960 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900961 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900962 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900963 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900964 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900965 const unsigned char *bytes,
966 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400967 const std::string &base_dir = "",
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +0200968 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900969
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900970 ///
971 /// Write glTF to file.
972 ///
johan bowald642a3432018-04-01 12:37:18 +0200973 bool WriteGltfSceneToFile(Model *model, const std::string &filename,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000974 bool embedImages, bool embedBuffers,
975 bool prettyPrint, bool writeBinary);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000976
Squareysff644d82018-03-13 22:36:18 +0100977 ///
978 /// Set callback to use for loading image data
979 ///
980 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
981
johan bowald642a3432018-04-01 12:37:18 +0200982 ///
983 /// Set callback to use for writing image data
984 ///
985 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
986
Paolo Jovone6601bf2018-07-07 20:43:33 +0200987 ///
988 /// Set callbacks to use for filesystem (fs) access and their user data
989 ///
990 void SetFsCallbacks(FsCallbacks callbacks);
991
Syoyo Fujitabeded612016-05-01 20:03:43 +0900992 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900993 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900994 /// Loads glTF asset from string(memory).
995 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900996 /// Set warning message to `warn` for example it fails to load asserts
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900997 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900998 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900999 bool LoadFromString(Model *model, std::string *err, std::string *warn,
1000 const char *str, const unsigned int length,
1001 const std::string &base_dir, unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001002
Syoyo Fujitabeded612016-05-01 20:03:43 +09001003 const unsigned char *bin_data_;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001004 size_t bin_size_;
1005 bool is_binary_;
Squareysff644d82018-03-13 22:36:18 +01001006
Paolo Jovone6601bf2018-07-07 20:43:33 +02001007 FsCallbacks fs = {
1008#ifndef TINYGLTF_NO_FS
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001009 &tinygltf::FileExists, &tinygltf::ExpandFilePath,
1010 &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001011
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001012 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001013#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001014 nullptr, nullptr, nullptr, nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001015
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001016 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001017#endif
1018 };
1019
Squareysff644d82018-03-13 22:36:18 +01001020 LoadImageDataFunction LoadImageData =
1021#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001022 &tinygltf::LoadImageData;
Squareysff644d82018-03-13 22:36:18 +01001023#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001024 nullptr;
Squareysff644d82018-03-13 22:36:18 +01001025#endif
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001026 void *load_image_user_data_ = reinterpret_cast<void *>(&fs);
johan bowald642a3432018-04-01 12:37:18 +02001027
1028 WriteImageDataFunction WriteImageData =
1029#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001030 &tinygltf::WriteImageData;
johan bowald642a3432018-04-01 12:37:18 +02001031#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001032 nullptr;
johan bowald642a3432018-04-01 12:37:18 +02001033#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02001034 void *write_image_user_data_ = reinterpret_cast<void *>(&fs);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001035};
1036
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001037#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001038#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001039#endif
1040
Syoyo Fujita7c877972016-03-08 01:31:49 +09001041} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001042
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001043#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001044
Selmar Kok31cb7f92018-10-03 15:39:05 +02001045#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
Syoyo Fujita7c877972016-03-08 01:31:49 +09001046#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001047//#include <cassert>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001048#ifndef TINYGLTF_NO_FS
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001049#include <fstream>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001050#endif
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001051#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +09001052
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001053#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001054// Disable some warnings for external files.
1055#pragma clang diagnostic push
1056#pragma clang diagnostic ignored "-Wfloat-equal"
1057#pragma clang diagnostic ignored "-Wexit-time-destructors"
1058#pragma clang diagnostic ignored "-Wconversion"
1059#pragma clang diagnostic ignored "-Wold-style-cast"
Syoyo Fujita643ce102016-05-01 17:19:37 +09001060#pragma clang diagnostic ignored "-Wglobal-constructors"
1061#pragma clang diagnostic ignored "-Wreserved-id-macro"
1062#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1063#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001064#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001065#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001066#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1067#pragma clang diagnostic ignored "-Wswitch-enum"
1068#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001069#pragma clang diagnostic ignored "-Wweak-vtables"
1070#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujitaa8f0b1c2018-08-28 21:33:40 +09001071#if __has_warning("-Wdouble-promotion")
1072#pragma clang diagnostic ignored "-Wdouble-promotion"
1073#endif
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001074#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001075#pragma clang diagnostic ignored "-Wcomma"
1076#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001077#if __has_warning("-Wzero-as-null-pointer-constant")
1078#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1079#endif
1080#if __has_warning("-Wcast-qual")
1081#pragma clang diagnostic ignored "-Wcast-qual"
1082#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001083#if __has_warning("-Wmissing-variable-declarations")
1084#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1085#endif
1086#if __has_warning("-Wmissing-prototypes")
1087#pragma clang diagnostic ignored "-Wmissing-prototypes"
1088#endif
1089#if __has_warning("-Wcast-align")
1090#pragma clang diagnostic ignored "-Wcast-align"
1091#endif
1092#if __has_warning("-Wnewline-eof")
1093#pragma clang diagnostic ignored "-Wnewline-eof"
1094#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001095#if __has_warning("-Wunused-parameter")
1096#pragma clang diagnostic ignored "-Wunused-parameter"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001097#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001098#if __has_warning("-Wmismatched-tags")
1099#pragma clang diagnostic ignored "-Wmismatched-tags"
1100#endif
1101#endif
1102
1103// Disable GCC warnigs
1104#ifdef __GNUC__
1105#pragma GCC diagnostic push
1106#pragma GCC diagnostic ignored "-Wtype-limits"
Syoyo Fujitaca56f722019-03-07 21:04:25 +09001107#endif // __GNUC__
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001108
krokofc0116b2019-03-03 08:28:49 +02001109#ifndef TINYGLTF_NO_INCLUDE_JSON
Alex Wood7319db72019-01-24 15:38:16 -05001110#include "json.hpp"
krokof4b6d112019-03-03 01:11:31 +02001111#endif
Alex Wood7319db72019-01-24 15:38:16 -05001112
1113#ifdef TINYGLTF_ENABLE_DRACO
Alex Wood7319db72019-01-24 15:38:16 -05001114#include "draco/compression/decode.h"
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001115#include "draco/core/decoder_buffer.h"
Alex Wood7319db72019-01-24 15:38:16 -05001116#endif
Squareys2d3594d2018-03-13 22:40:53 +01001117
1118#ifndef TINYGLTF_NO_STB_IMAGE
krokofc0116b2019-03-03 08:28:49 +02001119#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
Alex Wood7319db72019-01-24 15:38:16 -05001120#include "stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +01001121#endif
krokof4b6d112019-03-03 01:11:31 +02001122#endif
Squareys2d3594d2018-03-13 22:40:53 +01001123
johan bowald642a3432018-04-01 12:37:18 +02001124#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
krokofc0116b2019-03-03 08:28:49 +02001125#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
Alex Wood7319db72019-01-24 15:38:16 -05001126#include "stb_image_write.h"
johan bowald642a3432018-04-01 12:37:18 +02001127#endif
krokof4b6d112019-03-03 01:11:31 +02001128#endif
johan bowald642a3432018-04-01 12:37:18 +02001129
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001130#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001131#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001132#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001133
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001134#ifdef __GNUC__
1135#pragma GCC diagnostic pop
1136#endif
1137
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001138#ifdef _WIN32
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001139
1140// issue 143.
1141// Define NOMINMAX to avoid min/max defines,
1142// but undef it after included windows.h
1143#ifndef NOMINMAX
1144#define TINYGLTF_INTERNAL_NOMINMAX
1145#define NOMINMAX
1146#endif
1147
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001148#ifndef WIN32_LEAN_AND_MEAN
1149#define WIN32_LEAN_AND_MEAN
1150#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1151#endif
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001152#include <windows.h> // include API for expanding a file path
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001153
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001154#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1155#undef WIN32_LEAN_AND_MEAN
1156#endif
1157
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001158#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1159#undef NOMINMAX
1160#endif
1161
Florian Märkld525e192017-09-22 15:25:48 +02001162#elif !defined(__ANDROID__)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001163#include <wordexp.h>
1164#endif
1165
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001166#if defined(__sparcv9)
1167// Big endian
1168#else
1169#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1170#define TINYGLTF_LITTLE_ENDIAN 1
1171#endif
1172#endif
1173
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001174using nlohmann::json;
1175
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001176#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001177#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +09001178#endif
1179
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001180#ifdef __clang__
1181#pragma clang diagnostic push
1182#pragma clang diagnostic ignored "-Wc++98-compat"
1183#endif
1184
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001185namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001186
Selmar Kok31cb7f92018-10-03 15:39:05 +02001187// Equals function for Value, for recursivity
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001188static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1189 if (one.Type() != other.Type()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001190
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001191 switch (one.Type()) {
1192 case NULL_TYPE:
1193 return true;
1194 case BOOL_TYPE:
1195 return one.Get<bool>() == other.Get<bool>();
1196 case NUMBER_TYPE:
1197 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1198 case INT_TYPE:
1199 return one.Get<int>() == other.Get<int>();
1200 case OBJECT_TYPE: {
1201 auto oneObj = one.Get<tinygltf::Value::Object>();
1202 auto otherObj = other.Get<tinygltf::Value::Object>();
1203 if (oneObj.size() != otherObj.size()) return false;
1204 for (auto &it : oneObj) {
1205 auto otherIt = otherObj.find(it.first);
1206 if (otherIt == otherObj.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001207
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001208 if (!Equals(it.second, otherIt->second)) return false;
1209 }
1210 return true;
1211 }
1212 case ARRAY_TYPE: {
1213 if (one.Size() != other.Size()) return false;
1214 for (int i = 0; i < int(one.Size()); ++i)
Selmara63cc632019-04-16 16:57:43 +02001215 if (!Equals(one.Get(i), other.Get(i))) return false;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001216 return true;
1217 }
1218 case STRING_TYPE:
1219 return one.Get<std::string>() == other.Get<std::string>();
1220 case BINARY_TYPE:
1221 return one.Get<std::vector<unsigned char> >() ==
1222 other.Get<std::vector<unsigned char> >();
1223 default: {
1224 // unhandled type
1225 return false;
1226 }
1227 }
Selmar Kok31cb7f92018-10-03 15:39:05 +02001228}
1229
1230// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001231static bool Equals(const std::vector<double> &one,
1232 const std::vector<double> &other) {
1233 if (one.size() != other.size()) return false;
1234 for (int i = 0; i < int(one.size()); ++i) {
1235 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1236 }
1237 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001238}
1239
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001240bool Accessor::operator==(const Accessor &other) const {
1241 return this->bufferView == other.bufferView &&
1242 this->byteOffset == other.byteOffset &&
1243 this->componentType == other.componentType &&
1244 this->count == other.count && this->extras == other.extras &&
1245 Equals(this->maxValues, other.maxValues) &&
1246 Equals(this->minValues, other.minValues) && this->name == other.name &&
1247 this->normalized == other.normalized && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001248}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001249bool Animation::operator==(const Animation &other) const {
1250 return this->channels == other.channels && this->extras == other.extras &&
1251 this->name == other.name && this->samplers == other.samplers;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001252}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001253bool AnimationChannel::operator==(const AnimationChannel &other) const {
1254 return this->extras == other.extras &&
1255 this->target_node == other.target_node &&
1256 this->target_path == other.target_path &&
1257 this->sampler == other.sampler;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001258}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001259bool AnimationSampler::operator==(const AnimationSampler &other) const {
1260 return this->extras == other.extras && this->input == other.input &&
1261 this->interpolation == other.interpolation &&
1262 this->output == other.output;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001263}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001264bool Asset::operator==(const Asset &other) const {
1265 return this->copyright == other.copyright &&
1266 this->extensions == other.extensions && this->extras == other.extras &&
1267 this->generator == other.generator &&
1268 this->minVersion == other.minVersion && this->version == other.version;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001269}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001270bool Buffer::operator==(const Buffer &other) const {
1271 return this->data == other.data && this->extras == other.extras &&
1272 this->name == other.name && this->uri == other.uri;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001273}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001274bool BufferView::operator==(const BufferView &other) const {
1275 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1276 this->byteOffset == other.byteOffset &&
1277 this->byteStride == other.byteStride && this->name == other.name &&
Alexander Wood0d77a292019-01-26 08:58:45 -05001278 this->target == other.target && this->extras == other.extras &&
1279 this->dracoDecoded == other.dracoDecoded;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001280}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001281bool Camera::operator==(const Camera &other) const {
1282 return this->name == other.name && this->extensions == other.extensions &&
1283 this->extras == other.extras &&
1284 this->orthographic == other.orthographic &&
1285 this->perspective == other.perspective && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001286}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001287bool Image::operator==(const Image &other) const {
1288 return this->bufferView == other.bufferView &&
1289 this->component == other.component && this->extras == other.extras &&
1290 this->height == other.height && this->image == other.image &&
1291 this->mimeType == other.mimeType && this->name == other.name &&
1292 this->uri == other.uri && this->width == other.width;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001293}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001294bool Light::operator==(const Light &other) const {
1295 return Equals(this->color, other.color) && this->name == other.name &&
1296 this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001297}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001298bool Material::operator==(const Material &other) const {
1299 return this->additionalValues == other.additionalValues &&
1300 this->extensions == other.extensions && this->extras == other.extras &&
1301 this->name == other.name && this->values == other.values;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001302}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001303bool Mesh::operator==(const Mesh &other) const {
1304 return this->extensions == other.extensions && this->extras == other.extras &&
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02001305 this->name == other.name && this->primitives == other.primitives;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001306}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001307bool Model::operator==(const Model &other) const {
1308 return this->accessors == other.accessors &&
1309 this->animations == other.animations && this->asset == other.asset &&
1310 this->buffers == other.buffers &&
1311 this->bufferViews == other.bufferViews &&
1312 this->cameras == other.cameras &&
1313 this->defaultScene == other.defaultScene &&
1314 this->extensions == other.extensions &&
1315 this->extensionsRequired == other.extensionsRequired &&
1316 this->extensionsUsed == other.extensionsUsed &&
1317 this->extras == other.extras && this->images == other.images &&
1318 this->lights == other.lights && this->materials == other.materials &&
1319 this->meshes == other.meshes && this->nodes == other.nodes &&
1320 this->samplers == other.samplers && this->scenes == other.scenes &&
1321 this->skins == other.skins && this->textures == other.textures;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001322}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001323bool Node::operator==(const Node &other) const {
1324 return this->camera == other.camera && this->children == other.children &&
1325 this->extensions == other.extensions && this->extras == other.extras &&
1326 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
1327 this->name == other.name && Equals(this->rotation, other.rotation) &&
1328 Equals(this->scale, other.scale) && this->skin == other.skin &&
1329 Equals(this->translation, other.translation) &&
1330 Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001331}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001332bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
1333 return this->extensions == other.extensions && this->extras == other.extras &&
1334 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
1335 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
1336 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1337 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001338}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001339bool Parameter::operator==(const Parameter &other) const {
1340 if (this->bool_value != other.bool_value ||
1341 this->has_number_value != other.has_number_value)
1342 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001343
Selmar Kok2bda71c2018-10-05 14:36:05 +02001344 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
1345 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001346
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001347 if (this->json_double_value.size() != other.json_double_value.size())
1348 return false;
1349 for (auto &it : this->json_double_value) {
1350 auto otherIt = other.json_double_value.find(it.first);
1351 if (otherIt == other.json_double_value.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001352
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001353 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
1354 }
1355
1356 if (!Equals(this->number_array, other.number_array)) return false;
1357
1358 if (this->string_value != other.string_value) return false;
1359
1360 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001361}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001362bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
1363 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
1364 this->extensions == other.extensions && this->extras == other.extras &&
1365 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
1366 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1367 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001368}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001369bool Primitive::operator==(const Primitive &other) const {
1370 return this->attributes == other.attributes && this->extras == other.extras &&
1371 this->indices == other.indices && this->material == other.material &&
1372 this->mode == other.mode && this->targets == other.targets;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001373}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001374bool Sampler::operator==(const Sampler &other) const {
1375 return this->extras == other.extras && this->magFilter == other.magFilter &&
1376 this->minFilter == other.minFilter && this->name == other.name &&
1377 this->wrapR == other.wrapR && this->wrapS == other.wrapS &&
1378 this->wrapT == other.wrapT;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001379}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001380bool Scene::operator==(const Scene &other) const {
1381 return this->extensions == other.extensions && this->extras == other.extras &&
1382 this->name == other.name && this->nodes == other.nodes;
1383 ;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001384}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001385bool Skin::operator==(const Skin &other) const {
1386 return this->inverseBindMatrices == other.inverseBindMatrices &&
1387 this->joints == other.joints && this->name == other.name &&
1388 this->skeleton == other.skeleton;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001389}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001390bool Texture::operator==(const Texture &other) const {
1391 return this->extensions == other.extensions && this->extras == other.extras &&
1392 this->name == other.name && this->sampler == other.sampler &&
1393 this->source == other.source;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001394}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001395bool Value::operator==(const Value &other) const {
1396 return Equals(*this, other);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001397}
1398
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001399static void swap4(unsigned int *val) {
1400#ifdef TINYGLTF_LITTLE_ENDIAN
1401 (void)val;
1402#else
1403 unsigned int tmp = *val;
1404 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
1405 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
1406
1407 dst[0] = src[3];
1408 dst[1] = src[2];
1409 dst[2] = src[1];
1410 dst[3] = src[0];
1411#endif
1412}
1413
Syoyo Fujitabeded612016-05-01 20:03:43 +09001414static std::string JoinPath(const std::string &path0,
1415 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001416 if (path0.empty()) {
1417 return path1;
1418 } else {
1419 // check '/'
1420 char lastChar = *path0.rbegin();
1421 if (lastChar != '/') {
1422 return path0 + std::string("/") + path1;
1423 } else {
1424 return path0 + path1;
1425 }
1426 }
1427}
1428
Syoyo Fujita643ce102016-05-01 17:19:37 +09001429static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001430 const std::string &filepath, FsCallbacks *fs) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001431 if (fs == nullptr || fs->ExpandFilePath == nullptr ||
1432 fs->FileExists == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001433 // Error, fs callback[s] missing
1434 return std::string();
1435 }
1436
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001437 for (size_t i = 0; i < paths.size(); i++) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001438 std::string absPath =
1439 fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001440 if (fs->FileExists(absPath, fs->user_data)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001441 return absPath;
1442 }
1443 }
1444
1445 return std::string();
1446}
1447
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001448static std::string GetFilePathExtension(const std::string &FileName) {
johan bowald642a3432018-04-01 12:37:18 +02001449 if (FileName.find_last_of(".") != std::string::npos)
1450 return FileName.substr(FileName.find_last_of(".") + 1);
1451 return "";
1452}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001453
Syoyo Fujita643ce102016-05-01 17:19:37 +09001454static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001455 if (filepath.find_last_of("/\\") != std::string::npos)
1456 return filepath.substr(0, filepath.find_last_of("/\\"));
1457 return "";
1458}
1459
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001460// https://stackoverflow.com/questions/8520560/get-a-file-name-from-a-path
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001461static std::string GetBaseFilename(const std::string &filepath) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001462 return filepath.substr(filepath.find_last_of("/\\") + 1);
1463}
1464
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001465std::string base64_encode(unsigned char const *, unsigned int len);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001466std::string base64_decode(std::string const &s);
1467
1468/*
1469 base64.cpp and base64.h
1470
1471 Copyright (C) 2004-2008 René Nyffenegger
1472
1473 This source code is provided 'as-is', without any express or implied
1474 warranty. In no event will the author be held liable for any damages
1475 arising from the use of this software.
1476
1477 Permission is granted to anyone to use this software for any purpose,
1478 including commercial applications, and to alter it and redistribute it
1479 freely, subject to the following restrictions:
1480
1481 1. The origin of this source code must not be misrepresented; you must not
1482 claim that you wrote the original source code. If you use this source code
1483 in a product, an acknowledgment in the product documentation would be
1484 appreciated but is not required.
1485
1486 2. Altered source versions must be plainly marked as such, and must not be
1487 misrepresented as being the original source code.
1488
1489 3. This notice may not be removed or altered from any source distribution.
1490
1491 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
1492
1493*/
1494
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001495#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001496#pragma clang diagnostic push
1497#pragma clang diagnostic ignored "-Wexit-time-destructors"
1498#pragma clang diagnostic ignored "-Wglobal-constructors"
1499#pragma clang diagnostic ignored "-Wsign-conversion"
1500#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001501#endif
Syoyo Fujita7c877972016-03-08 01:31:49 +09001502static const std::string base64_chars =
1503 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1504 "abcdefghijklmnopqrstuvwxyz"
1505 "0123456789+/";
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001506
1507static inline bool is_base64(unsigned char c) {
1508 return (isalnum(c) || (c == '+') || (c == '/'));
1509}
1510
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001511std::string base64_encode(unsigned char const *bytes_to_encode,
1512 unsigned int in_len) {
johan bowald30c53472018-03-30 11:49:36 +02001513 std::string ret;
1514 int i = 0;
1515 int j = 0;
1516 unsigned char char_array_3[3];
1517 unsigned char char_array_4[4];
1518
1519 while (in_len--) {
1520 char_array_3[i++] = *(bytes_to_encode++);
1521 if (i == 3) {
1522 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001523 char_array_4[1] =
1524 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
1525 char_array_4[2] =
1526 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02001527 char_array_4[3] = char_array_3[2] & 0x3f;
1528
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001529 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
johan bowald30c53472018-03-30 11:49:36 +02001530 i = 0;
1531 }
1532 }
1533
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001534 if (i) {
1535 for (j = i; j < 3; j++) char_array_3[j] = '\0';
johan bowald30c53472018-03-30 11:49:36 +02001536
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001537 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
1538 char_array_4[1] =
1539 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
1540 char_array_4[2] =
1541 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02001542
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001543 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
johan bowald30c53472018-03-30 11:49:36 +02001544
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001545 while ((i++ < 3)) ret += '=';
johan bowald30c53472018-03-30 11:49:36 +02001546 }
1547
1548 return ret;
johan bowald30c53472018-03-30 11:49:36 +02001549}
1550
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001551std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +09001552 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001553 int i = 0;
1554 int j = 0;
1555 int in_ = 0;
1556 unsigned char char_array_4[4], char_array_3[3];
1557 std::string ret;
1558
1559 while (in_len-- && (encoded_string[in_] != '=') &&
1560 is_base64(encoded_string[in_])) {
1561 char_array_4[i++] = encoded_string[in_];
1562 in_++;
1563 if (i == 4) {
1564 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09001565 char_array_4[i] =
1566 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001567
1568 char_array_3[0] =
1569 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
1570 char_array_3[1] =
1571 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
1572 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
1573
Syoyo Fujita7c877972016-03-08 01:31:49 +09001574 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001575 i = 0;
1576 }
1577 }
1578
1579 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001580 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001581
1582 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09001583 char_array_4[j] =
1584 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001585
1586 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
1587 char_array_3[1] =
1588 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
1589 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
1590
Syoyo Fujita7c877972016-03-08 01:31:49 +09001591 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001592 }
1593
1594 return ret;
1595}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001596#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001597#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001598#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001599
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001600static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001601 std::string *warn, const std::string &filename,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001602 const std::string &basedir, bool required,
1603 size_t reqBytes, bool checkSize, FsCallbacks *fs) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001604 if (fs == nullptr || fs->FileExists == nullptr ||
1605 fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001606 // This is a developer error, assert() ?
1607 if (err) {
1608 (*err) += "FS callback[s] not set\n";
1609 }
1610 return false;
1611 }
1612
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001613 std::string *failMsgOut = required ? err : warn;
Selmar Koke3b3fa92018-08-22 19:04:21 +02001614
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001615 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001616
1617 std::vector<std::string> paths;
1618 paths.push_back(basedir);
1619 paths.push_back(".");
1620
Paolo Jovone6601bf2018-07-07 20:43:33 +02001621 std::string filepath = FindFile(paths, filename, fs);
jianghaosenb721fb72017-08-25 21:01:17 +08001622 if (filepath.empty() || filename.empty()) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02001623 if (failMsgOut) {
1624 (*failMsgOut) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001625 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001626 return false;
1627 }
1628
Paolo Jovone6601bf2018-07-07 20:43:33 +02001629 std::vector<unsigned char> buf;
1630 std::string fileReadErr;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001631 bool fileRead =
1632 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001633 if (!fileRead) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02001634 if (failMsgOut) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001635 (*failMsgOut) +=
1636 "File read error : " + filepath + " : " + fileReadErr + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001637 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001638 return false;
1639 }
1640
Paolo Jovone6601bf2018-07-07 20:43:33 +02001641 size_t sz = buf.size();
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09001642 if (sz == 0) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001643 if (failMsgOut) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02001644 (*failMsgOut) += "File is empty : " + filepath + "\n";
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001645 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09001646 return false;
1647 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001648
1649 if (checkSize) {
1650 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001651 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001652 return true;
1653 } else {
1654 std::stringstream ss;
1655 ss << "File size mismatch : " << filepath << ", requestedBytes "
1656 << reqBytes << ", but got " << sz << std::endl;
Selmar Koke3b3fa92018-08-22 19:04:21 +02001657 if (failMsgOut) {
1658 (*failMsgOut) += ss.str();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001659 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001660 return false;
1661 }
1662 }
1663
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001664 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001665 return true;
1666}
1667
Squareysff644d82018-03-13 22:36:18 +01001668void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001669 LoadImageData = func;
1670 load_image_user_data_ = user_data;
Squareysff644d82018-03-13 22:36:18 +01001671}
1672
Squareys2d3594d2018-03-13 22:40:53 +01001673#ifndef TINYGLTF_NO_STB_IMAGE
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01001674bool LoadImageData(Image *image, const int image_idx, std::string *err,
1675 std::string *warn, int req_width, int req_height,
1676 const unsigned char *bytes, int size, void *user_data) {
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001677 (void)user_data;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001678 (void)warn;
1679
Victor Bushong18ef3382018-08-22 22:03:30 -05001680 int w, h, comp, req_comp;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001681
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01001682 unsigned char *data = nullptr;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01001683
Victor Bushong18ef3382018-08-22 22:03:30 -05001684 // force 32-bit textures for common Vulkan compatibility. It appears that
1685 // some GPU drivers do not support 24-bit images for Vulkan
1686 req_comp = 4;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01001687 int bits = 8;
1688 int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001689
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01001690 // It is possible that the image we want to load is a 16bit per channel image
1691 // We are going to attempt to load it as 16bit per channel, and if it worked,
1692 // set the image data accodingly. We are casting the returned pointer into
1693 // unsigned char, because we are representing "bytes". But we are updating
1694 // the Image metadata to signal that this image uses 2 bytes (16bits) per
1695 // channel:
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01001696 if (stbi_is_16_bit_from_memory(bytes, size)) {
1697 data = (unsigned char *)stbi_load_16_from_memory(bytes, size, &w, &h, &comp,
1698 req_comp);
1699 if (data) {
1700 bits = 16;
1701 pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
1702 }
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01001703 }
1704
1705 // at this point, if data is still NULL, it means that the image wasn't
1706 // 16bit per channel, we are going to load it as a normal 8bit per channel
1707 // mage as we used to do:
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00001708 // if image cannot be decoded, ignore parsing and keep it by its path
1709 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +09001710 // FIXME we should only enter this function if the image is embedded. If
1711 // image->uri references
1712 // an image file, it should be left as it is. Image loading should not be
1713 // mandatory (to support other formats)
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01001714 if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001715 if (!data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001716 // NOTE: you can use `warn` instead of `err`
Syoyo Fujitabeded612016-05-01 20:03:43 +09001717 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01001718 (*err) +=
1719 "Unknown image format. STB cannot decode image data for image[" +
1720 std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001721 }
Omar C. Fd492efc2018-02-10 09:50:35 +02001722 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001723 }
1724
1725 if (w < 1 || h < 1) {
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01001726 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001727 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01001728 (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
1729 "] name = \"" + image->name + "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001730 }
Omar C. Fd492efc2018-02-10 09:50:35 +02001731 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001732 }
1733
1734 if (req_width > 0) {
1735 if (req_width != w) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01001736 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001737 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01001738 (*err) += "Image width mismatch for image[" +
1739 std::to_string(image_idx) + "] name = \"" + image->name +
1740 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001741 }
1742 return false;
1743 }
1744 }
1745
1746 if (req_height > 0) {
1747 if (req_height != h) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01001748 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001749 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01001750 (*err) += "Image height mismatch. for image[" +
1751 std::to_string(image_idx) + "] name = \"" + image->name +
1752 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001753 }
1754 return false;
1755 }
1756 }
1757
1758 image->width = w;
1759 image->height = h;
Victor Bushong18ef3382018-08-22 22:03:30 -05001760 image->component = req_comp;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01001761 image->bits = bits;
1762 image->pixel_type = pixel_type;
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01001763 image->image.resize(static_cast<size_t>(w * h * req_comp) * (bits / 8));
1764 std::copy(data, data + w * h * req_comp * (bits / 8), image->image.begin());
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01001765 stbi_image_free(data);
Syoyo Fujita4be2f882016-11-24 16:20:34 +09001766
Syoyo Fujitabeded612016-05-01 20:03:43 +09001767 return true;
1768}
Squareys2d3594d2018-03-13 22:40:53 +01001769#endif
Syoyo Fujitabeded612016-05-01 20:03:43 +09001770
johan bowald642a3432018-04-01 12:37:18 +02001771void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
1772 WriteImageData = func;
1773 write_image_user_data_ = user_data;
1774}
1775
1776#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1777static void WriteToMemory_stbi(void *context, void *data, int size) {
1778 std::vector<unsigned char> *buffer =
1779 reinterpret_cast<std::vector<unsigned char> *>(context);
1780
1781 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
1782
1783 buffer->insert(buffer->end(), pData, pData + size);
1784}
1785
1786bool WriteImageData(const std::string *basepath, const std::string *filename,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001787 Image *image, bool embedImages, void *fsPtr) {
johan bowald642a3432018-04-01 12:37:18 +02001788 const std::string ext = GetFilePathExtension(*filename);
1789
Paolo Jovone6601bf2018-07-07 20:43:33 +02001790 // Write image to temporary buffer
1791 std::string header;
1792 std::vector<unsigned char> data;
johan bowald642a3432018-04-01 12:37:18 +02001793
Paolo Jovone6601bf2018-07-07 20:43:33 +02001794 if (ext == "png") {
Syoyo Fujitaca56f722019-03-07 21:04:25 +09001795 if ((image->bits != 8) ||
1796 (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
Syoyo Fujitae8a46c42019-03-07 20:50:58 +09001797 // Unsupported pixel format
1798 return false;
1799 }
1800
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09001801 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001802 image->height, image->component,
1803 &image->image[0], 0)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09001804 return false;
1805 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02001806 header = "data:image/png;base64,";
1807 } else if (ext == "jpg") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09001808 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001809 image->height, image->component,
1810 &image->image[0], 100)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09001811 return false;
1812 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02001813 header = "data:image/jpeg;base64,";
1814 } else if (ext == "bmp") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09001815 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001816 image->height, image->component,
1817 &image->image[0])) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09001818 return false;
1819 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02001820 header = "data:image/bmp;base64,";
1821 } else if (!embedImages) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001822 // Error: can't output requested format to file
1823 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02001824 }
johan bowald642a3432018-04-01 12:37:18 +02001825
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001826 if (embedImages) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001827 // Embed base64-encoded image into URI
johan bowald642a3432018-04-01 12:37:18 +02001828 if (data.size()) {
1829 image->uri =
1830 header +
1831 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
1832 } else {
1833 // Throw error?
1834 }
1835 } else {
1836 // Write image to disc
Paolo Jovone6601bf2018-07-07 20:43:33 +02001837 FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
Syoyo Fujita33514af2018-11-05 13:32:43 +09001838 if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001839 const std::string imagefilepath = JoinPath(*basepath, *filename);
1840 std::string writeError;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001841 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
1842 fs->user_data)) {
1843 // Could not write image file to disc; Throw error ?
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09001844 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02001845 }
johan bowald642a3432018-04-01 12:37:18 +02001846 } else {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001847 // Throw error?
johan bowald642a3432018-04-01 12:37:18 +02001848 }
1849 image->uri = *filename;
1850 }
1851
1852 return true;
1853}
1854#endif
1855
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001856void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
Paolo Jovone6601bf2018-07-07 20:43:33 +02001857
1858#ifndef TINYGLTF_NO_FS
1859// Default implementations of filesystem functions
1860
1861bool FileExists(const std::string &abs_filename, void *) {
1862 bool ret;
Sascha Willems5f9cb242018-12-28 20:53:41 +01001863#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001864 if (asset_manager) {
1865 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
1866 AASSET_MODE_STREAMING);
1867 if (!asset) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01001868 return false;
1869 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001870 AAsset_close(asset);
1871 ret = true;
1872 } else {
1873 return false;
1874 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01001875#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02001876#ifdef _WIN32
1877 FILE *fp;
1878 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
1879 if (err != 0) {
1880 return false;
1881 }
1882#else
1883 FILE *fp = fopen(abs_filename.c_str(), "rb");
1884#endif
1885 if (fp) {
1886 ret = true;
1887 fclose(fp);
1888 } else {
1889 ret = false;
1890 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01001891#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02001892
1893 return ret;
1894}
1895
1896std::string ExpandFilePath(const std::string &filepath, void *) {
1897#ifdef _WIN32
1898 DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
1899 char *str = new char[len];
1900 ExpandEnvironmentStringsA(filepath.c_str(), str, len);
1901
1902 std::string s(str);
1903
1904 delete[] str;
1905
1906 return s;
1907#else
1908
1909#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
Vladimír Vondruš9f045832018-07-24 23:32:17 +02001910 defined(__ANDROID__) || defined(__EMSCRIPTEN__)
Paolo Jovone6601bf2018-07-07 20:43:33 +02001911 // no expansion
1912 std::string s = filepath;
1913#else
1914 std::string s;
1915 wordexp_t p;
1916
1917 if (filepath.empty()) {
1918 return "";
1919 }
1920
1921 // char** w;
1922 int ret = wordexp(filepath.c_str(), &p, 0);
1923 if (ret) {
1924 // err
1925 s = filepath;
1926 return s;
1927 }
1928
1929 // Use first element only.
1930 if (p.we_wordv) {
1931 s = std::string(p.we_wordv[0]);
1932 wordfree(&p);
1933 } else {
1934 s = filepath;
1935 }
1936
1937#endif
1938
1939 return s;
1940#endif
1941}
1942
1943bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
1944 const std::string &filepath, void *) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01001945#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
1946 if (asset_manager) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001947 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
1948 AASSET_MODE_STREAMING);
Sascha Willems5f9cb242018-12-28 20:53:41 +01001949 if (!asset) {
1950 if (err) {
1951 (*err) += "File open error : " + filepath + "\n";
1952 }
1953 return false;
1954 }
1955 size_t size = AAsset_getLength(asset);
1956 if (size <= 0) {
1957 if (err) {
1958 (*err) += "Invalid file size : " + filepath +
1959 " (does the path point to a directory?)";
1960 }
1961 }
1962 out->resize(size);
1963 AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
1964 AAsset_close(asset);
1965 return true;
1966 } else {
1967 if (err) {
1968 (*err) += "No asset manager specified : " + filepath + "\n";
1969 }
1970 return false;
1971 }
1972#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02001973 std::ifstream f(filepath.c_str(), std::ifstream::binary);
1974 if (!f) {
1975 if (err) {
1976 (*err) += "File open error : " + filepath + "\n";
1977 }
1978 return false;
1979 }
1980
1981 f.seekg(0, f.end);
1982 size_t sz = static_cast<size_t>(f.tellg());
1983 f.seekg(0, f.beg);
1984
1985 if (int(sz) < 0) {
1986 if (err) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001987 (*err) += "Invalid file size : " + filepath +
1988 " (does the path point to a directory?)";
Paolo Jovone6601bf2018-07-07 20:43:33 +02001989 }
1990 return false;
1991 } else if (sz == 0) {
1992 if (err) {
1993 (*err) += "File is empty : " + filepath + "\n";
1994 }
1995 return false;
1996 }
1997
1998 out->resize(sz);
1999 f.read(reinterpret_cast<char *>(&out->at(0)),
2000 static_cast<std::streamsize>(sz));
2001 f.close();
2002
2003 return true;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002004#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002005}
2006
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002007bool WriteWholeFile(std::string *err, const std::string &filepath,
2008 const std::vector<unsigned char> &contents, void *) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002009 std::ofstream f(filepath.c_str(), std::ofstream::binary);
2010 if (!f) {
2011 if (err) {
2012 (*err) += "File open error for writing : " + filepath + "\n";
2013 }
2014 return false;
2015 }
2016
2017 f.write(reinterpret_cast<const char *>(&contents.at(0)),
2018 static_cast<std::streamsize>(contents.size()));
2019 if (!f) {
2020 if (err) {
2021 (*err) += "File write error: " + filepath + "\n";
2022 }
2023 return false;
2024 }
2025
2026 f.close();
2027 return true;
2028}
2029
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002030#endif // TINYGLTF_NO_FS
Paolo Jovone6601bf2018-07-07 20:43:33 +02002031
johan bowald642a3432018-04-01 12:37:18 +02002032static std::string MimeToExt(const std::string &mimeType) {
2033 if (mimeType == "image/jpeg") {
2034 return "jpg";
2035 } else if (mimeType == "image/png") {
2036 return "png";
2037 } else if (mimeType == "image/bmp") {
2038 return "bmp";
2039 } else if (mimeType == "image/gif") {
2040 return "gif";
2041 }
2042
2043 return "";
2044}
2045
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002046static void UpdateImageObject(Image &image, std::string &baseDir, int index,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002047 bool embedImages,
2048 WriteImageDataFunction *WriteImageData = nullptr,
2049 void *user_data = nullptr) {
johan bowald642a3432018-04-01 12:37:18 +02002050 std::string filename;
2051 std::string ext;
2052
2053 // If image have uri. Use it it as a filename
2054 if (image.uri.size()) {
2055 filename = GetBaseFilename(image.uri);
2056 ext = GetFilePathExtension(filename);
2057
2058 } else if (image.name.size()) {
2059 ext = MimeToExt(image.mimeType);
2060 // Otherwise use name as filename
2061 filename = image.name + "." + ext;
2062 } else {
2063 ext = MimeToExt(image.mimeType);
2064 // Fallback to index of image as filename
2065 filename = std::to_string(index) + "." + ext;
2066 }
2067
2068 // If callback is set, modify image data object
2069 if (*WriteImageData != nullptr) {
2070 std::string uri;
2071 (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
2072 }
2073}
2074
Selmar Kok0d0e97e2018-08-22 14:01:57 +02002075bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002076 std::string header = "data:application/octet-stream;base64,";
2077 if (in.find(header) == 0) {
2078 return true;
2079 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002080
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002081 header = "data:image/jpeg;base64,";
2082 if (in.find(header) == 0) {
2083 return true;
2084 }
Squareys43374632018-03-13 22:20:48 +01002085
Syoyo Fujita620eed12016-01-02 23:37:12 +09002086 header = "data:image/png;base64,";
2087 if (in.find(header) == 0) {
2088 return true;
2089 }
Squareys43374632018-03-13 22:20:48 +01002090
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002091 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002092 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002093 return true;
2094 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002095
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002096 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002097 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002098 return true;
2099 }
2100
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002101 header = "data:text/plain;base64,";
2102 if (in.find(header) == 0) {
2103 return true;
2104 }
2105
Syoyo Fujita20244e12018-03-15 11:01:05 -05002106 header = "data:application/gltf-buffer;base64,";
2107 if (in.find(header) == 0) {
2108 return true;
2109 }
2110
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002111 return false;
2112}
2113
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002114bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
2115 const std::string &in, size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002116 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09002117 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002118 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002119 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002120 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002121
2122 if (data.empty()) {
2123 header = "data:image/jpeg;base64,";
2124 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002125 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002126 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002127 }
2128 }
2129
2130 if (data.empty()) {
2131 header = "data:image/png;base64,";
2132 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002133 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002134 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002135 }
2136 }
Squareys43374632018-03-13 22:20:48 +01002137
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002138 if (data.empty()) {
2139 header = "data:image/bmp;base64,";
2140 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002141 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002142 data = base64_decode(in.substr(header.size())); // cut mime string.
2143 }
2144 }
2145
2146 if (data.empty()) {
2147 header = "data:image/gif;base64,";
2148 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002149 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002150 data = base64_decode(in.substr(header.size())); // cut mime string.
2151 }
2152 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002153
2154 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002155 header = "data:text/plain;base64,";
2156 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002157 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002158 data = base64_decode(in.substr(header.size()));
2159 }
2160 }
2161
2162 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002163 header = "data:application/gltf-buffer;base64,";
2164 if (in.find(header) == 0) {
2165 data = base64_decode(in.substr(header.size()));
2166 }
2167 }
2168
2169 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002170 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09002171 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002172
2173 if (checkSize) {
2174 if (data.size() != reqBytes) {
2175 return false;
2176 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002177 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09002178 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002179 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002180 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002181 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002182 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002183}
2184
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002185static bool ParseJsonAsValue(Value *ret, const json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02002186 Value val{};
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002187 switch (o.type()) {
2188 case json::value_t::object: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02002189 Value::Object value_object;
2190 for (auto it = o.begin(); it != o.end(); it++) {
2191 Value entry;
2192 ParseJsonAsValue(&entry, it.value());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002193 if (entry.Type() != NULL_TYPE) value_object[it.key()] = entry;
Selmar Kokfa7022f2018-04-04 18:10:20 +02002194 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002195 if (value_object.size() > 0) val = Value(value_object);
2196 } break;
2197 case json::value_t::array: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02002198 Value::Array value_array;
2199 for (auto it = o.begin(); it != o.end(); it++) {
2200 Value entry;
2201 ParseJsonAsValue(&entry, it.value());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002202 if (entry.Type() != NULL_TYPE) value_array.push_back(entry);
Selmar Kokfa7022f2018-04-04 18:10:20 +02002203 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002204 if (value_array.size() > 0) val = Value(value_array);
2205 } break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02002206 case json::value_t::string:
2207 val = Value(o.get<std::string>());
2208 break;
2209 case json::value_t::boolean:
2210 val = Value(o.get<bool>());
2211 break;
2212 case json::value_t::number_integer:
2213 case json::value_t::number_unsigned:
2214 val = Value(static_cast<int>(o.get<int64_t>()));
2215 break;
2216 case json::value_t::number_float:
2217 val = Value(o.get<double>());
2218 break;
2219 case json::value_t::null:
2220 case json::value_t::discarded:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002221 // default:
Selmar Kokfa7022f2018-04-04 18:10:20 +02002222 break;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002223 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002224 if (ret) *ret = val;
2225
Selmar Kokfa7022f2018-04-04 18:10:20 +02002226 return val.Type() != NULL_TYPE;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002227}
2228
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002229static bool ParseExtrasProperty(Value *ret, const json &o) {
2230 json::const_iterator it = o.find("extras");
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002231 if (it == o.end()) {
2232 return false;
2233 }
2234
Selmar Kokfa7022f2018-04-04 18:10:20 +02002235 return ParseJsonAsValue(ret, it.value());
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002236}
2237
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002238static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09002239 const std::string &property,
2240 const bool required,
2241 const std::string &parent_node = "") {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002242 json::const_iterator it = o.find(property);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002243 if (it == o.end()) {
2244 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002245 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09002246 (*err) += "'" + property + "' property is missing";
2247 if (!parent_node.empty()) {
2248 (*err) += " in " + parent_node;
2249 }
2250 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002251 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002252 }
2253 return false;
2254 }
2255
Syoyo Fujita83675312017-12-02 21:14:13 +09002256 if (!it.value().is_boolean()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002257 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002258 if (err) {
2259 (*err) += "'" + property + "' property is not a bool type.\n";
2260 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002261 }
2262 return false;
2263 }
2264
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002265 if (ret) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002266 (*ret) = it.value().get<bool>();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002267 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002268
2269 return true;
2270}
2271
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002272static bool ParseIntegerProperty(int *ret, std::string *err, const json &o,
2273 const std::string &property,
2274 const bool required,
2275 const std::string &parent_node = "") {
2276 json::const_iterator it = o.find(property);
2277 if (it == o.end()) {
2278 if (required) {
2279 if (err) {
2280 (*err) += "'" + property + "' property is missing";
2281 if (!parent_node.empty()) {
2282 (*err) += " in " + parent_node;
2283 }
2284 (*err) += ".\n";
2285 }
2286 }
2287 return false;
2288 }
2289
2290 if (!it.value().is_number_integer()) {
2291 if (required) {
2292 if (err) {
2293 (*err) += "'" + property + "' property is not an integer type.\n";
2294 }
2295 }
2296 return false;
2297 }
2298
2299 if (ret) {
2300 (*ret) = it.value().get<int>();
2301 }
2302
2303 return true;
2304}
2305
2306static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o,
2307 const std::string &property,
2308 const bool required,
2309 const std::string &parent_node = "") {
2310 json::const_iterator it = o.find(property);
2311 if (it == o.end()) {
2312 if (required) {
2313 if (err) {
2314 (*err) += "'" + property + "' property is missing";
2315 if (!parent_node.empty()) {
2316 (*err) += " in " + parent_node;
2317 }
2318 (*err) += ".\n";
2319 }
2320 }
2321 return false;
2322 }
2323
2324 if (!it.value().is_number_unsigned()) {
2325 if (required) {
2326 if (err) {
2327 (*err) += "'" + property + "' property is not a positive integer.\n";
2328 }
2329 }
2330 return false;
2331 }
2332
2333 if (ret) {
2334 (*ret) = it.value().get<size_t>();
2335 }
2336
2337 return true;
2338}
2339
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002340static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09002341 const std::string &property,
2342 const bool required,
2343 const std::string &parent_node = "") {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002344 json::const_iterator it = o.find(property);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002345 if (it == o.end()) {
2346 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002347 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002348 (*err) += "'" + property + "' property is missing";
2349 if (!parent_node.empty()) {
2350 (*err) += " in " + parent_node;
2351 }
2352 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002353 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002354 }
2355 return false;
2356 }
2357
Syoyo Fujita83675312017-12-02 21:14:13 +09002358 if (!it.value().is_number()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002359 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002360 if (err) {
2361 (*err) += "'" + property + "' property is not a number type.\n";
2362 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002363 }
2364 return false;
2365 }
2366
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002367 if (ret) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002368 (*ret) = it.value().get<double>();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002369 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002370
2371 return true;
2372}
2373
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002374static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002375 const json &o, const std::string &property,
2376 bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002377 const std::string &parent_node = "") {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002378 json::const_iterator it = o.find(property);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002379 if (it == o.end()) {
2380 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002381 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002382 (*err) += "'" + property + "' property is missing";
2383 if (!parent_node.empty()) {
2384 (*err) += " in " + parent_node;
2385 }
2386 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002387 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002388 }
2389 return false;
2390 }
2391
Syoyo Fujita83675312017-12-02 21:14:13 +09002392 if (!it.value().is_array()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002393 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002394 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002395 (*err) += "'" + property + "' property is not an array";
2396 if (!parent_node.empty()) {
2397 (*err) += " in " + parent_node;
2398 }
2399 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002400 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002401 }
2402 return false;
2403 }
2404
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002405 ret->clear();
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002406 for (json::const_iterator i = it.value().begin(); i != it.value().end();
2407 i++) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002408 if (!i.value().is_number()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002409 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002410 if (err) {
2411 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002412 if (!parent_node.empty()) {
2413 (*err) += " in " + parent_node;
2414 }
2415 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002416 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002417 }
2418 return false;
2419 }
Syoyo Fujita83675312017-12-02 21:14:13 +09002420 ret->push_back(i.value());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002421 }
2422
2423 return true;
2424}
2425
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002426static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
2427 const json &o,
2428 const std::string &property,
2429 bool required,
2430 const std::string &parent_node = "") {
2431 json::const_iterator it = o.find(property);
2432 if (it == o.end()) {
2433 if (required) {
2434 if (err) {
2435 (*err) += "'" + property + "' property is missing";
2436 if (!parent_node.empty()) {
2437 (*err) += " in " + parent_node;
2438 }
2439 (*err) += ".\n";
2440 }
2441 }
2442 return false;
2443 }
2444
2445 if (!it.value().is_array()) {
2446 if (required) {
2447 if (err) {
2448 (*err) += "'" + property + "' property is not an array";
2449 if (!parent_node.empty()) {
2450 (*err) += " in " + parent_node;
2451 }
2452 (*err) += ".\n";
2453 }
2454 }
2455 return false;
2456 }
2457
2458 ret->clear();
2459 for (json::const_iterator i = it.value().begin(); i != it.value().end();
2460 i++) {
2461 if (!i.value().is_number_integer()) {
2462 if (required) {
2463 if (err) {
2464 (*err) += "'" + property + "' property is not an integer type.\n";
2465 if (!parent_node.empty()) {
2466 (*err) += " in " + parent_node;
2467 }
2468 (*err) += ".\n";
2469 }
2470 }
2471 return false;
2472 }
2473 ret->push_back(i.value());
2474 }
2475
2476 return true;
2477}
2478
Syoyo Fujita0614eb82016-10-14 18:50:14 +09002479static bool ParseStringProperty(
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002480 std::string *ret, std::string *err, const json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09002481 const std::string &property, bool required,
2482 const std::string &parent_node = std::string()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002483 json::const_iterator it = o.find(property);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002484 if (it == o.end()) {
2485 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002486 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09002487 (*err) += "'" + property + "' property is missing";
2488 if (parent_node.empty()) {
2489 (*err) += ".\n";
2490 } else {
2491 (*err) += " in `" + parent_node + "'.\n";
2492 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002493 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002494 }
2495 return false;
2496 }
2497
Syoyo Fujita83675312017-12-02 21:14:13 +09002498 if (!it.value().is_string()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002499 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002500 if (err) {
2501 (*err) += "'" + property + "' property is not a string type.\n";
2502 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002503 }
2504 return false;
2505 }
2506
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002507 if (ret) {
Unknownae2cf8e2018-11-15 18:36:59 +00002508 (*ret) = it.value().get<std::string>();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002509 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002510
2511 return true;
2512}
2513
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002514static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
2515 std::string *err, const json &o,
2516 const std::string &property,
2517 bool required,
2518 const std::string &parent = "") {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002519 json::const_iterator it = o.find(property);
Luke San Antonio19894c72016-06-14 21:19:51 -04002520 if (it == o.end()) {
2521 if (required) {
2522 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09002523 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002524 (*err) +=
2525 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09002526 } else {
2527 (*err) += "'" + property + "' property is missing.\n";
2528 }
Luke San Antonio19894c72016-06-14 21:19:51 -04002529 }
2530 }
2531 return false;
2532 }
2533
2534 // Make sure we are dealing with an object / dictionary.
Syoyo Fujita83675312017-12-02 21:14:13 +09002535 if (!it.value().is_object()) {
Luke San Antonio19894c72016-06-14 21:19:51 -04002536 if (required) {
2537 if (err) {
2538 (*err) += "'" + property + "' property is not an object.\n";
2539 }
2540 }
2541 return false;
2542 }
2543
2544 ret->clear();
Syoyo Fujita83675312017-12-02 21:14:13 +09002545 const json &dict = it.value();
Luke San Antonio19894c72016-06-14 21:19:51 -04002546
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002547 json::const_iterator dictIt(dict.begin());
2548 json::const_iterator dictItEnd(dict.end());
Luke San Antonio19894c72016-06-14 21:19:51 -04002549
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002550 for (; dictIt != dictItEnd; ++dictIt) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002551 if (!dictIt.value().is_number_integer()) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002552 if (required) {
2553 if (err) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002554 (*err) += "'" + property + "' value is not an integer type.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04002555 }
2556 }
2557 return false;
2558 }
2559
2560 // Insert into the list.
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002561 (*ret)[dictIt.key()] = dictIt.value();
Luke San Antonio19894c72016-06-14 21:19:51 -04002562 }
2563 return true;
2564}
2565
Syoyo Fujita5b407452017-06-04 17:42:41 +09002566static bool ParseJSONProperty(std::map<std::string, double> *ret,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002567 std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09002568 const std::string &property, bool required) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002569 json::const_iterator it = o.find(property);
Syoyo Fujita5b407452017-06-04 17:42:41 +09002570 if (it == o.end()) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002571 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09002572 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002573 (*err) += "'" + property + "' property is missing. \n'";
2574 }
2575 }
2576 return false;
2577 }
2578
Syoyo Fujita83675312017-12-02 21:14:13 +09002579 if (!it.value().is_object()) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002580 if (required) {
2581 if (err) {
2582 (*err) += "'" + property + "' property is not a JSON object.\n";
2583 }
2584 }
2585 return false;
2586 }
2587
2588 ret->clear();
Syoyo Fujita83675312017-12-02 21:14:13 +09002589 const json &obj = it.value();
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002590 json::const_iterator it2(obj.begin());
2591 json::const_iterator itEnd(obj.end());
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002592 for (; it2 != itEnd; it2++) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002593 if (it2.value().is_number())
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002594 ret->insert(std::pair<std::string, double>(it2.key(), it2.value()));
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002595 }
2596
2597 return true;
2598}
2599
Selmar09d2ff12018-03-15 17:30:42 +01002600static bool ParseParameterProperty(Parameter *param, std::string *err,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002601 const json &o, const std::string &prop,
2602 bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01002603 // A parameter value can either be a string or an array of either a boolean or
2604 // a number. Booleans of any kind aren't supported here. Granted, it
2605 // complicates the Parameter structure and breaks it semantically in the sense
2606 // that the client probably works off the assumption that if the string is
2607 // empty the vector is used, etc. Would a tagged union work?
2608 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
2609 // Found string property.
2610 return true;
2611 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
2612 false)) {
2613 // Found a number array.
2614 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07002615 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
2616 return param->has_number_value = true;
Selmar09d2ff12018-03-15 17:30:42 +01002617 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
2618 false)) {
2619 return true;
2620 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
2621 return true;
2622 } else {
2623 if (required) {
2624 if (err) {
2625 (*err) += "parameter must be a string or number / number array.\n";
2626 }
2627 }
2628 return false;
2629 }
2630}
2631
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002632static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
2633 const json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09002634 (void)err;
2635
Selmar09d2ff12018-03-15 17:30:42 +01002636 json::const_iterator it = o.find("extensions");
2637 if (it == o.end()) {
2638 return false;
2639 }
2640 if (!it.value().is_object()) {
2641 return false;
2642 }
2643 ExtensionMap extensions;
2644 json::const_iterator extIt = it.value().begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002645 for (; extIt != it.value().end(); extIt++) {
2646 if (!extIt.value().is_object()) continue;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002647 if (!ParseJsonAsValue(&extensions[extIt.key()], extIt.value())) {
Selmar Kokee3d0662018-10-08 16:20:43 +02002648 if (!extIt.key().empty()) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002649 // create empty object so that an extension object is still of type
2650 // object
2651 extensions[extIt.key()] = Value{Value::Object{}};
Selmar Kokee3d0662018-10-08 16:20:43 +02002652 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002653 }
Selmar09d2ff12018-03-15 17:30:42 +01002654 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002655 if (ret) {
Selmar09d2ff12018-03-15 17:30:42 +01002656 (*ret) = extensions;
2657 }
2658 return true;
2659}
2660
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002661static bool ParseAsset(Asset *asset, std::string *err, const json &o) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09002662 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
2663 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
2664 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002665
Selmar09d2ff12018-03-15 17:30:42 +01002666 ParseExtensionsProperty(&asset->extensions, err, o);
2667
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00002668 // Unity exporter version is added as extra here
2669 ParseExtrasProperty(&(asset->extras), o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002670
2671 return true;
2672}
2673
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002674static bool ParseImage(Image *image, const int image_idx, std::string *err,
2675 std::string *warn, const json &o,
2676 const std::string &basedir, FsCallbacks *fs,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002677 LoadImageDataFunction *LoadImageData = nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02002678 void *load_image_user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002679 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002680
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002681 // schema says oneOf [`bufferView`, `uri`]
2682 // TODO(syoyo): Check the type of each parameters.
2683 bool hasBufferView = (o.find("bufferView") != o.end());
2684 bool hasURI = (o.find("uri") != o.end());
2685
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09002686 ParseStringProperty(&image->name, err, o, "name", false);
2687
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002688 if (hasBufferView && hasURI) {
2689 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002690 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002691 (*err) +=
2692 "Only one of `bufferView` or `uri` should be defined, but both are "
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002693 "defined for image[" +
2694 std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002695 }
2696 return false;
2697 }
2698
2699 if (!hasBufferView && !hasURI) {
2700 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002701 (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
2702 std::to_string(image_idx) + "] name = \"" + image->name +
2703 "\"\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002704 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002705 return false;
2706 }
2707
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09002708 ParseExtensionsProperty(&image->extensions, err, o);
Selmar Kok8eb39042018-10-05 14:29:35 +02002709 ParseExtrasProperty(&image->extras, o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002710
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002711 if (hasBufferView) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002712 int bufferView = -1;
2713 if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002714 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002715 (*err) += "Failed to parse `bufferView` for image[" +
2716 std::to_string(image_idx) + "] name = \"" + image->name +
2717 "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002718 }
2719 return false;
2720 }
2721
2722 std::string mime_type;
2723 ParseStringProperty(&mime_type, err, o, "mimeType", false);
2724
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002725 int width = 0;
2726 ParseIntegerProperty(&width, err, o, "width", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002727
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002728 int height = 0;
2729 ParseIntegerProperty(&height, err, o, "height", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002730
2731 // Just only save some information here. Loading actual image data from
2732 // bufferView is done after this `ParseImage` function.
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002733 image->bufferView = bufferView;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002734 image->mimeType = mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002735 image->width = width;
2736 image->height = height;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002737
2738 return true;
2739 }
2740
Syoyo Fujita246654a2018-03-21 20:32:22 +09002741 // Parse URI & Load image data.
2742
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002743 std::string uri;
2744 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09002745 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
2746 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002747 (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
2748 "] name = \"" + image->name + "\".\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002749 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09002750 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002751 }
2752
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002753 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002754
Syoyo Fujita246654a2018-03-21 20:32:22 +09002755 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02002756 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09002757 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002758 (*err) += "Failed to decode 'uri' for image[" +
2759 std::to_string(image_idx) + "] name = [" + image->name +
2760 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002761 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09002762 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002763 }
2764 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09002765 // Assume external file
2766 // Keep texture path (for textures that cannot be decoded)
2767 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01002768#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09002769 return true;
Selmar67af3c92018-03-16 11:48:19 +01002770#endif
Selmar Koke3b3fa92018-08-22 19:04:21 +02002771 if (!LoadExternalFile(&img, err, warn, uri, basedir, false, 0, false, fs)) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002772 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002773 (*warn) += "Failed to load external 'uri' for image[" +
2774 std::to_string(image_idx) + "] name = [" + image->name +
2775 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002776 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09002777 // If the image cannot be loaded, keep uri as image->uri.
2778 return true;
2779 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002780
Syoyo Fujita246654a2018-03-21 20:32:22 +09002781 if (img.empty()) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002782 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002783 (*warn) += "Image data is empty for image[" +
2784 std::to_string(image_idx) + "] name = [" + image->name +
2785 "] \n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002786 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09002787 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002788 }
2789 }
2790
Squareysff644d82018-03-13 22:36:18 +01002791 if (*LoadImageData == nullptr) {
2792 if (err) {
2793 (*err) += "No LoadImageData callback specified.\n";
2794 }
2795 return false;
2796 }
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09002797 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
Paolo Jovone6601bf2018-07-07 20:43:33 +02002798 static_cast<int>(img.size()), load_image_user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002799}
2800
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002801static bool ParseTexture(Texture *texture, std::string *err, const json &o,
Syoyo Fujitabeded612016-05-01 20:03:43 +09002802 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002803 (void)basedir;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002804 int sampler = -1;
2805 int source = -1;
2806 ParseIntegerProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002807
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002808 ParseIntegerProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09002809
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002810 texture->sampler = sampler;
2811 texture->source = source;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002812
Selmar Kokfa7022f2018-04-04 18:10:20 +02002813 ParseExtensionsProperty(&texture->extensions, err, o);
2814 ParseExtrasProperty(&texture->extras, o);
Syoyo Fujitabde70212016-02-07 17:38:17 +09002815
Vladimír Vondruš239be2c2018-07-24 23:23:56 +02002816 ParseStringProperty(&texture->name, err, o, "name", false);
2817
Syoyo Fujitabde70212016-02-07 17:38:17 +09002818 return true;
2819}
2820
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002821static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002822 FsCallbacks *fs, const std::string &basedir,
2823 bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09002824 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09002825 size_t bin_size = 0) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002826 size_t byteLength;
2827 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
2828 "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002829 return false;
2830 }
2831
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00002832 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05002833 buffer->uri.clear();
2834 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002835
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00002836 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05002837 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002838 if (err) {
2839 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
2840 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00002841 }
2842
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002843 json::const_iterator type = o.find("type");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002844 if (type != o.end()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002845 if (type.value().is_string()) {
2846 const std::string &ty = type.value();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002847 if (ty.compare("arraybuffer") == 0) {
2848 // buffer.type = "arraybuffer";
2849 }
2850 }
2851 }
2852
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002853 if (is_binary) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002854 // Still binary glTF accepts external dataURI.
Syoyo Fujita20244e12018-03-15 11:01:05 -05002855 if (!buffer->uri.empty()) {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09002856 // First try embedded data URI.
2857 if (IsDataURI(buffer->uri)) {
2858 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002859 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002860 true)) {
2861 if (err) {
2862 (*err) +=
2863 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
2864 }
2865 return false;
Syoyo Fujita39abfb52018-07-11 02:46:52 +09002866 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002867 } else {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09002868 // External .bin file.
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002869 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002870 buffer->uri, basedir, true, byteLength, true,
2871 fs)) {
Vladimír Vondrušfd84ceb2018-08-16 21:07:56 +02002872 return false;
2873 }
Syoyo Fujita39abfb52018-07-11 02:46:52 +09002874 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002875 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09002876 // load data from (embedded) binary data
2877
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09002878 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09002879 if (err) {
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09002880 (*err) += "Invalid binary data in `Buffer'.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002881 }
2882 return false;
2883 }
2884
2885 if (byteLength > bin_size) {
2886 if (err) {
2887 std::stringstream ss;
2888 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002889 "`byteLength' = "
2890 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002891 (*err) += ss.str();
2892 }
2893 return false;
2894 }
2895
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00002896 // Read buffer data
2897 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002898 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09002899 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002900
2901 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002902 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02002903 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002904 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
2905 true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002906 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002907 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002908 }
2909 return false;
2910 }
2911 } else {
2912 // Assume external .bin file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002913 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, buffer->uri,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002914 basedir, true, byteLength, true, fs)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002915 return false;
2916 }
2917 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002918 }
2919
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002920 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002921
2922 return true;
2923}
2924
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002925static bool ParseBufferView(BufferView *bufferView, std::string *err,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002926 const json &o) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002927 int buffer = -1;
2928 if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002929 return false;
2930 }
2931
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002932 size_t byteOffset = 0;
2933 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002934
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002935 size_t byteLength = 1;
2936 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
2937 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002938 return false;
2939 }
2940
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09002941 size_t byteStride = 0;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002942 if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09002943 // Spec says: When byteStride of referenced bufferView is not defined, it
2944 // means that accessor elements are tightly packed, i.e., effective stride
2945 // equals the size of the element.
2946 // We cannot determine the actual byteStride until Accessor are parsed, thus
2947 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
2948 byteStride = 0;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09002949 }
2950
2951 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
2952 if (err) {
2953 std::stringstream ss;
2954 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
2955 "4 : "
2956 << byteStride << std::endl;
2957
2958 (*err) += ss.str();
2959 }
2960 return false;
2961 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002962
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002963 int target = 0;
2964 ParseIntegerProperty(&target, err, o, "target", false);
2965 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
2966 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002967 // OK
2968 } else {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002969 target = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002970 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002971 bufferView->target = target;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002972
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002973 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002974
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002975 bufferView->buffer = buffer;
2976 bufferView->byteOffset = byteOffset;
2977 bufferView->byteLength = byteLength;
2978 bufferView->byteStride = byteStride;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002979 return true;
2980}
2981
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002982static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
2983 const json &o) {
2984 accessor->sparse.isSparse = true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00002985
Jeff McGlynn19b806e2019-04-26 14:49:38 -07002986 int count = 0;
2987 ParseIntegerProperty(&count, err, o, "count", true);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00002988
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002989 const auto indices_iterator = o.find("indices");
2990 const auto values_iterator = o.find("values");
2991 if (indices_iterator == o.end()) {
2992 (*err) = "the sparse object of this accessor doesn't have indices";
2993 return false;
2994 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00002995
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002996 if (values_iterator == o.end()) {
2997 (*err) = "the sparse object ob ths accessor doesn't have values";
2998 return false;
2999 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00003000
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003001 const json &indices_obj = *indices_iterator;
3002 const json &values_obj = *values_iterator;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00003003
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003004 int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
3005 ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView",
3006 true);
3007 ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
3008 true);
3009 ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
3010 true);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00003011
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003012 int values_buffer_view = 0, values_byte_offset = 0;
3013 ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
3014 true);
3015 ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
3016 true);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00003017
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003018 accessor->sparse.count = count;
3019 accessor->sparse.indices.bufferView = indices_buffer_view;
3020 accessor->sparse.indices.byteOffset = indices_byte_offset;
3021 accessor->sparse.indices.componentType = component_type;
3022 accessor->sparse.values.bufferView = values_buffer_view;
3023 accessor->sparse.values.byteOffset = values_byte_offset;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00003024
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003025 // todo check theses values
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00003026
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003027 return true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00003028}
3029
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003030static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003031 int bufferView = -1;
3032 ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003033
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003034 size_t byteOffset = 0;
3035 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003036
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003037 bool normalized = false;
3038 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
3039
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003040 size_t componentType = 0;
3041 if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
3042 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003043 return false;
3044 }
3045
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003046 size_t count = 0;
3047 if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003048 return false;
3049 }
3050
3051 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003052 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003053 return false;
3054 }
3055
3056 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003057 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003058 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003059 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003060 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003061 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003062 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003063 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003064 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003065 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003066 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003067 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003068 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003069 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003070 } else {
3071 std::stringstream ss;
3072 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003073 if (err) {
3074 (*err) += ss.str();
3075 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003076 return false;
3077 }
3078
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003079 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003080
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003081 accessor->minValues.clear();
3082 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003083 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
3084 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003085
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003086 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
3087 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003088
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003089 accessor->count = count;
3090 accessor->bufferView = bufferView;
3091 accessor->byteOffset = byteOffset;
jianghaosen4748ad62017-08-29 20:08:37 +08003092 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003093 {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003094 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
3095 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003096 // OK
Arthur Brainville (Ybalrid)811e1d32019-06-15 07:32:38 +02003097 accessor->componentType = int(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003098 } else {
3099 std::stringstream ss;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003100 ss << "Invalid `componentType` in accessor. Got " << componentType
3101 << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003102 if (err) {
3103 (*err) += ss.str();
3104 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003105 return false;
3106 }
3107 }
3108
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003109 ParseExtrasProperty(&(accessor->extras), o);
3110
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003111 // check if accessor has a "sparse" object:
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00003112 const auto iterator = o.find("sparse");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003113 if (iterator != o.end()) {
3114 // here this accessor has a "sparse" subobject
3115 return ParseSparseAccessor(accessor, err, *iterator);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00003116 }
3117
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003118 return true;
3119}
3120
Alex Wood7319db72019-01-24 15:38:16 -05003121#ifdef TINYGLTF_ENABLE_DRACO
3122
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003123static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
3124 std::vector<uint8_t> &outBuffer) {
3125 if (componentSize == 4) {
Alex Wood7319db72019-01-24 15:38:16 -05003126 assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003127 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
3128 outBuffer.size());
3129 } else {
Alex Wood7319db72019-01-24 15:38:16 -05003130 size_t faceStride = componentSize * 3;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003131 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
3132 const draco::Mesh::Face &face = mesh->face(f);
3133 if (componentSize == 2) {
3134 uint16_t indices[3] = {(uint16_t)face[0].value(),
3135 (uint16_t)face[1].value(),
3136 (uint16_t)face[2].value()};
3137 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
3138 faceStride);
3139 } else {
3140 uint8_t indices[3] = {(uint8_t)face[0].value(),
3141 (uint8_t)face[1].value(),
3142 (uint8_t)face[2].value()};
3143 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
3144 faceStride);
Alex Wood7319db72019-01-24 15:38:16 -05003145 }
3146 }
3147 }
3148}
3149
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003150template <typename T>
3151static bool GetAttributeForAllPoints(draco::Mesh *mesh,
3152 const draco::PointAttribute *pAttribute,
3153 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05003154 size_t byteOffset = 0;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003155 T values[4] = {0, 0, 0, 0};
3156 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
Alex Wood7319db72019-01-24 15:38:16 -05003157 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003158 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
3159 values))
Alex Wood7319db72019-01-24 15:38:16 -05003160 return false;
3161
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003162 memcpy(outBuffer.data() + byteOffset, &values[0],
3163 sizeof(T) * pAttribute->num_components());
Alex Wood7319db72019-01-24 15:38:16 -05003164 byteOffset += sizeof(T) * pAttribute->num_components();
3165 }
3166
3167 return true;
3168}
3169
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003170static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
3171 const draco::PointAttribute *pAttribute,
3172 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05003173 bool decodeResult = false;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003174 switch (componentType) {
3175 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
3176 decodeResult =
3177 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
3178 break;
3179 case TINYGLTF_COMPONENT_TYPE_BYTE:
3180 decodeResult =
3181 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
3182 break;
3183 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
3184 decodeResult =
3185 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
3186 break;
3187 case TINYGLTF_COMPONENT_TYPE_SHORT:
3188 decodeResult =
3189 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
3190 break;
3191 case TINYGLTF_COMPONENT_TYPE_INT:
3192 decodeResult =
3193 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
3194 break;
3195 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
3196 decodeResult =
3197 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
3198 break;
3199 case TINYGLTF_COMPONENT_TYPE_FLOAT:
3200 decodeResult =
3201 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
3202 break;
3203 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
3204 decodeResult =
3205 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
3206 break;
3207 default:
3208 return false;
Alex Wood7319db72019-01-24 15:38:16 -05003209 }
3210
3211 return decodeResult;
3212}
3213
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003214static bool ParseDracoExtension(Primitive *primitive, Model *model,
3215 std::string *err,
3216 const Value &dracoExtensionValue) {
Alex Wood7319db72019-01-24 15:38:16 -05003217 auto bufferViewValue = dracoExtensionValue.Get("bufferView");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003218 if (!bufferViewValue.IsInt()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05003219 auto attributesValue = dracoExtensionValue.Get("attributes");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003220 if (!attributesValue.IsObject()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05003221
3222 auto attributesObject = attributesValue.Get<Value::Object>();
3223 int bufferView = bufferViewValue.Get<int>();
3224
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003225 BufferView &view = model->bufferViews[bufferView];
3226 Buffer &buffer = model->buffers[view.buffer];
Alex Wood7319db72019-01-24 15:38:16 -05003227 // BufferView has already been decoded
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003228 if (view.dracoDecoded) return true;
Alex Wood7319db72019-01-24 15:38:16 -05003229 view.dracoDecoded = true;
3230
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003231 const char *bufferViewData =
3232 reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
Alex Wood7319db72019-01-24 15:38:16 -05003233 size_t bufferViewSize = view.byteLength;
3234
3235 // decode draco
3236 draco::DecoderBuffer decoderBuffer;
3237 decoderBuffer.Init(bufferViewData, bufferViewSize);
3238 draco::Decoder decoder;
3239 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
3240 if (!decodeResult.ok()) {
3241 return false;
3242 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003243 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
Alex Wood7319db72019-01-24 15:38:16 -05003244
3245 // create new bufferView for indices
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003246 if (primitive->indices >= 0) {
3247 int32_t componentSize = GetComponentSizeInBytes(
3248 model->accessors[primitive->indices].componentType);
Alex Wood7319db72019-01-24 15:38:16 -05003249 Buffer decodedIndexBuffer;
3250 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
3251
3252 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
3253
3254 model->buffers.emplace_back(std::move(decodedIndexBuffer));
3255
3256 BufferView decodedIndexBufferView;
3257 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003258 decodedIndexBufferView.byteLength =
3259 int(mesh->num_faces() * 3 * componentSize);
Alex Wood7319db72019-01-24 15:38:16 -05003260 decodedIndexBufferView.byteOffset = 0;
3261 decodedIndexBufferView.byteStride = 0;
3262 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
3263 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
3264
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003265 model->accessors[primitive->indices].bufferView =
3266 int(model->bufferViews.size() - 1);
Alexander Wood0d77a292019-01-26 08:58:45 -05003267 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
Alex Wood7319db72019-01-24 15:38:16 -05003268 }
3269
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003270 for (const auto &attribute : attributesObject) {
3271 if (!attribute.second.IsInt()) return false;
Alexander Wood0d77a292019-01-26 08:58:45 -05003272 auto primitiveAttribute = primitive->attributes.find(attribute.first);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003273 if (primitiveAttribute == primitive->attributes.end()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05003274
3275 int dracoAttributeIndex = attribute.second.Get<int>();
3276 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
3277 const auto pBuffer = pAttribute->buffer();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003278 const auto componentType =
3279 model->accessors[primitiveAttribute->second].componentType;
Alex Wood7319db72019-01-24 15:38:16 -05003280
3281 // Create a new buffer for this decoded buffer
3282 Buffer decodedBuffer;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003283 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
3284 GetComponentSizeInBytes(componentType);
Alex Wood7319db72019-01-24 15:38:16 -05003285 decodedBuffer.data.resize(bufferSize);
3286
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003287 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
3288 decodedBuffer.data))
Alex Wood7319db72019-01-24 15:38:16 -05003289 return false;
3290
3291 model->buffers.emplace_back(std::move(decodedBuffer));
3292
3293 BufferView decodedBufferView;
3294 decodedBufferView.buffer = int(model->buffers.size() - 1);
3295 decodedBufferView.byteLength = bufferSize;
3296 decodedBufferView.byteOffset = pAttribute->byte_offset();
3297 decodedBufferView.byteStride = pAttribute->byte_stride();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003298 decodedBufferView.target = primitive->indices >= 0
3299 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
3300 : TINYGLTF_TARGET_ARRAY_BUFFER;
Alex Wood7319db72019-01-24 15:38:16 -05003301 model->bufferViews.emplace_back(std::move(decodedBufferView));
3302
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003303 model->accessors[primitiveAttribute->second].bufferView =
3304 int(model->bufferViews.size() - 1);
3305 model->accessors[primitiveAttribute->second].count =
3306 int(mesh->num_points());
Alex Wood7319db72019-01-24 15:38:16 -05003307 }
3308
3309 return true;
3310}
3311#endif
3312
3313static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003314 const json &o) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003315 int material = -1;
3316 ParseIntegerProperty(&material, err, o, "material", false);
3317 primitive->material = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003318
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003319 int mode = TINYGLTF_MODE_TRIANGLES;
3320 ParseIntegerProperty(&mode, err, o, "mode", false);
3321 primitive->mode = mode; // Why only triangled were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003322
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003323 int indices = -1;
3324 ParseIntegerProperty(&indices, err, o, "indices", false);
3325 primitive->indices = indices;
3326 if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
3327 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003328 return false;
3329 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003330
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00003331 // Look for morph targets
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003332 json::const_iterator targetsObject = o.find("targets");
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003333 if ((targetsObject != o.end()) && targetsObject.value().is_array()) {
3334 for (json::const_iterator i = targetsObject.value().begin();
3335 i != targetsObject.value().end(); i++) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00003336 std::map<std::string, int> targetAttribues;
3337
Syoyo Fujita83675312017-12-02 21:14:13 +09003338 const json &dict = i.value();
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003339 json::const_iterator dictIt(dict.begin());
3340 json::const_iterator dictItEnd(dict.end());
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00003341
3342 for (; dictIt != dictItEnd; ++dictIt) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003343 targetAttribues[dictIt.key()] = static_cast<int>(dictIt.value());
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00003344 }
3345 primitive->targets.push_back(targetAttribues);
3346 }
3347 }
3348
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003349 ParseExtrasProperty(&(primitive->extras), o);
3350
Alex Wood7319db72019-01-24 15:38:16 -05003351 ParseExtensionsProperty(&primitive->extensions, err, o);
3352
3353#ifdef TINYGLTF_ENABLE_DRACO
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003354 auto dracoExtension =
3355 primitive->extensions.find("KHR_draco_mesh_compression");
3356 if (dracoExtension != primitive->extensions.end()) {
3357 ParseDracoExtension(primitive, model, err, dracoExtension->second);
Alex Wood7319db72019-01-24 15:38:16 -05003358 }
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09003359#else
3360 (void)model;
Alex Wood7319db72019-01-24 15:38:16 -05003361#endif
3362
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003363 return true;
3364}
3365
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003366static bool ParseMesh(Mesh *mesh, Model *model, std::string *err,
3367 const json &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003368 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003369
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003370 mesh->primitives.clear();
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003371 json::const_iterator primObject = o.find("primitives");
Syoyo Fujita83675312017-12-02 21:14:13 +09003372 if ((primObject != o.end()) && primObject.value().is_array()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003373 for (json::const_iterator i = primObject.value().begin();
3374 i != primObject.value().end(); i++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003375 Primitive primitive;
Alex Wood7319db72019-01-24 15:38:16 -05003376 if (ParsePrimitive(&primitive, model, err, i.value())) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04003377 // Only add the primitive if the parsing succeeds.
3378 mesh->primitives.push_back(primitive);
3379 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003380 }
3381 }
3382
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00003383 // Should probably check if has targets and if dimensions fit
3384 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
3385
Selmar09d2ff12018-03-15 17:30:42 +01003386 ParseExtensionsProperty(&mesh->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003387 ParseExtrasProperty(&(mesh->extras), o);
3388
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003389 return true;
3390}
3391
Syoyo Fujita83675312017-12-02 21:14:13 +09003392static bool ParseLight(Light *light, std::string *err, const json &o) {
Emanuel Schrade186322b2017-11-06 11:14:41 +01003393 ParseStringProperty(&light->name, err, o, "name", false);
3394 ParseNumberArrayProperty(&light->color, err, o, "color", false);
3395 ParseStringProperty(&light->type, err, o, "type", false);
3396 return true;
3397}
3398
Syoyo Fujita83675312017-12-02 21:14:13 +09003399static bool ParseNode(Node *node, std::string *err, const json &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003400 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003401
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003402 int skin = -1;
3403 ParseIntegerProperty(&skin, err, o, "skin", false);
3404 node->skin = skin;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00003405
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003406 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09003407 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003408 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
3409 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
3410 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
3411 }
3412
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003413 int camera = -1;
3414 ParseIntegerProperty(&camera, err, o, "camera", false);
3415 node->camera = camera;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003416
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003417 int mesh = -1;
3418 ParseIntegerProperty(&mesh, err, o, "mesh", false);
3419 node->mesh = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003420
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003421 node->children.clear();
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003422 ParseIntegerArrayProperty(&node->children, err, o, "children", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003423
Selmar09d2ff12018-03-15 17:30:42 +01003424 ParseExtensionsProperty(&node->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003425 ParseExtrasProperty(&(node->extras), o);
3426
Emanuel Schrade186322b2017-11-06 11:14:41 +01003427 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04003428}
3429
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003430static bool ParseMaterial(Material *material, std::string *err, const json &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003431 material->values.clear();
Selmar09d2ff12018-03-15 17:30:42 +01003432 material->extensions.clear();
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003433 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04003434
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003435 json::const_iterator it(o.begin());
3436 json::const_iterator itEnd(o.end());
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04003437
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003438 for (; it != itEnd; it++) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003439 if (it.key() == "pbrMetallicRoughness") {
Syoyo Fujita83675312017-12-02 21:14:13 +09003440 if (it.value().is_object()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003441 const json &values_object = it.value();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003442
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003443 json::const_iterator itVal(values_object.begin());
3444 json::const_iterator itValEnd(values_object.end());
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003445
Syoyo Fujitaf6120152017-05-27 23:51:23 +09003446 for (; itVal != itValEnd; itVal++) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003447 Parameter param;
Syoyo Fujita83675312017-12-02 21:14:13 +09003448 if (ParseParameterProperty(&param, err, values_object, itVal.key(),
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003449 false)) {
Syoyo Fujita83675312017-12-02 21:14:13 +09003450 material->values[itVal.key()] = param;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003451 }
3452 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003453 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003454 } else if (it.key() == "extensions" || it.key() == "extras") {
3455 // done later, skip, otherwise poorly parsed contents will be saved in the
3456 // parametermap and serialized again later
3457 } else {
Syoyo Fujita5b407452017-06-04 17:42:41 +09003458 Parameter param;
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003459 if (ParseParameterProperty(&param, err, o, it.key(), false)) {
3460 material->additionalValues[it.key()] = param;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003461 }
3462 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09003463 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003464
Selmar09d2ff12018-03-15 17:30:42 +01003465 ParseExtensionsProperty(&material->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003466 ParseExtrasProperty(&(material->extras), o);
3467
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04003468 return true;
3469}
3470
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003471static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003472 const json &o) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003473 int samplerIndex = -1;
3474 int targetIndex = -1;
3475 if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
3476 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003477 if (err) {
3478 (*err) += "`sampler` field is missing in animation channels\n";
3479 }
3480 return false;
3481 }
3482
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003483 json::const_iterator targetIt = o.find("target");
Syoyo Fujita83675312017-12-02 21:14:13 +09003484 if ((targetIt != o.end()) && targetIt.value().is_object()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003485 const json &target_object = targetIt.value();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003486
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003487 if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003488 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003489 (*err) += "`node` field is missing in animation.channels.target\n";
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003490 }
3491 return false;
3492 }
3493
3494 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
3495 true)) {
3496 if (err) {
3497 (*err) += "`path` field is missing in animation.channels.target\n";
3498 }
3499 return false;
3500 }
3501 }
3502
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003503 channel->sampler = samplerIndex;
3504 channel->target_node = targetIndex;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003505
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003506 ParseExtrasProperty(&(channel->extras), o);
3507
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003508 return true;
3509}
3510
3511static bool ParseAnimation(Animation *animation, std::string *err,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003512 const json &o) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003513 {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003514 json::const_iterator channelsIt = o.find("channels");
Syoyo Fujita83675312017-12-02 21:14:13 +09003515 if ((channelsIt != o.end()) && channelsIt.value().is_array()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003516 for (json::const_iterator i = channelsIt.value().begin();
3517 i != channelsIt.value().end(); i++) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003518 AnimationChannel channel;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003519 if (ParseAnimationChannel(&channel, err, i.value())) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003520 // Only add the channel if the parsing succeeds.
3521 animation->channels.push_back(channel);
3522 }
3523 }
3524 }
3525 }
3526
3527 {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003528 json::const_iterator samplerIt = o.find("samplers");
Syoyo Fujita83675312017-12-02 21:14:13 +09003529 if ((samplerIt != o.end()) && samplerIt.value().is_array()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003530 const json &sampler_array = samplerIt.value();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003531
Syoyo Fujita83675312017-12-02 21:14:13 +09003532 json::const_iterator it = sampler_array.begin();
3533 json::const_iterator itEnd = sampler_array.end();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003534
3535 for (; it != itEnd; it++) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003536 const json &s = it->get<json>();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003537
3538 AnimationSampler sampler;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003539 int inputIndex = -1;
3540 int outputIndex = -1;
3541 if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003542 if (err) {
3543 (*err) += "`input` field is missing in animation.sampler\n";
3544 }
3545 return false;
3546 }
Evan Birenbaum6bdffed2019-02-14 13:30:57 -08003547 ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
3548 false);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003549 if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003550 if (err) {
3551 (*err) += "`output` field is missing in animation.sampler\n";
3552 }
3553 return false;
3554 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003555 sampler.input = inputIndex;
3556 sampler.output = outputIndex;
Jens Olssonb3af2f12018-06-04 10:17:49 +02003557 ParseExtrasProperty(&(sampler.extras), s);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003558 animation->samplers.push_back(sampler);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003559 }
3560 }
3561 }
3562
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003563 ParseStringProperty(&animation->name, err, o, "name", false);
3564
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003565 ParseExtrasProperty(&(animation->extras), o);
3566
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003567 return true;
3568}
3569
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003570static bool ParseSampler(Sampler *sampler, std::string *err, const json &o) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09003571 ParseStringProperty(&sampler->name, err, o, "name", false);
3572
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003573 int minFilter = TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR;
3574 int magFilter = TINYGLTF_TEXTURE_FILTER_LINEAR;
3575 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
3576 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
3577 ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
3578 ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
3579 ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
3580 ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
Syoyo Fujitac2615632016-06-19 21:56:06 +09003581
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003582 sampler->minFilter = minFilter;
3583 sampler->magFilter = magFilter;
3584 sampler->wrapS = wrapS;
3585 sampler->wrapT = wrapT;
Syoyo Fujitac2615632016-06-19 21:56:06 +09003586
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003587 ParseExtrasProperty(&(sampler->extras), o);
3588
Syoyo Fujitac2615632016-06-19 21:56:06 +09003589 return true;
3590}
3591
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003592static bool ParseSkin(Skin *skin, std::string *err, const json &o) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003593 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00003594
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003595 std::vector<int> joints;
3596 if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00003597 return false;
3598 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003599 skin->joints = std::move(joints);
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00003600
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003601 int skeleton = -1;
3602 ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
3603 skin->skeleton = skeleton;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00003604
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003605 int invBind = -1;
3606 ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
3607 skin->inverseBindMatrices = invBind;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00003608
3609 return true;
3610}
3611
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003612static bool ParsePerspectiveCamera(PerspectiveCamera *camera, std::string *err,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003613 const json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003614 double yfov = 0.0;
3615 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
3616 return false;
3617 }
3618
3619 double znear = 0.0;
3620 if (!ParseNumberProperty(&znear, err, o, "znear", true,
3621 "PerspectiveCamera")) {
3622 return false;
3623 }
3624
3625 double aspectRatio = 0.0; // = invalid
3626 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
3627 "PerspectiveCamera");
3628
3629 double zfar = 0.0; // = invalid
3630 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
3631
Selmar Kok31cb7f92018-10-03 15:39:05 +02003632 camera->aspectRatio = aspectRatio;
3633 camera->zfar = zfar;
3634 camera->yfov = yfov;
3635 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003636
Selmar09d2ff12018-03-15 17:30:42 +01003637 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003638 ParseExtrasProperty(&(camera->extras), o);
3639
3640 // TODO(syoyo): Validate parameter values.
3641
3642 return true;
3643}
3644
3645static bool ParseOrthographicCamera(OrthographicCamera *camera,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003646 std::string *err, const json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003647 double xmag = 0.0;
3648 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
3649 return false;
3650 }
3651
3652 double ymag = 0.0;
3653 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
3654 return false;
3655 }
3656
3657 double zfar = 0.0;
3658 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
3659 return false;
3660 }
3661
3662 double znear = 0.0;
3663 if (!ParseNumberProperty(&znear, err, o, "znear", true,
3664 "OrthographicCamera")) {
3665 return false;
3666 }
3667
Selmar09d2ff12018-03-15 17:30:42 +01003668 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003669 ParseExtrasProperty(&(camera->extras), o);
3670
Selmar Kok31cb7f92018-10-03 15:39:05 +02003671 camera->xmag = xmag;
3672 camera->ymag = ymag;
3673 camera->zfar = zfar;
3674 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003675
3676 // TODO(syoyo): Validate parameter values.
3677
3678 return true;
3679}
3680
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003681static bool ParseCamera(Camera *camera, std::string *err, const json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003682 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
3683 return false;
3684 }
3685
3686 if (camera->type.compare("orthographic") == 0) {
3687 if (o.find("orthographic") == o.end()) {
3688 if (err) {
3689 std::stringstream ss;
3690 ss << "Orhographic camera description not found." << std::endl;
3691 (*err) += ss.str();
3692 }
3693 return false;
3694 }
3695
Syoyo Fujita83675312017-12-02 21:14:13 +09003696 const json &v = o.find("orthographic").value();
3697 if (!v.is_object()) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003698 if (err) {
3699 std::stringstream ss;
3700 ss << "\"orthographic\" is not a JSON object." << std::endl;
3701 (*err) += ss.str();
3702 }
3703 return false;
3704 }
3705
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003706 if (!ParseOrthographicCamera(&camera->orthographic, err, v.get<json>())) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003707 return false;
3708 }
3709 } else if (camera->type.compare("perspective") == 0) {
3710 if (o.find("perspective") == o.end()) {
3711 if (err) {
3712 std::stringstream ss;
3713 ss << "Perspective camera description not found." << std::endl;
3714 (*err) += ss.str();
3715 }
3716 return false;
3717 }
3718
Syoyo Fujita83675312017-12-02 21:14:13 +09003719 const json &v = o.find("perspective").value();
3720 if (!v.is_object()) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003721 if (err) {
3722 std::stringstream ss;
3723 ss << "\"perspective\" is not a JSON object." << std::endl;
3724 (*err) += ss.str();
3725 }
3726 return false;
3727 }
3728
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003729 if (!ParsePerspectiveCamera(&camera->perspective, err, v.get<json>())) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003730 return false;
3731 }
3732 } else {
3733 if (err) {
3734 std::stringstream ss;
3735 ss << "Invalid camera type: \"" << camera->type
3736 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
3737 (*err) += ss.str();
3738 }
3739 return false;
3740 }
3741
3742 ParseStringProperty(&camera->name, err, o, "name", false);
3743
Selmar09d2ff12018-03-15 17:30:42 +01003744 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003745 ParseExtrasProperty(&(camera->extras), o);
3746
3747 return true;
3748}
3749
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003750bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
3751 const char *str, unsigned int length,
3752 const std::string &base_dir,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003753 unsigned int check_sections) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09003754 if (length < 4) {
3755 if (err) {
3756 (*err) = "JSON string too short.\n";
3757 }
3758 return false;
3759 }
3760
Syoyo Fujita83675312017-12-02 21:14:13 +09003761 json v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09003762
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003763#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
3764 defined(_CPPUNWIND)) && \
3765 not defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09003766 try {
Syoyo Fujita83675312017-12-02 21:14:13 +09003767 v = json::parse(str, str + length);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003768
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09003769 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003770 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09003771 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003772 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003773 return false;
3774 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09003775#else
3776 {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003777 v = json::parse(str, str + length, nullptr, /* exception */ false);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09003778
3779 if (!v.is_object()) {
3780 // Assume parsing was failed.
3781 if (err) {
3782 (*err) = "Failed to parse JSON object\n";
3783 }
3784 return false;
3785 }
3786 }
3787#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003788
Syoyo Fujita83675312017-12-02 21:14:13 +09003789 if (!v.is_object()) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09003790 // root is not an object.
3791 if (err) {
3792 (*err) = "Root element is not a JSON object\n";
3793 }
3794 return false;
3795 }
3796
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02003797 {
3798 bool version_found = false;
3799 json::const_iterator it = v.find("asset");
3800 if ((it != v.end()) && it.value().is_object()) {
3801 json::const_iterator version_it = it.value().find("version");
3802 if ((version_it != it.value().end() && version_it.value().is_string())) {
3803 version_found = true;
3804 }
3805 }
3806 if (version_found) {
3807 // OK
3808 } else if (check_sections & REQUIRE_VERSION) {
3809 if (err) {
3810 (*err) += "\"asset\" object not found in .gltf or not an object type\n";
3811 }
3812 return false;
3813 }
3814 }
3815
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003816 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09003817 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003818
Syoyo Fujita83675312017-12-02 21:14:13 +09003819 {
Squareys43374632018-03-13 22:20:48 +01003820 json::const_iterator it = v.find("scenes");
Syoyo Fujita83675312017-12-02 21:14:13 +09003821 if ((it != v.end()) && it.value().is_array()) {
3822 // OK
3823 } else if (check_sections & REQUIRE_SCENES) {
3824 if (err) {
3825 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
3826 }
3827 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003828 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003829 }
3830
Syoyo Fujita83675312017-12-02 21:14:13 +09003831 {
Squareys43374632018-03-13 22:20:48 +01003832 json::const_iterator it = v.find("nodes");
Syoyo Fujita83675312017-12-02 21:14:13 +09003833 if ((it != v.end()) && it.value().is_array()) {
3834 // OK
3835 } else if (check_sections & REQUIRE_NODES) {
3836 if (err) {
3837 (*err) += "\"nodes\" object not found in .gltf\n";
3838 }
3839 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003840 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003841 }
3842
Syoyo Fujita83675312017-12-02 21:14:13 +09003843 {
Squareys43374632018-03-13 22:20:48 +01003844 json::const_iterator it = v.find("accessors");
Syoyo Fujita83675312017-12-02 21:14:13 +09003845 if ((it != v.end()) && it.value().is_array()) {
3846 // OK
3847 } else if (check_sections & REQUIRE_ACCESSORS) {
3848 if (err) {
3849 (*err) += "\"accessors\" object not found in .gltf\n";
3850 }
3851 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003852 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003853 }
3854
Syoyo Fujita83675312017-12-02 21:14:13 +09003855 {
Squareys43374632018-03-13 22:20:48 +01003856 json::const_iterator it = v.find("buffers");
Syoyo Fujita83675312017-12-02 21:14:13 +09003857 if ((it != v.end()) && it.value().is_array()) {
3858 // OK
3859 } else if (check_sections & REQUIRE_BUFFERS) {
3860 if (err) {
3861 (*err) += "\"buffers\" object not found in .gltf\n";
3862 }
3863 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003864 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003865 }
3866
Syoyo Fujita83675312017-12-02 21:14:13 +09003867 {
Squareys43374632018-03-13 22:20:48 +01003868 json::const_iterator it = v.find("bufferViews");
Syoyo Fujita83675312017-12-02 21:14:13 +09003869 if ((it != v.end()) && it.value().is_array()) {
3870 // OK
3871 } else if (check_sections & REQUIRE_BUFFER_VIEWS) {
3872 if (err) {
3873 (*err) += "\"bufferViews\" object not found in .gltf\n";
3874 }
3875 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003876 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003877 }
3878
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003879 model->buffers.clear();
3880 model->bufferViews.clear();
3881 model->accessors.clear();
3882 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003883 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003884 model->nodes.clear();
3885 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00003886 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01003887 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003888 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003889
Syoyo Fujita83675312017-12-02 21:14:13 +09003890 // 1. Parse Asset
3891 {
Squareys43374632018-03-13 22:20:48 +01003892 json::const_iterator it = v.find("asset");
Syoyo Fujita83675312017-12-02 21:14:13 +09003893 if ((it != v.end()) && it.value().is_object()) {
3894 const json &root = it.value();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003895
Syoyo Fujita83675312017-12-02 21:14:13 +09003896 ParseAsset(&model->asset, err, root);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00003897 }
3898 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003899
Syoyo Fujita83675312017-12-02 21:14:13 +09003900 // 2. Parse extensionUsed
3901 {
Squareys43374632018-03-13 22:20:48 +01003902 json::const_iterator it = v.find("extensionsUsed");
Syoyo Fujita83675312017-12-02 21:14:13 +09003903 if ((it != v.end()) && it.value().is_array()) {
3904 const json &root = it.value();
3905 for (unsigned int i = 0; i < root.size(); ++i) {
3906 model->extensionsUsed.push_back(root[i].get<std::string>());
Syoyo Fujita59130d12017-08-01 20:23:11 +09003907 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003908 }
3909 }
3910
Syoyo Fujita83675312017-12-02 21:14:13 +09003911 {
Squareys43374632018-03-13 22:20:48 +01003912 json::const_iterator it = v.find("extensionsRequired");
Syoyo Fujita83675312017-12-02 21:14:13 +09003913 if ((it != v.end()) && it.value().is_array()) {
3914 const json &root = it.value();
3915 for (unsigned int i = 0; i < root.size(); ++i) {
3916 model->extensionsRequired.push_back(root[i].get<std::string>());
Syoyo Fujita59130d12017-08-01 20:23:11 +09003917 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003918 }
3919 }
3920
Syoyo Fujita83675312017-12-02 21:14:13 +09003921 // 3. Parse Buffer
3922 {
Squareys43374632018-03-13 22:20:48 +01003923 json::const_iterator rootIt = v.find("buffers");
Syoyo Fujita83675312017-12-02 21:14:13 +09003924 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3925 const json &root = rootIt.value();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003926
Syoyo Fujita83675312017-12-02 21:14:13 +09003927 json::const_iterator it(root.begin());
3928 json::const_iterator itEnd(root.end());
3929 for (; it != itEnd; it++) {
3930 if (!it.value().is_object()) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09003931 if (err) {
Syoyo Fujita83675312017-12-02 21:14:13 +09003932 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09003933 }
3934 return false;
3935 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003936 Buffer buffer;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003937 if (!ParseBuffer(&buffer, err, it->get<json>(), &fs, base_dir,
3938 is_binary_, bin_data_, bin_size_)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09003939 return false;
3940 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09003941
Syoyo Fujita83675312017-12-02 21:14:13 +09003942 model->buffers.push_back(buffer);
3943 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003944 }
3945 }
Squareys43374632018-03-13 22:20:48 +01003946
Syoyo Fujita83675312017-12-02 21:14:13 +09003947 // 4. Parse BufferView
3948 {
Squareys43374632018-03-13 22:20:48 +01003949 json::const_iterator rootIt = v.find("bufferViews");
Syoyo Fujita83675312017-12-02 21:14:13 +09003950 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3951 const json &root = rootIt.value();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003952
Syoyo Fujita83675312017-12-02 21:14:13 +09003953 json::const_iterator it(root.begin());
3954 json::const_iterator itEnd(root.end());
3955 for (; it != itEnd; it++) {
3956 if (!it.value().is_object()) {
3957 if (err) {
3958 (*err) += "`bufferViews' does not contain an JSON object.";
3959 }
3960 return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09003961 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003962 BufferView bufferView;
3963 if (!ParseBufferView(&bufferView, err, it->get<json>())) {
3964 return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09003965 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003966
Syoyo Fujita83675312017-12-02 21:14:13 +09003967 model->bufferViews.push_back(bufferView);
3968 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003969 }
3970 }
3971
Syoyo Fujita83675312017-12-02 21:14:13 +09003972 // 5. Parse Accessor
3973 {
Squareys43374632018-03-13 22:20:48 +01003974 json::const_iterator rootIt = v.find("accessors");
Syoyo Fujita83675312017-12-02 21:14:13 +09003975 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3976 const json &root = rootIt.value();
Syoyo Fujitac2615632016-06-19 21:56:06 +09003977
Syoyo Fujita83675312017-12-02 21:14:13 +09003978 json::const_iterator it(root.begin());
3979 json::const_iterator itEnd(root.end());
3980 for (; it != itEnd; it++) {
3981 if (!it.value().is_object()) {
3982 if (err) {
3983 (*err) += "`accessors' does not contain an JSON object.";
3984 }
3985 return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09003986 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003987 Accessor accessor;
3988 if (!ParseAccessor(&accessor, err, it->get<json>())) {
3989 return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09003990 }
Syoyo Fujitac2615632016-06-19 21:56:06 +09003991
Syoyo Fujita83675312017-12-02 21:14:13 +09003992 model->accessors.push_back(accessor);
3993 }
Syoyo Fujitac2615632016-06-19 21:56:06 +09003994 }
3995 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003996
Syoyo Fujita83675312017-12-02 21:14:13 +09003997 // 6. Parse Mesh
3998 {
Squareys43374632018-03-13 22:20:48 +01003999 json::const_iterator rootIt = v.find("meshes");
Syoyo Fujita83675312017-12-02 21:14:13 +09004000 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4001 const json &root = rootIt.value();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004002
Syoyo Fujita83675312017-12-02 21:14:13 +09004003 json::const_iterator it(root.begin());
4004 json::const_iterator itEnd(root.end());
4005 for (; it != itEnd; it++) {
4006 if (!it.value().is_object()) {
4007 if (err) {
4008 (*err) += "`meshes' does not contain an JSON object.";
4009 }
4010 return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09004011 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004012 Mesh mesh;
Alex Wood7319db72019-01-24 15:38:16 -05004013 if (!ParseMesh(&mesh, model, err, it->get<json>())) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004014 return false;
4015 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004016
Syoyo Fujita83675312017-12-02 21:14:13 +09004017 model->meshes.push_back(mesh);
4018 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004019 }
4020 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01004021
viperscape9df05802018-12-05 14:11:01 -05004022 // Assign missing bufferView target types
4023 // - Look for missing Mesh indices
4024 // - Look for missing bufferView targets
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004025 for (auto &mesh : model->meshes) {
4026 for (auto &primitive : mesh.primitives) {
4027 if (primitive.indices >
4028 -1) // has indices from parsing step, must be Element Array Buffer
viperscape9df05802018-12-05 14:11:01 -05004029 {
Jeff McGlynn89152522019-04-25 16:33:56 -07004030 if (size_t(primitive.indices) >= model->accessors.size()) {
4031 if (err) {
4032 (*err) += "primitive indices accessor out of bounds";
4033 }
4034 return false;
4035 }
4036
4037 auto bufferView = model->accessors[primitive.indices].bufferView;
4038 if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) {
4039 if (err) {
4040 (*err) += "accessor[" + std::to_string(primitive.indices) +
4041 "] invalid bufferView";
4042 }
4043 return false;
4044 }
4045
4046 model->bufferViews[bufferView].target =
4047 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004048 // we could optionally check if acessors' bufferView type is Scalar, as
4049 // it should be
viperscape9df05802018-12-05 14:11:01 -05004050 }
4051 }
4052 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004053 // find any missing targets, must be an array buffer type if not fulfilled
4054 // from previous check
4055 for (auto &bufferView : model->bufferViews) {
4056 if (bufferView.target == 0) // missing target type
viperscape9df05802018-12-05 14:11:01 -05004057 {
4058 bufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4059 }
4060 }
4061
Syoyo Fujita83675312017-12-02 21:14:13 +09004062 // 7. Parse Node
4063 {
Squareys43374632018-03-13 22:20:48 +01004064 json::const_iterator rootIt = v.find("nodes");
Syoyo Fujita83675312017-12-02 21:14:13 +09004065 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4066 const json &root = rootIt.value();
Emanuel Schrade186322b2017-11-06 11:14:41 +01004067
Syoyo Fujita83675312017-12-02 21:14:13 +09004068 json::const_iterator it(root.begin());
4069 json::const_iterator itEnd(root.end());
4070 for (; it != itEnd; it++) {
4071 if (!it.value().is_object()) {
4072 if (err) {
4073 (*err) += "`nodes' does not contain an JSON object.";
4074 }
4075 return false;
4076 }
4077 Node node;
4078 if (!ParseNode(&node, err, it->get<json>())) {
4079 return false;
4080 }
4081
4082 model->nodes.push_back(node);
4083 }
4084 }
4085 }
4086
4087 // 8. Parse scenes.
4088 {
Squareys43374632018-03-13 22:20:48 +01004089 json::const_iterator rootIt = v.find("scenes");
Syoyo Fujita83675312017-12-02 21:14:13 +09004090 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4091 const json &root = rootIt.value();
4092
Syoyo Fujita83675312017-12-02 21:14:13 +09004093 json::const_iterator it(root.begin());
4094 json::const_iterator itEnd(root.end());
4095 for (; it != itEnd; it++) {
4096 if (!(it.value().is_object())) {
4097 if (err) {
4098 (*err) += "`scenes' does not contain an JSON object.";
4099 }
4100 return false;
4101 }
4102 const json &o = it->get<json>();
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004103 std::vector<int> nodes;
4104 if (!ParseIntegerArrayProperty(&nodes, err, o, "nodes", false)) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004105 return false;
4106 }
4107
4108 Scene scene;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004109 scene.nodes = std::move(nodes);
4110
Syoyo Fujita83675312017-12-02 21:14:13 +09004111 ParseStringProperty(&scene.name, err, o, "name", false);
Syoyo Fujita83675312017-12-02 21:14:13 +09004112
Selmar09d2ff12018-03-15 17:30:42 +01004113 ParseExtensionsProperty(&scene.extensions, err, o);
4114 ParseExtrasProperty(&scene.extras, o);
4115
Syoyo Fujita83675312017-12-02 21:14:13 +09004116 model->scenes.push_back(scene);
4117 }
4118 }
4119 }
4120
4121 // 9. Parse default scenes.
4122 {
Squareys43374632018-03-13 22:20:48 +01004123 json::const_iterator rootIt = v.find("scene");
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004124 if ((rootIt != v.end()) && rootIt.value().is_number_integer()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004125 const int defaultScene = rootIt.value();
4126
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004127 model->defaultScene = defaultScene;
Syoyo Fujita83675312017-12-02 21:14:13 +09004128 }
4129 }
4130
4131 // 10. Parse Material
4132 {
Squareys43374632018-03-13 22:20:48 +01004133 json::const_iterator rootIt = v.find("materials");
Syoyo Fujita83675312017-12-02 21:14:13 +09004134 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4135 const json &root = rootIt.value();
4136
4137 json::const_iterator it(root.begin());
4138 json::const_iterator itEnd(root.end());
4139 for (; it != itEnd; it++) {
4140 if (!it.value().is_object()) {
4141 if (err) {
4142 (*err) += "`materials' does not contain an JSON object.";
4143 }
4144 return false;
4145 }
4146 json jsonMaterial = it->get<json>();
4147
4148 Material material;
4149 ParseStringProperty(&material.name, err, jsonMaterial, "name", false);
4150
4151 if (!ParseMaterial(&material, err, jsonMaterial)) {
4152 return false;
4153 }
4154
4155 model->materials.push_back(material);
4156 }
4157 }
4158 }
4159
4160 // 11. Parse Image
4161 {
Squareys43374632018-03-13 22:20:48 +01004162 json::const_iterator rootIt = v.find("images");
Syoyo Fujita83675312017-12-02 21:14:13 +09004163 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4164 const json &root = rootIt.value();
4165
4166 json::const_iterator it(root.begin());
4167 json::const_iterator itEnd(root.end());
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09004168 int idx = 0;
4169 for (; it != itEnd; it++, idx++) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004170 if (!it.value().is_object()) {
4171 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004172 (*err) +=
4173 "image[" + std::to_string(idx) + "] is not a JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09004174 }
4175 return false;
4176 }
4177 Image image;
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09004178 if (!ParseImage(&image, idx, err, warn, it.value(), base_dir, &fs,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004179 &this->LoadImageData, load_image_user_data_)) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004180 return false;
4181 }
4182
4183 if (image.bufferView != -1) {
4184 // Load image from the buffer view.
4185 if (size_t(image.bufferView) >= model->bufferViews.size()) {
4186 if (err) {
4187 std::stringstream ss;
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09004188 ss << "image[" << idx << "] bufferView \"" << image.bufferView
Syoyo Fujita83675312017-12-02 21:14:13 +09004189 << "\" not found in the scene." << std::endl;
4190 (*err) += ss.str();
4191 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01004192 return false;
4193 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004194
4195 const BufferView &bufferView =
4196 model->bufferViews[size_t(image.bufferView)];
Jeff McGlynn89152522019-04-25 16:33:56 -07004197 if (size_t(bufferView.buffer) >= model->buffers.size()) {
4198 if (err) {
4199 std::stringstream ss;
4200 ss << "image[" << idx << "] buffer \"" << bufferView.buffer
4201 << "\" not found in the scene." << std::endl;
4202 (*err) += ss.str();
4203 }
4204 return false;
4205 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004206 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
4207
Squareysff644d82018-03-13 22:36:18 +01004208 if (*LoadImageData == nullptr) {
4209 if (err) {
4210 (*err) += "No LoadImageData callback specified.\n";
4211 }
4212 return false;
4213 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004214 bool ret = LoadImageData(
4215 &image, idx, err, warn, image.width, image.height,
4216 &buffer.data[bufferView.byteOffset],
4217 static_cast<int>(bufferView.byteLength), load_image_user_data_);
Syoyo Fujita83675312017-12-02 21:14:13 +09004218 if (!ret) {
4219 return false;
4220 }
4221 }
4222
4223 model->images.push_back(image);
4224 }
4225 }
4226 }
4227
4228 // 12. Parse Texture
4229 {
Squareys43374632018-03-13 22:20:48 +01004230 json::const_iterator rootIt = v.find("textures");
Syoyo Fujita83675312017-12-02 21:14:13 +09004231 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4232 const json &root = rootIt.value();
4233
4234 json::const_iterator it(root.begin());
4235 json::const_iterator itEnd(root.end());
4236 for (; it != itEnd; it++) {
4237 if (!it.value().is_object()) {
4238 if (err) {
4239 (*err) += "`textures' does not contain an JSON object.";
4240 }
4241 return false;
4242 }
4243 Texture texture;
4244 if (!ParseTexture(&texture, err, it->get<json>(), base_dir)) {
4245 return false;
4246 }
4247
4248 model->textures.push_back(texture);
4249 }
4250 }
4251 }
4252
4253 // 13. Parse Animation
4254 {
Squareys43374632018-03-13 22:20:48 +01004255 json::const_iterator rootIt = v.find("animations");
Syoyo Fujita83675312017-12-02 21:14:13 +09004256 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4257 const json &root = rootIt.value();
4258
4259 json::const_iterator it(root.begin());
4260 json::const_iterator itEnd(root.end());
4261 for (; it != itEnd; ++it) {
4262 if (!it.value().is_object()) {
4263 if (err) {
4264 (*err) += "`animations' does not contain an JSON object.";
4265 }
4266 return false;
4267 }
4268 Animation animation;
4269 if (!ParseAnimation(&animation, err, it->get<json>())) {
4270 return false;
4271 }
4272
4273 model->animations.push_back(animation);
4274 }
4275 }
4276 }
4277
4278 // 14. Parse Skin
4279 {
Squareys43374632018-03-13 22:20:48 +01004280 json::const_iterator rootIt = v.find("skins");
Syoyo Fujita83675312017-12-02 21:14:13 +09004281 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4282 const json &root = rootIt.value();
4283
4284 json::const_iterator it(root.begin());
4285 json::const_iterator itEnd(root.end());
4286 for (; it != itEnd; ++it) {
4287 if (!it.value().is_object()) {
4288 if (err) {
4289 (*err) += "`skins' does not contain an JSON object.";
4290 }
4291 return false;
4292 }
4293 Skin skin;
4294 if (!ParseSkin(&skin, err, it->get<json>())) {
4295 return false;
4296 }
4297
4298 model->skins.push_back(skin);
4299 }
4300 }
4301 }
4302
4303 // 15. Parse Sampler
4304 {
Squareys43374632018-03-13 22:20:48 +01004305 json::const_iterator rootIt = v.find("samplers");
Syoyo Fujita83675312017-12-02 21:14:13 +09004306 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4307 const json &root = rootIt.value();
4308
4309 json::const_iterator it(root.begin());
4310 json::const_iterator itEnd(root.end());
4311 for (; it != itEnd; ++it) {
4312 if (!it.value().is_object()) {
4313 if (err) {
4314 (*err) += "`samplers' does not contain an JSON object.";
4315 }
4316 return false;
4317 }
4318 Sampler sampler;
4319 if (!ParseSampler(&sampler, err, it->get<json>())) {
4320 return false;
4321 }
4322
4323 model->samplers.push_back(sampler);
4324 }
4325 }
4326 }
4327
4328 // 16. Parse Camera
4329 {
Squareys43374632018-03-13 22:20:48 +01004330 json::const_iterator rootIt = v.find("cameras");
Syoyo Fujita83675312017-12-02 21:14:13 +09004331 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4332 const json &root = rootIt.value();
4333
4334 json::const_iterator it(root.begin());
4335 json::const_iterator itEnd(root.end());
4336 for (; it != itEnd; ++it) {
4337 if (!it.value().is_object()) {
4338 if (err) {
4339 (*err) += "`cameras' does not contain an JSON object.";
4340 }
4341 return false;
4342 }
4343 Camera camera;
4344 if (!ParseCamera(&camera, err, it->get<json>())) {
4345 return false;
4346 }
4347
4348 model->cameras.push_back(camera);
4349 }
4350 }
4351 }
4352
4353 // 17. Parse Extensions
Selmar09d2ff12018-03-15 17:30:42 +01004354 ParseExtensionsProperty(&model->extensions, err, v);
4355
4356 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09004357 {
Squareys43374632018-03-13 22:20:48 +01004358 json::const_iterator rootIt = v.find("extensions");
Syoyo Fujita83675312017-12-02 21:14:13 +09004359 if ((rootIt != v.end()) && rootIt.value().is_object()) {
4360 const json &root = rootIt.value();
4361
4362 json::const_iterator it(root.begin());
4363 json::const_iterator itEnd(root.end());
4364 for (; it != itEnd; ++it) {
4365 // parse KHR_lights_cmn extension
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004366 if ((it.key().compare("KHR_lights_cmn") == 0) &&
4367 it.value().is_object()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004368 const json &object = it.value();
4369 json::const_iterator itLight(object.find("lights"));
4370 json::const_iterator itLightEnd(object.end());
4371 if (itLight == itLightEnd) {
4372 continue;
4373 }
4374
4375 if (!itLight.value().is_array()) {
4376 continue;
4377 }
4378
4379 const json &lights = itLight.value();
4380 json::const_iterator arrayIt(lights.begin());
4381 json::const_iterator arrayItEnd(lights.end());
4382 for (; arrayIt != arrayItEnd; ++arrayIt) {
4383 Light light;
4384 if (!ParseLight(&light, err, arrayIt.value())) {
4385 return false;
4386 }
4387 model->lights.push_back(light);
4388 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01004389 }
4390 }
4391 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01004392 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004393
mynzc0d4d1c2018-06-28 23:06:00 +09004394 // 19. Parse Extras
4395 ParseExtrasProperty(&model->extras, v);
4396
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004397 return true;
4398}
4399
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004400bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004401 std::string *warn, const char *str,
4402 unsigned int length,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004403 const std::string &base_dir,
4404 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004405 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004406 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004407 bin_size_ = 0;
4408
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004409 return LoadFromString(model, err, warn, str, length, base_dir,
4410 check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004411}
4412
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004413bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004414 std::string *warn, const std::string &filename,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004415 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09004416 std::stringstream ss;
4417
Paolo Jovone6601bf2018-07-07 20:43:33 +02004418 if (fs.ReadWholeFile == nullptr) {
4419 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004420 ss << "Failed to read file: " << filename
4421 << ": one or more FS callback not set" << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004422 if (err) {
4423 (*err) = ss.str();
4424 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09004425 return false;
4426 }
4427
Paolo Jovone6601bf2018-07-07 20:43:33 +02004428 std::vector<unsigned char> data;
4429 std::string fileerr;
4430 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004431 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02004432 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
4433 if (err) {
4434 (*err) = ss.str();
4435 }
4436 return false;
4437 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09004438
Paolo Jovone6601bf2018-07-07 20:43:33 +02004439 size_t sz = data.size();
Luke San Antoniob310b4a2016-06-23 14:17:37 -04004440 if (sz == 0) {
4441 if (err) {
4442 (*err) = "Empty file.";
4443 }
4444 return false;
4445 }
4446
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09004447 std::string basedir = GetBaseDir(filename);
4448
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004449 bool ret = LoadASCIIFromString(
4450 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
4451 static_cast<unsigned int>(data.size()), basedir, check_sections);
Luke San Antonio9e36b612016-06-23 14:10:51 -04004452
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004453 return ret;
4454}
4455
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004456bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004457 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004458 const unsigned char *bytes,
4459 unsigned int size,
4460 const std::string &base_dir,
4461 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004462 if (size < 20) {
4463 if (err) {
4464 (*err) = "Too short data size for glTF Binary.";
4465 }
4466 return false;
4467 }
4468
Syoyo Fujitabeded612016-05-01 20:03:43 +09004469 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
4470 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004471 // ok
4472 } else {
4473 if (err) {
4474 (*err) = "Invalid magic.";
4475 }
4476 return false;
4477 }
4478
Syoyo Fujitabeded612016-05-01 20:03:43 +09004479 unsigned int version; // 4 bytes
4480 unsigned int length; // 4 bytes
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004481 unsigned int model_length; // 4 bytes
4482 unsigned int model_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004483
4484 // @todo { Endian swap for big endian machine. }
Syoyo Fujitabeded612016-05-01 20:03:43 +09004485 memcpy(&version, bytes + 4, 4);
4486 swap4(&version);
4487 memcpy(&length, bytes + 8, 4);
4488 swap4(&length);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004489 memcpy(&model_length, bytes + 12, 4);
4490 swap4(&model_length);
4491 memcpy(&model_format, bytes + 16, 4);
4492 swap4(&model_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004493
Syoyo Fujitae69069d2018-03-15 22:01:18 -05004494 // In case the Bin buffer is not present, the size is exactly 20 + size of
4495 // JSON contents,
4496 // so use "greater than" operator.
Jeff McGlynn89152522019-04-25 16:33:56 -07004497 if ((20 + model_length > size) || (model_length < 1) || (length > size) ||
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09004498 (model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004499 if (err) {
4500 (*err) = "Invalid glTF binary.";
4501 }
4502 return false;
4503 }
4504
4505 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09004506 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004507 model_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004508
4509 is_binary_ = true;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004510 bin_data_ = bytes + 20 + model_length +
4511 8; // 4 bytes (buffer_length) + 4 bytes(buffer_format)
Syoyo Fujitabeded612016-05-01 20:03:43 +09004512 bin_size_ =
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004513 length - (20 + model_length); // extract header + JSON scene data.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004514
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004515 bool ret = LoadFromString(model, err, warn,
4516 reinterpret_cast<const char *>(&bytes[20]),
4517 model_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004518 if (!ret) {
4519 return ret;
4520 }
4521
4522 return true;
4523}
4524
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004525bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004526 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004527 const std::string &filename,
4528 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004529 std::stringstream ss;
4530
Paolo Jovone6601bf2018-07-07 20:43:33 +02004531 if (fs.ReadWholeFile == nullptr) {
4532 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004533 ss << "Failed to read file: " << filename
4534 << ": one or more FS callback not set" << std::endl;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004535 if (err) {
4536 (*err) = ss.str();
4537 }
4538 return false;
4539 }
4540
Paolo Jovone6601bf2018-07-07 20:43:33 +02004541 std::vector<unsigned char> data;
4542 std::string fileerr;
4543 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004544 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02004545 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
4546 if (err) {
4547 (*err) = ss.str();
4548 }
4549 return false;
4550 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004551
Syoyo Fujitabeded612016-05-01 20:03:43 +09004552 std::string basedir = GetBaseDir(filename);
4553
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004554 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
4555 static_cast<unsigned int>(data.size()),
4556 basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09004557
4558 return ret;
4559}
4560
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004561///////////////////////
4562// GLTF Serialization
4563///////////////////////
4564
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004565// typedef std::pair<std::string, json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004566
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004567template <typename T>
4568static void SerializeNumberProperty(const std::string &key, T number,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004569 json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004570 // obj.insert(
Syoyo Fujita83675312017-12-02 21:14:13 +09004571 // json_object_pair(key, json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004572 // obj[key] = static_cast<double>(number);
4573 obj[key] = number;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004574}
4575
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004576template <typename T>
4577static void SerializeNumberArrayProperty(const std::string &key,
4578 const std::vector<T> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004579 json &obj) {
4580 json o;
Syoyo Fujita83675312017-12-02 21:14:13 +09004581 json vals;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004582
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004583 for (unsigned int i = 0; i < value.size(); ++i) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004584 vals.push_back(static_cast<T>(value[i]));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004585 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02004586 if (!vals.is_null()) {
4587 obj[key] = vals;
4588 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004589}
4590
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004591static void SerializeStringProperty(const std::string &key,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004592 const std::string &value, json &obj) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004593 obj[key] = value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004594}
4595
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004596static void SerializeStringArrayProperty(const std::string &key,
4597 const std::vector<std::string> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004598 json &obj) {
4599 json o;
Syoyo Fujita83675312017-12-02 21:14:13 +09004600 json vals;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004601
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004602 for (unsigned int i = 0; i < value.size(); ++i) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004603 vals.push_back(value[i]);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004604 }
4605
Syoyo Fujita83675312017-12-02 21:14:13 +09004606 obj[key] = vals;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004607}
4608
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004609static bool ValueToJson(const Value &value, json *ret) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02004610 json obj;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004611 switch (value.Type()) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02004612 case NUMBER_TYPE:
4613 obj = json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004614 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02004615 case INT_TYPE:
4616 obj = json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004617 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02004618 case BOOL_TYPE:
4619 obj = json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004620 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02004621 case STRING_TYPE:
4622 obj = json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004623 break;
4624 case ARRAY_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02004625 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
4626 Value elementValue = value.Get(int(i));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004627 json elementJson;
4628 if (ValueToJson(value.Get(int(i)), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02004629 obj.push_back(elementJson);
4630 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004631 break;
4632 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02004633 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004634 // TODO
4635 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02004636 return false;
4637 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004638 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02004639 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004640 for (auto &it : objMap) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02004641 json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004642 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02004643 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004644 break;
4645 }
4646 case NULL_TYPE:
4647 default:
4648 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02004649 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004650 if (ret) *ret = obj;
Selmar Kokfa7022f2018-04-04 18:10:20 +02004651 return true;
4652}
4653
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004654static void SerializeValue(const std::string &key, const Value &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004655 json &obj) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02004656 json ret;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004657 if (ValueToJson(value, &ret)) obj[key] = ret;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004658}
4659
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004660static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
johan bowald30c53472018-03-30 11:49:36 +02004661 json &o) {
4662 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita719d7e42018-03-30 19:26:35 +09004663 std::string encodedData =
4664 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
johan bowald30c53472018-03-30 11:49:36 +02004665 SerializeStringProperty("uri", header + encodedData, o);
4666}
4667
Selmar Koke4677492018-10-25 16:45:49 +02004668static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004669 const std::string &binFilename) {
4670 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004671 if (!output.is_open()) return false;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004672 output.write(reinterpret_cast<const char *>(&data[0]),
4673 std::streamsize(data.size()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004674 output.close();
Selmar Koke4677492018-10-25 16:45:49 +02004675 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004676}
4677
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004678static void SerializeParameterMap(ParameterMap &param, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004679 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
4680 ++paramIt) {
4681 if (paramIt->second.number_array.size()) {
4682 SerializeNumberArrayProperty<double>(paramIt->first,
4683 paramIt->second.number_array, o);
4684 } else if (paramIt->second.json_double_value.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004685 json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004686 for (std::map<std::string, double>::iterator it =
4687 paramIt->second.json_double_value.begin();
4688 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004689 if (it->first == "index") {
4690 json_double_value[it->first] = paramIt->second.TextureIndex();
4691 } else {
4692 json_double_value[it->first] = it->second;
4693 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004694 }
4695
Syoyo Fujita83675312017-12-02 21:14:13 +09004696 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004697 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004698 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07004699 } else if (paramIt->second.has_number_value) {
4700 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004701 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09004702 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004703 }
4704 }
4705}
4706
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004707static void SerializeExtensionMap(ExtensionMap &extensions, json &o) {
4708 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01004709
4710 json extMap;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004711 for (ExtensionMap::iterator extIt = extensions.begin();
4712 extIt != extensions.end(); ++extIt) {
Selmar09d2ff12018-03-15 17:30:42 +01004713 json extension_values;
Syoyo Fujita924d86e2018-10-08 21:15:12 +09004714
4715 // Allow an empty object for extension(#97)
4716 json ret;
4717 if (ValueToJson(extIt->second, &ret)) {
4718 extMap[extIt->first] = ret;
Selmar Kokdb7f4e42018-10-10 18:10:58 +02004719 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004720 if (ret.is_null()) {
4721 if (!(extIt->first.empty())) { // name should not be empty, but for sure
4722 // create empty object so that an extension name is still included in
4723 // json.
Syoyo Fujita924d86e2018-10-08 21:15:12 +09004724 extMap[extIt->first] = json({});
4725 }
4726 }
Selmar09d2ff12018-03-15 17:30:42 +01004727 }
4728 o["extensions"] = extMap;
4729}
4730
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004731static void SerializeGltfAccessor(Accessor &accessor, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004732 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004733
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004734 if (accessor.byteOffset != 0.0)
4735 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004736
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004737 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
4738 SerializeNumberProperty<size_t>("count", accessor.count, o);
4739 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
4740 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
Fabien Freling9056aee2019-03-21 17:03:18 +01004741 SerializeValue("normalized", Value(accessor.normalized), o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004742 std::string type;
4743 switch (accessor.type) {
4744 case TINYGLTF_TYPE_SCALAR:
4745 type = "SCALAR";
4746 break;
4747 case TINYGLTF_TYPE_VEC2:
4748 type = "VEC2";
4749 break;
4750 case TINYGLTF_TYPE_VEC3:
4751 type = "VEC3";
4752 break;
4753 case TINYGLTF_TYPE_VEC4:
4754 type = "VEC4";
4755 break;
4756 case TINYGLTF_TYPE_MAT2:
4757 type = "MAT2";
4758 break;
4759 case TINYGLTF_TYPE_MAT3:
4760 type = "MAT3";
4761 break;
4762 case TINYGLTF_TYPE_MAT4:
4763 type = "MAT4";
4764 break;
4765 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004766
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004767 SerializeStringProperty("type", type, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004768 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02004769
4770 if (accessor.extras.Type() != NULL_TYPE) {
4771 SerializeValue("extras", accessor.extras, o);
4772 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004773}
4774
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004775static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004776 SerializeNumberProperty("sampler", channel.sampler, o);
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004777 json target;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004778 SerializeNumberProperty("node", channel.target_node, target);
4779 SerializeStringProperty("path", channel.target_path, target);
4780
Syoyo Fujita83675312017-12-02 21:14:13 +09004781 o["target"] = target;
Jens Olssonb3af2f12018-06-04 10:17:49 +02004782
4783 if (channel.extras.Type() != NULL_TYPE) {
4784 SerializeValue("extras", channel.extras, o);
4785 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004786}
4787
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004788static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004789 SerializeNumberProperty("input", sampler.input, o);
4790 SerializeNumberProperty("output", sampler.output, o);
4791 SerializeStringProperty("interpolation", sampler.interpolation, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02004792
4793 if (sampler.extras.Type() != NULL_TYPE) {
4794 SerializeValue("extras", sampler.extras, o);
4795 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004796}
4797
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004798static void SerializeGltfAnimation(Animation &animation, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004799 if (!animation.name.empty())
4800 SerializeStringProperty("name", animation.name, o);
Syoyo Fujita83675312017-12-02 21:14:13 +09004801 json channels;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004802 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004803 json channel;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004804 AnimationChannel gltfChannel = animation.channels[i];
4805 SerializeGltfAnimationChannel(gltfChannel, channel);
Syoyo Fujita83675312017-12-02 21:14:13 +09004806 channels.push_back(channel);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004807 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004808 o["channels"] = channels;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004809
Syoyo Fujita83675312017-12-02 21:14:13 +09004810 json samplers;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004811 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004812 json sampler;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004813 AnimationSampler gltfSampler = animation.samplers[i];
4814 SerializeGltfAnimationSampler(gltfSampler, sampler);
Syoyo Fujita83675312017-12-02 21:14:13 +09004815 samplers.push_back(sampler);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004816 }
4817
Syoyo Fujita83675312017-12-02 21:14:13 +09004818 o["samplers"] = samplers;
Jens Olssonb3af2f12018-06-04 10:17:49 +02004819
4820 if (animation.extras.Type() != NULL_TYPE) {
4821 SerializeValue("extras", animation.extras, o);
4822 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004823}
4824
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004825static void SerializeGltfAsset(Asset &asset, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004826 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004827 SerializeStringProperty("generator", asset.generator, o);
4828 }
4829
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004830 if (!asset.version.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004831 SerializeStringProperty("version", asset.version, o);
4832 }
4833
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004834 if (asset.extras.Keys().size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004835 SerializeValue("extras", asset.extras, o);
4836 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004837
Selmar09d2ff12018-03-15 17:30:42 +01004838 SerializeExtensionMap(asset.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004839}
4840
johan bowald30c53472018-03-30 11:49:36 +02004841static void SerializeGltfBuffer(Buffer &buffer, json &o) {
4842 SerializeNumberProperty("byteLength", buffer.data.size(), o);
4843 SerializeGltfBufferData(buffer.data, o);
4844
4845 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02004846
4847 if (buffer.extras.Type() != NULL_TYPE) {
4848 SerializeValue("extras", buffer.extras, o);
4849 }
johan bowald30c53472018-03-30 11:49:36 +02004850}
4851
Selmar Koke4677492018-10-25 16:45:49 +02004852static bool SerializeGltfBuffer(Buffer &buffer, json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004853 const std::string &binFilename,
4854 const std::string &binBaseFilename) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004855 if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004856 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004857 SerializeStringProperty("uri", binBaseFilename, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004858
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004859 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02004860
4861 if (buffer.extras.Type() != NULL_TYPE) {
4862 SerializeValue("extras", buffer.extras, o);
4863 }
Selmar Koke4677492018-10-25 16:45:49 +02004864 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004865}
4866
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004867static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004868 SerializeNumberProperty("buffer", bufferView.buffer, o);
4869 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004870
Johan Bowaldfaa27222018-03-28 14:44:45 +02004871 // byteStride is optional, minimum allowed is 4
4872 if (bufferView.byteStride >= 4) {
4873 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
4874 }
4875 // byteOffset is optional, default is 0
4876 if (bufferView.byteOffset > 0) {
4877 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
4878 }
4879 // Target is optional, check if it contains a valid value
4880 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
4881 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
4882 SerializeNumberProperty("target", bufferView.target, o);
4883 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004884 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004885 SerializeStringProperty("name", bufferView.name, o);
4886 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02004887
4888 if (bufferView.extras.Type() != NULL_TYPE) {
4889 SerializeValue("extras", bufferView.extras, o);
4890 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004891}
4892
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004893static void SerializeGltfImage(Image &image, json &o) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02004894 // if uri empty, the mimeType and bufferview should be set
rainliang00062be8d02019-04-29 09:54:27 +08004895 if (image.uri.empty()) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02004896 SerializeStringProperty("mimeType", image.mimeType, o);
4897 SerializeNumberProperty<int>("bufferView", image.bufferView, o);
4898 } else {
4899 SerializeStringProperty("uri", image.uri, o);
4900 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004901
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004902 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004903 SerializeStringProperty("name", image.name, o);
4904 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02004905
4906 if (image.extras.Type() != NULL_TYPE) {
4907 SerializeValue("extras", image.extras, o);
4908 }
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09004909
4910 SerializeExtensionMap(image.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004911}
4912
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004913static void SerializeGltfMaterial(Material &material, json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004914 if (material.extras.Size()) SerializeValue("extras", material.extras, o);
Selmar09d2ff12018-03-15 17:30:42 +01004915 SerializeExtensionMap(material.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004916
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004917 if (material.values.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004918 json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004919 SerializeParameterMap(material.values, pbrMetallicRoughness);
Syoyo Fujita83675312017-12-02 21:14:13 +09004920 o["pbrMetallicRoughness"] = pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004921 }
4922
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004923 SerializeParameterMap(material.additionalValues, o);
4924
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004925 if (material.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004926 SerializeStringProperty("name", material.name, o);
4927 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02004928
4929 if (material.extras.Type() != NULL_TYPE) {
4930 SerializeValue("extras", material.extras, o);
4931 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004932}
4933
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004934static void SerializeGltfMesh(Mesh &mesh, json &o) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004935 json primitives;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004936 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004937 json primitive;
4938 json attributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004939 Primitive gltfPrimitive = mesh.primitives[i];
4940 for (std::map<std::string, int>::iterator attrIt =
4941 gltfPrimitive.attributes.begin();
4942 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
4943 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
4944 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004945
Syoyo Fujita83675312017-12-02 21:14:13 +09004946 primitive["attributes"] = attributes;
Johan Bowaldfaa27222018-03-28 14:44:45 +02004947
4948 // Indicies is optional
4949 if (gltfPrimitive.indices > -1) {
4950 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
4951 }
4952 // Material is optional
4953 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09004954 SerializeNumberProperty<int>("material", gltfPrimitive.material,
4955 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02004956 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004957 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004958
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004959 // Morph targets
4960 if (gltfPrimitive.targets.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004961 json targets;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004962 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004963 json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004964 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
4965 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
4966 attrIt != targetData.end(); ++attrIt) {
4967 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
4968 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004969 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004970
Syoyo Fujita83675312017-12-02 21:14:13 +09004971 targets.push_back(targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004972 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004973 primitive["targets"] = targets;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004974 }
4975
Jens Olssonb3af2f12018-06-04 10:17:49 +02004976 if (gltfPrimitive.extras.Type() != NULL_TYPE) {
4977 SerializeValue("extras", gltfPrimitive.extras, primitive);
4978 }
4979
Syoyo Fujita83675312017-12-02 21:14:13 +09004980 primitives.push_back(primitive);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004981 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004982
Syoyo Fujita83675312017-12-02 21:14:13 +09004983 o["primitives"] = primitives;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004984 if (mesh.weights.size()) {
4985 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
4986 }
4987
4988 if (mesh.name.size()) {
4989 SerializeStringProperty("name", mesh.name, o);
4990 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02004991
4992 if (mesh.extras.Type() != NULL_TYPE) {
4993 SerializeValue("extras", mesh.extras, o);
4994 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004995}
4996
Syoyo Fujita83675312017-12-02 21:14:13 +09004997static void SerializeGltfLight(Light &light, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004998 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
Emanuel Schrade186322b2017-11-06 11:14:41 +01004999 SerializeNumberArrayProperty("color", light.color, o);
5000 SerializeStringProperty("type", light.type, o);
5001}
5002
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005003static void SerializeGltfNode(Node &node, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005004 if (node.translation.size() > 0) {
5005 SerializeNumberArrayProperty<double>("translation", node.translation, o);
5006 }
5007 if (node.rotation.size() > 0) {
5008 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
5009 }
5010 if (node.scale.size() > 0) {
5011 SerializeNumberArrayProperty<double>("scale", node.scale, o);
5012 }
5013 if (node.matrix.size() > 0) {
5014 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
5015 }
5016 if (node.mesh != -1) {
5017 SerializeNumberProperty<int>("mesh", node.mesh, o);
5018 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005019
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005020 if (node.skin != -1) {
5021 SerializeNumberProperty<int>("skin", node.skin, o);
5022 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005023
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005024 if (node.camera != -1) {
5025 SerializeNumberProperty<int>("camera", node.camera, o);
5026 }
5027
Jens Olssona9718662018-05-24 15:48:49 +02005028 if (node.extras.Type() != NULL_TYPE) {
Jens Olssonb96f6962018-05-24 15:29:54 +02005029 SerializeValue("extras", node.extras, o);
5030 }
5031
Selmar09d2ff12018-03-15 17:30:42 +01005032 SerializeExtensionMap(node.extensions, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09005033 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005034 SerializeNumberArrayProperty<int>("children", node.children, o);
5035}
5036
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005037static void SerializeGltfSampler(Sampler &sampler, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005038 SerializeNumberProperty("magFilter", sampler.magFilter, o);
5039 SerializeNumberProperty("minFilter", sampler.minFilter, o);
Selmar Koka3595482018-11-09 10:34:39 +01005040 SerializeNumberProperty("wrapR", sampler.wrapR, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005041 SerializeNumberProperty("wrapS", sampler.wrapS, o);
5042 SerializeNumberProperty("wrapT", sampler.wrapT, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02005043
5044 if (sampler.extras.Type() != NULL_TYPE) {
5045 SerializeValue("extras", sampler.extras, o);
5046 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005047}
5048
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005049static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005050 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005051 SerializeNumberProperty("zfar", camera.zfar, o);
5052 SerializeNumberProperty("znear", camera.znear, o);
5053 SerializeNumberProperty("xmag", camera.xmag, o);
5054 SerializeNumberProperty("ymag", camera.ymag, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02005055
5056 if (camera.extras.Type() != NULL_TYPE) {
5057 SerializeValue("extras", camera.extras, o);
5058 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005059}
5060
5061static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005062 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005063 SerializeNumberProperty("zfar", camera.zfar, o);
5064 SerializeNumberProperty("znear", camera.znear, o);
5065 if (camera.aspectRatio > 0) {
5066 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
5067 }
5068
5069 if (camera.yfov > 0) {
5070 SerializeNumberProperty("yfov", camera.yfov, o);
5071 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02005072
5073 if (camera.extras.Type() != NULL_TYPE) {
5074 SerializeValue("extras", camera.extras, o);
5075 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005076}
5077
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005078static void SerializeGltfCamera(const Camera &camera, json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005079 SerializeStringProperty("type", camera.type, o);
5080 if (!camera.name.empty()) {
Selmar Kokee3d0662018-10-08 16:20:43 +02005081 SerializeStringProperty("name", camera.name, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005082 }
5083
5084 if (camera.type.compare("orthographic") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005085 json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005086 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
Syoyo Fujita83675312017-12-02 21:14:13 +09005087 o["orthographic"] = orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005088 } else if (camera.type.compare("perspective") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005089 json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005090 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
Syoyo Fujita83675312017-12-02 21:14:13 +09005091 o["perspective"] = perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005092 } else {
5093 // ???
5094 }
5095}
5096
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005097static void SerializeGltfScene(Scene &scene, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005098 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
5099
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005100 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005101 SerializeStringProperty("name", scene.name, o);
5102 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02005103 if (scene.extras.Type() != NULL_TYPE) {
Selmar09d2ff12018-03-15 17:30:42 +01005104 SerializeValue("extras", scene.extras, o);
5105 }
5106 SerializeExtensionMap(scene.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005107}
5108
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005109static void SerializeGltfSkin(Skin &skin, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005110 if (skin.inverseBindMatrices != -1)
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005111 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
5112
5113 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
5114 SerializeNumberProperty("skeleton", skin.skeleton, o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005115 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005116 SerializeStringProperty("name", skin.name, o);
5117 }
5118}
5119
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005120static void SerializeGltfTexture(Texture &texture, json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02005121 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02005122 SerializeNumberProperty("sampler", texture.sampler, o);
5123 }
Selmar Kok8eb39042018-10-05 14:29:35 +02005124 if (texture.source > -1) {
5125 SerializeNumberProperty("source", texture.source, o);
5126 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02005127 if (texture.extras.Type() != NULL_TYPE) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005128 SerializeValue("extras", texture.extras, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005129 }
Selmar Kok9eae1102018-04-04 18:10:37 +02005130 SerializeExtensionMap(texture.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005131}
5132
Selmar Kok31cb7f92018-10-03 15:39:05 +02005133static bool WriteGltfFile(const std::string &output,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005134 const std::string &content) {
5135 std::ofstream gltfFile(output.c_str());
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09005136 if (!gltfFile.is_open()) return false;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005137 gltfFile << content << std::endl;
Selmar Kok31cb7f92018-10-03 15:39:05 +02005138 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005139}
5140
David Harmonda9eac22018-08-30 08:06:05 -04005141static void WriteBinaryGltfFile(const std::string &output,
5142 const std::string &content) {
5143 std::ofstream gltfFile(output.c_str(), std::ios::binary);
5144
5145 const std::string header = "glTF";
5146 const int version = 2;
5147 const int padding_size = content.size() % 4;
5148
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005149 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info,
5150 // padding
Alexander Wood0d77a292019-01-26 08:58:45 -05005151 const int length = 12 + 8 + int(content.size()) + padding_size;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005152
David Harmonda9eac22018-08-30 08:06:05 -04005153 gltfFile.write(header.c_str(), header.size());
5154 gltfFile.write(reinterpret_cast<const char *>(&version), sizeof(version));
5155 gltfFile.write(reinterpret_cast<const char *>(&length), sizeof(length));
5156
5157 // JSON chunk info, then JSON data
Alexander Wood0d77a292019-01-26 08:58:45 -05005158 const int model_length = int(content.size()) + padding_size;
David Harmonda9eac22018-08-30 08:06:05 -04005159 const int model_format = 0x4E4F534A;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005160 gltfFile.write(reinterpret_cast<const char *>(&model_length),
5161 sizeof(model_length));
5162 gltfFile.write(reinterpret_cast<const char *>(&model_format),
5163 sizeof(model_format));
David Harmonda9eac22018-08-30 08:06:05 -04005164 gltfFile.write(content.c_str(), content.size());
5165
5166 // Chunk must be multiplies of 4, so pad with spaces
5167 if (padding_size > 0) {
5168 const std::string padding = std::string(padding_size, ' ');
5169 gltfFile.write(padding.c_str(), padding.size());
5170 }
5171}
5172
johan bowald642a3432018-04-01 12:37:18 +02005173bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
5174 bool embedImages = false,
Selmar Kokee3d0662018-10-08 16:20:43 +02005175 bool embedBuffers = false,
Syoyo Fujitab5726502018-11-10 20:07:15 +09005176 bool prettyPrint = true,
David Harmonda9eac22018-08-30 08:06:05 -04005177 bool writeBinary = false) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005178 json output;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005179
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005180 // ACCESSORS
Syoyo Fujita83675312017-12-02 21:14:13 +09005181 json accessors;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005182 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005183 json accessor;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005184 SerializeGltfAccessor(model->accessors[i], accessor);
Syoyo Fujita83675312017-12-02 21:14:13 +09005185 accessors.push_back(accessor);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005186 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005187 output["accessors"] = accessors;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005188
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005189 // ANIMATIONS
5190 if (model->animations.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005191 json animations;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005192 for (unsigned int i = 0; i < model->animations.size(); ++i) {
5193 if (model->animations[i].channels.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005194 json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005195 SerializeGltfAnimation(model->animations[i], animation);
Syoyo Fujita83675312017-12-02 21:14:13 +09005196 animations.push_back(animation);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005197 }
5198 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005199 output["animations"] = animations;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005200 }
5201
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005202 // ASSET
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005203 json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005204 SerializeGltfAsset(model->asset, asset);
Syoyo Fujita83675312017-12-02 21:14:13 +09005205 output["asset"] = asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005206
Selmar Kok7cb31e42018-10-05 16:02:29 +02005207 std::string defaultBinFilename = GetBaseFilename(filename);
5208 std::string defaultBinFileExt = ".bin";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005209 std::string::size_type pos =
5210 defaultBinFilename.rfind('.', defaultBinFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005211
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005212 if (pos != std::string::npos) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02005213 defaultBinFilename = defaultBinFilename.substr(0, pos);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005214 }
johan bowald642a3432018-04-01 12:37:18 +02005215 std::string baseDir = GetBaseDir(filename);
5216 if (baseDir.empty()) {
5217 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05005218 }
5219
Selmar Kok7cb31e42018-10-05 16:02:29 +02005220 // BUFFERS
Selmar Kokc884e582018-10-05 16:25:54 +02005221 std::vector<std::string> usedUris;
Syoyo Fujita83675312017-12-02 21:14:13 +09005222 json buffers;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005223 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005224 json buffer;
johan bowald30c53472018-03-30 11:49:36 +02005225 if (embedBuffers) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09005226 SerializeGltfBuffer(model->buffers[i], buffer);
5227 } else {
Selmar Kok7cb31e42018-10-05 16:02:29 +02005228 std::string binSavePath;
Selmar Kokc884e582018-10-05 16:25:54 +02005229 std::string binUri;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005230 if (!model->buffers[i].uri.empty() && !IsDataURI(model->buffers[i].uri)) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02005231 binUri = model->buffers[i].uri;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005232 } else {
Selmar Kokc884e582018-10-05 16:25:54 +02005233 binUri = defaultBinFilename + defaultBinFileExt;
5234 bool inUse = true;
Selmar Kok440cb1e2018-10-05 16:30:50 +02005235 int numUsed = 0;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005236 while (inUse) {
Selmar Kokc884e582018-10-05 16:25:54 +02005237 inUse = false;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005238 for (const std::string &usedName : usedUris) {
Selmar Kokc884e582018-10-05 16:25:54 +02005239 if (binUri.compare(usedName) != 0) continue;
5240 inUse = true;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005241 binUri = defaultBinFilename + std::to_string(numUsed++) +
5242 defaultBinFileExt;
Selmar Kokc884e582018-10-05 16:25:54 +02005243 break;
5244 }
5245 }
5246 }
5247 usedUris.push_back(binUri);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005248 binSavePath = JoinPath(baseDir, binUri);
5249 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
5250 binUri)) {
Selmar Koke4677492018-10-25 16:45:49 +02005251 return false;
5252 }
johan bowald30c53472018-03-30 11:49:36 +02005253 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005254 buffers.push_back(buffer);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005255 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005256 output["buffers"] = buffers;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005257
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005258 // BUFFERVIEWS
Syoyo Fujita83675312017-12-02 21:14:13 +09005259 json bufferViews;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005260 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005261 json bufferView;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005262 SerializeGltfBufferView(model->bufferViews[i], bufferView);
Syoyo Fujita83675312017-12-02 21:14:13 +09005263 bufferViews.push_back(bufferView);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005264 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005265 output["bufferViews"] = bufferViews;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005266
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005267 // Extensions used
5268 if (model->extensionsUsed.size()) {
5269 SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed,
5270 output);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005271 }
5272
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005273 // Extensions required
5274 if (model->extensionsRequired.size()) {
5275 SerializeStringArrayProperty("extensionsRequired",
5276 model->extensionsRequired, output);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005277 }
5278
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005279 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02005280 if (model->images.size()) {
5281 json images;
5282 for (unsigned int i = 0; i < model->images.size(); ++i) {
5283 json image;
johan bowald642a3432018-04-01 12:37:18 +02005284
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09005285 UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
Johan Bowald77decfa2018-11-06 14:28:20 +01005286 &this->WriteImageData, this->write_image_user_data_);
Johan Bowaldfaa27222018-03-28 14:44:45 +02005287 SerializeGltfImage(model->images[i], image);
5288 images.push_back(image);
5289 }
5290 output["images"] = images;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005291 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005292
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005293 // MATERIALS
Johan Bowaldfaa27222018-03-28 14:44:45 +02005294 if (model->materials.size()) {
5295 json materials;
5296 for (unsigned int i = 0; i < model->materials.size(); ++i) {
5297 json material;
5298 SerializeGltfMaterial(model->materials[i], material);
5299 materials.push_back(material);
5300 }
5301 output["materials"] = materials;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005302 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005303
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005304 // MESHES
Johan Bowaldfaa27222018-03-28 14:44:45 +02005305 if (model->meshes.size()) {
5306 json meshes;
5307 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
5308 json mesh;
5309 SerializeGltfMesh(model->meshes[i], mesh);
5310 meshes.push_back(mesh);
5311 }
5312 output["meshes"] = meshes;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005313 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005314
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005315 // NODES
Johan Bowaldfaa27222018-03-28 14:44:45 +02005316 if (model->nodes.size()) {
5317 json nodes;
5318 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
5319 json node;
5320 SerializeGltfNode(model->nodes[i], node);
5321 nodes.push_back(node);
5322 }
5323 output["nodes"] = nodes;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005324 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005325
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005326 // SCENE
Johan Bowaldfaa27222018-03-28 14:44:45 +02005327 if (model->defaultScene > -1) {
5328 SerializeNumberProperty<int>("scene", model->defaultScene, output);
5329 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005330
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005331 // SCENES
Johan Bowaldfaa27222018-03-28 14:44:45 +02005332 if (model->scenes.size()) {
5333 json scenes;
5334 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
5335 json currentScene;
5336 SerializeGltfScene(model->scenes[i], currentScene);
5337 scenes.push_back(currentScene);
5338 }
5339 output["scenes"] = scenes;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005340 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005341
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005342 // SKINS
5343 if (model->skins.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005344 json skins;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005345 for (unsigned int i = 0; i < model->skins.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09005346 json skin;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005347 SerializeGltfSkin(model->skins[i], skin);
Syoyo Fujita83675312017-12-02 21:14:13 +09005348 skins.push_back(skin);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005349 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005350 output["skins"] = skins;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005351 }
5352
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005353 // TEXTURES
Johan Bowaldfaa27222018-03-28 14:44:45 +02005354 if (model->textures.size()) {
5355 json textures;
5356 for (unsigned int i = 0; i < model->textures.size(); ++i) {
5357 json texture;
5358 SerializeGltfTexture(model->textures[i], texture);
5359 textures.push_back(texture);
5360 }
5361 output["textures"] = textures;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005362 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005363
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005364 // SAMPLERS
Johan Bowaldfaa27222018-03-28 14:44:45 +02005365 if (model->samplers.size()) {
5366 json samplers;
5367 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
5368 json sampler;
5369 SerializeGltfSampler(model->samplers[i], sampler);
5370 samplers.push_back(sampler);
5371 }
5372 output["samplers"] = samplers;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005373 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005374
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005375 // CAMERAS
Johan Bowaldfaa27222018-03-28 14:44:45 +02005376 if (model->cameras.size()) {
5377 json cameras;
5378 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
5379 json camera;
5380 SerializeGltfCamera(model->cameras[i], camera);
5381 cameras.push_back(camera);
5382 }
5383 output["cameras"] = cameras;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005384 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005385
Selmar09d2ff12018-03-15 17:30:42 +01005386 // EXTENSIONS
5387 SerializeExtensionMap(model->extensions, output);
5388
Syoyo Fujitad5b02442018-03-17 16:12:42 -05005389 // LIGHTS as KHR_lights_cmn
Johan Bowaldfaa27222018-03-28 14:44:45 +02005390 if (model->lights.size()) {
5391 json lights;
5392 for (unsigned int i = 0; i < model->lights.size(); ++i) {
5393 json light;
5394 SerializeGltfLight(model->lights[i], light);
5395 lights.push_back(light);
5396 }
Syoyo Fujitad5b02442018-03-17 16:12:42 -05005397 json khr_lights_cmn;
5398 khr_lights_cmn["lights"] = lights;
5399 json ext_j;
5400
5401 if (output.find("extensions") != output.end()) {
5402 ext_j = output["extensions"];
5403 }
5404
5405 ext_j["KHR_lights_cmn"] = khr_lights_cmn;
5406
5407 output["extensions"] = ext_j;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07005408 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01005409
Jens Olssonb3af2f12018-06-04 10:17:49 +02005410 // EXTRAS
5411 if (model->extras.Type() != NULL_TYPE) {
5412 SerializeValue("extras", model->extras, output);
5413 }
5414
David Harmonda9eac22018-08-30 08:06:05 -04005415 if (writeBinary) {
5416 WriteBinaryGltfFile(filename, output.dump());
5417 } else {
Selmar Kok27aab612018-11-30 18:01:31 +01005418 WriteGltfFile(filename, output.dump(prettyPrint ? 2 : -1));
David Harmonda9eac22018-08-30 08:06:05 -04005419 }
5420
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00005421 return true;
5422}
5423
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09005424} // namespace tinygltf
5425
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09005426#ifdef __clang__
5427#pragma clang diagnostic pop
5428#endif
5429
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005430#endif // TINYGLTF_IMPLEMENTATION