blob: c9203cfac29cc438bdbedd7627c2fe20b116a339 [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 Fujitadc4bb862018-04-02 02:04:24 +09007// Copyright (c) 2015 - 2018 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 Fujita0820d832018-10-04 15:45:13 +090029// - v2.0.1 Add comparsion feature(Thanks to @Selmar).
Syoyo Fujitaf6120152017-05-27 23:51:23 +090030// - v2.0.0 glTF 2.0!.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090031//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090032// Tiny glTF loader is using following third party libraries:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090033//
Syoyo Fujita2e21be72017-11-05 17:13:01 +090034// - jsonhpp: C++ JSON library.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090035// - base64: base64 decode/encode library.
36// - stb_image: Image loading library.
37//
Syoyo Fujita2840a0e2017-06-21 02:52:11 +090038#ifndef TINY_GLTF_H_
39#define TINY_GLTF_H_
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090040
Syoyo Fujitad42767e2018-03-15 21:52:00 -050041#include <array>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090042#include <cassert>
Syoyo Fujitad42767e2018-03-15 21:52:00 -050043#include <cstdint>
Selmar Kok31cb7f92018-10-03 15:39:05 +020044#include <cstdlib>
Syoyo Fujita641b3cc2018-10-04 15:43:33 +090045#include <cstring>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090046#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090047#include <string>
48#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090049
50namespace tinygltf {
51
52#define TINYGLTF_MODE_POINTS (0)
53#define TINYGLTF_MODE_LINE (1)
54#define TINYGLTF_MODE_LINE_LOOP (2)
55#define TINYGLTF_MODE_TRIANGLES (4)
56#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
57#define TINYGLTF_MODE_TRIANGLE_FAN (6)
58
59#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
60#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
61#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
62#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
63#define TINYGLTF_COMPONENT_TYPE_INT (5124)
64#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
65#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
Syoyo Fujita476a8b22018-01-21 12:19:01 +090066#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090067
Syoyo Fujitac2615632016-06-19 21:56:06 +090068#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
69#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
70#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
71#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
72#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
73#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
74
Cemalettin Dervis246d8662017-12-07 20:29:51 +010075#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
Syoyo Fujitac2615632016-06-19 21:56:06 +090076#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -040077#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +090078
Luke San Antoniocdf4cb72016-06-14 21:32:11 -040079// Redeclarations of the above for technique.parameters.
80#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
81#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
82#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
83#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
84#define TINYGLTF_PARAMETER_TYPE_INT (5124)
85#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
86#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
87
88#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
89#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
90#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
91
92#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
93#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
94#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
95
96#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
97#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
98#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
99#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
100
101#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
102#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
103#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
104
105#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
106
107// End parameter types
108
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900109#define TINYGLTF_TYPE_VEC2 (2)
110#define TINYGLTF_TYPE_VEC3 (3)
111#define TINYGLTF_TYPE_VEC4 (4)
112#define TINYGLTF_TYPE_MAT2 (32 + 2)
113#define TINYGLTF_TYPE_MAT3 (32 + 3)
114#define TINYGLTF_TYPE_MAT4 (32 + 4)
115#define TINYGLTF_TYPE_SCALAR (64 + 1)
116#define TINYGLTF_TYPE_VECTOR (64 + 4)
117#define TINYGLTF_TYPE_MATRIX (64 + 16)
118
119#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
120#define TINYGLTF_IMAGE_FORMAT_PNG (1)
121#define TINYGLTF_IMAGE_FORMAT_BMP (2)
122#define TINYGLTF_IMAGE_FORMAT_GIF (3)
123
Luke San Antonio6d616f52016-06-23 14:09:23 -0400124#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
125#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900126#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400127#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
128#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
129
Syoyo Fujitabde70212016-02-07 17:38:17 +0900130#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
131#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
132
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900133#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
134#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
135
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400136#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
137#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
138
Selmar Kok31cb7f92018-10-03 15:39:05 +0200139#define TINYGLTF_DOUBLE_EPS (1.e-12)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900140#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
Selmar Kok31cb7f92018-10-03 15:39:05 +0200141
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900142typedef enum {
143 NULL_TYPE = 0,
144 NUMBER_TYPE = 1,
145 INT_TYPE = 2,
146 BOOL_TYPE = 3,
147 STRING_TYPE = 4,
148 ARRAY_TYPE = 5,
149 BINARY_TYPE = 6,
150 OBJECT_TYPE = 7
151} Type;
152
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500153static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900154 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
155 return 1;
156 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
157 return 1;
158 } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
159 return 2;
160 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
161 return 2;
162 } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
163 return 4;
164 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
165 return 4;
166 } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
167 return 4;
168 } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
169 return 8;
170 } else {
171 // Unknown componenty type
172 return -1;
173 }
174}
175
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500176static inline int32_t GetTypeSizeInBytes(uint32_t ty) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900177 if (ty == TINYGLTF_TYPE_SCALAR) {
178 return 1;
179 } else if (ty == TINYGLTF_TYPE_VEC2) {
180 return 2;
181 } else if (ty == TINYGLTF_TYPE_VEC3) {
182 return 3;
183 } else if (ty == TINYGLTF_TYPE_VEC4) {
184 return 4;
185 } else if (ty == TINYGLTF_TYPE_MAT2) {
186 return 4;
187 } else if (ty == TINYGLTF_TYPE_MAT3) {
188 return 9;
189 } else if (ty == TINYGLTF_TYPE_MAT4) {
190 return 16;
191 } else {
192 // Unknown componenty type
193 return -1;
194 }
195}
196
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200197bool IsDataURI(const std::string &in);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900198bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
199 const std::string &in, size_t reqBytes, bool checkSize);
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200200
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900201#ifdef __clang__
202#pragma clang diagnostic push
203// Suppress warning for : static Value null_value
204// https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang
205#pragma clang diagnostic ignored "-Wexit-time-destructors"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900206#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900207#endif
208
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900209// Simple class to represent JSON object
210class Value {
211 public:
212 typedef std::vector<Value> Array;
213 typedef std::map<std::string, Value> Object;
214
215 Value() : type_(NULL_TYPE) {}
216
217 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900218 explicit Value(int i) : type_(INT_TYPE) { int_value_ = i; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900219 explicit Value(double n) : type_(NUMBER_TYPE) { number_value_ = n; }
220 explicit Value(const std::string &s) : type_(STRING_TYPE) {
221 string_value_ = s;
222 }
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900223 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900224 binary_value_.resize(n);
225 memcpy(binary_value_.data(), p, n);
226 }
227 explicit Value(const Array &a) : type_(ARRAY_TYPE) {
228 array_value_ = Array(a);
229 }
230 explicit Value(const Object &o) : type_(OBJECT_TYPE) {
231 object_value_ = Object(o);
232 }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900233
234 char Type() const { return static_cast<const char>(type_); }
235
236 bool IsBool() const { return (type_ == BOOL_TYPE); }
237
238 bool IsInt() const { return (type_ == INT_TYPE); }
239
240 bool IsNumber() const { return (type_ == NUMBER_TYPE); }
241
242 bool IsString() const { return (type_ == STRING_TYPE); }
243
244 bool IsBinary() const { return (type_ == BINARY_TYPE); }
245
246 bool IsArray() const { return (type_ == ARRAY_TYPE); }
247
248 bool IsObject() const { return (type_ == OBJECT_TYPE); }
249
250 // Accessor
251 template <typename T>
252 const T &Get() const;
253 template <typename T>
254 T &Get();
255
256 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900257 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900258 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900259 assert(IsArray());
260 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900261 return (static_cast<size_t>(idx) < array_value_.size())
262 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900263 : null_value;
264 }
265
266 // Lookup value from a key-value pair
267 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900268 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900269 assert(IsObject());
270 Object::const_iterator it = object_value_.find(key);
271 return (it != object_value_.end()) ? it->second : null_value;
272 }
273
274 size_t ArrayLen() const {
275 if (!IsArray()) return 0;
276 return array_value_.size();
277 }
278
279 // Valid only for object type.
280 bool Has(const std::string &key) const {
281 if (!IsObject()) return false;
282 Object::const_iterator it = object_value_.find(key);
283 return (it != object_value_.end()) ? true : false;
284 }
285
286 // List keys
287 std::vector<std::string> Keys() const {
288 std::vector<std::string> keys;
289 if (!IsObject()) return keys; // empty
290
291 for (Object::const_iterator it = object_value_.begin();
292 it != object_value_.end(); ++it) {
293 keys.push_back(it->first);
294 }
295
296 return keys;
297 }
298
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900299 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900300
301 bool operator==(const tinygltf::Value &other) const;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000302
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900303 protected:
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900304 int type_;
305
306 int int_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900307 double number_value_;
308 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900309 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900310 Array array_value_;
311 Object object_value_;
312 bool boolean_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900313};
314
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900315#ifdef __clang__
316#pragma clang diagnostic pop
317#endif
318
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900319#define TINYGLTF_VALUE_GET(ctype, var) \
320 template <> \
321 inline const ctype &Value::Get<ctype>() const { \
322 return var; \
323 } \
324 template <> \
325 inline ctype &Value::Get<ctype>() { \
326 return var; \
327 }
328TINYGLTF_VALUE_GET(bool, boolean_value_)
329TINYGLTF_VALUE_GET(double, number_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900330TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900331TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900332TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900333TINYGLTF_VALUE_GET(Value::Array, array_value_)
334TINYGLTF_VALUE_GET(Value::Object, object_value_)
335#undef TINYGLTF_VALUE_GET
336
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900337#ifdef __clang__
338#pragma clang diagnostic push
339#pragma clang diagnostic ignored "-Wc++98-compat"
340#pragma clang diagnostic ignored "-Wpadded"
341#endif
342
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500343/// Agregate object for representing a color
Arthur Brainville58453192018-01-08 18:25:52 +0100344using ColorValue = std::array<double, 4>;
345
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500346struct Parameter {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200347 bool bool_value = false;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700348 bool has_number_value = false;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900349 std::string string_value;
350 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000351 std::map<std::string, double> json_double_value;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200352 double number_value = 0.0;
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500353 // context sensitive methods. depending the type of the Parameter you are
354 // accessing, these are either valid or not
355 // If this parameter represent a texture map in a material, will return the
356 // texture index
Arthur Brainville58453192018-01-08 18:25:52 +0100357
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500358 /// Return the index of a texture if this Parameter is a texture map.
359 /// Returned value is only valid if the parameter represent a texture from a
360 /// material
Arthur Brainville41fe7722018-01-08 18:32:48 +0100361 int TextureIndex() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100362 const auto it = json_double_value.find("index");
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500363 if (it != std::end(json_double_value)) {
Arthur Brainville58453192018-01-08 18:25:52 +0100364 return int(it->second);
365 }
366 return -1;
367 }
368
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500369 /// Material factor, like the roughness or metalness of a material
370 /// Returned value is only valid if the parameter represent a texture from a
371 /// material
Ben Buzbeef6af2242018-04-25 15:13:05 -0700372 double Factor() const { return number_value; }
Arthur Brainville58453192018-01-08 18:25:52 +0100373
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500374 /// Return the color of a material
375 /// Returned value is only valid if the parameter represent a texture from a
376 /// material
Arthur Brainville95853912018-01-08 18:37:44 +0100377 ColorValue ColorFactor() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100378 return {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500379 {// this agregate intialize the std::array object, and uses C++11 RVO.
380 number_array[0], number_array[1], number_array[2],
381 (number_array.size() > 3 ? number_array[3] : 1.0)}};
Arthur Brainville58453192018-01-08 18:25:52 +0100382 }
Selmar Kok31cb7f92018-10-03 15:39:05 +0200383
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900384 bool operator==(const Parameter &) const;
Arthur Brainville58453192018-01-08 18:25:52 +0100385};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900386
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900387#ifdef __clang__
388#pragma clang diagnostic pop
389#endif
390
391#ifdef __clang__
392#pragma clang diagnostic push
393#pragma clang diagnostic ignored "-Wpadded"
394#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900395
Syoyo Fujitabde70212016-02-07 17:38:17 +0900396typedef std::map<std::string, Parameter> ParameterMap;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200397typedef std::map<std::string, Value> ExtensionMap;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900398
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000399struct AnimationChannel {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900400 int sampler; // required
401 int target_node; // required (index of the node to target)
402 std::string target_path; // required in ["translation", "rotation", "scale",
403 // "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900404 Value extras;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900405
Syoyo Fujita5b407452017-06-04 17:42:41 +0900406 AnimationChannel() : sampler(-1), target_node(-1) {}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900407 bool operator==(const AnimationChannel &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000408};
409
410struct AnimationSampler {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900411 int input; // required
412 int output; // required
413 std::string interpolation; // in ["LINEAR", "STEP", "CATMULLROMSPLINE",
414 // "CUBICSPLINE"], default "LINEAR"
Jens Olssonb3af2f12018-06-04 10:17:49 +0200415 Value extras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000416
Syoyo Fujita5b407452017-06-04 17:42:41 +0900417 AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900418 bool operator==(const AnimationSampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000419};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900420
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900421struct Animation {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900422 std::string name;
423 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000424 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900425 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200426
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900427 bool operator==(const Animation &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900428};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900429
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000430struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900431 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900432 int inverseBindMatrices; // required here but not in the spec
433 int skeleton; // The index of the node used as a skeleton root
434 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000435
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900436 Skin() {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000437 inverseBindMatrices = -1;
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +0000438 skeleton = -1;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000439 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900440 bool operator==(const Skin &) const;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000441};
442
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000443struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900444 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900445 int minFilter; // ["NEAREST", "LINEAR", "NEAREST_MIPMAP_LINEAR",
446 // "LINEAR_MIPMAP_NEAREST", "NEAREST_MIPMAP_LINEAR",
447 // "LINEAR_MIPMAP_LINEAR"]
448 int magFilter; // ["NEAREST", "LINEAR"]
449 int wrapS; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default
450 // "REPEAT"
451 int wrapT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default
452 // "REPEAT"
453 int wrapR; // TinyGLTF extension
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900454 Value extras;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900455
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000456 Sampler()
Cemalettin Dervis246d8662017-12-07 20:29:51 +0100457 : wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
Selmar Kok5892d3e2018-12-04 19:55:56 +0100458 wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT),
459 wrapR(TINYGLTF_TEXTURE_WRAP_REPEAT){}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900460 bool operator==(const Sampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000461};
462
Syoyo Fujita5b407452017-06-04 17:42:41 +0900463struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900464 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900465 int width;
466 int height;
467 int component;
468 std::vector<unsigned char> image;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900469 int bufferView; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500470 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
471 // "image/bmp", "image/gif"]
Squareys188965b2018-03-13 22:20:01 +0100472 std::string uri; // (required if no mimeType)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900473 Value extras;
Syoyo Fujita3e53feb2018-09-02 15:36:17 +0900474 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900475
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900476 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
477 // compressed for "image/jpeg" mime) This feature is good if you use custom
478 // image loader function. (e.g. delayed decoding of images for faster glTF
479 // parsing) Default parser for Image does not provide as-is loading feature at
480 // the moment. (You can manipulate this by providing your own LoadImageData
481 // function)
Selmar Kokfa0a9982018-10-03 15:46:23 +0200482 bool as_is;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900483
484 Image() : as_is(false) {
485 bufferView = -1;
486 width = -1;
487 height = -1;
488 component = -1;
489 }
490 bool operator==(const Image &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000491};
492
493struct Texture {
Vladimír Vondruš239be2c2018-07-24 23:23:56 +0200494 std::string name;
495
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000496 int sampler;
Selmar Kok8eb39042018-10-05 14:29:35 +0200497 int source;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900498 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200499 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900500
Syoyo Fujita5b407452017-06-04 17:42:41 +0900501 Texture() : sampler(-1), source(-1) {}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900502 bool operator==(const Texture &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000503};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900504
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000505// Each extension should be stored in a ParameterMap.
506// members not in the values could be included in the ParameterMap
507// to keep a single material model
508struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900509 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900510
Syoyo Fujita5b407452017-06-04 17:42:41 +0900511 ParameterMap values; // PBR metal/roughness workflow
512 ParameterMap additionalValues; // normal/occlusion/emissive values
Selmar09d2ff12018-03-15 17:30:42 +0100513
514 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900515 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200516
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900517 bool operator==(const Material &) const;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000518};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900519
Syoyo Fujita5b407452017-06-04 17:42:41 +0900520struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900521 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900522 int buffer; // Required
523 size_t byteOffset; // minimum 0, default 0
524 size_t byteLength; // required, minimum 1
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900525 size_t byteStride; // minimum 4, maximum 252 (multiple of 4), default 0 =
526 // understood to be tightly packed
Syoyo Fujita5b407452017-06-04 17:42:41 +0900527 int target; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900528 Value extras;
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900529
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900530 BufferView() : byteOffset(0), byteStride(0) {}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900531 bool operator==(const BufferView &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000532};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900533
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000534struct Accessor {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900535 int bufferView; // optional in spec but required here since sparse accessor
536 // are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900537 std::string name;
538 size_t byteOffset;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900539 bool normalized; // optinal.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000540 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900541 size_t count; // required
542 int type; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900543 Value extras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000544
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900545 std::vector<double> minValues; // optional
546 std::vector<double> maxValues; // optional
547
548 // TODO(syoyo): "sparse"
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000549
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900550 ///
551 /// Utility function to compute byteStride for a given bufferView object.
552 /// Returns -1 upon invalid glTF value or parameter configuration.
553 ///
554 int ByteStride(const BufferView &bufferViewObject) const {
555 if (bufferViewObject.byteStride == 0) {
556 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500557 int componentSizeInBytes =
558 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900559 if (componentSizeInBytes <= 0) {
560 return -1;
561 }
562
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900563 int typeSizeInBytes = GetTypeSizeInBytes(static_cast<uint32_t>(type));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900564 if (typeSizeInBytes <= 0) {
565 return -1;
566 }
567
568 return componentSizeInBytes * typeSizeInBytes;
569 } else {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500570 // Check if byteStride is a mulple of the size of the accessor's component
571 // type.
572 int componentSizeInBytes =
573 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900574 if (componentSizeInBytes <= 0) {
575 return -1;
576 }
577
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900578 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900579 return -1;
580 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100581 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900582 }
583
584 return 0;
585 }
586
Syoyo Fujita5b407452017-06-04 17:42:41 +0900587 Accessor() { bufferView = -1; }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900588 bool operator==(const tinygltf::Accessor &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000589};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900590
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900591struct PerspectiveCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200592 double aspectRatio; // min > 0
593 double yfov; // required. min > 0
594 double zfar; // min > 0
595 double znear; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900596
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900597 PerspectiveCamera()
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900598 : aspectRatio(0.0),
599 yfov(0.0),
600 zfar(0.0) // 0 = use infinite projecton matrix
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900601 ,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900602 znear(0.0) {}
603 bool operator==(const PerspectiveCamera &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900604
Selmar09d2ff12018-03-15 17:30:42 +0100605 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900606 Value extras;
607};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000608
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900609struct OrthographicCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200610 double xmag; // required. must not be zero.
611 double ymag; // required. must not be zero.
612 double zfar; // required. `zfar` must be greater than `znear`.
613 double znear; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000614
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900615 OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {}
616 bool operator==(const OrthographicCamera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900617
Selmar09d2ff12018-03-15 17:30:42 +0100618 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900619 Value extras;
620};
621
622struct Camera {
623 std::string type; // required. "perspective" or "orthographic"
624 std::string name;
625
626 PerspectiveCamera perspective;
627 OrthographicCamera orthographic;
628
629 Camera() {}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900630 bool operator==(const Camera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900631
Selmar09d2ff12018-03-15 17:30:42 +0100632 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000633 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900634};
635
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000636struct Primitive {
637 std::map<std::string, int> attributes; // (required) A dictionary object of
638 // integer, where each integer
639 // is the index of the accessor
640 // containing an attribute.
641 int material; // The index of the material to apply to this primitive
Syoyo Fujita5b407452017-06-04 17:42:41 +0900642 // when rendering.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000643 int indices; // The index of the accessor that contains the indices.
644 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900645 std::vector<std::map<std::string, int> > targets; // array of morph targets,
Syoyo Fujita5b407452017-06-04 17:42:41 +0900646 // where each target is a dict with attribues in ["POSITION, "NORMAL",
647 // "TANGENT"] pointing
648 // to their corresponding accessors
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000649 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900650
Syoyo Fujita5b407452017-06-04 17:42:41 +0900651 Primitive() {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000652 material = -1;
653 indices = -1;
654 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900655 bool operator==(const Primitive &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000656};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900657
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900658struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900659 std::string name;
660 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900661 std::vector<double> weights; // weights to be applied to the Morph Targets
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900662 std::vector<std::map<std::string, int> > targets;
Selmar09d2ff12018-03-15 17:30:42 +0100663 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900664 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200665
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900666 bool operator==(const Mesh &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900667};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900668
669class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900670 public:
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900671 Node() : camera(-1), skin(-1), mesh(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000672
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900673 Node(const Node &rhs) {
674 camera = rhs.camera;
675
676 name = rhs.name;
677 skin = rhs.skin;
678 mesh = rhs.mesh;
679 children = rhs.children;
680 rotation = rhs.rotation;
681 scale = rhs.scale;
682 translation = rhs.translation;
683 matrix = rhs.matrix;
684 weights = rhs.weights;
685
Selmar09d2ff12018-03-15 17:30:42 +0100686 extensions = rhs.extensions;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900687 extras = rhs.extras;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900688 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900689 ~Node() {}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900690 bool operator==(const Node &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900691
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000692 int camera; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900693
694 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000695 int skin;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000696 int mesh;
697 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900698 std::vector<double> rotation; // length must be 0 or 4
699 std::vector<double> scale; // length must be 0 or 3
700 std::vector<double> translation; // length must be 0 or 3
701 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +0900702 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -0700703
Selmar09d2ff12018-03-15 17:30:42 +0100704 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900705 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900706};
707
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900708struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900709 std::string name;
710 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900711 std::string
712 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900713 Value extras;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900714
715 bool operator==(const Buffer &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900716};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900717
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900718struct Asset {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900719 std::string version; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900720 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +0000721 std::string minVersion;
722 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +0100723 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900724 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200725
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900726 bool operator==(const Asset &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900727};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900728
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000729struct Scene {
730 std::string name;
731 std::vector<int> nodes;
732
Selmar09d2ff12018-03-15 17:30:42 +0100733 ExtensionMap extensions;
734 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200735
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900736 bool operator==(const Scene &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000737};
738
Emanuel Schrade186322b2017-11-06 11:14:41 +0100739struct Light {
740 std::string name;
741 std::vector<double> color;
742 std::string type;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200743
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900744 bool operator==(const Light &) const;
Emanuel Schrade186322b2017-11-06 11:14:41 +0100745};
746
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000747class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900748 public:
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000749 Model() {}
750 ~Model() {}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900751 bool operator==(const Model &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900752
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000753 std::vector<Accessor> accessors;
754 std::vector<Animation> animations;
755 std::vector<Buffer> buffers;
756 std::vector<BufferView> bufferViews;
757 std::vector<Material> materials;
758 std::vector<Mesh> meshes;
759 std::vector<Node> nodes;
760 std::vector<Texture> textures;
761 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000762 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000763 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900764 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000765 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +0100766 std::vector<Light> lights;
Selmar09d2ff12018-03-15 17:30:42 +0100767 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900768
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000769 int defaultScene;
770 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +0000771 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900772
773 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900774
775 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900776};
777
Syoyo Fujita0614eb82016-10-14 18:50:14 +0900778enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -0400779 NO_REQUIRE = 0x00,
780 REQUIRE_SCENE = 0x01,
781 REQUIRE_SCENES = 0x02,
782 REQUIRE_NODES = 0x04,
783 REQUIRE_ACCESSORS = 0x08,
784 REQUIRE_BUFFERS = 0x10,
785 REQUIRE_BUFFER_VIEWS = 0x20,
786 REQUIRE_ALL = 0x3f
787};
788
Squareysff644d82018-03-13 22:36:18 +0100789///
790/// LoadImageDataFunction type. Signature for custom image loading callbacks.
791///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900792typedef bool (*LoadImageDataFunction)(Image *, std::string *, std::string *,
793 int, int, const unsigned char *, int,
794 void *);
Squareysff644d82018-03-13 22:36:18 +0100795
johan bowald642a3432018-04-01 12:37:18 +0200796///
797/// WriteImageDataFunction type. Signature for custom image writing callbacks.
798///
799typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
800 Image *, bool, void *);
801
Squareys2d3594d2018-03-13 22:40:53 +0100802#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +0100803// Declaration of default image loader callback
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900804bool LoadImageData(Image *image, std::string *err, std::string *warn,
805 int req_width, int req_height, const unsigned char *bytes,
806 int size, void *);
Squareys2d3594d2018-03-13 22:40:53 +0100807#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900808
johan bowald642a3432018-04-01 12:37:18 +0200809#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
810// Declaration of default image writer callback
811bool WriteImageData(const std::string *basepath, const std::string *filename,
812 Image *image, bool embedImages, void *);
813#endif
814
Paolo Jovone6601bf2018-07-07 20:43:33 +0200815///
816/// FilExistsFunction type. Signature for custom filesystem callbacks.
817///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900818typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +0200819
820///
821/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
822///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900823typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +0200824
825///
826/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
827///
828typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900829 std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +0200830 void *);
831
832///
833/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
834///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900835typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +0200836 const std::vector<unsigned char> &,
837 void *);
838
839///
840/// A structure containing all required filesystem callbacks and a pointer to
841/// their user data.
842///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900843struct FsCallbacks {
844 FileExistsFunction FileExists;
845 ExpandFilePathFunction ExpandFilePath;
846 ReadWholeFileFunction ReadWholeFile;
847 WriteWholeFileFunction WriteWholeFile;
Paolo Jovone6601bf2018-07-07 20:43:33 +0200848
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900849 void *user_data; // An argument that is passed to all fs callbacks
Paolo Jovone6601bf2018-07-07 20:43:33 +0200850};
851
852#ifndef TINYGLTF_NO_FS
853// Declaration of default filesystem callbacks
854
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900855bool FileExists(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +0200856
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900857std::string ExpandFilePath(const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +0200858
859bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900860 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +0200861
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900862bool WriteWholeFile(std::string *err, const std::string &filepath,
863 const std::vector<unsigned char> &contents, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +0200864#endif
865
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900866class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900867 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900868#ifdef __clang__
869#pragma clang diagnostic push
870#pragma clang diagnostic ignored "-Wc++98-compat"
871#endif
872
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500873 TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {}
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900874
875#ifdef __clang__
876#pragma clang diagnostic pop
877#endif
878
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900879 ~TinyGLTF() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900880
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900881 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900882 /// Loads glTF ASCII asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900883 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900884 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900885 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900886 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400887 const std::string &filename,
888 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900889
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900890 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900891 /// Loads glTF ASCII asset from string(memory).
892 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900893 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900894 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900895 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900896 bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
897 const char *str, const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400898 const std::string &base_dir,
899 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900900
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900901 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900902 /// Loads glTF binary asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900903 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900904 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900905 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900906 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400907 const std::string &filename,
908 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900909
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900910 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900911 /// Loads glTF binary asset from memory.
912 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900913 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900914 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900915 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900916 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900917 const unsigned char *bytes,
918 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400919 const std::string &base_dir = "",
920 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900921
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900922 ///
923 /// Write glTF to file.
924 ///
johan bowald642a3432018-04-01 12:37:18 +0200925 bool WriteGltfSceneToFile(Model *model, const std::string &filename,
926 bool embedImages,
Selmar Kokee3d0662018-10-08 16:20:43 +0200927 bool embedBuffers,
Syoyo Fujitab5726502018-11-10 20:07:15 +0900928 bool prettyPrint,
David Harmonda9eac22018-08-30 08:06:05 -0400929 bool writeBinary);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000930
Squareysff644d82018-03-13 22:36:18 +0100931 ///
932 /// Set callback to use for loading image data
933 ///
934 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
935
johan bowald642a3432018-04-01 12:37:18 +0200936 ///
937 /// Set callback to use for writing image data
938 ///
939 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
940
Paolo Jovone6601bf2018-07-07 20:43:33 +0200941 ///
942 /// Set callbacks to use for filesystem (fs) access and their user data
943 ///
944 void SetFsCallbacks(FsCallbacks callbacks);
945
Syoyo Fujitabeded612016-05-01 20:03:43 +0900946 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900947 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900948 /// Loads glTF asset from string(memory).
949 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900950 /// Set warning message to `warn` for example it fails to load asserts
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900951 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900952 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900953 bool LoadFromString(Model *model, std::string *err, std::string *warn,
954 const char *str, const unsigned int length,
955 const std::string &base_dir, unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900956
Syoyo Fujitabeded612016-05-01 20:03:43 +0900957 const unsigned char *bin_data_;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900958 size_t bin_size_;
959 bool is_binary_;
Squareysff644d82018-03-13 22:36:18 +0100960
Paolo Jovone6601bf2018-07-07 20:43:33 +0200961 FsCallbacks fs = {
962#ifndef TINYGLTF_NO_FS
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900963 &tinygltf::FileExists, &tinygltf::ExpandFilePath,
964 &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile,
Paolo Jovone6601bf2018-07-07 20:43:33 +0200965
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900966 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +0200967#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900968 nullptr, nullptr, nullptr, nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +0200969
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900970 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +0200971#endif
972 };
973
Squareysff644d82018-03-13 22:36:18 +0100974 LoadImageDataFunction LoadImageData =
975#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900976 &tinygltf::LoadImageData;
Squareysff644d82018-03-13 22:36:18 +0100977#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900978 nullptr;
Squareysff644d82018-03-13 22:36:18 +0100979#endif
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900980 void *load_image_user_data_ = reinterpret_cast<void *>(&fs);
johan bowald642a3432018-04-01 12:37:18 +0200981
982 WriteImageDataFunction WriteImageData =
983#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900984 &tinygltf::WriteImageData;
johan bowald642a3432018-04-01 12:37:18 +0200985#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +0900986 nullptr;
johan bowald642a3432018-04-01 12:37:18 +0200987#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +0200988 void *write_image_user_data_ = reinterpret_cast<void *>(&fs);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900989};
990
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900991#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500992#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900993#endif
994
Syoyo Fujita7c877972016-03-08 01:31:49 +0900995} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900996
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900997#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900998
Selmar Kok31cb7f92018-10-03 15:39:05 +0200999#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
Syoyo Fujita7c877972016-03-08 01:31:49 +09001000#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001001//#include <cassert>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001002#ifndef TINYGLTF_NO_FS
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001003#include <fstream>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001004#endif
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001005#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +09001006
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001007#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001008// Disable some warnings for external files.
1009#pragma clang diagnostic push
1010#pragma clang diagnostic ignored "-Wfloat-equal"
1011#pragma clang diagnostic ignored "-Wexit-time-destructors"
1012#pragma clang diagnostic ignored "-Wconversion"
1013#pragma clang diagnostic ignored "-Wold-style-cast"
Syoyo Fujita643ce102016-05-01 17:19:37 +09001014#pragma clang diagnostic ignored "-Wglobal-constructors"
1015#pragma clang diagnostic ignored "-Wreserved-id-macro"
1016#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1017#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001018#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001019#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001020#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1021#pragma clang diagnostic ignored "-Wswitch-enum"
1022#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001023#pragma clang diagnostic ignored "-Wweak-vtables"
1024#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujitaa8f0b1c2018-08-28 21:33:40 +09001025#if __has_warning("-Wdouble-promotion")
1026#pragma clang diagnostic ignored "-Wdouble-promotion"
1027#endif
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001028#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001029#pragma clang diagnostic ignored "-Wcomma"
1030#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001031#if __has_warning("-Wzero-as-null-pointer-constant")
1032#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1033#endif
1034#if __has_warning("-Wcast-qual")
1035#pragma clang diagnostic ignored "-Wcast-qual"
1036#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001037#if __has_warning("-Wmissing-variable-declarations")
1038#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1039#endif
1040#if __has_warning("-Wmissing-prototypes")
1041#pragma clang diagnostic ignored "-Wmissing-prototypes"
1042#endif
1043#if __has_warning("-Wcast-align")
1044#pragma clang diagnostic ignored "-Wcast-align"
1045#endif
1046#if __has_warning("-Wnewline-eof")
1047#pragma clang diagnostic ignored "-Wnewline-eof"
1048#endif
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001049#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001050
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001051#include "./json.hpp"
Squareys2d3594d2018-03-13 22:40:53 +01001052
1053#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001054#include "./stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +01001055#endif
1056
johan bowald642a3432018-04-01 12:37:18 +02001057#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1058#include "./stb_image_write.h"
1059#endif
1060
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001061#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001062#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001063#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001064
1065#ifdef _WIN32
Syoyo Fujitaef2f49f2017-11-05 17:18:46 +09001066#include <windows.h>
Florian Märkld525e192017-09-22 15:25:48 +02001067#elif !defined(__ANDROID__)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001068#include <wordexp.h>
1069#endif
1070
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001071#if defined(__sparcv9)
1072// Big endian
1073#else
1074#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1075#define TINYGLTF_LITTLE_ENDIAN 1
1076#endif
1077#endif
1078
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001079using nlohmann::json;
1080
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001081#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001082#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +09001083#endif
1084
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001085#ifdef __clang__
1086#pragma clang diagnostic push
1087#pragma clang diagnostic ignored "-Wc++98-compat"
1088#endif
1089
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001090namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001091
Selmar Kok31cb7f92018-10-03 15:39:05 +02001092// Equals function for Value, for recursivity
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001093static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1094 if (one.Type() != other.Type()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001095
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001096 switch (one.Type()) {
1097 case NULL_TYPE:
1098 return true;
1099 case BOOL_TYPE:
1100 return one.Get<bool>() == other.Get<bool>();
1101 case NUMBER_TYPE:
1102 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1103 case INT_TYPE:
1104 return one.Get<int>() == other.Get<int>();
1105 case OBJECT_TYPE: {
1106 auto oneObj = one.Get<tinygltf::Value::Object>();
1107 auto otherObj = other.Get<tinygltf::Value::Object>();
1108 if (oneObj.size() != otherObj.size()) return false;
1109 for (auto &it : oneObj) {
1110 auto otherIt = otherObj.find(it.first);
1111 if (otherIt == otherObj.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001112
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001113 if (!Equals(it.second, otherIt->second)) return false;
1114 }
1115 return true;
1116 }
1117 case ARRAY_TYPE: {
1118 if (one.Size() != other.Size()) return false;
1119 for (int i = 0; i < int(one.Size()); ++i)
1120 if (Equals(one.Get(i), other.Get(i))) return false;
1121 return true;
1122 }
1123 case STRING_TYPE:
1124 return one.Get<std::string>() == other.Get<std::string>();
1125 case BINARY_TYPE:
1126 return one.Get<std::vector<unsigned char> >() ==
1127 other.Get<std::vector<unsigned char> >();
1128 default: {
1129 // unhandled type
1130 return false;
1131 }
1132 }
Selmar Kok31cb7f92018-10-03 15:39:05 +02001133}
1134
1135// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001136static bool Equals(const std::vector<double> &one,
1137 const std::vector<double> &other) {
1138 if (one.size() != other.size()) return false;
1139 for (int i = 0; i < int(one.size()); ++i) {
1140 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1141 }
1142 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001143}
1144
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001145bool Accessor::operator==(const Accessor &other) const {
1146 return this->bufferView == other.bufferView &&
1147 this->byteOffset == other.byteOffset &&
1148 this->componentType == other.componentType &&
1149 this->count == other.count && this->extras == other.extras &&
1150 Equals(this->maxValues, other.maxValues) &&
1151 Equals(this->minValues, other.minValues) && this->name == other.name &&
1152 this->normalized == other.normalized && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001153}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001154bool Animation::operator==(const Animation &other) const {
1155 return this->channels == other.channels && this->extras == other.extras &&
1156 this->name == other.name && this->samplers == other.samplers;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001157}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001158bool AnimationChannel::operator==(const AnimationChannel &other) const {
1159 return this->extras == other.extras &&
1160 this->target_node == other.target_node &&
1161 this->target_path == other.target_path &&
1162 this->sampler == other.sampler;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001163}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001164bool AnimationSampler::operator==(const AnimationSampler &other) const {
1165 return this->extras == other.extras && this->input == other.input &&
1166 this->interpolation == other.interpolation &&
1167 this->output == other.output;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001168}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001169bool Asset::operator==(const Asset &other) const {
1170 return this->copyright == other.copyright &&
1171 this->extensions == other.extensions && this->extras == other.extras &&
1172 this->generator == other.generator &&
1173 this->minVersion == other.minVersion && this->version == other.version;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001174}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001175bool Buffer::operator==(const Buffer &other) const {
1176 return this->data == other.data && this->extras == other.extras &&
1177 this->name == other.name && this->uri == other.uri;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001178}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001179bool BufferView::operator==(const BufferView &other) const {
1180 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1181 this->byteOffset == other.byteOffset &&
1182 this->byteStride == other.byteStride && this->name == other.name &&
1183 this->target == other.target && this->extras == other.extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001184}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001185bool Camera::operator==(const Camera &other) const {
1186 return this->name == other.name && this->extensions == other.extensions &&
1187 this->extras == other.extras &&
1188 this->orthographic == other.orthographic &&
1189 this->perspective == other.perspective && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001190}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001191bool Image::operator==(const Image &other) const {
1192 return this->bufferView == other.bufferView &&
1193 this->component == other.component && this->extras == other.extras &&
1194 this->height == other.height && this->image == other.image &&
1195 this->mimeType == other.mimeType && this->name == other.name &&
1196 this->uri == other.uri && this->width == other.width;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001197}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001198bool Light::operator==(const Light &other) const {
1199 return Equals(this->color, other.color) && this->name == other.name &&
1200 this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001201}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001202bool Material::operator==(const Material &other) const {
1203 return this->additionalValues == other.additionalValues &&
1204 this->extensions == other.extensions && this->extras == other.extras &&
1205 this->name == other.name && this->values == other.values;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001206}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001207bool Mesh::operator==(const Mesh &other) const {
1208 return this->extensions == other.extensions && this->extras == other.extras &&
1209 this->name == other.name && this->primitives == other.primitives &&
1210 this->targets == other.targets && Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001211}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001212bool Model::operator==(const Model &other) const {
1213 return this->accessors == other.accessors &&
1214 this->animations == other.animations && this->asset == other.asset &&
1215 this->buffers == other.buffers &&
1216 this->bufferViews == other.bufferViews &&
1217 this->cameras == other.cameras &&
1218 this->defaultScene == other.defaultScene &&
1219 this->extensions == other.extensions &&
1220 this->extensionsRequired == other.extensionsRequired &&
1221 this->extensionsUsed == other.extensionsUsed &&
1222 this->extras == other.extras && this->images == other.images &&
1223 this->lights == other.lights && this->materials == other.materials &&
1224 this->meshes == other.meshes && this->nodes == other.nodes &&
1225 this->samplers == other.samplers && this->scenes == other.scenes &&
1226 this->skins == other.skins && this->textures == other.textures;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001227}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001228bool Node::operator==(const Node &other) const {
1229 return this->camera == other.camera && this->children == other.children &&
1230 this->extensions == other.extensions && this->extras == other.extras &&
1231 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
1232 this->name == other.name && Equals(this->rotation, other.rotation) &&
1233 Equals(this->scale, other.scale) && this->skin == other.skin &&
1234 Equals(this->translation, other.translation) &&
1235 Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001236}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001237bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
1238 return this->extensions == other.extensions && this->extras == other.extras &&
1239 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
1240 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
1241 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1242 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001243}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001244bool Parameter::operator==(const Parameter &other) const {
1245 if (this->bool_value != other.bool_value ||
1246 this->has_number_value != other.has_number_value)
1247 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001248
Selmar Kok2bda71c2018-10-05 14:36:05 +02001249 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
1250 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001251
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001252 if (this->json_double_value.size() != other.json_double_value.size())
1253 return false;
1254 for (auto &it : this->json_double_value) {
1255 auto otherIt = other.json_double_value.find(it.first);
1256 if (otherIt == other.json_double_value.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001257
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001258 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
1259 }
1260
1261 if (!Equals(this->number_array, other.number_array)) return false;
1262
1263 if (this->string_value != other.string_value) return false;
1264
1265 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001266}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001267bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
1268 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
1269 this->extensions == other.extensions && this->extras == other.extras &&
1270 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
1271 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1272 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001273}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001274bool Primitive::operator==(const Primitive &other) const {
1275 return this->attributes == other.attributes && this->extras == other.extras &&
1276 this->indices == other.indices && this->material == other.material &&
1277 this->mode == other.mode && this->targets == other.targets;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001278}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001279bool Sampler::operator==(const Sampler &other) const {
1280 return this->extras == other.extras && this->magFilter == other.magFilter &&
1281 this->minFilter == other.minFilter && this->name == other.name &&
1282 this->wrapR == other.wrapR && this->wrapS == other.wrapS &&
1283 this->wrapT == other.wrapT;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001284}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001285bool Scene::operator==(const Scene &other) const {
1286 return this->extensions == other.extensions && this->extras == other.extras &&
1287 this->name == other.name && this->nodes == other.nodes;
1288 ;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001289}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001290bool Skin::operator==(const Skin &other) const {
1291 return this->inverseBindMatrices == other.inverseBindMatrices &&
1292 this->joints == other.joints && this->name == other.name &&
1293 this->skeleton == other.skeleton;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001294}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001295bool Texture::operator==(const Texture &other) const {
1296 return this->extensions == other.extensions && this->extras == other.extras &&
1297 this->name == other.name && this->sampler == other.sampler &&
1298 this->source == other.source;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001299}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001300bool Value::operator==(const Value &other) const {
1301 return Equals(*this, other);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001302}
1303
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001304static void swap4(unsigned int *val) {
1305#ifdef TINYGLTF_LITTLE_ENDIAN
1306 (void)val;
1307#else
1308 unsigned int tmp = *val;
1309 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
1310 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
1311
1312 dst[0] = src[3];
1313 dst[1] = src[2];
1314 dst[2] = src[1];
1315 dst[3] = src[0];
1316#endif
1317}
1318
Syoyo Fujitabeded612016-05-01 20:03:43 +09001319static std::string JoinPath(const std::string &path0,
1320 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001321 if (path0.empty()) {
1322 return path1;
1323 } else {
1324 // check '/'
1325 char lastChar = *path0.rbegin();
1326 if (lastChar != '/') {
1327 return path0 + std::string("/") + path1;
1328 } else {
1329 return path0 + path1;
1330 }
1331 }
1332}
1333
Syoyo Fujita643ce102016-05-01 17:19:37 +09001334static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001335 const std::string &filepath, FsCallbacks *fs) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001336 if (fs == nullptr || fs->ExpandFilePath == nullptr ||
1337 fs->FileExists == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001338 // Error, fs callback[s] missing
1339 return std::string();
1340 }
1341
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001342 for (size_t i = 0; i < paths.size(); i++) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001343 std::string absPath =
1344 fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001345 if (fs->FileExists(absPath, fs->user_data)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001346 return absPath;
1347 }
1348 }
1349
1350 return std::string();
1351}
1352
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001353static std::string GetFilePathExtension(const std::string &FileName) {
johan bowald642a3432018-04-01 12:37:18 +02001354 if (FileName.find_last_of(".") != std::string::npos)
1355 return FileName.substr(FileName.find_last_of(".") + 1);
1356 return "";
1357}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001358
Syoyo Fujita643ce102016-05-01 17:19:37 +09001359static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001360 if (filepath.find_last_of("/\\") != std::string::npos)
1361 return filepath.substr(0, filepath.find_last_of("/\\"));
1362 return "";
1363}
1364
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001365// https://stackoverflow.com/questions/8520560/get-a-file-name-from-a-path
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001366static std::string GetBaseFilename(const std::string &filepath) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001367 return filepath.substr(filepath.find_last_of("/\\") + 1);
1368}
1369
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001370std::string base64_encode(unsigned char const *, unsigned int len);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001371std::string base64_decode(std::string const &s);
1372
1373/*
1374 base64.cpp and base64.h
1375
1376 Copyright (C) 2004-2008 René Nyffenegger
1377
1378 This source code is provided 'as-is', without any express or implied
1379 warranty. In no event will the author be held liable for any damages
1380 arising from the use of this software.
1381
1382 Permission is granted to anyone to use this software for any purpose,
1383 including commercial applications, and to alter it and redistribute it
1384 freely, subject to the following restrictions:
1385
1386 1. The origin of this source code must not be misrepresented; you must not
1387 claim that you wrote the original source code. If you use this source code
1388 in a product, an acknowledgment in the product documentation would be
1389 appreciated but is not required.
1390
1391 2. Altered source versions must be plainly marked as such, and must not be
1392 misrepresented as being the original source code.
1393
1394 3. This notice may not be removed or altered from any source distribution.
1395
1396 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
1397
1398*/
1399
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001400#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001401#pragma clang diagnostic push
1402#pragma clang diagnostic ignored "-Wexit-time-destructors"
1403#pragma clang diagnostic ignored "-Wglobal-constructors"
1404#pragma clang diagnostic ignored "-Wsign-conversion"
1405#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001406#endif
Syoyo Fujita7c877972016-03-08 01:31:49 +09001407static const std::string base64_chars =
1408 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1409 "abcdefghijklmnopqrstuvwxyz"
1410 "0123456789+/";
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001411
1412static inline bool is_base64(unsigned char c) {
1413 return (isalnum(c) || (c == '+') || (c == '/'));
1414}
1415
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001416std::string base64_encode(unsigned char const *bytes_to_encode,
1417 unsigned int in_len) {
johan bowald30c53472018-03-30 11:49:36 +02001418 std::string ret;
1419 int i = 0;
1420 int j = 0;
1421 unsigned char char_array_3[3];
1422 unsigned char char_array_4[4];
1423
1424 while (in_len--) {
1425 char_array_3[i++] = *(bytes_to_encode++);
1426 if (i == 3) {
1427 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001428 char_array_4[1] =
1429 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
1430 char_array_4[2] =
1431 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02001432 char_array_4[3] = char_array_3[2] & 0x3f;
1433
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001434 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
johan bowald30c53472018-03-30 11:49:36 +02001435 i = 0;
1436 }
1437 }
1438
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001439 if (i) {
1440 for (j = i; j < 3; j++) char_array_3[j] = '\0';
johan bowald30c53472018-03-30 11:49:36 +02001441
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001442 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
1443 char_array_4[1] =
1444 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
1445 char_array_4[2] =
1446 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02001447
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001448 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
johan bowald30c53472018-03-30 11:49:36 +02001449
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001450 while ((i++ < 3)) ret += '=';
johan bowald30c53472018-03-30 11:49:36 +02001451 }
1452
1453 return ret;
johan bowald30c53472018-03-30 11:49:36 +02001454}
1455
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001456std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +09001457 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001458 int i = 0;
1459 int j = 0;
1460 int in_ = 0;
1461 unsigned char char_array_4[4], char_array_3[3];
1462 std::string ret;
1463
1464 while (in_len-- && (encoded_string[in_] != '=') &&
1465 is_base64(encoded_string[in_])) {
1466 char_array_4[i++] = encoded_string[in_];
1467 in_++;
1468 if (i == 4) {
1469 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09001470 char_array_4[i] =
1471 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001472
1473 char_array_3[0] =
1474 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
1475 char_array_3[1] =
1476 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
1477 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
1478
Syoyo Fujita7c877972016-03-08 01:31:49 +09001479 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001480 i = 0;
1481 }
1482 }
1483
1484 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001485 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001486
1487 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09001488 char_array_4[j] =
1489 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001490
1491 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
1492 char_array_3[1] =
1493 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
1494 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
1495
Syoyo Fujita7c877972016-03-08 01:31:49 +09001496 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001497 }
1498
1499 return ret;
1500}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001501#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001502#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001503#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001504
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001505static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001506 std::string *warn, const std::string &filename,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001507 const std::string &basedir, bool required,
1508 size_t reqBytes, bool checkSize, FsCallbacks *fs) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001509 if (fs == nullptr || fs->FileExists == nullptr ||
1510 fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001511 // This is a developer error, assert() ?
1512 if (err) {
1513 (*err) += "FS callback[s] not set\n";
1514 }
1515 return false;
1516 }
1517
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001518 std::string *failMsgOut = required ? err : warn;
Selmar Koke3b3fa92018-08-22 19:04:21 +02001519
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001520 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001521
1522 std::vector<std::string> paths;
1523 paths.push_back(basedir);
1524 paths.push_back(".");
1525
Paolo Jovone6601bf2018-07-07 20:43:33 +02001526 std::string filepath = FindFile(paths, filename, fs);
jianghaosenb721fb72017-08-25 21:01:17 +08001527 if (filepath.empty() || filename.empty()) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02001528 if (failMsgOut) {
1529 (*failMsgOut) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001530 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001531 return false;
1532 }
1533
Paolo Jovone6601bf2018-07-07 20:43:33 +02001534 std::vector<unsigned char> buf;
1535 std::string fileReadErr;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001536 bool fileRead =
1537 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001538 if (!fileRead) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02001539 if (failMsgOut) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001540 (*failMsgOut) +=
1541 "File read error : " + filepath + " : " + fileReadErr + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001542 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001543 return false;
1544 }
1545
Paolo Jovone6601bf2018-07-07 20:43:33 +02001546 size_t sz = buf.size();
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09001547 if (sz == 0) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001548 if (failMsgOut) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02001549 (*failMsgOut) += "File is empty : " + filepath + "\n";
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001550 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09001551 return false;
1552 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001553
1554 if (checkSize) {
1555 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001556 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001557 return true;
1558 } else {
1559 std::stringstream ss;
1560 ss << "File size mismatch : " << filepath << ", requestedBytes "
1561 << reqBytes << ", but got " << sz << std::endl;
Selmar Koke3b3fa92018-08-22 19:04:21 +02001562 if (failMsgOut) {
1563 (*failMsgOut) += ss.str();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001564 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001565 return false;
1566 }
1567 }
1568
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001569 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001570 return true;
1571}
1572
Squareysff644d82018-03-13 22:36:18 +01001573void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001574 LoadImageData = func;
1575 load_image_user_data_ = user_data;
Squareysff644d82018-03-13 22:36:18 +01001576}
1577
Squareys2d3594d2018-03-13 22:40:53 +01001578#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001579bool LoadImageData(Image *image, std::string *err, std::string *warn,
1580 int req_width, int req_height, const unsigned char *bytes,
1581 int size, void *) {
1582 (void)warn;
1583
Victor Bushong18ef3382018-08-22 22:03:30 -05001584 int w, h, comp, req_comp;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001585
Victor Bushong18ef3382018-08-22 22:03:30 -05001586 // force 32-bit textures for common Vulkan compatibility. It appears that
1587 // some GPU drivers do not support 24-bit images for Vulkan
1588 req_comp = 4;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001589
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00001590 // if image cannot be decoded, ignore parsing and keep it by its path
1591 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +09001592 // FIXME we should only enter this function if the image is embedded. If
1593 // image->uri references
1594 // an image file, it should be left as it is. Image loading should not be
1595 // mandatory (to support other formats)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001596 unsigned char *data =
1597 stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001598 if (!data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001599 // NOTE: you can use `warn` instead of `err`
Syoyo Fujitabeded612016-05-01 20:03:43 +09001600 if (err) {
1601 (*err) += "Unknown image format.\n";
1602 }
Omar C. Fd492efc2018-02-10 09:50:35 +02001603 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001604 }
1605
1606 if (w < 1 || h < 1) {
Syoyo Fujita4be2f882016-11-24 16:20:34 +09001607 free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001608 if (err) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09001609 (*err) += "Invalid image data.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001610 }
Omar C. Fd492efc2018-02-10 09:50:35 +02001611 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001612 }
1613
1614 if (req_width > 0) {
1615 if (req_width != w) {
Syoyo Fujita4be2f882016-11-24 16:20:34 +09001616 free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001617 if (err) {
1618 (*err) += "Image width mismatch.\n";
1619 }
1620 return false;
1621 }
1622 }
1623
1624 if (req_height > 0) {
1625 if (req_height != h) {
Syoyo Fujita4be2f882016-11-24 16:20:34 +09001626 free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001627 if (err) {
1628 (*err) += "Image height mismatch.\n";
1629 }
1630 return false;
1631 }
1632 }
1633
1634 image->width = w;
1635 image->height = h;
Victor Bushong18ef3382018-08-22 22:03:30 -05001636 image->component = req_comp;
1637 image->image.resize(static_cast<size_t>(w * h * req_comp));
1638 std::copy(data, data + w * h * req_comp, image->image.begin());
Syoyo Fujitabeded612016-05-01 20:03:43 +09001639
Syoyo Fujita4be2f882016-11-24 16:20:34 +09001640 free(data);
1641
Syoyo Fujitabeded612016-05-01 20:03:43 +09001642 return true;
1643}
Squareys2d3594d2018-03-13 22:40:53 +01001644#endif
Syoyo Fujitabeded612016-05-01 20:03:43 +09001645
johan bowald642a3432018-04-01 12:37:18 +02001646void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
1647 WriteImageData = func;
1648 write_image_user_data_ = user_data;
1649}
1650
1651#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1652static void WriteToMemory_stbi(void *context, void *data, int size) {
1653 std::vector<unsigned char> *buffer =
1654 reinterpret_cast<std::vector<unsigned char> *>(context);
1655
1656 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
1657
1658 buffer->insert(buffer->end(), pData, pData + size);
1659}
1660
1661bool WriteImageData(const std::string *basepath, const std::string *filename,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001662 Image *image, bool embedImages, void *fsPtr) {
johan bowald642a3432018-04-01 12:37:18 +02001663 const std::string ext = GetFilePathExtension(*filename);
1664
Paolo Jovone6601bf2018-07-07 20:43:33 +02001665 // Write image to temporary buffer
1666 std::string header;
1667 std::vector<unsigned char> data;
johan bowald642a3432018-04-01 12:37:18 +02001668
Paolo Jovone6601bf2018-07-07 20:43:33 +02001669 if (ext == "png") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09001670 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001671 image->height, image->component,
1672 &image->image[0], 0)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09001673 return false;
1674 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02001675 header = "data:image/png;base64,";
1676 } else if (ext == "jpg") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09001677 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001678 image->height, image->component,
1679 &image->image[0], 100)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09001680 return false;
1681 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02001682 header = "data:image/jpeg;base64,";
1683 } else if (ext == "bmp") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09001684 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001685 image->height, image->component,
1686 &image->image[0])) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09001687 return false;
1688 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02001689 header = "data:image/bmp;base64,";
1690 } else if (!embedImages) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001691 // Error: can't output requested format to file
1692 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02001693 }
johan bowald642a3432018-04-01 12:37:18 +02001694
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001695 if (embedImages) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001696 // Embed base64-encoded image into URI
johan bowald642a3432018-04-01 12:37:18 +02001697 if (data.size()) {
1698 image->uri =
1699 header +
1700 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
1701 } else {
1702 // Throw error?
1703 }
1704 } else {
1705 // Write image to disc
Paolo Jovone6601bf2018-07-07 20:43:33 +02001706 FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
Syoyo Fujita33514af2018-11-05 13:32:43 +09001707 if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001708 const std::string imagefilepath = JoinPath(*basepath, *filename);
1709 std::string writeError;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001710 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
1711 fs->user_data)) {
1712 // Could not write image file to disc; Throw error ?
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09001713 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02001714 }
johan bowald642a3432018-04-01 12:37:18 +02001715 } else {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001716 // Throw error?
johan bowald642a3432018-04-01 12:37:18 +02001717 }
1718 image->uri = *filename;
1719 }
1720
1721 return true;
1722}
1723#endif
1724
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001725void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
Paolo Jovone6601bf2018-07-07 20:43:33 +02001726
1727#ifndef TINYGLTF_NO_FS
1728// Default implementations of filesystem functions
1729
1730bool FileExists(const std::string &abs_filename, void *) {
1731 bool ret;
1732#ifdef _WIN32
1733 FILE *fp;
1734 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
1735 if (err != 0) {
1736 return false;
1737 }
1738#else
1739 FILE *fp = fopen(abs_filename.c_str(), "rb");
1740#endif
1741 if (fp) {
1742 ret = true;
1743 fclose(fp);
1744 } else {
1745 ret = false;
1746 }
1747
1748 return ret;
1749}
1750
1751std::string ExpandFilePath(const std::string &filepath, void *) {
1752#ifdef _WIN32
1753 DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
1754 char *str = new char[len];
1755 ExpandEnvironmentStringsA(filepath.c_str(), str, len);
1756
1757 std::string s(str);
1758
1759 delete[] str;
1760
1761 return s;
1762#else
1763
1764#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
Vladimír Vondruš9f045832018-07-24 23:32:17 +02001765 defined(__ANDROID__) || defined(__EMSCRIPTEN__)
Paolo Jovone6601bf2018-07-07 20:43:33 +02001766 // no expansion
1767 std::string s = filepath;
1768#else
1769 std::string s;
1770 wordexp_t p;
1771
1772 if (filepath.empty()) {
1773 return "";
1774 }
1775
1776 // char** w;
1777 int ret = wordexp(filepath.c_str(), &p, 0);
1778 if (ret) {
1779 // err
1780 s = filepath;
1781 return s;
1782 }
1783
1784 // Use first element only.
1785 if (p.we_wordv) {
1786 s = std::string(p.we_wordv[0]);
1787 wordfree(&p);
1788 } else {
1789 s = filepath;
1790 }
1791
1792#endif
1793
1794 return s;
1795#endif
1796}
1797
1798bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
1799 const std::string &filepath, void *) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001800 std::ifstream f(filepath.c_str(), std::ifstream::binary);
1801 if (!f) {
1802 if (err) {
1803 (*err) += "File open error : " + filepath + "\n";
1804 }
1805 return false;
1806 }
1807
1808 f.seekg(0, f.end);
1809 size_t sz = static_cast<size_t>(f.tellg());
1810 f.seekg(0, f.beg);
1811
1812 if (int(sz) < 0) {
1813 if (err) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001814 (*err) += "Invalid file size : " + filepath +
1815 " (does the path point to a directory?)";
Paolo Jovone6601bf2018-07-07 20:43:33 +02001816 }
1817 return false;
1818 } else if (sz == 0) {
1819 if (err) {
1820 (*err) += "File is empty : " + filepath + "\n";
1821 }
1822 return false;
1823 }
1824
1825 out->resize(sz);
1826 f.read(reinterpret_cast<char *>(&out->at(0)),
1827 static_cast<std::streamsize>(sz));
1828 f.close();
1829
1830 return true;
1831}
1832
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001833bool WriteWholeFile(std::string *err, const std::string &filepath,
1834 const std::vector<unsigned char> &contents, void *) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001835 std::ofstream f(filepath.c_str(), std::ofstream::binary);
1836 if (!f) {
1837 if (err) {
1838 (*err) += "File open error for writing : " + filepath + "\n";
1839 }
1840 return false;
1841 }
1842
1843 f.write(reinterpret_cast<const char *>(&contents.at(0)),
1844 static_cast<std::streamsize>(contents.size()));
1845 if (!f) {
1846 if (err) {
1847 (*err) += "File write error: " + filepath + "\n";
1848 }
1849 return false;
1850 }
1851
1852 f.close();
1853 return true;
1854}
1855
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001856#endif // TINYGLTF_NO_FS
Paolo Jovone6601bf2018-07-07 20:43:33 +02001857
johan bowald642a3432018-04-01 12:37:18 +02001858static std::string MimeToExt(const std::string &mimeType) {
1859 if (mimeType == "image/jpeg") {
1860 return "jpg";
1861 } else if (mimeType == "image/png") {
1862 return "png";
1863 } else if (mimeType == "image/bmp") {
1864 return "bmp";
1865 } else if (mimeType == "image/gif") {
1866 return "gif";
1867 }
1868
1869 return "";
1870}
1871
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001872static void UpdateImageObject(Image &image, std::string &baseDir, int index,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001873 bool embedImages,
1874 WriteImageDataFunction *WriteImageData = nullptr,
1875 void *user_data = nullptr) {
johan bowald642a3432018-04-01 12:37:18 +02001876 std::string filename;
1877 std::string ext;
1878
1879 // If image have uri. Use it it as a filename
1880 if (image.uri.size()) {
1881 filename = GetBaseFilename(image.uri);
1882 ext = GetFilePathExtension(filename);
1883
1884 } else if (image.name.size()) {
1885 ext = MimeToExt(image.mimeType);
1886 // Otherwise use name as filename
1887 filename = image.name + "." + ext;
1888 } else {
1889 ext = MimeToExt(image.mimeType);
1890 // Fallback to index of image as filename
1891 filename = std::to_string(index) + "." + ext;
1892 }
1893
1894 // If callback is set, modify image data object
1895 if (*WriteImageData != nullptr) {
1896 std::string uri;
1897 (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
1898 }
1899}
1900
Selmar Kok0d0e97e2018-08-22 14:01:57 +02001901bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001902 std::string header = "data:application/octet-stream;base64,";
1903 if (in.find(header) == 0) {
1904 return true;
1905 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001906
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02001907 header = "data:image/jpeg;base64,";
1908 if (in.find(header) == 0) {
1909 return true;
1910 }
Squareys43374632018-03-13 22:20:48 +01001911
Syoyo Fujita620eed12016-01-02 23:37:12 +09001912 header = "data:image/png;base64,";
1913 if (in.find(header) == 0) {
1914 return true;
1915 }
Squareys43374632018-03-13 22:20:48 +01001916
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02001917 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001918 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02001919 return true;
1920 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001921
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02001922 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001923 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09001924 return true;
1925 }
1926
Luke San Antoniocaa24b02016-06-15 02:23:09 -04001927 header = "data:text/plain;base64,";
1928 if (in.find(header) == 0) {
1929 return true;
1930 }
1931
Syoyo Fujita20244e12018-03-15 11:01:05 -05001932 header = "data:application/gltf-buffer;base64,";
1933 if (in.find(header) == 0) {
1934 return true;
1935 }
1936
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001937 return false;
1938}
1939
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001940bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
1941 const std::string &in, size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001942 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09001943 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001944 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001945 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001946 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001947
1948 if (data.empty()) {
1949 header = "data:image/jpeg;base64,";
1950 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02001951 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09001952 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09001953 }
1954 }
1955
1956 if (data.empty()) {
1957 header = "data:image/png;base64,";
1958 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02001959 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09001960 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09001961 }
1962 }
Squareys43374632018-03-13 22:20:48 +01001963
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02001964 if (data.empty()) {
1965 header = "data:image/bmp;base64,";
1966 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02001967 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02001968 data = base64_decode(in.substr(header.size())); // cut mime string.
1969 }
1970 }
1971
1972 if (data.empty()) {
1973 header = "data:image/gif;base64,";
1974 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02001975 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02001976 data = base64_decode(in.substr(header.size())); // cut mime string.
1977 }
1978 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001979
1980 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04001981 header = "data:text/plain;base64,";
1982 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02001983 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04001984 data = base64_decode(in.substr(header.size()));
1985 }
1986 }
1987
1988 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05001989 header = "data:application/gltf-buffer;base64,";
1990 if (in.find(header) == 0) {
1991 data = base64_decode(in.substr(header.size()));
1992 }
1993 }
1994
1995 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09001996 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09001997 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001998
1999 if (checkSize) {
2000 if (data.size() != reqBytes) {
2001 return false;
2002 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002003 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09002004 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002005 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002006 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002007 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002008 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002009}
2010
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002011static bool ParseJsonAsValue(Value *ret, const json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02002012 Value val{};
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002013 switch (o.type()) {
2014 case json::value_t::object: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02002015 Value::Object value_object;
2016 for (auto it = o.begin(); it != o.end(); it++) {
2017 Value entry;
2018 ParseJsonAsValue(&entry, it.value());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002019 if (entry.Type() != NULL_TYPE) value_object[it.key()] = entry;
Selmar Kokfa7022f2018-04-04 18:10:20 +02002020 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002021 if (value_object.size() > 0) val = Value(value_object);
2022 } break;
2023 case json::value_t::array: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02002024 Value::Array value_array;
2025 for (auto it = o.begin(); it != o.end(); it++) {
2026 Value entry;
2027 ParseJsonAsValue(&entry, it.value());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002028 if (entry.Type() != NULL_TYPE) value_array.push_back(entry);
Selmar Kokfa7022f2018-04-04 18:10:20 +02002029 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002030 if (value_array.size() > 0) val = Value(value_array);
2031 } break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02002032 case json::value_t::string:
2033 val = Value(o.get<std::string>());
2034 break;
2035 case json::value_t::boolean:
2036 val = Value(o.get<bool>());
2037 break;
2038 case json::value_t::number_integer:
2039 case json::value_t::number_unsigned:
2040 val = Value(static_cast<int>(o.get<int64_t>()));
2041 break;
2042 case json::value_t::number_float:
2043 val = Value(o.get<double>());
2044 break;
2045 case json::value_t::null:
2046 case json::value_t::discarded:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002047 // default:
Selmar Kokfa7022f2018-04-04 18:10:20 +02002048 break;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002049 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002050 if (ret) *ret = val;
2051
Selmar Kokfa7022f2018-04-04 18:10:20 +02002052 return val.Type() != NULL_TYPE;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002053}
2054
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002055static bool ParseExtrasProperty(Value *ret, const json &o) {
2056 json::const_iterator it = o.find("extras");
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002057 if (it == o.end()) {
2058 return false;
2059 }
2060
Selmar Kokfa7022f2018-04-04 18:10:20 +02002061 return ParseJsonAsValue(ret, it.value());
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002062}
2063
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002064static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09002065 const std::string &property,
2066 const bool required,
2067 const std::string &parent_node = "") {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002068 json::const_iterator it = o.find(property);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002069 if (it == o.end()) {
2070 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002071 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09002072 (*err) += "'" + property + "' property is missing";
2073 if (!parent_node.empty()) {
2074 (*err) += " in " + parent_node;
2075 }
2076 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002077 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002078 }
2079 return false;
2080 }
2081
Syoyo Fujita83675312017-12-02 21:14:13 +09002082 if (!it.value().is_boolean()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002083 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002084 if (err) {
2085 (*err) += "'" + property + "' property is not a bool type.\n";
2086 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002087 }
2088 return false;
2089 }
2090
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002091 if (ret) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002092 (*ret) = it.value().get<bool>();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002093 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002094
2095 return true;
2096}
2097
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002098static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09002099 const std::string &property,
2100 const bool required,
2101 const std::string &parent_node = "") {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002102 json::const_iterator it = o.find(property);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002103 if (it == o.end()) {
2104 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002105 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002106 (*err) += "'" + property + "' property is missing";
2107 if (!parent_node.empty()) {
2108 (*err) += " in " + parent_node;
2109 }
2110 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002111 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002112 }
2113 return false;
2114 }
2115
Syoyo Fujita83675312017-12-02 21:14:13 +09002116 if (!it.value().is_number()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002117 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002118 if (err) {
2119 (*err) += "'" + property + "' property is not a number type.\n";
2120 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002121 }
2122 return false;
2123 }
2124
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002125 if (ret) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002126 (*ret) = it.value().get<double>();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002127 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002128
2129 return true;
2130}
2131
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002132static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002133 const json &o, const std::string &property,
2134 bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002135 const std::string &parent_node = "") {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002136 json::const_iterator it = o.find(property);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002137 if (it == o.end()) {
2138 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002139 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002140 (*err) += "'" + property + "' property is missing";
2141 if (!parent_node.empty()) {
2142 (*err) += " in " + parent_node;
2143 }
2144 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002145 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002146 }
2147 return false;
2148 }
2149
Syoyo Fujita83675312017-12-02 21:14:13 +09002150 if (!it.value().is_array()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002151 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002152 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002153 (*err) += "'" + property + "' property is not an array";
2154 if (!parent_node.empty()) {
2155 (*err) += " in " + parent_node;
2156 }
2157 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002158 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002159 }
2160 return false;
2161 }
2162
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002163 ret->clear();
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002164 for (json::const_iterator i = it.value().begin(); i != it.value().end();
2165 i++) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002166 if (!i.value().is_number()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002167 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002168 if (err) {
2169 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002170 if (!parent_node.empty()) {
2171 (*err) += " in " + parent_node;
2172 }
2173 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002174 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002175 }
2176 return false;
2177 }
Syoyo Fujita83675312017-12-02 21:14:13 +09002178 ret->push_back(i.value());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002179 }
2180
2181 return true;
2182}
2183
Syoyo Fujita0614eb82016-10-14 18:50:14 +09002184static bool ParseStringProperty(
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002185 std::string *ret, std::string *err, const json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09002186 const std::string &property, bool required,
2187 const std::string &parent_node = std::string()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002188 json::const_iterator it = o.find(property);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002189 if (it == o.end()) {
2190 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002191 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09002192 (*err) += "'" + property + "' property is missing";
2193 if (parent_node.empty()) {
2194 (*err) += ".\n";
2195 } else {
2196 (*err) += " in `" + parent_node + "'.\n";
2197 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002198 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002199 }
2200 return false;
2201 }
2202
Syoyo Fujita83675312017-12-02 21:14:13 +09002203 if (!it.value().is_string()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002204 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002205 if (err) {
2206 (*err) += "'" + property + "' property is not a string type.\n";
2207 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002208 }
2209 return false;
2210 }
2211
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002212 if (ret) {
Unknownae2cf8e2018-11-15 18:36:59 +00002213 (*ret) = it.value().get<std::string>();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002214 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002215
2216 return true;
2217}
2218
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002219static bool ParseStringIntProperty(std::map<std::string, int> *ret,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002220 std::string *err, const json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002221 const std::string &property, bool required,
2222 const std::string &parent = "") {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002223 json::const_iterator it = o.find(property);
Luke San Antonio19894c72016-06-14 21:19:51 -04002224 if (it == o.end()) {
2225 if (required) {
2226 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09002227 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002228 (*err) +=
2229 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09002230 } else {
2231 (*err) += "'" + property + "' property is missing.\n";
2232 }
Luke San Antonio19894c72016-06-14 21:19:51 -04002233 }
2234 }
2235 return false;
2236 }
2237
2238 // Make sure we are dealing with an object / dictionary.
Syoyo Fujita83675312017-12-02 21:14:13 +09002239 if (!it.value().is_object()) {
Luke San Antonio19894c72016-06-14 21:19:51 -04002240 if (required) {
2241 if (err) {
2242 (*err) += "'" + property + "' property is not an object.\n";
2243 }
2244 }
2245 return false;
2246 }
2247
2248 ret->clear();
Syoyo Fujita83675312017-12-02 21:14:13 +09002249 const json &dict = it.value();
Luke San Antonio19894c72016-06-14 21:19:51 -04002250
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002251 json::const_iterator dictIt(dict.begin());
2252 json::const_iterator dictItEnd(dict.end());
Luke San Antonio19894c72016-06-14 21:19:51 -04002253
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002254 for (; dictIt != dictItEnd; ++dictIt) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002255 if (!dictIt.value().is_number()) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002256 if (required) {
2257 if (err) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002258 (*err) += "'" + property + "' value is not an int.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04002259 }
2260 }
2261 return false;
2262 }
2263
2264 // Insert into the list.
Syoyo Fujita83675312017-12-02 21:14:13 +09002265 (*ret)[dictIt.key()] = static_cast<int>(dictIt.value());
Luke San Antonio19894c72016-06-14 21:19:51 -04002266 }
2267 return true;
2268}
2269
Syoyo Fujita5b407452017-06-04 17:42:41 +09002270static bool ParseJSONProperty(std::map<std::string, double> *ret,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002271 std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09002272 const std::string &property, bool required) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002273 json::const_iterator it = o.find(property);
Syoyo Fujita5b407452017-06-04 17:42:41 +09002274 if (it == o.end()) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002275 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09002276 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002277 (*err) += "'" + property + "' property is missing. \n'";
2278 }
2279 }
2280 return false;
2281 }
2282
Syoyo Fujita83675312017-12-02 21:14:13 +09002283 if (!it.value().is_object()) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002284 if (required) {
2285 if (err) {
2286 (*err) += "'" + property + "' property is not a JSON object.\n";
2287 }
2288 }
2289 return false;
2290 }
2291
2292 ret->clear();
Syoyo Fujita83675312017-12-02 21:14:13 +09002293 const json &obj = it.value();
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002294 json::const_iterator it2(obj.begin());
2295 json::const_iterator itEnd(obj.end());
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002296 for (; it2 != itEnd; it2++) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002297 if (it2.value().is_number())
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002298 ret->insert(std::pair<std::string, double>(it2.key(), it2.value()));
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002299 }
2300
2301 return true;
2302}
2303
Selmar09d2ff12018-03-15 17:30:42 +01002304static bool ParseParameterProperty(Parameter *param, std::string *err,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002305 const json &o, const std::string &prop,
2306 bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01002307 // A parameter value can either be a string or an array of either a boolean or
2308 // a number. Booleans of any kind aren't supported here. Granted, it
2309 // complicates the Parameter structure and breaks it semantically in the sense
2310 // that the client probably works off the assumption that if the string is
2311 // empty the vector is used, etc. Would a tagged union work?
2312 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
2313 // Found string property.
2314 return true;
2315 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
2316 false)) {
2317 // Found a number array.
2318 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07002319 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
2320 return param->has_number_value = true;
Selmar09d2ff12018-03-15 17:30:42 +01002321 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
2322 false)) {
2323 return true;
2324 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
2325 return true;
2326 } else {
2327 if (required) {
2328 if (err) {
2329 (*err) += "parameter must be a string or number / number array.\n";
2330 }
2331 }
2332 return false;
2333 }
2334}
2335
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002336static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
2337 const json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09002338 (void)err;
2339
Selmar09d2ff12018-03-15 17:30:42 +01002340 json::const_iterator it = o.find("extensions");
2341 if (it == o.end()) {
2342 return false;
2343 }
2344 if (!it.value().is_object()) {
2345 return false;
2346 }
2347 ExtensionMap extensions;
2348 json::const_iterator extIt = it.value().begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002349 for (; extIt != it.value().end(); extIt++) {
2350 if (!extIt.value().is_object()) continue;
Selmar Kokee3d0662018-10-08 16:20:43 +02002351 if (!ParseJsonAsValue(&extensions[extIt.key()], extIt.value())) {
2352 if (!extIt.key().empty()) {
2353 // create empty object so that an extension object is still of type object
2354 extensions[extIt.key()] = Value{ Value::Object{} };
2355 }
2356 }
Selmar09d2ff12018-03-15 17:30:42 +01002357 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002358 if (ret) {
Selmar09d2ff12018-03-15 17:30:42 +01002359 (*ret) = extensions;
2360 }
2361 return true;
2362}
2363
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002364static bool ParseAsset(Asset *asset, std::string *err, const json &o) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09002365 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
2366 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
2367 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002368
Selmar09d2ff12018-03-15 17:30:42 +01002369 ParseExtensionsProperty(&asset->extensions, err, o);
2370
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00002371 // Unity exporter version is added as extra here
2372 ParseExtrasProperty(&(asset->extras), o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002373
2374 return true;
2375}
2376
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002377static bool ParseImage(Image *image, std::string *err, std::string *warn,
2378 const json &o, const std::string &basedir,
2379 FsCallbacks *fs,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002380 LoadImageDataFunction *LoadImageData = nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02002381 void *load_image_user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002382 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002383
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002384 // schema says oneOf [`bufferView`, `uri`]
2385 // TODO(syoyo): Check the type of each parameters.
2386 bool hasBufferView = (o.find("bufferView") != o.end());
2387 bool hasURI = (o.find("uri") != o.end());
2388
2389 if (hasBufferView && hasURI) {
2390 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002391 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002392 (*err) +=
2393 "Only one of `bufferView` or `uri` should be defined, but both are "
2394 "defined for Image.\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002395 }
2396 return false;
2397 }
2398
2399 if (!hasBufferView && !hasURI) {
2400 if (err) {
2401 (*err) += "Neither required `bufferView` nor `uri` defined for Image.\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002402 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002403 return false;
2404 }
2405
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002406 ParseStringProperty(&image->name, err, o, "name", false);
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09002407 ParseExtensionsProperty(&image->extensions, err, o);
Selmar Kok8eb39042018-10-05 14:29:35 +02002408 ParseExtrasProperty(&image->extras, o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002409
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002410 if (hasBufferView) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09002411 double bufferView = -1;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002412 if (!ParseNumberProperty(&bufferView, err, o, "bufferView", true)) {
2413 if (err) {
Syoyo Fujitaba28ddc2018-03-21 20:05:11 +09002414 (*err) += "Failed to parse `bufferView` for Image.\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002415 }
2416 return false;
2417 }
2418
2419 std::string mime_type;
2420 ParseStringProperty(&mime_type, err, o, "mimeType", false);
2421
2422 double width = 0.0;
2423 ParseNumberProperty(&width, err, o, "width", false);
2424
2425 double height = 0.0;
2426 ParseNumberProperty(&height, err, o, "height", false);
2427
2428 // Just only save some information here. Loading actual image data from
2429 // bufferView is done after this `ParseImage` function.
2430 image->bufferView = static_cast<int>(bufferView);
2431 image->mimeType = mime_type;
2432 image->width = static_cast<int>(width);
2433 image->height = static_cast<int>(height);
2434
2435 return true;
2436 }
2437
Syoyo Fujita246654a2018-03-21 20:32:22 +09002438 // Parse URI & Load image data.
2439
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002440 std::string uri;
2441 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09002442 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
2443 if (err) {
2444 (*err) += "Failed to parse `uri` for Image.\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002445 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09002446 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002447 }
2448
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002449 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002450
Syoyo Fujita246654a2018-03-21 20:32:22 +09002451 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02002452 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09002453 if (err) {
2454 (*err) += "Failed to decode 'uri' for image parameter.\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002455 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09002456 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002457 }
2458 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09002459 // Assume external file
2460 // Keep texture path (for textures that cannot be decoded)
2461 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01002462#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09002463 return true;
Selmar67af3c92018-03-16 11:48:19 +01002464#endif
Selmar Koke3b3fa92018-08-22 19:04:21 +02002465 if (!LoadExternalFile(&img, err, warn, uri, basedir, false, 0, false, fs)) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002466 if (warn) {
2467 (*warn) += "Failed to load external 'uri' for image parameter\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002468 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09002469 // If the image cannot be loaded, keep uri as image->uri.
2470 return true;
2471 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002472
Syoyo Fujita246654a2018-03-21 20:32:22 +09002473 if (img.empty()) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002474 if (warn) {
2475 (*warn) += "Image is empty.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002476 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09002477 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002478 }
2479 }
2480
Squareysff644d82018-03-13 22:36:18 +01002481 if (*LoadImageData == nullptr) {
2482 if (err) {
2483 (*err) += "No LoadImageData callback specified.\n";
2484 }
2485 return false;
2486 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002487 return (*LoadImageData)(image, err, warn, 0, 0, &img.at(0),
Paolo Jovone6601bf2018-07-07 20:43:33 +02002488 static_cast<int>(img.size()), load_image_user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002489}
2490
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002491static bool ParseTexture(Texture *texture, std::string *err, const json &o,
Syoyo Fujitabeded612016-05-01 20:03:43 +09002492 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002493 (void)basedir;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002494 double sampler = -1.0;
2495 double source = -1.0;
2496 ParseNumberProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002497
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +00002498 ParseNumberProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09002499
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002500 texture->sampler = static_cast<int>(sampler);
2501 texture->source = static_cast<int>(source);
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002502
Selmar Kokfa7022f2018-04-04 18:10:20 +02002503 ParseExtensionsProperty(&texture->extensions, err, o);
2504 ParseExtrasProperty(&texture->extras, o);
Syoyo Fujitabde70212016-02-07 17:38:17 +09002505
Vladimír Vondruš239be2c2018-07-24 23:23:56 +02002506 ParseStringProperty(&texture->name, err, o, "name", false);
2507
Syoyo Fujitabde70212016-02-07 17:38:17 +09002508 return true;
2509}
2510
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002511static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002512 FsCallbacks *fs, const std::string &basedir,
2513 bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09002514 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09002515 size_t bin_size = 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002516 double byteLength;
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09002517 if (!ParseNumberProperty(&byteLength, err, o, "byteLength", true, "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002518 return false;
2519 }
2520
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00002521 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05002522 buffer->uri.clear();
2523 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002524
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00002525 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05002526 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002527 if (err) {
2528 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
2529 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00002530 }
2531
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002532 json::const_iterator type = o.find("type");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002533 if (type != o.end()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002534 if (type.value().is_string()) {
2535 const std::string &ty = type.value();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002536 if (ty.compare("arraybuffer") == 0) {
2537 // buffer.type = "arraybuffer";
2538 }
2539 }
2540 }
2541
2542 size_t bytes = static_cast<size_t>(byteLength);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002543 if (is_binary) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002544 // Still binary glTF accepts external dataURI.
Syoyo Fujita20244e12018-03-15 11:01:05 -05002545 if (!buffer->uri.empty()) {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09002546 // First try embedded data URI.
2547 if (IsDataURI(buffer->uri)) {
2548 std::string mime_type;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002549 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, bytes,
2550 true)) {
2551 if (err) {
2552 (*err) +=
2553 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
2554 }
2555 return false;
Syoyo Fujita39abfb52018-07-11 02:46:52 +09002556 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002557 } else {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09002558 // External .bin file.
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002559 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
2560 buffer->uri, basedir, true, bytes, true, fs)) {
Vladimír Vondrušfd84ceb2018-08-16 21:07:56 +02002561 return false;
2562 }
Syoyo Fujita39abfb52018-07-11 02:46:52 +09002563 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002564 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09002565 // load data from (embedded) binary data
2566
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09002567 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09002568 if (err) {
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09002569 (*err) += "Invalid binary data in `Buffer'.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002570 }
2571 return false;
2572 }
2573
2574 if (byteLength > bin_size) {
2575 if (err) {
2576 std::stringstream ss;
2577 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002578 "`byteLength' = "
2579 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002580 (*err) += ss.str();
2581 }
2582 return false;
2583 }
2584
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00002585 // Read buffer data
2586 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002587 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09002588 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002589
2590 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002591 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02002592 std::string mime_type;
2593 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, bytes, true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002594 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002595 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002596 }
2597 return false;
2598 }
2599 } else {
2600 // Assume external .bin file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002601 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, buffer->uri,
Selmar Koke3b3fa92018-08-22 19:04:21 +02002602 basedir, true, bytes, true, fs)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002603 return false;
2604 }
2605 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002606 }
2607
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002608 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002609
2610 return true;
2611}
2612
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002613static bool ParseBufferView(BufferView *bufferView, std::string *err,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002614 const json &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002615 double buffer = -1.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002616 if (!ParseNumberProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002617 return false;
2618 }
2619
Syoyo Fujitad17ff662017-05-29 02:53:12 +09002620 double byteOffset = 0.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002621 ParseNumberProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002622
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002623 double byteLength = 1.0;
Syoyo Fujita5b407452017-06-04 17:42:41 +09002624 if (!ParseNumberProperty(&byteLength, err, o, "byteLength", true,
2625 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002626 return false;
2627 }
2628
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09002629 size_t byteStride = 0;
2630 double byteStrideValue = 0.0;
2631 if (!ParseNumberProperty(&byteStrideValue, err, o, "byteStride", false)) {
2632 // Spec says: When byteStride of referenced bufferView is not defined, it
2633 // means that accessor elements are tightly packed, i.e., effective stride
2634 // equals the size of the element.
2635 // We cannot determine the actual byteStride until Accessor are parsed, thus
2636 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
2637 byteStride = 0;
2638 } else {
2639 byteStride = static_cast<size_t>(byteStrideValue);
2640 }
2641
2642 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
2643 if (err) {
2644 std::stringstream ss;
2645 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
2646 "4 : "
2647 << byteStride << std::endl;
2648
2649 (*err) += ss.str();
2650 }
2651 return false;
2652 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002653
2654 double target = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002655 ParseNumberProperty(&target, err, o, "target", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002656 int targetValue = static_cast<int>(target);
2657 if ((targetValue == TINYGLTF_TARGET_ARRAY_BUFFER) ||
2658 (targetValue == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
2659 // OK
2660 } else {
2661 targetValue = 0;
2662 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002663 bufferView->target = targetValue;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002664
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002665 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002666
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002667 bufferView->buffer = static_cast<int>(buffer);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002668 bufferView->byteOffset = static_cast<size_t>(byteOffset);
2669 bufferView->byteLength = static_cast<size_t>(byteLength);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002670 bufferView->byteStride = static_cast<size_t>(byteStride);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002671
2672 return true;
2673}
2674
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002675static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002676 double bufferView = -1.0;
Syoyo Fujita5b407452017-06-04 17:42:41 +09002677 if (!ParseNumberProperty(&bufferView, err, o, "bufferView", true,
2678 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002679 return false;
2680 }
2681
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002682 double byteOffset = 0.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002683 ParseNumberProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002684
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09002685 bool normalized = false;
2686 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
2687
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002688 double componentType = 0.0;
Syoyo Fujita5b407452017-06-04 17:42:41 +09002689 if (!ParseNumberProperty(&componentType, err, o, "componentType", true,
2690 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002691 return false;
2692 }
2693
2694 double count = 0.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002695 if (!ParseNumberProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002696 return false;
2697 }
2698
2699 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002700 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002701 return false;
2702 }
2703
2704 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002705 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002706 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002707 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002708 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002709 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002710 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002711 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002712 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002713 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002714 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002715 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002716 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002717 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002718 } else {
2719 std::stringstream ss;
2720 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002721 if (err) {
2722 (*err) += ss.str();
2723 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002724 return false;
2725 }
2726
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002727 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002728
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002729 accessor->minValues.clear();
2730 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09002731 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
2732 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002733
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09002734 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
2735 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002736
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002737 accessor->count = static_cast<size_t>(count);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002738 accessor->bufferView = static_cast<int>(bufferView);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002739 accessor->byteOffset = static_cast<size_t>(byteOffset);
jianghaosen4748ad62017-08-29 20:08:37 +08002740 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002741 {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002742 int comp = static_cast<int>(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002743 if (comp >= TINYGLTF_COMPONENT_TYPE_BYTE &&
2744 comp <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
2745 // OK
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002746 accessor->componentType = comp;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002747 } else {
2748 std::stringstream ss;
2749 ss << "Invalid `componentType` in accessor. Got " << comp << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002750 if (err) {
2751 (*err) += ss.str();
2752 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002753 return false;
2754 }
2755 }
2756
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002757 ParseExtrasProperty(&(accessor->extras), o);
2758
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002759 return true;
2760}
2761
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002762static bool ParsePrimitive(Primitive *primitive, std::string *err,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002763 const json &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002764 double material = -1.0;
2765 ParseNumberProperty(&material, err, o, "material", false);
2766 primitive->material = static_cast<int>(material);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002767
2768 double mode = static_cast<double>(TINYGLTF_MODE_TRIANGLES);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002769 ParseNumberProperty(&mode, err, o, "mode", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002770
2771 int primMode = static_cast<int>(mode);
Syoyo Fujita5b407452017-06-04 17:42:41 +09002772 primitive->mode = primMode; // Why only triangled were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002773
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002774 double indices = -1.0;
2775 ParseNumberProperty(&indices, err, o, "indices", false);
2776 primitive->indices = static_cast<int>(indices);
2777 if (!ParseStringIntProperty(&primitive->attributes, err, o, "attributes",
Syoyo Fujita57c10182017-10-19 18:48:26 +09002778 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002779 return false;
2780 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002781
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00002782 // Look for morph targets
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002783 json::const_iterator targetsObject = o.find("targets");
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002784 if ((targetsObject != o.end()) && targetsObject.value().is_array()) {
2785 for (json::const_iterator i = targetsObject.value().begin();
2786 i != targetsObject.value().end(); i++) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00002787 std::map<std::string, int> targetAttribues;
2788
Syoyo Fujita83675312017-12-02 21:14:13 +09002789 const json &dict = i.value();
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002790 json::const_iterator dictIt(dict.begin());
2791 json::const_iterator dictItEnd(dict.end());
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00002792
2793 for (; dictIt != dictItEnd; ++dictIt) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002794 targetAttribues[dictIt.key()] = static_cast<int>(dictIt.value());
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00002795 }
2796 primitive->targets.push_back(targetAttribues);
2797 }
2798 }
2799
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002800 ParseExtrasProperty(&(primitive->extras), o);
2801
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002802 return true;
2803}
2804
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002805static bool ParseMesh(Mesh *mesh, std::string *err, const json &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002806 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002807
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002808 mesh->primitives.clear();
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002809 json::const_iterator primObject = o.find("primitives");
Syoyo Fujita83675312017-12-02 21:14:13 +09002810 if ((primObject != o.end()) && primObject.value().is_array()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002811 for (json::const_iterator i = primObject.value().begin();
2812 i != primObject.value().end(); i++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002813 Primitive primitive;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002814 if (ParsePrimitive(&primitive, err, i.value())) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04002815 // Only add the primitive if the parsing succeeds.
2816 mesh->primitives.push_back(primitive);
2817 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002818 }
2819 }
2820
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00002821 // Look for morph targets
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002822 json::const_iterator targetsObject = o.find("targets");
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002823 if ((targetsObject != o.end()) && targetsObject.value().is_array()) {
2824 for (json::const_iterator i = targetsObject.value().begin();
2825 i != targetsObject.value().end(); i++) {
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00002826 std::map<std::string, int> targetAttribues;
2827
Syoyo Fujita83675312017-12-02 21:14:13 +09002828 const json &dict = i.value();
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002829 json::const_iterator dictIt(dict.begin());
2830 json::const_iterator dictItEnd(dict.end());
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00002831
2832 for (; dictIt != dictItEnd; ++dictIt) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002833 targetAttribues[dictIt.key()] = static_cast<int>(dictIt.value());
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00002834 }
2835 mesh->targets.push_back(targetAttribues);
2836 }
2837 }
2838
2839 // Should probably check if has targets and if dimensions fit
2840 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
2841
Selmar09d2ff12018-03-15 17:30:42 +01002842 ParseExtensionsProperty(&mesh->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002843 ParseExtrasProperty(&(mesh->extras), o);
2844
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002845 return true;
2846}
2847
Syoyo Fujita83675312017-12-02 21:14:13 +09002848static bool ParseLight(Light *light, std::string *err, const json &o) {
Emanuel Schrade186322b2017-11-06 11:14:41 +01002849 ParseStringProperty(&light->name, err, o, "name", false);
2850 ParseNumberArrayProperty(&light->color, err, o, "color", false);
2851 ParseStringProperty(&light->type, err, o, "type", false);
2852 return true;
2853}
2854
Syoyo Fujita83675312017-12-02 21:14:13 +09002855static bool ParseNode(Node *node, std::string *err, const json &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002856 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002857
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002858 double skin = -1.0;
2859 ParseNumberProperty(&skin, err, o, "skin", false);
2860 node->skin = static_cast<int>(skin);
2861
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002862 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09002863 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002864 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
2865 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
2866 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
2867 }
2868
2869 double camera = -1.0;
2870 ParseNumberProperty(&camera, err, o, "camera", false);
2871 node->camera = static_cast<int>(camera);
2872
2873 double mesh = -1.0;
2874 ParseNumberProperty(&mesh, err, o, "mesh", false);
Syoyo Fujitaf6120152017-05-27 23:51:23 +09002875 node->mesh = int(mesh);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002876
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002877 node->children.clear();
Syoyo Fujita83675312017-12-02 21:14:13 +09002878 json::const_iterator childrenObject = o.find("children");
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002879 if ((childrenObject != o.end()) && childrenObject.value().is_array()) {
2880 for (json::const_iterator i = childrenObject.value().begin();
2881 i != childrenObject.value().end(); i++) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002882 if (!i.value().is_number()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002883 if (err) {
2884 (*err) += "Invalid `children` array.\n";
2885 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002886 return false;
2887 }
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002888 const int &childrenNode = static_cast<int>(i.value());
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002889 node->children.push_back(childrenNode);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002890 }
2891 }
2892
Selmar09d2ff12018-03-15 17:30:42 +01002893 ParseExtensionsProperty(&node->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002894 ParseExtrasProperty(&(node->extras), o);
2895
Emanuel Schrade186322b2017-11-06 11:14:41 +01002896 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002897}
2898
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002899static bool ParseMaterial(Material *material, std::string *err, const json &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002900 material->values.clear();
Selmar09d2ff12018-03-15 17:30:42 +01002901 material->extensions.clear();
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002902 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002903
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002904 json::const_iterator it(o.begin());
2905 json::const_iterator itEnd(o.end());
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002906
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002907 for (; it != itEnd; it++) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002908 if (it.key() == "pbrMetallicRoughness") {
Syoyo Fujita83675312017-12-02 21:14:13 +09002909 if (it.value().is_object()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002910 const json &values_object = it.value();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002911
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002912 json::const_iterator itVal(values_object.begin());
2913 json::const_iterator itValEnd(values_object.end());
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002914
Syoyo Fujitaf6120152017-05-27 23:51:23 +09002915 for (; itVal != itValEnd; itVal++) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002916 Parameter param;
Syoyo Fujita83675312017-12-02 21:14:13 +09002917 if (ParseParameterProperty(&param, err, values_object, itVal.key(),
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002918 false)) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002919 material->values[itVal.key()] = param;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002920 }
2921 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002922 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002923 } else if (it.key() == "extensions" || it.key() == "extras") {
2924 // done later, skip, otherwise poorly parsed contents will be saved in the
2925 // parametermap and serialized again later
2926 } else {
Syoyo Fujita5b407452017-06-04 17:42:41 +09002927 Parameter param;
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002928 if (ParseParameterProperty(&param, err, o, it.key(), false)) {
2929 material->additionalValues[it.key()] = param;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002930 }
2931 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09002932 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002933
Selmar09d2ff12018-03-15 17:30:42 +01002934 ParseExtensionsProperty(&material->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002935 ParseExtrasProperty(&(material->extras), o);
2936
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002937 return true;
2938}
2939
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002940static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002941 const json &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002942 double samplerIndex = -1.0;
2943 double targetIndex = -1.0;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002944 if (!ParseNumberProperty(&samplerIndex, err, o, "sampler", true,
2945 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002946 if (err) {
2947 (*err) += "`sampler` field is missing in animation channels\n";
2948 }
2949 return false;
2950 }
2951
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002952 json::const_iterator targetIt = o.find("target");
Syoyo Fujita83675312017-12-02 21:14:13 +09002953 if ((targetIt != o.end()) && targetIt.value().is_object()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002954 const json &target_object = targetIt.value();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002955
Syoyo Fujita5b407452017-06-04 17:42:41 +09002956 if (!ParseNumberProperty(&targetIndex, err, target_object, "node", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002957 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09002958 (*err) += "`node` field is missing in animation.channels.target\n";
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002959 }
2960 return false;
2961 }
2962
2963 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
2964 true)) {
2965 if (err) {
2966 (*err) += "`path` field is missing in animation.channels.target\n";
2967 }
2968 return false;
2969 }
2970 }
2971
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002972 channel->sampler = static_cast<int>(samplerIndex);
2973 channel->target_node = static_cast<int>(targetIndex);
2974
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002975 ParseExtrasProperty(&(channel->extras), o);
2976
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002977 return true;
2978}
2979
2980static bool ParseAnimation(Animation *animation, std::string *err,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002981 const json &o) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002982 {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002983 json::const_iterator channelsIt = o.find("channels");
Syoyo Fujita83675312017-12-02 21:14:13 +09002984 if ((channelsIt != o.end()) && channelsIt.value().is_array()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002985 for (json::const_iterator i = channelsIt.value().begin();
2986 i != channelsIt.value().end(); i++) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002987 AnimationChannel channel;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002988 if (ParseAnimationChannel(&channel, err, i.value())) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002989 // Only add the channel if the parsing succeeds.
2990 animation->channels.push_back(channel);
2991 }
2992 }
2993 }
2994 }
2995
2996 {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002997 json::const_iterator samplerIt = o.find("samplers");
Syoyo Fujita83675312017-12-02 21:14:13 +09002998 if ((samplerIt != o.end()) && samplerIt.value().is_array()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002999 const json &sampler_array = samplerIt.value();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003000
Syoyo Fujita83675312017-12-02 21:14:13 +09003001 json::const_iterator it = sampler_array.begin();
3002 json::const_iterator itEnd = sampler_array.end();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003003
3004 for (; it != itEnd; it++) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003005 const json &s = it->get<json>();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003006
3007 AnimationSampler sampler;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003008 double inputIndex = -1.0;
3009 double outputIndex = -1.0;
3010 if (!ParseNumberProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003011 if (err) {
3012 (*err) += "`input` field is missing in animation.sampler\n";
3013 }
3014 return false;
3015 }
3016 if (!ParseStringProperty(&sampler.interpolation, err, s,
3017 "interpolation", true)) {
3018 if (err) {
3019 (*err) += "`interpolation` field is missing in animation.sampler\n";
3020 }
3021 return false;
3022 }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003023 if (!ParseNumberProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003024 if (err) {
3025 (*err) += "`output` field is missing in animation.sampler\n";
3026 }
3027 return false;
3028 }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003029 sampler.input = static_cast<int>(inputIndex);
3030 sampler.output = static_cast<int>(outputIndex);
Jens Olssonb3af2f12018-06-04 10:17:49 +02003031 ParseExtrasProperty(&(sampler.extras), s);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003032 animation->samplers.push_back(sampler);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003033 }
3034 }
3035 }
3036
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003037 ParseStringProperty(&animation->name, err, o, "name", false);
3038
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003039 ParseExtrasProperty(&(animation->extras), o);
3040
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003041 return true;
3042}
3043
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003044static bool ParseSampler(Sampler *sampler, std::string *err, const json &o) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09003045 ParseStringProperty(&sampler->name, err, o, "name", false);
3046
3047 double minFilter =
3048 static_cast<double>(TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR);
3049 double magFilter = static_cast<double>(TINYGLTF_TEXTURE_FILTER_LINEAR);
Cemalettin Dervis246d8662017-12-07 20:29:51 +01003050 double wrapS = static_cast<double>(TINYGLTF_TEXTURE_WRAP_REPEAT);
3051 double wrapT = static_cast<double>(TINYGLTF_TEXTURE_WRAP_REPEAT);
Syoyo Fujitac2615632016-06-19 21:56:06 +09003052 ParseNumberProperty(&minFilter, err, o, "minFilter", false);
3053 ParseNumberProperty(&magFilter, err, o, "magFilter", false);
3054 ParseNumberProperty(&wrapS, err, o, "wrapS", false);
3055 ParseNumberProperty(&wrapT, err, o, "wrapT", false);
3056
3057 sampler->minFilter = static_cast<int>(minFilter);
3058 sampler->magFilter = static_cast<int>(magFilter);
3059 sampler->wrapS = static_cast<int>(wrapS);
3060 sampler->wrapT = static_cast<int>(wrapT);
3061
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003062 ParseExtrasProperty(&(sampler->extras), o);
3063
Syoyo Fujitac2615632016-06-19 21:56:06 +09003064 return true;
3065}
3066
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003067static bool ParseSkin(Skin *skin, std::string *err, const json &o) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003068 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00003069
3070 std::vector<double> joints;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003071 if (!ParseNumberArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00003072 return false;
3073 }
3074
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +00003075 double skeleton = -1.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003076 ParseNumberProperty(&skeleton, err, o, "skeleton", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00003077 skin->skeleton = static_cast<int>(skeleton);
3078
Syoyo Fujita73cd7b92017-06-20 03:02:57 +09003079 skin->joints.resize(joints.size());
3080 for (size_t i = 0; i < joints.size(); i++) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003081 skin->joints[i] = static_cast<int>(joints[i]);
Syoyo Fujita73cd7b92017-06-20 03:02:57 +09003082 }
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00003083
3084 double invBind = -1.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003085 ParseNumberProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00003086 skin->inverseBindMatrices = static_cast<int>(invBind);
3087
3088 return true;
3089}
3090
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003091static bool ParsePerspectiveCamera(PerspectiveCamera *camera, std::string *err,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003092 const json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003093 double yfov = 0.0;
3094 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
3095 return false;
3096 }
3097
3098 double znear = 0.0;
3099 if (!ParseNumberProperty(&znear, err, o, "znear", true,
3100 "PerspectiveCamera")) {
3101 return false;
3102 }
3103
3104 double aspectRatio = 0.0; // = invalid
3105 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
3106 "PerspectiveCamera");
3107
3108 double zfar = 0.0; // = invalid
3109 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
3110
Selmar Kok31cb7f92018-10-03 15:39:05 +02003111 camera->aspectRatio = aspectRatio;
3112 camera->zfar = zfar;
3113 camera->yfov = yfov;
3114 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003115
Selmar09d2ff12018-03-15 17:30:42 +01003116 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003117 ParseExtrasProperty(&(camera->extras), o);
3118
3119 // TODO(syoyo): Validate parameter values.
3120
3121 return true;
3122}
3123
3124static bool ParseOrthographicCamera(OrthographicCamera *camera,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003125 std::string *err, const json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003126 double xmag = 0.0;
3127 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
3128 return false;
3129 }
3130
3131 double ymag = 0.0;
3132 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
3133 return false;
3134 }
3135
3136 double zfar = 0.0;
3137 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
3138 return false;
3139 }
3140
3141 double znear = 0.0;
3142 if (!ParseNumberProperty(&znear, err, o, "znear", true,
3143 "OrthographicCamera")) {
3144 return false;
3145 }
3146
Selmar09d2ff12018-03-15 17:30:42 +01003147 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003148 ParseExtrasProperty(&(camera->extras), o);
3149
Selmar Kok31cb7f92018-10-03 15:39:05 +02003150 camera->xmag = xmag;
3151 camera->ymag = ymag;
3152 camera->zfar = zfar;
3153 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003154
3155 // TODO(syoyo): Validate parameter values.
3156
3157 return true;
3158}
3159
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003160static bool ParseCamera(Camera *camera, std::string *err, const json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003161 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
3162 return false;
3163 }
3164
3165 if (camera->type.compare("orthographic") == 0) {
3166 if (o.find("orthographic") == o.end()) {
3167 if (err) {
3168 std::stringstream ss;
3169 ss << "Orhographic camera description not found." << std::endl;
3170 (*err) += ss.str();
3171 }
3172 return false;
3173 }
3174
Syoyo Fujita83675312017-12-02 21:14:13 +09003175 const json &v = o.find("orthographic").value();
3176 if (!v.is_object()) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003177 if (err) {
3178 std::stringstream ss;
3179 ss << "\"orthographic\" is not a JSON object." << std::endl;
3180 (*err) += ss.str();
3181 }
3182 return false;
3183 }
3184
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003185 if (!ParseOrthographicCamera(&camera->orthographic, err, v.get<json>())) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003186 return false;
3187 }
3188 } else if (camera->type.compare("perspective") == 0) {
3189 if (o.find("perspective") == o.end()) {
3190 if (err) {
3191 std::stringstream ss;
3192 ss << "Perspective camera description not found." << std::endl;
3193 (*err) += ss.str();
3194 }
3195 return false;
3196 }
3197
Syoyo Fujita83675312017-12-02 21:14:13 +09003198 const json &v = o.find("perspective").value();
3199 if (!v.is_object()) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003200 if (err) {
3201 std::stringstream ss;
3202 ss << "\"perspective\" is not a JSON object." << std::endl;
3203 (*err) += ss.str();
3204 }
3205 return false;
3206 }
3207
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003208 if (!ParsePerspectiveCamera(&camera->perspective, err, v.get<json>())) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003209 return false;
3210 }
3211 } else {
3212 if (err) {
3213 std::stringstream ss;
3214 ss << "Invalid camera type: \"" << camera->type
3215 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
3216 (*err) += ss.str();
3217 }
3218 return false;
3219 }
3220
3221 ParseStringProperty(&camera->name, err, o, "name", false);
3222
Selmar09d2ff12018-03-15 17:30:42 +01003223 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003224 ParseExtrasProperty(&(camera->extras), o);
3225
3226 return true;
3227}
3228
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003229bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
3230 const char *str, unsigned int length,
3231 const std::string &base_dir,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003232 unsigned int check_sections) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09003233 if (length < 4) {
3234 if (err) {
3235 (*err) = "JSON string too short.\n";
3236 }
3237 return false;
3238 }
3239
Syoyo Fujita83675312017-12-02 21:14:13 +09003240 json v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09003241
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003242#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
3243 defined(_CPPUNWIND)) && \
3244 not defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09003245 try {
Syoyo Fujita83675312017-12-02 21:14:13 +09003246 v = json::parse(str, str + length);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003247
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09003248 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003249 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09003250 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003251 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003252 return false;
3253 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09003254#else
3255 {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003256 v = json::parse(str, str + length, nullptr, /* exception */ false);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09003257
3258 if (!v.is_object()) {
3259 // Assume parsing was failed.
3260 if (err) {
3261 (*err) = "Failed to parse JSON object\n";
3262 }
3263 return false;
3264 }
3265 }
3266#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003267
Syoyo Fujita83675312017-12-02 21:14:13 +09003268 if (!v.is_object()) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09003269 // root is not an object.
3270 if (err) {
3271 (*err) = "Root element is not a JSON object\n";
3272 }
3273 return false;
3274 }
3275
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003276 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09003277 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003278
Syoyo Fujita83675312017-12-02 21:14:13 +09003279 {
Squareys43374632018-03-13 22:20:48 +01003280 json::const_iterator it = v.find("scenes");
Syoyo Fujita83675312017-12-02 21:14:13 +09003281 if ((it != v.end()) && it.value().is_array()) {
3282 // OK
3283 } else if (check_sections & REQUIRE_SCENES) {
3284 if (err) {
3285 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
3286 }
3287 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003288 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003289 }
3290
Syoyo Fujita83675312017-12-02 21:14:13 +09003291 {
Squareys43374632018-03-13 22:20:48 +01003292 json::const_iterator it = v.find("nodes");
Syoyo Fujita83675312017-12-02 21:14:13 +09003293 if ((it != v.end()) && it.value().is_array()) {
3294 // OK
3295 } else if (check_sections & REQUIRE_NODES) {
3296 if (err) {
3297 (*err) += "\"nodes\" object not found in .gltf\n";
3298 }
3299 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003300 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003301 }
3302
Syoyo Fujita83675312017-12-02 21:14:13 +09003303 {
Squareys43374632018-03-13 22:20:48 +01003304 json::const_iterator it = v.find("accessors");
Syoyo Fujita83675312017-12-02 21:14:13 +09003305 if ((it != v.end()) && it.value().is_array()) {
3306 // OK
3307 } else if (check_sections & REQUIRE_ACCESSORS) {
3308 if (err) {
3309 (*err) += "\"accessors\" object not found in .gltf\n";
3310 }
3311 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003312 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003313 }
3314
Syoyo Fujita83675312017-12-02 21:14:13 +09003315 {
Squareys43374632018-03-13 22:20:48 +01003316 json::const_iterator it = v.find("buffers");
Syoyo Fujita83675312017-12-02 21:14:13 +09003317 if ((it != v.end()) && it.value().is_array()) {
3318 // OK
3319 } else if (check_sections & REQUIRE_BUFFERS) {
3320 if (err) {
3321 (*err) += "\"buffers\" object not found in .gltf\n";
3322 }
3323 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003324 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003325 }
3326
Syoyo Fujita83675312017-12-02 21:14:13 +09003327 {
Squareys43374632018-03-13 22:20:48 +01003328 json::const_iterator it = v.find("bufferViews");
Syoyo Fujita83675312017-12-02 21:14:13 +09003329 if ((it != v.end()) && it.value().is_array()) {
3330 // OK
3331 } else if (check_sections & REQUIRE_BUFFER_VIEWS) {
3332 if (err) {
3333 (*err) += "\"bufferViews\" object not found in .gltf\n";
3334 }
3335 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003336 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003337 }
3338
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003339 model->buffers.clear();
3340 model->bufferViews.clear();
3341 model->accessors.clear();
3342 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003343 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003344 model->nodes.clear();
3345 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00003346 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01003347 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003348 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003349
Syoyo Fujita83675312017-12-02 21:14:13 +09003350 // 1. Parse Asset
3351 {
Squareys43374632018-03-13 22:20:48 +01003352 json::const_iterator it = v.find("asset");
Syoyo Fujita83675312017-12-02 21:14:13 +09003353 if ((it != v.end()) && it.value().is_object()) {
3354 const json &root = it.value();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003355
Syoyo Fujita83675312017-12-02 21:14:13 +09003356 ParseAsset(&model->asset, err, root);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00003357 }
3358 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003359
Syoyo Fujita83675312017-12-02 21:14:13 +09003360 // 2. Parse extensionUsed
3361 {
Squareys43374632018-03-13 22:20:48 +01003362 json::const_iterator it = v.find("extensionsUsed");
Syoyo Fujita83675312017-12-02 21:14:13 +09003363 if ((it != v.end()) && it.value().is_array()) {
3364 const json &root = it.value();
3365 for (unsigned int i = 0; i < root.size(); ++i) {
3366 model->extensionsUsed.push_back(root[i].get<std::string>());
Syoyo Fujita59130d12017-08-01 20:23:11 +09003367 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003368 }
3369 }
3370
Syoyo Fujita83675312017-12-02 21:14:13 +09003371 {
Squareys43374632018-03-13 22:20:48 +01003372 json::const_iterator it = v.find("extensionsRequired");
Syoyo Fujita83675312017-12-02 21:14:13 +09003373 if ((it != v.end()) && it.value().is_array()) {
3374 const json &root = it.value();
3375 for (unsigned int i = 0; i < root.size(); ++i) {
3376 model->extensionsRequired.push_back(root[i].get<std::string>());
Syoyo Fujita59130d12017-08-01 20:23:11 +09003377 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003378 }
3379 }
3380
Syoyo Fujita83675312017-12-02 21:14:13 +09003381 // 3. Parse Buffer
3382 {
Squareys43374632018-03-13 22:20:48 +01003383 json::const_iterator rootIt = v.find("buffers");
Syoyo Fujita83675312017-12-02 21:14:13 +09003384 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3385 const json &root = rootIt.value();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003386
Syoyo Fujita83675312017-12-02 21:14:13 +09003387 json::const_iterator it(root.begin());
3388 json::const_iterator itEnd(root.end());
3389 for (; it != itEnd; it++) {
3390 if (!it.value().is_object()) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09003391 if (err) {
Syoyo Fujita83675312017-12-02 21:14:13 +09003392 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09003393 }
3394 return false;
3395 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003396 Buffer buffer;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003397 if (!ParseBuffer(&buffer, err, it->get<json>(), &fs, base_dir,
3398 is_binary_, bin_data_, bin_size_)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09003399 return false;
3400 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09003401
Syoyo Fujita83675312017-12-02 21:14:13 +09003402 model->buffers.push_back(buffer);
3403 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003404 }
3405 }
Squareys43374632018-03-13 22:20:48 +01003406
Syoyo Fujita83675312017-12-02 21:14:13 +09003407 // 4. Parse BufferView
3408 {
Squareys43374632018-03-13 22:20:48 +01003409 json::const_iterator rootIt = v.find("bufferViews");
Syoyo Fujita83675312017-12-02 21:14:13 +09003410 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3411 const json &root = rootIt.value();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003412
Syoyo Fujita83675312017-12-02 21:14:13 +09003413 json::const_iterator it(root.begin());
3414 json::const_iterator itEnd(root.end());
3415 for (; it != itEnd; it++) {
3416 if (!it.value().is_object()) {
3417 if (err) {
3418 (*err) += "`bufferViews' does not contain an JSON object.";
3419 }
3420 return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09003421 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003422 BufferView bufferView;
3423 if (!ParseBufferView(&bufferView, err, it->get<json>())) {
3424 return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09003425 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003426
Syoyo Fujita83675312017-12-02 21:14:13 +09003427 model->bufferViews.push_back(bufferView);
3428 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003429 }
3430 }
3431
Syoyo Fujita83675312017-12-02 21:14:13 +09003432 // 5. Parse Accessor
3433 {
Squareys43374632018-03-13 22:20:48 +01003434 json::const_iterator rootIt = v.find("accessors");
Syoyo Fujita83675312017-12-02 21:14:13 +09003435 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3436 const json &root = rootIt.value();
Syoyo Fujitac2615632016-06-19 21:56:06 +09003437
Syoyo Fujita83675312017-12-02 21:14:13 +09003438 json::const_iterator it(root.begin());
3439 json::const_iterator itEnd(root.end());
3440 for (; it != itEnd; it++) {
3441 if (!it.value().is_object()) {
3442 if (err) {
3443 (*err) += "`accessors' does not contain an JSON object.";
3444 }
3445 return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09003446 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003447 Accessor accessor;
3448 if (!ParseAccessor(&accessor, err, it->get<json>())) {
3449 return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09003450 }
Syoyo Fujitac2615632016-06-19 21:56:06 +09003451
Syoyo Fujita83675312017-12-02 21:14:13 +09003452 model->accessors.push_back(accessor);
3453 }
Syoyo Fujitac2615632016-06-19 21:56:06 +09003454 }
3455 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003456
Syoyo Fujita83675312017-12-02 21:14:13 +09003457 // 6. Parse Mesh
3458 {
Squareys43374632018-03-13 22:20:48 +01003459 json::const_iterator rootIt = v.find("meshes");
Syoyo Fujita83675312017-12-02 21:14:13 +09003460 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3461 const json &root = rootIt.value();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003462
Syoyo Fujita83675312017-12-02 21:14:13 +09003463 json::const_iterator it(root.begin());
3464 json::const_iterator itEnd(root.end());
3465 for (; it != itEnd; it++) {
3466 if (!it.value().is_object()) {
3467 if (err) {
3468 (*err) += "`meshes' does not contain an JSON object.";
3469 }
3470 return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09003471 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003472 Mesh mesh;
3473 if (!ParseMesh(&mesh, err, it->get<json>())) {
3474 return false;
3475 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003476
Syoyo Fujita83675312017-12-02 21:14:13 +09003477 model->meshes.push_back(mesh);
3478 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003479 }
3480 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01003481
Syoyo Fujita83675312017-12-02 21:14:13 +09003482 // 7. Parse Node
3483 {
Squareys43374632018-03-13 22:20:48 +01003484 json::const_iterator rootIt = v.find("nodes");
Syoyo Fujita83675312017-12-02 21:14:13 +09003485 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3486 const json &root = rootIt.value();
Emanuel Schrade186322b2017-11-06 11:14:41 +01003487
Syoyo Fujita83675312017-12-02 21:14:13 +09003488 json::const_iterator it(root.begin());
3489 json::const_iterator itEnd(root.end());
3490 for (; it != itEnd; it++) {
3491 if (!it.value().is_object()) {
3492 if (err) {
3493 (*err) += "`nodes' does not contain an JSON object.";
3494 }
3495 return false;
3496 }
3497 Node node;
3498 if (!ParseNode(&node, err, it->get<json>())) {
3499 return false;
3500 }
3501
3502 model->nodes.push_back(node);
3503 }
3504 }
3505 }
3506
3507 // 8. Parse scenes.
3508 {
Squareys43374632018-03-13 22:20:48 +01003509 json::const_iterator rootIt = v.find("scenes");
Syoyo Fujita83675312017-12-02 21:14:13 +09003510 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3511 const json &root = rootIt.value();
3512
Syoyo Fujita83675312017-12-02 21:14:13 +09003513 json::const_iterator it(root.begin());
3514 json::const_iterator itEnd(root.end());
3515 for (; it != itEnd; it++) {
3516 if (!(it.value().is_object())) {
3517 if (err) {
3518 (*err) += "`scenes' does not contain an JSON object.";
3519 }
3520 return false;
3521 }
3522 const json &o = it->get<json>();
3523 std::vector<double> nodes;
3524 if (!ParseNumberArrayProperty(&nodes, err, o, "nodes", false)) {
3525 return false;
3526 }
3527
3528 Scene scene;
3529 ParseStringProperty(&scene.name, err, o, "name", false);
3530 std::vector<int> nodesIds;
3531 for (size_t i = 0; i < nodes.size(); i++) {
3532 nodesIds.push_back(static_cast<int>(nodes[i]));
3533 }
3534 scene.nodes = nodesIds;
3535
Selmar09d2ff12018-03-15 17:30:42 +01003536 ParseExtensionsProperty(&scene.extensions, err, o);
3537 ParseExtrasProperty(&scene.extras, o);
3538
Syoyo Fujita83675312017-12-02 21:14:13 +09003539 model->scenes.push_back(scene);
3540 }
3541 }
3542 }
3543
3544 // 9. Parse default scenes.
3545 {
Squareys43374632018-03-13 22:20:48 +01003546 json::const_iterator rootIt = v.find("scene");
Syoyo Fujita580d7c82018-03-15 22:09:01 -05003547 if ((rootIt != v.end()) && rootIt.value().is_number()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09003548 const int defaultScene = rootIt.value();
3549
3550 model->defaultScene = static_cast<int>(defaultScene);
3551 }
3552 }
3553
3554 // 10. Parse Material
3555 {
Squareys43374632018-03-13 22:20:48 +01003556 json::const_iterator rootIt = v.find("materials");
Syoyo Fujita83675312017-12-02 21:14:13 +09003557 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3558 const json &root = rootIt.value();
3559
3560 json::const_iterator it(root.begin());
3561 json::const_iterator itEnd(root.end());
3562 for (; it != itEnd; it++) {
3563 if (!it.value().is_object()) {
3564 if (err) {
3565 (*err) += "`materials' does not contain an JSON object.";
3566 }
3567 return false;
3568 }
3569 json jsonMaterial = it->get<json>();
3570
3571 Material material;
3572 ParseStringProperty(&material.name, err, jsonMaterial, "name", false);
3573
3574 if (!ParseMaterial(&material, err, jsonMaterial)) {
3575 return false;
3576 }
3577
3578 model->materials.push_back(material);
3579 }
3580 }
3581 }
3582
3583 // 11. Parse Image
3584 {
Squareys43374632018-03-13 22:20:48 +01003585 json::const_iterator rootIt = v.find("images");
Syoyo Fujita83675312017-12-02 21:14:13 +09003586 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3587 const json &root = rootIt.value();
3588
3589 json::const_iterator it(root.begin());
3590 json::const_iterator itEnd(root.end());
3591 for (; it != itEnd; it++) {
3592 if (!it.value().is_object()) {
3593 if (err) {
3594 (*err) += "`images' does not contain an JSON object.";
3595 }
3596 return false;
3597 }
3598 Image image;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003599 if (!ParseImage(&image, err, warn, it.value(), base_dir, &fs,
3600 &this->LoadImageData, load_image_user_data_)) {
Syoyo Fujita83675312017-12-02 21:14:13 +09003601 return false;
3602 }
3603
3604 if (image.bufferView != -1) {
3605 // Load image from the buffer view.
3606 if (size_t(image.bufferView) >= model->bufferViews.size()) {
3607 if (err) {
3608 std::stringstream ss;
3609 ss << "bufferView \"" << image.bufferView
3610 << "\" not found in the scene." << std::endl;
3611 (*err) += ss.str();
3612 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01003613 return false;
3614 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003615
3616 const BufferView &bufferView =
3617 model->bufferViews[size_t(image.bufferView)];
3618 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
3619
Squareysff644d82018-03-13 22:36:18 +01003620 if (*LoadImageData == nullptr) {
3621 if (err) {
3622 (*err) += "No LoadImageData callback specified.\n";
3623 }
3624 return false;
3625 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003626 bool ret = LoadImageData(&image, err, warn, image.width, image.height,
Syoyo Fujita83675312017-12-02 21:14:13 +09003627 &buffer.data[bufferView.byteOffset],
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003628 static_cast<int>(bufferView.byteLength),
3629 load_image_user_data_);
Syoyo Fujita83675312017-12-02 21:14:13 +09003630 if (!ret) {
3631 return false;
3632 }
3633 }
3634
3635 model->images.push_back(image);
3636 }
3637 }
3638 }
3639
3640 // 12. Parse Texture
3641 {
Squareys43374632018-03-13 22:20:48 +01003642 json::const_iterator rootIt = v.find("textures");
Syoyo Fujita83675312017-12-02 21:14:13 +09003643 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3644 const json &root = rootIt.value();
3645
3646 json::const_iterator it(root.begin());
3647 json::const_iterator itEnd(root.end());
3648 for (; it != itEnd; it++) {
3649 if (!it.value().is_object()) {
3650 if (err) {
3651 (*err) += "`textures' does not contain an JSON object.";
3652 }
3653 return false;
3654 }
3655 Texture texture;
3656 if (!ParseTexture(&texture, err, it->get<json>(), base_dir)) {
3657 return false;
3658 }
3659
3660 model->textures.push_back(texture);
3661 }
3662 }
3663 }
3664
3665 // 13. Parse Animation
3666 {
Squareys43374632018-03-13 22:20:48 +01003667 json::const_iterator rootIt = v.find("animations");
Syoyo Fujita83675312017-12-02 21:14:13 +09003668 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3669 const json &root = rootIt.value();
3670
3671 json::const_iterator it(root.begin());
3672 json::const_iterator itEnd(root.end());
3673 for (; it != itEnd; ++it) {
3674 if (!it.value().is_object()) {
3675 if (err) {
3676 (*err) += "`animations' does not contain an JSON object.";
3677 }
3678 return false;
3679 }
3680 Animation animation;
3681 if (!ParseAnimation(&animation, err, it->get<json>())) {
3682 return false;
3683 }
3684
3685 model->animations.push_back(animation);
3686 }
3687 }
3688 }
3689
3690 // 14. Parse Skin
3691 {
Squareys43374632018-03-13 22:20:48 +01003692 json::const_iterator rootIt = v.find("skins");
Syoyo Fujita83675312017-12-02 21:14:13 +09003693 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3694 const json &root = rootIt.value();
3695
3696 json::const_iterator it(root.begin());
3697 json::const_iterator itEnd(root.end());
3698 for (; it != itEnd; ++it) {
3699 if (!it.value().is_object()) {
3700 if (err) {
3701 (*err) += "`skins' does not contain an JSON object.";
3702 }
3703 return false;
3704 }
3705 Skin skin;
3706 if (!ParseSkin(&skin, err, it->get<json>())) {
3707 return false;
3708 }
3709
3710 model->skins.push_back(skin);
3711 }
3712 }
3713 }
3714
3715 // 15. Parse Sampler
3716 {
Squareys43374632018-03-13 22:20:48 +01003717 json::const_iterator rootIt = v.find("samplers");
Syoyo Fujita83675312017-12-02 21:14:13 +09003718 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3719 const json &root = rootIt.value();
3720
3721 json::const_iterator it(root.begin());
3722 json::const_iterator itEnd(root.end());
3723 for (; it != itEnd; ++it) {
3724 if (!it.value().is_object()) {
3725 if (err) {
3726 (*err) += "`samplers' does not contain an JSON object.";
3727 }
3728 return false;
3729 }
3730 Sampler sampler;
3731 if (!ParseSampler(&sampler, err, it->get<json>())) {
3732 return false;
3733 }
3734
3735 model->samplers.push_back(sampler);
3736 }
3737 }
3738 }
3739
3740 // 16. Parse Camera
3741 {
Squareys43374632018-03-13 22:20:48 +01003742 json::const_iterator rootIt = v.find("cameras");
Syoyo Fujita83675312017-12-02 21:14:13 +09003743 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3744 const json &root = rootIt.value();
3745
3746 json::const_iterator it(root.begin());
3747 json::const_iterator itEnd(root.end());
3748 for (; it != itEnd; ++it) {
3749 if (!it.value().is_object()) {
3750 if (err) {
3751 (*err) += "`cameras' does not contain an JSON object.";
3752 }
3753 return false;
3754 }
3755 Camera camera;
3756 if (!ParseCamera(&camera, err, it->get<json>())) {
3757 return false;
3758 }
3759
3760 model->cameras.push_back(camera);
3761 }
3762 }
3763 }
3764
3765 // 17. Parse Extensions
Selmar09d2ff12018-03-15 17:30:42 +01003766 ParseExtensionsProperty(&model->extensions, err, v);
3767
3768 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09003769 {
Squareys43374632018-03-13 22:20:48 +01003770 json::const_iterator rootIt = v.find("extensions");
Syoyo Fujita83675312017-12-02 21:14:13 +09003771 if ((rootIt != v.end()) && rootIt.value().is_object()) {
3772 const json &root = rootIt.value();
3773
3774 json::const_iterator it(root.begin());
3775 json::const_iterator itEnd(root.end());
3776 for (; it != itEnd; ++it) {
3777 // parse KHR_lights_cmn extension
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003778 if ((it.key().compare("KHR_lights_cmn") == 0) &&
3779 it.value().is_object()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09003780 const json &object = it.value();
3781 json::const_iterator itLight(object.find("lights"));
3782 json::const_iterator itLightEnd(object.end());
3783 if (itLight == itLightEnd) {
3784 continue;
3785 }
3786
3787 if (!itLight.value().is_array()) {
3788 continue;
3789 }
3790
3791 const json &lights = itLight.value();
3792 json::const_iterator arrayIt(lights.begin());
3793 json::const_iterator arrayItEnd(lights.end());
3794 for (; arrayIt != arrayItEnd; ++arrayIt) {
3795 Light light;
3796 if (!ParseLight(&light, err, arrayIt.value())) {
3797 return false;
3798 }
3799 model->lights.push_back(light);
3800 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01003801 }
3802 }
3803 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01003804 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003805
mynzc0d4d1c2018-06-28 23:06:00 +09003806 // 19. Parse Extras
3807 ParseExtrasProperty(&model->extras, v);
3808
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003809 return true;
3810}
3811
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003812bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003813 std::string *warn, const char *str,
3814 unsigned int length,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003815 const std::string &base_dir,
3816 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003817 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09003818 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003819 bin_size_ = 0;
3820
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003821 return LoadFromString(model, err, warn, str, length, base_dir,
3822 check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003823}
3824
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003825bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003826 std::string *warn, const std::string &filename,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003827 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09003828 std::stringstream ss;
3829
Paolo Jovone6601bf2018-07-07 20:43:33 +02003830 if (fs.ReadWholeFile == nullptr) {
3831 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003832 ss << "Failed to read file: " << filename
3833 << ": one or more FS callback not set" << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003834 if (err) {
3835 (*err) = ss.str();
3836 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09003837 return false;
3838 }
3839
Paolo Jovone6601bf2018-07-07 20:43:33 +02003840 std::vector<unsigned char> data;
3841 std::string fileerr;
3842 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003843 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02003844 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
3845 if (err) {
3846 (*err) = ss.str();
3847 }
3848 return false;
3849 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09003850
Paolo Jovone6601bf2018-07-07 20:43:33 +02003851 size_t sz = data.size();
Luke San Antoniob310b4a2016-06-23 14:17:37 -04003852 if (sz == 0) {
3853 if (err) {
3854 (*err) = "Empty file.";
3855 }
3856 return false;
3857 }
3858
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09003859 std::string basedir = GetBaseDir(filename);
3860
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003861 bool ret = LoadASCIIFromString(
3862 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
3863 static_cast<unsigned int>(data.size()), basedir, check_sections);
Luke San Antonio9e36b612016-06-23 14:10:51 -04003864
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003865 return ret;
3866}
3867
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003868bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003869 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003870 const unsigned char *bytes,
3871 unsigned int size,
3872 const std::string &base_dir,
3873 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003874 if (size < 20) {
3875 if (err) {
3876 (*err) = "Too short data size for glTF Binary.";
3877 }
3878 return false;
3879 }
3880
Syoyo Fujitabeded612016-05-01 20:03:43 +09003881 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
3882 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003883 // ok
3884 } else {
3885 if (err) {
3886 (*err) = "Invalid magic.";
3887 }
3888 return false;
3889 }
3890
Syoyo Fujitabeded612016-05-01 20:03:43 +09003891 unsigned int version; // 4 bytes
3892 unsigned int length; // 4 bytes
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003893 unsigned int model_length; // 4 bytes
3894 unsigned int model_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003895
3896 // @todo { Endian swap for big endian machine. }
Syoyo Fujitabeded612016-05-01 20:03:43 +09003897 memcpy(&version, bytes + 4, 4);
3898 swap4(&version);
3899 memcpy(&length, bytes + 8, 4);
3900 swap4(&length);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003901 memcpy(&model_length, bytes + 12, 4);
3902 swap4(&model_length);
3903 memcpy(&model_format, bytes + 16, 4);
3904 swap4(&model_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003905
Syoyo Fujitae69069d2018-03-15 22:01:18 -05003906 // In case the Bin buffer is not present, the size is exactly 20 + size of
3907 // JSON contents,
3908 // so use "greater than" operator.
3909 if ((20 + model_length > size) || (model_length < 1) ||
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09003910 (model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003911 if (err) {
3912 (*err) = "Invalid glTF binary.";
3913 }
3914 return false;
3915 }
3916
3917 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09003918 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003919 model_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003920
3921 is_binary_ = true;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003922 bin_data_ = bytes + 20 + model_length +
3923 8; // 4 bytes (buffer_length) + 4 bytes(buffer_format)
Syoyo Fujitabeded612016-05-01 20:03:43 +09003924 bin_size_ =
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003925 length - (20 + model_length); // extract header + JSON scene data.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003926
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003927 bool ret = LoadFromString(model, err, warn,
3928 reinterpret_cast<const char *>(&bytes[20]),
3929 model_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003930 if (!ret) {
3931 return ret;
3932 }
3933
3934 return true;
3935}
3936
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003937bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003938 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003939 const std::string &filename,
3940 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003941 std::stringstream ss;
3942
Paolo Jovone6601bf2018-07-07 20:43:33 +02003943 if (fs.ReadWholeFile == nullptr) {
3944 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003945 ss << "Failed to read file: " << filename
3946 << ": one or more FS callback not set" << std::endl;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003947 if (err) {
3948 (*err) = ss.str();
3949 }
3950 return false;
3951 }
3952
Paolo Jovone6601bf2018-07-07 20:43:33 +02003953 std::vector<unsigned char> data;
3954 std::string fileerr;
3955 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003956 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02003957 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
3958 if (err) {
3959 (*err) = ss.str();
3960 }
3961 return false;
3962 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003963
Syoyo Fujitabeded612016-05-01 20:03:43 +09003964 std::string basedir = GetBaseDir(filename);
3965
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003966 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
3967 static_cast<unsigned int>(data.size()),
3968 basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09003969
3970 return ret;
3971}
3972
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003973///////////////////////
3974// GLTF Serialization
3975///////////////////////
3976
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003977// typedef std::pair<std::string, json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003978
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003979template <typename T>
3980static void SerializeNumberProperty(const std::string &key, T number,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003981 json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003982 // obj.insert(
Syoyo Fujita83675312017-12-02 21:14:13 +09003983 // json_object_pair(key, json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003984 // obj[key] = static_cast<double>(number);
3985 obj[key] = number;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003986}
3987
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003988template <typename T>
3989static void SerializeNumberArrayProperty(const std::string &key,
3990 const std::vector<T> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003991 json &obj) {
3992 json o;
Syoyo Fujita83675312017-12-02 21:14:13 +09003993 json vals;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003994
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003995 for (unsigned int i = 0; i < value.size(); ++i) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003996 vals.push_back(static_cast<T>(value[i]));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003997 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02003998 if (!vals.is_null()) {
3999 obj[key] = vals;
4000 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004001}
4002
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004003static void SerializeStringProperty(const std::string &key,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004004 const std::string &value, json &obj) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004005 obj[key] = value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004006}
4007
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004008static void SerializeStringArrayProperty(const std::string &key,
4009 const std::vector<std::string> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004010 json &obj) {
4011 json o;
Syoyo Fujita83675312017-12-02 21:14:13 +09004012 json vals;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004013
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004014 for (unsigned int i = 0; i < value.size(); ++i) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004015 vals.push_back(value[i]);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004016 }
4017
Syoyo Fujita83675312017-12-02 21:14:13 +09004018 obj[key] = vals;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004019}
4020
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004021static bool ValueToJson(const Value &value, json *ret) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02004022 json obj;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004023 switch (value.Type()) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02004024 case NUMBER_TYPE:
4025 obj = json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004026 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02004027 case INT_TYPE:
4028 obj = json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004029 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02004030 case BOOL_TYPE:
4031 obj = json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004032 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02004033 case STRING_TYPE:
4034 obj = json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004035 break;
4036 case ARRAY_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02004037 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
4038 Value elementValue = value.Get(int(i));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004039 json elementJson;
4040 if (ValueToJson(value.Get(int(i)), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02004041 obj.push_back(elementJson);
4042 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004043 break;
4044 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02004045 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004046 // TODO
4047 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02004048 return false;
4049 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004050 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02004051 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004052 for (auto &it : objMap) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02004053 json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004054 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02004055 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004056 break;
4057 }
4058 case NULL_TYPE:
4059 default:
4060 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02004061 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004062 if (ret) *ret = obj;
Selmar Kokfa7022f2018-04-04 18:10:20 +02004063 return true;
4064}
4065
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004066static void SerializeValue(const std::string &key, const Value &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004067 json &obj) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02004068 json ret;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004069 if (ValueToJson(value, &ret)) obj[key] = ret;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004070}
4071
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004072static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
johan bowald30c53472018-03-30 11:49:36 +02004073 json &o) {
4074 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita719d7e42018-03-30 19:26:35 +09004075 std::string encodedData =
4076 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
johan bowald30c53472018-03-30 11:49:36 +02004077 SerializeStringProperty("uri", header + encodedData, o);
4078}
4079
Selmar Koke4677492018-10-25 16:45:49 +02004080static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004081 const std::string &binFilename) {
4082 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Selmar Koke4677492018-10-25 16:45:49 +02004083 if(!output.is_open()) return false;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004084 output.write(reinterpret_cast<const char *>(&data[0]),
4085 std::streamsize(data.size()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004086 output.close();
Selmar Koke4677492018-10-25 16:45:49 +02004087 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004088}
4089
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004090static void SerializeParameterMap(ParameterMap &param, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004091 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
4092 ++paramIt) {
4093 if (paramIt->second.number_array.size()) {
4094 SerializeNumberArrayProperty<double>(paramIt->first,
4095 paramIt->second.number_array, o);
4096 } else if (paramIt->second.json_double_value.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004097 json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004098 for (std::map<std::string, double>::iterator it =
4099 paramIt->second.json_double_value.begin();
4100 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004101 if (it->first == "index") {
4102 json_double_value[it->first] = paramIt->second.TextureIndex();
4103 } else {
4104 json_double_value[it->first] = it->second;
4105 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004106 }
4107
Syoyo Fujita83675312017-12-02 21:14:13 +09004108 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004109 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004110 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07004111 } else if (paramIt->second.has_number_value) {
4112 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004113 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09004114 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004115 }
4116 }
4117}
4118
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004119static void SerializeExtensionMap(ExtensionMap &extensions, json &o) {
4120 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01004121
4122 json extMap;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004123 for (ExtensionMap::iterator extIt = extensions.begin();
4124 extIt != extensions.end(); ++extIt) {
Selmar09d2ff12018-03-15 17:30:42 +01004125 json extension_values;
Syoyo Fujita924d86e2018-10-08 21:15:12 +09004126
4127 // Allow an empty object for extension(#97)
4128 json ret;
4129 if (ValueToJson(extIt->second, &ret)) {
4130 extMap[extIt->first] = ret;
Selmar Kokdb7f4e42018-10-10 18:10:58 +02004131 }
4132 if(ret.is_null()) {
Johan Bowald77decfa2018-11-06 14:28:20 +01004133 if (!(extIt->first.empty())) { // name should not be empty, but for sure
Syoyo Fujita924d86e2018-10-08 21:15:12 +09004134 // create empty object so that an extension name is still included in json.
4135 extMap[extIt->first] = json({});
4136 }
4137 }
Selmar09d2ff12018-03-15 17:30:42 +01004138 }
4139 o["extensions"] = extMap;
4140}
4141
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004142static void SerializeGltfAccessor(Accessor &accessor, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004143 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004144
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004145 if (accessor.byteOffset != 0.0)
4146 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004147
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004148 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
4149 SerializeNumberProperty<size_t>("count", accessor.count, o);
4150 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
4151 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
4152 std::string type;
4153 switch (accessor.type) {
4154 case TINYGLTF_TYPE_SCALAR:
4155 type = "SCALAR";
4156 break;
4157 case TINYGLTF_TYPE_VEC2:
4158 type = "VEC2";
4159 break;
4160 case TINYGLTF_TYPE_VEC3:
4161 type = "VEC3";
4162 break;
4163 case TINYGLTF_TYPE_VEC4:
4164 type = "VEC4";
4165 break;
4166 case TINYGLTF_TYPE_MAT2:
4167 type = "MAT2";
4168 break;
4169 case TINYGLTF_TYPE_MAT3:
4170 type = "MAT3";
4171 break;
4172 case TINYGLTF_TYPE_MAT4:
4173 type = "MAT4";
4174 break;
4175 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004176
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004177 SerializeStringProperty("type", type, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004178 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02004179
4180 if (accessor.extras.Type() != NULL_TYPE) {
4181 SerializeValue("extras", accessor.extras, o);
4182 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004183}
4184
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004185static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004186 SerializeNumberProperty("sampler", channel.sampler, o);
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004187 json target;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004188 SerializeNumberProperty("node", channel.target_node, target);
4189 SerializeStringProperty("path", channel.target_path, target);
4190
Syoyo Fujita83675312017-12-02 21:14:13 +09004191 o["target"] = target;
Jens Olssonb3af2f12018-06-04 10:17:49 +02004192
4193 if (channel.extras.Type() != NULL_TYPE) {
4194 SerializeValue("extras", channel.extras, o);
4195 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004196}
4197
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004198static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004199 SerializeNumberProperty("input", sampler.input, o);
4200 SerializeNumberProperty("output", sampler.output, o);
4201 SerializeStringProperty("interpolation", sampler.interpolation, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02004202
4203 if (sampler.extras.Type() != NULL_TYPE) {
4204 SerializeValue("extras", sampler.extras, o);
4205 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004206}
4207
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004208static void SerializeGltfAnimation(Animation &animation, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004209 if (!animation.name.empty())
4210 SerializeStringProperty("name", animation.name, o);
Syoyo Fujita83675312017-12-02 21:14:13 +09004211 json channels;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004212 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004213 json channel;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004214 AnimationChannel gltfChannel = animation.channels[i];
4215 SerializeGltfAnimationChannel(gltfChannel, channel);
Syoyo Fujita83675312017-12-02 21:14:13 +09004216 channels.push_back(channel);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004217 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004218 o["channels"] = channels;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004219
Syoyo Fujita83675312017-12-02 21:14:13 +09004220 json samplers;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004221 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004222 json sampler;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004223 AnimationSampler gltfSampler = animation.samplers[i];
4224 SerializeGltfAnimationSampler(gltfSampler, sampler);
Syoyo Fujita83675312017-12-02 21:14:13 +09004225 samplers.push_back(sampler);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004226 }
4227
Syoyo Fujita83675312017-12-02 21:14:13 +09004228 o["samplers"] = samplers;
Jens Olssonb3af2f12018-06-04 10:17:49 +02004229
4230 if (animation.extras.Type() != NULL_TYPE) {
4231 SerializeValue("extras", animation.extras, o);
4232 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004233}
4234
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004235static void SerializeGltfAsset(Asset &asset, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004236 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004237 SerializeStringProperty("generator", asset.generator, o);
4238 }
4239
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004240 if (!asset.version.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004241 SerializeStringProperty("version", asset.version, o);
4242 }
4243
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004244 if (asset.extras.Keys().size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004245 SerializeValue("extras", asset.extras, o);
4246 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004247
Selmar09d2ff12018-03-15 17:30:42 +01004248 SerializeExtensionMap(asset.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004249}
4250
johan bowald30c53472018-03-30 11:49:36 +02004251static void SerializeGltfBuffer(Buffer &buffer, json &o) {
4252 SerializeNumberProperty("byteLength", buffer.data.size(), o);
4253 SerializeGltfBufferData(buffer.data, o);
4254
4255 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02004256
4257 if (buffer.extras.Type() != NULL_TYPE) {
4258 SerializeValue("extras", buffer.extras, o);
4259 }
johan bowald30c53472018-03-30 11:49:36 +02004260}
4261
Selmar Koke4677492018-10-25 16:45:49 +02004262static bool SerializeGltfBuffer(Buffer &buffer, json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004263 const std::string &binFilename,
4264 const std::string &binBaseFilename) {
Selmar Koke4677492018-10-25 16:45:49 +02004265 if(!SerializeGltfBufferData(buffer.data, binFilename)) return false;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004266 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004267 SerializeStringProperty("uri", binBaseFilename, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004268
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004269 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02004270
4271 if (buffer.extras.Type() != NULL_TYPE) {
4272 SerializeValue("extras", buffer.extras, o);
4273 }
Selmar Koke4677492018-10-25 16:45:49 +02004274 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004275}
4276
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004277static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004278 SerializeNumberProperty("buffer", bufferView.buffer, o);
4279 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004280
Johan Bowaldfaa27222018-03-28 14:44:45 +02004281 // byteStride is optional, minimum allowed is 4
4282 if (bufferView.byteStride >= 4) {
4283 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
4284 }
4285 // byteOffset is optional, default is 0
4286 if (bufferView.byteOffset > 0) {
4287 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
4288 }
4289 // Target is optional, check if it contains a valid value
4290 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
4291 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
4292 SerializeNumberProperty("target", bufferView.target, o);
4293 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004294 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004295 SerializeStringProperty("name", bufferView.name, o);
4296 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02004297
4298 if (bufferView.extras.Type() != NULL_TYPE) {
4299 SerializeValue("extras", bufferView.extras, o);
4300 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004301}
4302
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004303static void SerializeGltfImage(Image &image, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004304 SerializeStringProperty("uri", image.uri, o);
4305
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004306 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004307 SerializeStringProperty("name", image.name, o);
4308 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02004309
4310 if (image.extras.Type() != NULL_TYPE) {
4311 SerializeValue("extras", image.extras, o);
4312 }
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09004313
4314 SerializeExtensionMap(image.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004315}
4316
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004317static void SerializeGltfMaterial(Material &material, json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004318 if (material.extras.Size()) SerializeValue("extras", material.extras, o);
Selmar09d2ff12018-03-15 17:30:42 +01004319 SerializeExtensionMap(material.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004320
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004321 if (material.values.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004322 json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004323 SerializeParameterMap(material.values, pbrMetallicRoughness);
Syoyo Fujita83675312017-12-02 21:14:13 +09004324 o["pbrMetallicRoughness"] = pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004325 }
4326
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004327 SerializeParameterMap(material.additionalValues, o);
4328
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004329 if (material.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004330 SerializeStringProperty("name", material.name, o);
4331 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02004332
4333 if (material.extras.Type() != NULL_TYPE) {
4334 SerializeValue("extras", material.extras, o);
4335 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004336}
4337
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004338static void SerializeGltfMesh(Mesh &mesh, json &o) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004339 json primitives;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004340 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004341 json primitive;
4342 json attributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004343 Primitive gltfPrimitive = mesh.primitives[i];
4344 for (std::map<std::string, int>::iterator attrIt =
4345 gltfPrimitive.attributes.begin();
4346 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
4347 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
4348 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004349
Syoyo Fujita83675312017-12-02 21:14:13 +09004350 primitive["attributes"] = attributes;
Johan Bowaldfaa27222018-03-28 14:44:45 +02004351
4352 // Indicies is optional
4353 if (gltfPrimitive.indices > -1) {
4354 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
4355 }
4356 // Material is optional
4357 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09004358 SerializeNumberProperty<int>("material", gltfPrimitive.material,
4359 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02004360 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004361 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004362
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004363 // Morph targets
4364 if (gltfPrimitive.targets.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004365 json targets;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004366 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004367 json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004368 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
4369 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
4370 attrIt != targetData.end(); ++attrIt) {
4371 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
4372 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004373 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004374
Syoyo Fujita83675312017-12-02 21:14:13 +09004375 targets.push_back(targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004376 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004377 primitive["targets"] = targets;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004378 }
4379
Jens Olssonb3af2f12018-06-04 10:17:49 +02004380 if (gltfPrimitive.extras.Type() != NULL_TYPE) {
4381 SerializeValue("extras", gltfPrimitive.extras, primitive);
4382 }
4383
Syoyo Fujita83675312017-12-02 21:14:13 +09004384 primitives.push_back(primitive);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004385 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004386
Syoyo Fujita83675312017-12-02 21:14:13 +09004387 o["primitives"] = primitives;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004388 if (mesh.weights.size()) {
4389 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
4390 }
4391
4392 if (mesh.name.size()) {
4393 SerializeStringProperty("name", mesh.name, o);
4394 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02004395
4396 if (mesh.extras.Type() != NULL_TYPE) {
4397 SerializeValue("extras", mesh.extras, o);
4398 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004399}
4400
Syoyo Fujita83675312017-12-02 21:14:13 +09004401static void SerializeGltfLight(Light &light, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004402 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
Emanuel Schrade186322b2017-11-06 11:14:41 +01004403 SerializeNumberArrayProperty("color", light.color, o);
4404 SerializeStringProperty("type", light.type, o);
4405}
4406
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004407static void SerializeGltfNode(Node &node, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004408 if (node.translation.size() > 0) {
4409 SerializeNumberArrayProperty<double>("translation", node.translation, o);
4410 }
4411 if (node.rotation.size() > 0) {
4412 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
4413 }
4414 if (node.scale.size() > 0) {
4415 SerializeNumberArrayProperty<double>("scale", node.scale, o);
4416 }
4417 if (node.matrix.size() > 0) {
4418 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
4419 }
4420 if (node.mesh != -1) {
4421 SerializeNumberProperty<int>("mesh", node.mesh, o);
4422 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004423
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004424 if (node.skin != -1) {
4425 SerializeNumberProperty<int>("skin", node.skin, o);
4426 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004427
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004428 if (node.camera != -1) {
4429 SerializeNumberProperty<int>("camera", node.camera, o);
4430 }
4431
Jens Olssona9718662018-05-24 15:48:49 +02004432 if (node.extras.Type() != NULL_TYPE) {
Jens Olssonb96f6962018-05-24 15:29:54 +02004433 SerializeValue("extras", node.extras, o);
4434 }
4435
Selmar09d2ff12018-03-15 17:30:42 +01004436 SerializeExtensionMap(node.extensions, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004437 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004438 SerializeNumberArrayProperty<int>("children", node.children, o);
4439}
4440
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004441static void SerializeGltfSampler(Sampler &sampler, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004442 SerializeNumberProperty("magFilter", sampler.magFilter, o);
4443 SerializeNumberProperty("minFilter", sampler.minFilter, o);
Selmar Koka3595482018-11-09 10:34:39 +01004444 SerializeNumberProperty("wrapR", sampler.wrapR, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004445 SerializeNumberProperty("wrapS", sampler.wrapS, o);
4446 SerializeNumberProperty("wrapT", sampler.wrapT, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02004447
4448 if (sampler.extras.Type() != NULL_TYPE) {
4449 SerializeValue("extras", sampler.extras, o);
4450 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004451}
4452
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004453static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004454 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004455 SerializeNumberProperty("zfar", camera.zfar, o);
4456 SerializeNumberProperty("znear", camera.znear, o);
4457 SerializeNumberProperty("xmag", camera.xmag, o);
4458 SerializeNumberProperty("ymag", camera.ymag, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02004459
4460 if (camera.extras.Type() != NULL_TYPE) {
4461 SerializeValue("extras", camera.extras, o);
4462 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004463}
4464
4465static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004466 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004467 SerializeNumberProperty("zfar", camera.zfar, o);
4468 SerializeNumberProperty("znear", camera.znear, o);
4469 if (camera.aspectRatio > 0) {
4470 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
4471 }
4472
4473 if (camera.yfov > 0) {
4474 SerializeNumberProperty("yfov", camera.yfov, o);
4475 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02004476
4477 if (camera.extras.Type() != NULL_TYPE) {
4478 SerializeValue("extras", camera.extras, o);
4479 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004480}
4481
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004482static void SerializeGltfCamera(const Camera &camera, json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004483 SerializeStringProperty("type", camera.type, o);
4484 if (!camera.name.empty()) {
Selmar Kokee3d0662018-10-08 16:20:43 +02004485 SerializeStringProperty("name", camera.name, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004486 }
4487
4488 if (camera.type.compare("orthographic") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004489 json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004490 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
Syoyo Fujita83675312017-12-02 21:14:13 +09004491 o["orthographic"] = orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004492 } else if (camera.type.compare("perspective") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004493 json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004494 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
Syoyo Fujita83675312017-12-02 21:14:13 +09004495 o["perspective"] = perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004496 } else {
4497 // ???
4498 }
4499}
4500
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004501static void SerializeGltfScene(Scene &scene, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004502 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
4503
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004504 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004505 SerializeStringProperty("name", scene.name, o);
4506 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02004507 if (scene.extras.Type() != NULL_TYPE) {
Selmar09d2ff12018-03-15 17:30:42 +01004508 SerializeValue("extras", scene.extras, o);
4509 }
4510 SerializeExtensionMap(scene.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004511}
4512
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004513static void SerializeGltfSkin(Skin &skin, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004514 if (skin.inverseBindMatrices != -1)
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004515 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
4516
4517 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
4518 SerializeNumberProperty("skeleton", skin.skeleton, o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004519 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004520 SerializeStringProperty("name", skin.name, o);
4521 }
4522}
4523
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004524static void SerializeGltfTexture(Texture &texture, json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02004525 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02004526 SerializeNumberProperty("sampler", texture.sampler, o);
4527 }
Selmar Kok8eb39042018-10-05 14:29:35 +02004528 if (texture.source > -1) {
4529 SerializeNumberProperty("source", texture.source, o);
4530 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02004531 if (texture.extras.Type() != NULL_TYPE) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004532 SerializeValue("extras", texture.extras, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004533 }
Selmar Kok9eae1102018-04-04 18:10:37 +02004534 SerializeExtensionMap(texture.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004535}
4536
Selmar Kok31cb7f92018-10-03 15:39:05 +02004537static bool WriteGltfFile(const std::string &output,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004538 const std::string &content) {
4539 std::ofstream gltfFile(output.c_str());
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004540 if (!gltfFile.is_open()) return false;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004541 gltfFile << content << std::endl;
Selmar Kok31cb7f92018-10-03 15:39:05 +02004542 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004543}
4544
David Harmonda9eac22018-08-30 08:06:05 -04004545static void WriteBinaryGltfFile(const std::string &output,
4546 const std::string &content) {
4547 std::ofstream gltfFile(output.c_str(), std::ios::binary);
4548
4549 const std::string header = "glTF";
4550 const int version = 2;
4551 const int padding_size = content.size() % 4;
4552
4553 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info, padding
4554 const int length = 12 + 8 + content.size() + padding_size;
4555
4556 gltfFile.write(header.c_str(), header.size());
4557 gltfFile.write(reinterpret_cast<const char *>(&version), sizeof(version));
4558 gltfFile.write(reinterpret_cast<const char *>(&length), sizeof(length));
4559
4560 // JSON chunk info, then JSON data
4561 const int model_length = content.size() + padding_size;
4562 const int model_format = 0x4E4F534A;
4563 gltfFile.write(reinterpret_cast<const char *>(&model_length), sizeof(model_length));
4564 gltfFile.write(reinterpret_cast<const char *>(&model_format), sizeof(model_format));
4565 gltfFile.write(content.c_str(), content.size());
4566
4567 // Chunk must be multiplies of 4, so pad with spaces
4568 if (padding_size > 0) {
4569 const std::string padding = std::string(padding_size, ' ');
4570 gltfFile.write(padding.c_str(), padding.size());
4571 }
4572}
4573
johan bowald642a3432018-04-01 12:37:18 +02004574bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
4575 bool embedImages = false,
Selmar Kokee3d0662018-10-08 16:20:43 +02004576 bool embedBuffers = false,
Syoyo Fujitab5726502018-11-10 20:07:15 +09004577 bool prettyPrint = true,
David Harmonda9eac22018-08-30 08:06:05 -04004578 bool writeBinary = false) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004579 json output;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004580
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004581 // ACCESSORS
Syoyo Fujita83675312017-12-02 21:14:13 +09004582 json accessors;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004583 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004584 json accessor;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004585 SerializeGltfAccessor(model->accessors[i], accessor);
Syoyo Fujita83675312017-12-02 21:14:13 +09004586 accessors.push_back(accessor);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004587 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004588 output["accessors"] = accessors;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004589
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004590 // ANIMATIONS
4591 if (model->animations.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004592 json animations;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004593 for (unsigned int i = 0; i < model->animations.size(); ++i) {
4594 if (model->animations[i].channels.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004595 json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004596 SerializeGltfAnimation(model->animations[i], animation);
Syoyo Fujita83675312017-12-02 21:14:13 +09004597 animations.push_back(animation);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004598 }
4599 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004600 output["animations"] = animations;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004601 }
4602
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004603 // ASSET
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004604 json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004605 SerializeGltfAsset(model->asset, asset);
Syoyo Fujita83675312017-12-02 21:14:13 +09004606 output["asset"] = asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004607
Selmar Kok7cb31e42018-10-05 16:02:29 +02004608 std::string defaultBinFilename = GetBaseFilename(filename);
4609 std::string defaultBinFileExt = ".bin";
4610 std::string::size_type pos = defaultBinFilename.rfind('.', defaultBinFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004611
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004612 if (pos != std::string::npos) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02004613 defaultBinFilename = defaultBinFilename.substr(0, pos);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004614 }
johan bowald642a3432018-04-01 12:37:18 +02004615 std::string baseDir = GetBaseDir(filename);
4616 if (baseDir.empty()) {
4617 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004618 }
4619
Selmar Kok7cb31e42018-10-05 16:02:29 +02004620 // BUFFERS
Selmar Kokc884e582018-10-05 16:25:54 +02004621 std::vector<std::string> usedUris;
Syoyo Fujita83675312017-12-02 21:14:13 +09004622 json buffers;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004623 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004624 json buffer;
johan bowald30c53472018-03-30 11:49:36 +02004625 if (embedBuffers) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09004626 SerializeGltfBuffer(model->buffers[i], buffer);
4627 } else {
Selmar Kok7cb31e42018-10-05 16:02:29 +02004628 std::string binSavePath;
Selmar Kokc884e582018-10-05 16:25:54 +02004629 std::string binUri;
Johan Bowald77decfa2018-11-06 14:28:20 +01004630 if (!model->buffers[i].uri.empty()
Selmar Kok7cb31e42018-10-05 16:02:29 +02004631 && !IsDataURI(model->buffers[i].uri)) {
4632 binUri = model->buffers[i].uri;
Selmar Kokc884e582018-10-05 16:25:54 +02004633 }
4634 else {
4635 binUri = defaultBinFilename + defaultBinFileExt;
4636 bool inUse = true;
Selmar Kok440cb1e2018-10-05 16:30:50 +02004637 int numUsed = 0;
Selmar Kokc884e582018-10-05 16:25:54 +02004638 while(inUse) {
4639 inUse = false;
4640 for (const std::string& usedName : usedUris) {
4641 if (binUri.compare(usedName) != 0) continue;
4642 inUse = true;
4643 binUri = defaultBinFilename + std::to_string(numUsed++) + defaultBinFileExt;
4644 break;
4645 }
4646 }
4647 }
4648 usedUris.push_back(binUri);
Selmar Kok7cb31e42018-10-05 16:02:29 +02004649 binSavePath = JoinPath(baseDir, binUri);
Selmar Koke4677492018-10-25 16:45:49 +02004650 if(!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
4651 binUri)) {
4652 return false;
4653 }
johan bowald30c53472018-03-30 11:49:36 +02004654 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004655 buffers.push_back(buffer);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004656 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004657 output["buffers"] = buffers;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004658
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004659 // BUFFERVIEWS
Syoyo Fujita83675312017-12-02 21:14:13 +09004660 json bufferViews;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004661 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004662 json bufferView;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004663 SerializeGltfBufferView(model->bufferViews[i], bufferView);
Syoyo Fujita83675312017-12-02 21:14:13 +09004664 bufferViews.push_back(bufferView);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004665 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004666 output["bufferViews"] = bufferViews;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004667
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004668 // Extensions used
4669 if (model->extensionsUsed.size()) {
4670 SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed,
4671 output);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004672 }
4673
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004674 // Extensions required
4675 if (model->extensionsRequired.size()) {
4676 SerializeStringArrayProperty("extensionsRequired",
4677 model->extensionsRequired, output);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004678 }
4679
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004680 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02004681 if (model->images.size()) {
4682 json images;
4683 for (unsigned int i = 0; i < model->images.size(); ++i) {
4684 json image;
johan bowald642a3432018-04-01 12:37:18 +02004685
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09004686 UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
Johan Bowald77decfa2018-11-06 14:28:20 +01004687 &this->WriteImageData, this->write_image_user_data_);
Johan Bowaldfaa27222018-03-28 14:44:45 +02004688 SerializeGltfImage(model->images[i], image);
4689 images.push_back(image);
4690 }
4691 output["images"] = images;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004692 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004693
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004694 // MATERIALS
Johan Bowaldfaa27222018-03-28 14:44:45 +02004695 if (model->materials.size()) {
4696 json materials;
4697 for (unsigned int i = 0; i < model->materials.size(); ++i) {
4698 json material;
4699 SerializeGltfMaterial(model->materials[i], material);
4700 materials.push_back(material);
4701 }
4702 output["materials"] = materials;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004703 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004704
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004705 // MESHES
Johan Bowaldfaa27222018-03-28 14:44:45 +02004706 if (model->meshes.size()) {
4707 json meshes;
4708 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
4709 json mesh;
4710 SerializeGltfMesh(model->meshes[i], mesh);
4711 meshes.push_back(mesh);
4712 }
4713 output["meshes"] = meshes;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004714 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004715
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004716 // NODES
Johan Bowaldfaa27222018-03-28 14:44:45 +02004717 if (model->nodes.size()) {
4718 json nodes;
4719 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
4720 json node;
4721 SerializeGltfNode(model->nodes[i], node);
4722 nodes.push_back(node);
4723 }
4724 output["nodes"] = nodes;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004725 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004726
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004727 // SCENE
Johan Bowaldfaa27222018-03-28 14:44:45 +02004728 if (model->defaultScene > -1) {
4729 SerializeNumberProperty<int>("scene", model->defaultScene, output);
4730 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004731
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004732 // SCENES
Johan Bowaldfaa27222018-03-28 14:44:45 +02004733 if (model->scenes.size()) {
4734 json scenes;
4735 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
4736 json currentScene;
4737 SerializeGltfScene(model->scenes[i], currentScene);
4738 scenes.push_back(currentScene);
4739 }
4740 output["scenes"] = scenes;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004741 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004742
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004743 // SKINS
4744 if (model->skins.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004745 json skins;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004746 for (unsigned int i = 0; i < model->skins.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004747 json skin;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004748 SerializeGltfSkin(model->skins[i], skin);
Syoyo Fujita83675312017-12-02 21:14:13 +09004749 skins.push_back(skin);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004750 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004751 output["skins"] = skins;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004752 }
4753
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004754 // TEXTURES
Johan Bowaldfaa27222018-03-28 14:44:45 +02004755 if (model->textures.size()) {
4756 json textures;
4757 for (unsigned int i = 0; i < model->textures.size(); ++i) {
4758 json texture;
4759 SerializeGltfTexture(model->textures[i], texture);
4760 textures.push_back(texture);
4761 }
4762 output["textures"] = textures;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004763 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004764
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004765 // SAMPLERS
Johan Bowaldfaa27222018-03-28 14:44:45 +02004766 if (model->samplers.size()) {
4767 json samplers;
4768 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
4769 json sampler;
4770 SerializeGltfSampler(model->samplers[i], sampler);
4771 samplers.push_back(sampler);
4772 }
4773 output["samplers"] = samplers;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004774 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004775
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004776 // CAMERAS
Johan Bowaldfaa27222018-03-28 14:44:45 +02004777 if (model->cameras.size()) {
4778 json cameras;
4779 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
4780 json camera;
4781 SerializeGltfCamera(model->cameras[i], camera);
4782 cameras.push_back(camera);
4783 }
4784 output["cameras"] = cameras;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004785 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004786
Selmar09d2ff12018-03-15 17:30:42 +01004787 // EXTENSIONS
4788 SerializeExtensionMap(model->extensions, output);
4789
Syoyo Fujitad5b02442018-03-17 16:12:42 -05004790 // LIGHTS as KHR_lights_cmn
Johan Bowaldfaa27222018-03-28 14:44:45 +02004791 if (model->lights.size()) {
4792 json lights;
4793 for (unsigned int i = 0; i < model->lights.size(); ++i) {
4794 json light;
4795 SerializeGltfLight(model->lights[i], light);
4796 lights.push_back(light);
4797 }
Syoyo Fujitad5b02442018-03-17 16:12:42 -05004798 json khr_lights_cmn;
4799 khr_lights_cmn["lights"] = lights;
4800 json ext_j;
4801
4802 if (output.find("extensions") != output.end()) {
4803 ext_j = output["extensions"];
4804 }
4805
4806 ext_j["KHR_lights_cmn"] = khr_lights_cmn;
4807
4808 output["extensions"] = ext_j;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004809 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01004810
Jens Olssonb3af2f12018-06-04 10:17:49 +02004811 // EXTRAS
4812 if (model->extras.Type() != NULL_TYPE) {
4813 SerializeValue("extras", model->extras, output);
4814 }
4815
David Harmonda9eac22018-08-30 08:06:05 -04004816 if (writeBinary) {
4817 WriteBinaryGltfFile(filename, output.dump());
4818 } else {
Selmar Kok27aab612018-11-30 18:01:31 +01004819 WriteGltfFile(filename, output.dump(prettyPrint ? 2 : -1));
David Harmonda9eac22018-08-30 08:06:05 -04004820 }
4821
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004822 return true;
4823}
4824
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09004825} // namespace tinygltf
4826
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004827#ifdef __clang__
4828#pragma clang diagnostic pop
4829#endif
4830
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004831#endif // TINYGLTF_IMPLEMENTATION