blob: b1ef164cb0f0ed2c8ef95ae73a14757bbaedc39d [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 Fujitaf6120152017-05-27 23:51:23 +090029// - v2.0.0 glTF 2.0!.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090030//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090031// Tiny glTF loader is using following third party libraries:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090032//
Syoyo Fujita2e21be72017-11-05 17:13:01 +090033// - jsonhpp: C++ JSON library.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090034// - base64: base64 decode/encode library.
35// - stb_image: Image loading library.
36//
Syoyo Fujita2840a0e2017-06-21 02:52:11 +090037#ifndef TINY_GLTF_H_
38#define TINY_GLTF_H_
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090039
Syoyo Fujitad42767e2018-03-15 21:52:00 -050040#include <array>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090041#include <cassert>
Syoyo Fujitad42767e2018-03-15 21:52:00 -050042#include <cstdint>
Syoyo Fujita7680abf2016-10-17 18:02:45 +090043#include <cstring>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090044#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090045#include <string>
46#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090047
48namespace tinygltf {
49
50#define TINYGLTF_MODE_POINTS (0)
51#define TINYGLTF_MODE_LINE (1)
52#define TINYGLTF_MODE_LINE_LOOP (2)
53#define TINYGLTF_MODE_TRIANGLES (4)
54#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
55#define TINYGLTF_MODE_TRIANGLE_FAN (6)
56
57#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
58#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
59#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
60#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
61#define TINYGLTF_COMPONENT_TYPE_INT (5124)
62#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
63#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
Syoyo Fujita476a8b22018-01-21 12:19:01 +090064#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090065
Syoyo Fujitac2615632016-06-19 21:56:06 +090066#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
67#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
68#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
69#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
70#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
71#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
72
Cemalettin Dervis246d8662017-12-07 20:29:51 +010073#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
Syoyo Fujitac2615632016-06-19 21:56:06 +090074#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -040075#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +090076
Luke San Antoniocdf4cb72016-06-14 21:32:11 -040077// Redeclarations of the above for technique.parameters.
78#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
79#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
80#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
81#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
82#define TINYGLTF_PARAMETER_TYPE_INT (5124)
83#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
84#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
85
86#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
87#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
88#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
89
90#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
91#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
92#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
93
94#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
95#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
96#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
97#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
98
99#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
100#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
101#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
102
103#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
104
105// End parameter types
106
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900107#define TINYGLTF_TYPE_VEC2 (2)
108#define TINYGLTF_TYPE_VEC3 (3)
109#define TINYGLTF_TYPE_VEC4 (4)
110#define TINYGLTF_TYPE_MAT2 (32 + 2)
111#define TINYGLTF_TYPE_MAT3 (32 + 3)
112#define TINYGLTF_TYPE_MAT4 (32 + 4)
113#define TINYGLTF_TYPE_SCALAR (64 + 1)
114#define TINYGLTF_TYPE_VECTOR (64 + 4)
115#define TINYGLTF_TYPE_MATRIX (64 + 16)
116
117#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
118#define TINYGLTF_IMAGE_FORMAT_PNG (1)
119#define TINYGLTF_IMAGE_FORMAT_BMP (2)
120#define TINYGLTF_IMAGE_FORMAT_GIF (3)
121
Luke San Antonio6d616f52016-06-23 14:09:23 -0400122#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
123#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900124#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400125#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
126#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
127
Syoyo Fujitabde70212016-02-07 17:38:17 +0900128#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
129#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
130
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900131#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
132#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
133
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400134#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
135#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
136
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900137typedef enum {
138 NULL_TYPE = 0,
139 NUMBER_TYPE = 1,
140 INT_TYPE = 2,
141 BOOL_TYPE = 3,
142 STRING_TYPE = 4,
143 ARRAY_TYPE = 5,
144 BINARY_TYPE = 6,
145 OBJECT_TYPE = 7
146} Type;
147
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500148static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900149 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
150 return 1;
151 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
152 return 1;
153 } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
154 return 2;
155 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
156 return 2;
157 } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
158 return 4;
159 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
160 return 4;
161 } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
162 return 4;
163 } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
164 return 8;
165 } else {
166 // Unknown componenty type
167 return -1;
168 }
169}
170
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500171static inline int32_t GetTypeSizeInBytes(uint32_t ty) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900172 if (ty == TINYGLTF_TYPE_SCALAR) {
173 return 1;
174 } else if (ty == TINYGLTF_TYPE_VEC2) {
175 return 2;
176 } else if (ty == TINYGLTF_TYPE_VEC3) {
177 return 3;
178 } else if (ty == TINYGLTF_TYPE_VEC4) {
179 return 4;
180 } else if (ty == TINYGLTF_TYPE_MAT2) {
181 return 4;
182 } else if (ty == TINYGLTF_TYPE_MAT3) {
183 return 9;
184 } else if (ty == TINYGLTF_TYPE_MAT4) {
185 return 16;
186 } else {
187 // Unknown componenty type
188 return -1;
189 }
190}
191
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900192#ifdef __clang__
193#pragma clang diagnostic push
194// Suppress warning for : static Value null_value
195// https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang
196#pragma clang diagnostic ignored "-Wexit-time-destructors"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900197#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900198#endif
199
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900200// Simple class to represent JSON object
201class Value {
202 public:
203 typedef std::vector<Value> Array;
204 typedef std::map<std::string, Value> Object;
205
206 Value() : type_(NULL_TYPE) {}
207
208 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900209 explicit Value(int i) : type_(INT_TYPE) { int_value_ = i; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900210 explicit Value(double n) : type_(NUMBER_TYPE) { number_value_ = n; }
211 explicit Value(const std::string &s) : type_(STRING_TYPE) {
212 string_value_ = s;
213 }
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900214 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900215 binary_value_.resize(n);
216 memcpy(binary_value_.data(), p, n);
217 }
218 explicit Value(const Array &a) : type_(ARRAY_TYPE) {
219 array_value_ = Array(a);
220 }
221 explicit Value(const Object &o) : type_(OBJECT_TYPE) {
222 object_value_ = Object(o);
223 }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900224
225 char Type() const { return static_cast<const char>(type_); }
226
227 bool IsBool() const { return (type_ == BOOL_TYPE); }
228
229 bool IsInt() const { return (type_ == INT_TYPE); }
230
231 bool IsNumber() const { return (type_ == NUMBER_TYPE); }
232
233 bool IsString() const { return (type_ == STRING_TYPE); }
234
235 bool IsBinary() const { return (type_ == BINARY_TYPE); }
236
237 bool IsArray() const { return (type_ == ARRAY_TYPE); }
238
239 bool IsObject() const { return (type_ == OBJECT_TYPE); }
240
241 // Accessor
242 template <typename T>
243 const T &Get() const;
244 template <typename T>
245 T &Get();
246
247 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900248 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900249 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900250 assert(IsArray());
251 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900252 return (static_cast<size_t>(idx) < array_value_.size())
253 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900254 : null_value;
255 }
256
257 // Lookup value from a key-value pair
258 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900259 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900260 assert(IsObject());
261 Object::const_iterator it = object_value_.find(key);
262 return (it != object_value_.end()) ? it->second : null_value;
263 }
264
265 size_t ArrayLen() const {
266 if (!IsArray()) return 0;
267 return array_value_.size();
268 }
269
270 // Valid only for object type.
271 bool Has(const std::string &key) const {
272 if (!IsObject()) return false;
273 Object::const_iterator it = object_value_.find(key);
274 return (it != object_value_.end()) ? true : false;
275 }
276
277 // List keys
278 std::vector<std::string> Keys() const {
279 std::vector<std::string> keys;
280 if (!IsObject()) return keys; // empty
281
282 for (Object::const_iterator it = object_value_.begin();
283 it != object_value_.end(); ++it) {
284 keys.push_back(it->first);
285 }
286
287 return keys;
288 }
289
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900290 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000291
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900292 protected:
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900293 int type_;
294
295 int int_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900296 double number_value_;
297 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900298 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900299 Array array_value_;
300 Object object_value_;
301 bool boolean_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900302};
303
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900304#ifdef __clang__
305#pragma clang diagnostic pop
306#endif
307
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900308#define TINYGLTF_VALUE_GET(ctype, var) \
309 template <> \
310 inline const ctype &Value::Get<ctype>() const { \
311 return var; \
312 } \
313 template <> \
314 inline ctype &Value::Get<ctype>() { \
315 return var; \
316 }
317TINYGLTF_VALUE_GET(bool, boolean_value_)
318TINYGLTF_VALUE_GET(double, number_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900319TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900320TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900321TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900322TINYGLTF_VALUE_GET(Value::Array, array_value_)
323TINYGLTF_VALUE_GET(Value::Object, object_value_)
324#undef TINYGLTF_VALUE_GET
325
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900326#ifdef __clang__
327#pragma clang diagnostic push
328#pragma clang diagnostic ignored "-Wc++98-compat"
329#pragma clang diagnostic ignored "-Wpadded"
330#endif
331
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500332/// Agregate object for representing a color
Arthur Brainville58453192018-01-08 18:25:52 +0100333using ColorValue = std::array<double, 4>;
334
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500335struct Parameter {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000336 bool bool_value;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700337 bool has_number_value = false;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900338 std::string string_value;
339 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000340 std::map<std::string, double> json_double_value;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700341 double number_value;
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500342 // context sensitive methods. depending the type of the Parameter you are
343 // accessing, these are either valid or not
344 // If this parameter represent a texture map in a material, will return the
345 // texture index
Arthur Brainville58453192018-01-08 18:25:52 +0100346
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500347 /// Return the index of a texture if this Parameter is a texture map.
348 /// Returned value is only valid if the parameter represent a texture from a
349 /// material
Arthur Brainville41fe7722018-01-08 18:32:48 +0100350 int TextureIndex() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100351 const auto it = json_double_value.find("index");
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500352 if (it != std::end(json_double_value)) {
Arthur Brainville58453192018-01-08 18:25:52 +0100353 return int(it->second);
354 }
355 return -1;
356 }
357
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500358 /// Material factor, like the roughness or metalness of a material
359 /// Returned value is only valid if the parameter represent a texture from a
360 /// material
Ben Buzbeef6af2242018-04-25 15:13:05 -0700361 double Factor() const { return number_value; }
Arthur Brainville58453192018-01-08 18:25:52 +0100362
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500363 /// Return the color of a material
364 /// Returned value is only valid if the parameter represent a texture from a
365 /// material
Arthur Brainville95853912018-01-08 18:37:44 +0100366 ColorValue ColorFactor() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100367 return {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500368 {// this agregate intialize the std::array object, and uses C++11 RVO.
369 number_array[0], number_array[1], number_array[2],
370 (number_array.size() > 3 ? number_array[3] : 1.0)}};
Arthur Brainville58453192018-01-08 18:25:52 +0100371 }
372};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900373
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900374#ifdef __clang__
375#pragma clang diagnostic pop
376#endif
377
378#ifdef __clang__
379#pragma clang diagnostic push
380#pragma clang diagnostic ignored "-Wpadded"
381#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900382
Syoyo Fujitabde70212016-02-07 17:38:17 +0900383typedef std::map<std::string, Parameter> ParameterMap;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200384typedef std::map<std::string, Value> ExtensionMap;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900385
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000386struct AnimationChannel {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900387 int sampler; // required
388 int target_node; // required (index of the node to target)
389 std::string target_path; // required in ["translation", "rotation", "scale",
390 // "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900391 Value extras;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900392
Syoyo Fujita5b407452017-06-04 17:42:41 +0900393 AnimationChannel() : sampler(-1), target_node(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000394};
395
396struct AnimationSampler {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900397 int input; // required
398 int output; // required
399 std::string interpolation; // in ["LINEAR", "STEP", "CATMULLROMSPLINE",
400 // "CUBICSPLINE"], default "LINEAR"
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000401
Syoyo Fujita5b407452017-06-04 17:42:41 +0900402 AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000403};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900404
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900405struct Animation {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900406 std::string name;
407 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000408 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900409 Value extras;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900410};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900411
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000412struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900413 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900414 int inverseBindMatrices; // required here but not in the spec
415 int skeleton; // The index of the node used as a skeleton root
416 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000417
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900418 Skin() {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000419 inverseBindMatrices = -1;
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +0000420 skeleton = -1;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000421 }
422};
423
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000424struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900425 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900426 int minFilter; // ["NEAREST", "LINEAR", "NEAREST_MIPMAP_LINEAR",
427 // "LINEAR_MIPMAP_NEAREST", "NEAREST_MIPMAP_LINEAR",
428 // "LINEAR_MIPMAP_LINEAR"]
429 int magFilter; // ["NEAREST", "LINEAR"]
430 int wrapS; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default
431 // "REPEAT"
432 int wrapT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default
433 // "REPEAT"
434 int wrapR; // TinyGLTF extension
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900435 Value extras;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900436
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000437 Sampler()
Cemalettin Dervis246d8662017-12-07 20:29:51 +0100438 : wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
439 wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000440};
441
Syoyo Fujita5b407452017-06-04 17:42:41 +0900442struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900443 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900444 int width;
445 int height;
446 int component;
447 std::vector<unsigned char> image;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900448 int bufferView; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500449 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
450 // "image/bmp", "image/gif"]
Squareys188965b2018-03-13 22:20:01 +0100451 std::string uri; // (required if no mimeType)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900452 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900453
Syoyo Fujita5b407452017-06-04 17:42:41 +0900454 Image() { bufferView = -1; }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000455};
456
457struct Texture {
458 int sampler;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900459 int source; // Required (not specified in the spec ?)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900460 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200461 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900462
Syoyo Fujita5b407452017-06-04 17:42:41 +0900463 Texture() : sampler(-1), source(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000464};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900465
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000466// Each extension should be stored in a ParameterMap.
467// members not in the values could be included in the ParameterMap
468// to keep a single material model
469struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900470 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900471
Syoyo Fujita5b407452017-06-04 17:42:41 +0900472 ParameterMap values; // PBR metal/roughness workflow
473 ParameterMap additionalValues; // normal/occlusion/emissive values
Selmar09d2ff12018-03-15 17:30:42 +0100474
475 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900476 Value extras;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000477};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900478
Syoyo Fujita5b407452017-06-04 17:42:41 +0900479struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900480 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900481 int buffer; // Required
482 size_t byteOffset; // minimum 0, default 0
483 size_t byteLength; // required, minimum 1
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900484 size_t byteStride; // minimum 4, maximum 252 (multiple of 4), default 0 =
485 // understood to be tightly packed
Syoyo Fujita5b407452017-06-04 17:42:41 +0900486 int target; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900487 Value extras;
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900488
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900489 BufferView() : byteOffset(0), byteStride(0) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000490};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900491
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000492struct Accessor {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900493 int bufferView; // optional in spec but required here since sparse accessor
494 // are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900495 std::string name;
496 size_t byteOffset;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900497 bool normalized; // optinal.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000498 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900499 size_t count; // required
500 int type; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900501 Value extras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000502
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900503 std::vector<double> minValues; // optional
504 std::vector<double> maxValues; // optional
505
506 // TODO(syoyo): "sparse"
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000507
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900508 ///
509 /// Utility function to compute byteStride for a given bufferView object.
510 /// Returns -1 upon invalid glTF value or parameter configuration.
511 ///
512 int ByteStride(const BufferView &bufferViewObject) const {
513 if (bufferViewObject.byteStride == 0) {
514 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500515 int componentSizeInBytes =
516 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900517 if (componentSizeInBytes <= 0) {
518 return -1;
519 }
520
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900521 int typeSizeInBytes = GetTypeSizeInBytes(static_cast<uint32_t>(type));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900522 if (typeSizeInBytes <= 0) {
523 return -1;
524 }
525
526 return componentSizeInBytes * typeSizeInBytes;
527 } else {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500528 // Check if byteStride is a mulple of the size of the accessor's component
529 // type.
530 int componentSizeInBytes =
531 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900532 if (componentSizeInBytes <= 0) {
533 return -1;
534 }
535
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900536 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900537 return -1;
538 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100539 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900540 }
541
542 return 0;
543 }
544
Syoyo Fujita5b407452017-06-04 17:42:41 +0900545 Accessor() { bufferView = -1; }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000546};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900547
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900548struct PerspectiveCamera {
549 float aspectRatio; // min > 0
550 float yfov; // required. min > 0
551 float zfar; // min > 0
552 float znear; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900553
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900554 PerspectiveCamera()
555 : aspectRatio(0.0f),
556 yfov(0.0f),
557 zfar(0.0f) // 0 = use infinite projecton matrix
558 ,
559 znear(0.0f) {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900560
Selmar09d2ff12018-03-15 17:30:42 +0100561 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900562 Value extras;
563};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000564
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900565struct OrthographicCamera {
566 float xmag; // required. must not be zero.
567 float ymag; // required. must not be zero.
568 float zfar; // required. `zfar` must be greater than `znear`.
Syoyo Fujita5b407452017-06-04 17:42:41 +0900569 float znear; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000570
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900571 OrthographicCamera() : xmag(0.0f), ymag(0.0f), zfar(0.0f), znear(0.0f) {}
572
Selmar09d2ff12018-03-15 17:30:42 +0100573 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900574 Value extras;
575};
576
577struct Camera {
578 std::string type; // required. "perspective" or "orthographic"
579 std::string name;
580
581 PerspectiveCamera perspective;
582 OrthographicCamera orthographic;
583
584 Camera() {}
585
Selmar09d2ff12018-03-15 17:30:42 +0100586 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000587 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900588};
589
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000590struct Primitive {
591 std::map<std::string, int> attributes; // (required) A dictionary object of
592 // integer, where each integer
593 // is the index of the accessor
594 // containing an attribute.
595 int material; // The index of the material to apply to this primitive
Syoyo Fujita5b407452017-06-04 17:42:41 +0900596 // when rendering.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000597 int indices; // The index of the accessor that contains the indices.
598 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900599 std::vector<std::map<std::string, int> > targets; // array of morph targets,
600 // where each target is a dict with attribues in ["POSITION, "NORMAL",
601 // "TANGENT"] pointing
602 // to their corresponding accessors
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000603 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900604
Syoyo Fujita5b407452017-06-04 17:42:41 +0900605 Primitive() {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000606 material = -1;
607 indices = -1;
608 }
609};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900610
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900611struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900612 std::string name;
613 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900614 std::vector<double> weights; // weights to be applied to the Morph Targets
615 std::vector<std::map<std::string, int> > targets;
Selmar09d2ff12018-03-15 17:30:42 +0100616 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900617 Value extras;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900618};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900619
620class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900621 public:
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900622 Node() : camera(-1), skin(-1), mesh(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000623
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900624 Node(const Node &rhs) {
625 camera = rhs.camera;
626
627 name = rhs.name;
628 skin = rhs.skin;
629 mesh = rhs.mesh;
630 children = rhs.children;
631 rotation = rhs.rotation;
632 scale = rhs.scale;
633 translation = rhs.translation;
634 matrix = rhs.matrix;
635 weights = rhs.weights;
636
Selmar09d2ff12018-03-15 17:30:42 +0100637 extensions = rhs.extensions;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900638 extras = rhs.extras;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900639 }
640
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900641 ~Node() {}
642
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000643 int camera; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900644
645 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000646 int skin;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000647 int mesh;
648 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900649 std::vector<double> rotation; // length must be 0 or 4
650 std::vector<double> scale; // length must be 0 or 3
651 std::vector<double> translation; // length must be 0 or 3
652 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +0900653 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -0700654
Selmar09d2ff12018-03-15 17:30:42 +0100655 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900656 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900657};
658
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900659struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900660 std::string name;
661 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900662 std::string
663 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900664 Value extras;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900665};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900666
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900667struct Asset {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900668 std::string version; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900669 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +0000670 std::string minVersion;
671 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +0100672 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900673 Value extras;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900674};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900675
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000676struct Scene {
677 std::string name;
678 std::vector<int> nodes;
679
Selmar09d2ff12018-03-15 17:30:42 +0100680 ExtensionMap extensions;
681 Value extras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000682};
683
Emanuel Schrade186322b2017-11-06 11:14:41 +0100684struct Light {
685 std::string name;
686 std::vector<double> color;
687 std::string type;
688};
689
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000690class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900691 public:
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000692 Model() {}
693 ~Model() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900694
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000695 std::vector<Accessor> accessors;
696 std::vector<Animation> animations;
697 std::vector<Buffer> buffers;
698 std::vector<BufferView> bufferViews;
699 std::vector<Material> materials;
700 std::vector<Mesh> meshes;
701 std::vector<Node> nodes;
702 std::vector<Texture> textures;
703 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000704 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000705 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900706 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000707 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +0100708 std::vector<Light> lights;
Selmar09d2ff12018-03-15 17:30:42 +0100709 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900710
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000711 int defaultScene;
712 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +0000713 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900714
715 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900716
717 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900718};
719
Syoyo Fujita0614eb82016-10-14 18:50:14 +0900720enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -0400721 NO_REQUIRE = 0x00,
722 REQUIRE_SCENE = 0x01,
723 REQUIRE_SCENES = 0x02,
724 REQUIRE_NODES = 0x04,
725 REQUIRE_ACCESSORS = 0x08,
726 REQUIRE_BUFFERS = 0x10,
727 REQUIRE_BUFFER_VIEWS = 0x20,
728 REQUIRE_ALL = 0x3f
729};
730
Squareysff644d82018-03-13 22:36:18 +0100731///
732/// LoadImageDataFunction type. Signature for custom image loading callbacks.
733///
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500734typedef bool (*LoadImageDataFunction)(Image *, std::string *, int, int,
735 const unsigned char *, int, void *);
Squareysff644d82018-03-13 22:36:18 +0100736
johan bowald642a3432018-04-01 12:37:18 +0200737///
738/// WriteImageDataFunction type. Signature for custom image writing callbacks.
739///
740typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
741 Image *, bool, void *);
742
Squareys2d3594d2018-03-13 22:40:53 +0100743#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +0100744// Declaration of default image loader callback
Arthur Brainvillecd366dd2018-03-19 19:40:04 +0100745bool LoadImageData(Image *image, std::string *err, int req_width,
746 int req_height, const unsigned char *bytes, int size,
747 void *);
Squareys2d3594d2018-03-13 22:40:53 +0100748#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900749
johan bowald642a3432018-04-01 12:37:18 +0200750#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
751// Declaration of default image writer callback
752bool WriteImageData(const std::string *basepath, const std::string *filename,
753 Image *image, bool embedImages, void *);
754#endif
755
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900756class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900757 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900758#ifdef __clang__
759#pragma clang diagnostic push
760#pragma clang diagnostic ignored "-Wc++98-compat"
761#endif
762
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500763 TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {}
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900764
765#ifdef __clang__
766#pragma clang diagnostic pop
767#endif
768
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900769 ~TinyGLTF() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900770
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900771 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900772 /// Loads glTF ASCII asset from a file.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900773 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900774 ///
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000775 bool LoadASCIIFromFile(Model *model, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400776 const std::string &filename,
777 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900778
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900779 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900780 /// Loads glTF ASCII asset from string(memory).
781 /// `length` = strlen(str);
782 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900783 ///
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000784 bool LoadASCIIFromString(Model *model, std::string *err, const char *str,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900785 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400786 const std::string &base_dir,
787 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900788
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900789 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900790 /// Loads glTF binary asset from a file.
791 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900792 ///
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000793 bool LoadBinaryFromFile(Model *model, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400794 const std::string &filename,
795 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900796
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900797 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900798 /// Loads glTF binary asset from memory.
799 /// `length` = strlen(str);
800 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900801 ///
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000802 bool LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900803 const unsigned char *bytes,
804 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400805 const std::string &base_dir = "",
806 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900807
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900808 ///
809 /// Write glTF to file.
810 ///
johan bowald642a3432018-04-01 12:37:18 +0200811 bool WriteGltfSceneToFile(Model *model, const std::string &filename,
812 bool embedImages,
813 bool embedBuffers /*, bool writeBinary*/);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000814
Squareysff644d82018-03-13 22:36:18 +0100815 ///
816 /// Set callback to use for loading image data
817 ///
818 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
819
johan bowald642a3432018-04-01 12:37:18 +0200820 ///
821 /// Set callback to use for writing image data
822 ///
823 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
824
Syoyo Fujitabeded612016-05-01 20:03:43 +0900825 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900826 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900827 /// Loads glTF asset from string(memory).
828 /// `length` = strlen(str);
829 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900830 ///
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000831 bool LoadFromString(Model *model, std::string *err, const char *str,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400832 const unsigned int length, const std::string &base_dir,
833 unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900834
Syoyo Fujitabeded612016-05-01 20:03:43 +0900835 const unsigned char *bin_data_;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900836 size_t bin_size_;
837 bool is_binary_;
Squareysff644d82018-03-13 22:36:18 +0100838
839 LoadImageDataFunction LoadImageData =
840#ifndef TINYGLTF_NO_STB_IMAGE
841 &tinygltf::LoadImageData;
842#else
843 nullptr;
844#endif
845 void *load_image_user_data_ = nullptr;
johan bowald642a3432018-04-01 12:37:18 +0200846
847 WriteImageDataFunction WriteImageData =
848#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
849 &tinygltf::WriteImageData;
850#else
851 nullptr;
852#endif
853 void *write_image_user_data_ = nullptr;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900854};
855
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900856#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500857#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900858#endif
859
Syoyo Fujita7c877972016-03-08 01:31:49 +0900860} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900861
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900862#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900863
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900864#ifdef TINYGLTF_IMPLEMENTATION
Syoyo Fujita7c877972016-03-08 01:31:49 +0900865#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900866//#include <cassert>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900867#include <fstream>
868#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +0900869
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900870#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +0900871// Disable some warnings for external files.
872#pragma clang diagnostic push
873#pragma clang diagnostic ignored "-Wfloat-equal"
874#pragma clang diagnostic ignored "-Wexit-time-destructors"
875#pragma clang diagnostic ignored "-Wconversion"
876#pragma clang diagnostic ignored "-Wold-style-cast"
877#pragma clang diagnostic ignored "-Wdouble-promotion"
878#pragma clang diagnostic ignored "-Wglobal-constructors"
879#pragma clang diagnostic ignored "-Wreserved-id-macro"
880#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
881#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +0900882#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +0900883#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +0900884#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
885#pragma clang diagnostic ignored "-Wswitch-enum"
886#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900887#pragma clang diagnostic ignored "-Wweak-vtables"
888#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujita2e21be72017-11-05 17:13:01 +0900889#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900890#pragma clang diagnostic ignored "-Wcomma"
891#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900892#if __has_warning("-Wzero-as-null-pointer-constant")
893#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
894#endif
895#if __has_warning("-Wcast-qual")
896#pragma clang diagnostic ignored "-Wcast-qual"
897#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +0900898#if __has_warning("-Wmissing-variable-declarations")
899#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
900#endif
901#if __has_warning("-Wmissing-prototypes")
902#pragma clang diagnostic ignored "-Wmissing-prototypes"
903#endif
904#if __has_warning("-Wcast-align")
905#pragma clang diagnostic ignored "-Wcast-align"
906#endif
907#if __has_warning("-Wnewline-eof")
908#pragma clang diagnostic ignored "-Wnewline-eof"
909#endif
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900910#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900911
Syoyo Fujita2e21be72017-11-05 17:13:01 +0900912#include "./json.hpp"
Squareys2d3594d2018-03-13 22:40:53 +0100913
914#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +0900915#include "./stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +0100916#endif
917
johan bowald642a3432018-04-01 12:37:18 +0200918#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
919#include "./stb_image_write.h"
920#endif
921
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900922#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +0900923#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900924#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900925
926#ifdef _WIN32
Syoyo Fujitaef2f49f2017-11-05 17:18:46 +0900927#include <windows.h>
Florian Märkld525e192017-09-22 15:25:48 +0200928#elif !defined(__ANDROID__)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900929#include <wordexp.h>
930#endif
931
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900932#if defined(__sparcv9)
933// Big endian
934#else
935#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
936#define TINYGLTF_LITTLE_ENDIAN 1
937#endif
938#endif
939
Syoyo Fujita2e21be72017-11-05 17:13:01 +0900940using nlohmann::json;
941
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900942#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500943#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +0900944#endif
945
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900946#ifdef __clang__
947#pragma clang diagnostic push
948#pragma clang diagnostic ignored "-Wc++98-compat"
949#endif
950
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +0900951namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900952
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900953static void swap4(unsigned int *val) {
954#ifdef TINYGLTF_LITTLE_ENDIAN
955 (void)val;
956#else
957 unsigned int tmp = *val;
958 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
959 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
960
961 dst[0] = src[3];
962 dst[1] = src[2];
963 dst[2] = src[1];
964 dst[3] = src[0];
965#endif
966}
967
Syoyo Fujita643ce102016-05-01 17:19:37 +0900968static bool FileExists(const std::string &abs_filename) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900969 bool ret;
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900970#ifdef _WIN32
971 FILE *fp;
972 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
973 if (err != 0) {
Syoyo Fujitabeded612016-05-01 20:03:43 +0900974 return false;
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900975 }
976#else
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900977 FILE *fp = fopen(abs_filename.c_str(), "rb");
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900978#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900979 if (fp) {
980 ret = true;
981 fclose(fp);
982 } else {
983 ret = false;
984 }
985
986 return ret;
987}
988
Syoyo Fujita643ce102016-05-01 17:19:37 +0900989static std::string ExpandFilePath(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900990#ifdef _WIN32
991 DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
992 char *str = new char[len];
993 ExpandEnvironmentStringsA(filepath.c_str(), str, len);
994
995 std::string s(str);
996
997 delete[] str;
998
999 return s;
1000#else
1001
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001002#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
1003 defined(__ANDROID__)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001004 // no expansion
1005 std::string s = filepath;
1006#else
1007 std::string s;
1008 wordexp_t p;
1009
1010 if (filepath.empty()) {
1011 return "";
1012 }
1013
1014 // char** w;
1015 int ret = wordexp(filepath.c_str(), &p, 0);
1016 if (ret) {
1017 // err
1018 s = filepath;
1019 return s;
1020 }
1021
1022 // Use first element only.
1023 if (p.we_wordv) {
1024 s = std::string(p.we_wordv[0]);
1025 wordfree(&p);
1026 } else {
1027 s = filepath;
1028 }
1029
1030#endif
1031
1032 return s;
1033#endif
1034}
1035
Syoyo Fujitabeded612016-05-01 20:03:43 +09001036static std::string JoinPath(const std::string &path0,
1037 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001038 if (path0.empty()) {
1039 return path1;
1040 } else {
1041 // check '/'
1042 char lastChar = *path0.rbegin();
1043 if (lastChar != '/') {
1044 return path0 + std::string("/") + path1;
1045 } else {
1046 return path0 + path1;
1047 }
1048 }
1049}
1050
Syoyo Fujita643ce102016-05-01 17:19:37 +09001051static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001052 const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001053 for (size_t i = 0; i < paths.size(); i++) {
1054 std::string absPath = ExpandFilePath(JoinPath(paths[i], filepath));
1055 if (FileExists(absPath)) {
1056 return absPath;
1057 }
1058 }
1059
1060 return std::string();
1061}
1062
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001063static std::string GetFilePathExtension(const std::string &FileName) {
johan bowald642a3432018-04-01 12:37:18 +02001064 if (FileName.find_last_of(".") != std::string::npos)
1065 return FileName.substr(FileName.find_last_of(".") + 1);
1066 return "";
1067}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001068
Syoyo Fujita643ce102016-05-01 17:19:37 +09001069static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001070 if (filepath.find_last_of("/\\") != std::string::npos)
1071 return filepath.substr(0, filepath.find_last_of("/\\"));
1072 return "";
1073}
1074
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001075// https://stackoverflow.com/questions/8520560/get-a-file-name-from-a-path
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001076static std::string GetBaseFilename(const std::string &filepath) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001077 return filepath.substr(filepath.find_last_of("/\\") + 1);
1078}
1079
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001080std::string base64_encode(unsigned char const *, unsigned int len);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001081std::string base64_decode(std::string const &s);
1082
1083/*
1084 base64.cpp and base64.h
1085
1086 Copyright (C) 2004-2008 René Nyffenegger
1087
1088 This source code is provided 'as-is', without any express or implied
1089 warranty. In no event will the author be held liable for any damages
1090 arising from the use of this software.
1091
1092 Permission is granted to anyone to use this software for any purpose,
1093 including commercial applications, and to alter it and redistribute it
1094 freely, subject to the following restrictions:
1095
1096 1. The origin of this source code must not be misrepresented; you must not
1097 claim that you wrote the original source code. If you use this source code
1098 in a product, an acknowledgment in the product documentation would be
1099 appreciated but is not required.
1100
1101 2. Altered source versions must be plainly marked as such, and must not be
1102 misrepresented as being the original source code.
1103
1104 3. This notice may not be removed or altered from any source distribution.
1105
1106 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
1107
1108*/
1109
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001110#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001111#pragma clang diagnostic push
1112#pragma clang diagnostic ignored "-Wexit-time-destructors"
1113#pragma clang diagnostic ignored "-Wglobal-constructors"
1114#pragma clang diagnostic ignored "-Wsign-conversion"
1115#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001116#endif
Syoyo Fujita7c877972016-03-08 01:31:49 +09001117static const std::string base64_chars =
1118 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1119 "abcdefghijklmnopqrstuvwxyz"
1120 "0123456789+/";
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001121
1122static inline bool is_base64(unsigned char c) {
1123 return (isalnum(c) || (c == '+') || (c == '/'));
1124}
1125
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001126std::string base64_encode(unsigned char const *bytes_to_encode,
1127 unsigned int in_len) {
johan bowald30c53472018-03-30 11:49:36 +02001128 std::string ret;
1129 int i = 0;
1130 int j = 0;
1131 unsigned char char_array_3[3];
1132 unsigned char char_array_4[4];
1133
1134 while (in_len--) {
1135 char_array_3[i++] = *(bytes_to_encode++);
1136 if (i == 3) {
1137 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001138 char_array_4[1] =
1139 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
1140 char_array_4[2] =
1141 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02001142 char_array_4[3] = char_array_3[2] & 0x3f;
1143
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001144 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
johan bowald30c53472018-03-30 11:49:36 +02001145 i = 0;
1146 }
1147 }
1148
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001149 if (i) {
1150 for (j = i; j < 3; j++) char_array_3[j] = '\0';
johan bowald30c53472018-03-30 11:49:36 +02001151
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001152 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
1153 char_array_4[1] =
1154 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
1155 char_array_4[2] =
1156 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02001157
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001158 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
johan bowald30c53472018-03-30 11:49:36 +02001159
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001160 while ((i++ < 3)) ret += '=';
johan bowald30c53472018-03-30 11:49:36 +02001161 }
1162
1163 return ret;
johan bowald30c53472018-03-30 11:49:36 +02001164}
1165
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001166std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +09001167 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001168 int i = 0;
1169 int j = 0;
1170 int in_ = 0;
1171 unsigned char char_array_4[4], char_array_3[3];
1172 std::string ret;
1173
1174 while (in_len-- && (encoded_string[in_] != '=') &&
1175 is_base64(encoded_string[in_])) {
1176 char_array_4[i++] = encoded_string[in_];
1177 in_++;
1178 if (i == 4) {
1179 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09001180 char_array_4[i] =
1181 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001182
1183 char_array_3[0] =
1184 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
1185 char_array_3[1] =
1186 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
1187 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
1188
Syoyo Fujita7c877972016-03-08 01:31:49 +09001189 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001190 i = 0;
1191 }
1192 }
1193
1194 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001195 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001196
1197 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09001198 char_array_4[j] =
1199 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001200
1201 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
1202 char_array_3[1] =
1203 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
1204 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
1205
Syoyo Fujita7c877972016-03-08 01:31:49 +09001206 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001207 }
1208
1209 return ret;
1210}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001211#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001212#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001213#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001214
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001215static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001216 const std::string &filename,
1217 const std::string &basedir, size_t reqBytes,
1218 bool checkSize) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001219 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001220
1221 std::vector<std::string> paths;
1222 paths.push_back(basedir);
1223 paths.push_back(".");
1224
1225 std::string filepath = FindFile(paths, filename);
jianghaosenb721fb72017-08-25 21:01:17 +08001226 if (filepath.empty() || filename.empty()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001227 if (err) {
Luke San Antonioda8b5d12016-06-14 22:17:40 -04001228 (*err) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001229 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001230 return false;
1231 }
1232
1233 std::ifstream f(filepath.c_str(), std::ifstream::binary);
1234 if (!f) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001235 if (err) {
Luke San Antonioda8b5d12016-06-14 22:17:40 -04001236 (*err) += "File open error : " + filepath + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001237 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001238 return false;
1239 }
1240
1241 f.seekg(0, f.end);
Syoyo Fujita643ce102016-05-01 17:19:37 +09001242 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09001243 if (int(sz) < 0) {
1244 // Looks reading directory, not a file.
1245 return false;
1246 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09001247
1248 if (sz == 0) {
1249 // Invalid file size.
1250 return false;
1251 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001252 std::vector<unsigned char> buf(sz);
1253
1254 f.seekg(0, f.beg);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001255 f.read(reinterpret_cast<char *>(&buf.at(0)),
1256 static_cast<std::streamsize>(sz));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001257 f.close();
1258
1259 if (checkSize) {
1260 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001261 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001262 return true;
1263 } else {
1264 std::stringstream ss;
1265 ss << "File size mismatch : " << filepath << ", requestedBytes "
1266 << reqBytes << ", but got " << sz << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001267 if (err) {
1268 (*err) += ss.str();
1269 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001270 return false;
1271 }
1272 }
1273
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001274 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001275 return true;
1276}
1277
Squareysff644d82018-03-13 22:36:18 +01001278void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001279 LoadImageData = func;
1280 load_image_user_data_ = user_data;
Squareysff644d82018-03-13 22:36:18 +01001281}
1282
Squareys2d3594d2018-03-13 22:40:53 +01001283#ifndef TINYGLTF_NO_STB_IMAGE
Arthur Brainvillecd366dd2018-03-19 19:40:04 +01001284bool LoadImageData(Image *image, std::string *err, int req_width,
1285 int req_height, const unsigned char *bytes, int size,
1286 void *) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001287 int w, h, comp;
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00001288 // if image cannot be decoded, ignore parsing and keep it by its path
1289 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +09001290 // FIXME we should only enter this function if the image is embedded. If
1291 // image->uri references
1292 // an image file, it should be left as it is. Image loading should not be
1293 // mandatory (to support other formats)
Syoyo Fujitabeded612016-05-01 20:03:43 +09001294 unsigned char *data = stbi_load_from_memory(bytes, size, &w, &h, &comp, 0);
1295 if (!data) {
1296 if (err) {
1297 (*err) += "Unknown image format.\n";
1298 }
Omar C. Fd492efc2018-02-10 09:50:35 +02001299 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001300 }
1301
1302 if (w < 1 || h < 1) {
Syoyo Fujita4be2f882016-11-24 16:20:34 +09001303 free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001304 if (err) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09001305 (*err) += "Invalid image data.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001306 }
Omar C. Fd492efc2018-02-10 09:50:35 +02001307 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001308 }
1309
1310 if (req_width > 0) {
1311 if (req_width != w) {
Syoyo Fujita4be2f882016-11-24 16:20:34 +09001312 free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001313 if (err) {
1314 (*err) += "Image width mismatch.\n";
1315 }
1316 return false;
1317 }
1318 }
1319
1320 if (req_height > 0) {
1321 if (req_height != h) {
Syoyo Fujita4be2f882016-11-24 16:20:34 +09001322 free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001323 if (err) {
1324 (*err) += "Image height mismatch.\n";
1325 }
1326 return false;
1327 }
1328 }
1329
1330 image->width = w;
1331 image->height = h;
1332 image->component = comp;
1333 image->image.resize(static_cast<size_t>(w * h * comp));
1334 std::copy(data, data + w * h * comp, image->image.begin());
1335
Syoyo Fujita4be2f882016-11-24 16:20:34 +09001336 free(data);
1337
Syoyo Fujitabeded612016-05-01 20:03:43 +09001338 return true;
1339}
Squareys2d3594d2018-03-13 22:40:53 +01001340#endif
Syoyo Fujitabeded612016-05-01 20:03:43 +09001341
johan bowald642a3432018-04-01 12:37:18 +02001342void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
1343 WriteImageData = func;
1344 write_image_user_data_ = user_data;
1345}
1346
1347#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1348static void WriteToMemory_stbi(void *context, void *data, int size) {
1349 std::vector<unsigned char> *buffer =
1350 reinterpret_cast<std::vector<unsigned char> *>(context);
1351
1352 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
1353
1354 buffer->insert(buffer->end(), pData, pData + size);
1355}
1356
1357bool WriteImageData(const std::string *basepath, const std::string *filename,
1358 Image *image, bool embedImages, void *) {
1359 const std::string ext = GetFilePathExtension(*filename);
1360
1361 if (embedImages) {
1362 // Write image to memory and embed in output
1363 std::string header;
1364 std::vector<unsigned char> data;
1365
1366 if (ext == "png") {
1367 stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
1368 image->height, image->component, &image->image[0],
1369 0);
1370 header = "data:image/png;base64,";
1371 } else if (ext == "jpg") {
1372 stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
1373 image->height, image->component, &image->image[0],
1374 100);
1375 header = "data:image/jpeg;base64,";
1376 } else if (ext == "bmp") {
1377 stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
1378 image->height, image->component, &image->image[0]);
1379 header = "data:image/bmp;base64,";
1380 }
1381
1382 if (data.size()) {
1383 image->uri =
1384 header +
1385 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
1386 } else {
1387 // Throw error?
1388 }
1389 } else {
1390 // Write image to disc
1391
1392 const std::string imagefilepath = JoinPath(*basepath, *filename);
1393 if (ext == "png") {
1394 stbi_write_png(imagefilepath.c_str(), image->width, image->height,
1395 image->component, &image->image[0], 0);
1396 } else if (ext == "jpg") {
1397 // TODO (Bowald): Give user the option to set output quality?
1398 const int quality = 100;
1399 stbi_write_jpg(imagefilepath.c_str(), image->width, image->height,
1400 image->component, &image->image[0], quality);
1401 } else if (ext == "bmp") {
1402 stbi_write_bmp(imagefilepath.c_str(), image->width, image->height,
1403 image->component, &image->image[0]);
1404 } else {
1405 // Throw error? Cant output requested format.
1406 }
1407 image->uri = *filename;
1408 }
1409
1410 return true;
1411}
1412#endif
1413
1414static std::string MimeToExt(const std::string &mimeType) {
1415 if (mimeType == "image/jpeg") {
1416 return "jpg";
1417 } else if (mimeType == "image/png") {
1418 return "png";
1419 } else if (mimeType == "image/bmp") {
1420 return "bmp";
1421 } else if (mimeType == "image/gif") {
1422 return "gif";
1423 }
1424
1425 return "";
1426}
1427
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001428static void UpdateImageObject(Image &image, std::string &baseDir, int index,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001429 bool embedImages,
1430 WriteImageDataFunction *WriteImageData = nullptr,
1431 void *user_data = nullptr) {
johan bowald642a3432018-04-01 12:37:18 +02001432 std::string filename;
1433 std::string ext;
1434
1435 // If image have uri. Use it it as a filename
1436 if (image.uri.size()) {
1437 filename = GetBaseFilename(image.uri);
1438 ext = GetFilePathExtension(filename);
1439
1440 } else if (image.name.size()) {
1441 ext = MimeToExt(image.mimeType);
1442 // Otherwise use name as filename
1443 filename = image.name + "." + ext;
1444 } else {
1445 ext = MimeToExt(image.mimeType);
1446 // Fallback to index of image as filename
1447 filename = std::to_string(index) + "." + ext;
1448 }
1449
1450 // If callback is set, modify image data object
1451 if (*WriteImageData != nullptr) {
1452 std::string uri;
1453 (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
1454 }
1455}
1456
Syoyo Fujita643ce102016-05-01 17:19:37 +09001457static bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001458 std::string header = "data:application/octet-stream;base64,";
1459 if (in.find(header) == 0) {
1460 return true;
1461 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001462
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02001463 header = "data:image/jpeg;base64,";
1464 if (in.find(header) == 0) {
1465 return true;
1466 }
Squareys43374632018-03-13 22:20:48 +01001467
Syoyo Fujita620eed12016-01-02 23:37:12 +09001468 header = "data:image/png;base64,";
1469 if (in.find(header) == 0) {
1470 return true;
1471 }
Squareys43374632018-03-13 22:20:48 +01001472
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02001473 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001474 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02001475 return true;
1476 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001477
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02001478 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001479 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09001480 return true;
1481 }
1482
Luke San Antoniocaa24b02016-06-15 02:23:09 -04001483 header = "data:text/plain;base64,";
1484 if (in.find(header) == 0) {
1485 return true;
1486 }
1487
Syoyo Fujita20244e12018-03-15 11:01:05 -05001488 header = "data:application/gltf-buffer;base64,";
1489 if (in.find(header) == 0) {
1490 return true;
1491 }
1492
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001493 return false;
1494}
1495
Syoyo Fujitabeded612016-05-01 20:03:43 +09001496static bool DecodeDataURI(std::vector<unsigned char> *out,
johan bowald642a3432018-04-01 12:37:18 +02001497 std::string &mime_type, const std::string &in,
1498 size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001499 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09001500 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001501 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001502 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001503 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001504
1505 if (data.empty()) {
1506 header = "data:image/jpeg;base64,";
1507 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02001508 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09001509 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09001510 }
1511 }
1512
1513 if (data.empty()) {
1514 header = "data:image/png;base64,";
1515 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02001516 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09001517 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09001518 }
1519 }
Squareys43374632018-03-13 22:20:48 +01001520
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02001521 if (data.empty()) {
1522 header = "data:image/bmp;base64,";
1523 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02001524 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02001525 data = base64_decode(in.substr(header.size())); // cut mime string.
1526 }
1527 }
1528
1529 if (data.empty()) {
1530 header = "data:image/gif;base64,";
1531 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02001532 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02001533 data = base64_decode(in.substr(header.size())); // cut mime string.
1534 }
1535 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001536
1537 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04001538 header = "data:text/plain;base64,";
1539 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02001540 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04001541 data = base64_decode(in.substr(header.size()));
1542 }
1543 }
1544
1545 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05001546 header = "data:application/gltf-buffer;base64,";
1547 if (in.find(header) == 0) {
1548 data = base64_decode(in.substr(header.size()));
1549 }
1550 }
1551
1552 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09001553 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09001554 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001555
1556 if (checkSize) {
1557 if (data.size() != reqBytes) {
1558 return false;
1559 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001560 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09001561 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001562 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09001563 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001564 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09001565 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001566}
1567
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001568static bool ParseJsonAsValue(Value *ret, const json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02001569 Value val{};
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001570 switch (o.type()) {
1571 case json::value_t::object: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02001572 Value::Object value_object;
1573 for (auto it = o.begin(); it != o.end(); it++) {
1574 Value entry;
1575 ParseJsonAsValue(&entry, it.value());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001576 if (entry.Type() != NULL_TYPE) value_object[it.key()] = entry;
Selmar Kokfa7022f2018-04-04 18:10:20 +02001577 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001578 if (value_object.size() > 0) val = Value(value_object);
1579 } break;
1580 case json::value_t::array: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02001581 Value::Array value_array;
1582 for (auto it = o.begin(); it != o.end(); it++) {
1583 Value entry;
1584 ParseJsonAsValue(&entry, it.value());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001585 if (entry.Type() != NULL_TYPE) value_array.push_back(entry);
Selmar Kokfa7022f2018-04-04 18:10:20 +02001586 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001587 if (value_array.size() > 0) val = Value(value_array);
1588 } break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02001589 case json::value_t::string:
1590 val = Value(o.get<std::string>());
1591 break;
1592 case json::value_t::boolean:
1593 val = Value(o.get<bool>());
1594 break;
1595 case json::value_t::number_integer:
1596 case json::value_t::number_unsigned:
1597 val = Value(static_cast<int>(o.get<int64_t>()));
1598 break;
1599 case json::value_t::number_float:
1600 val = Value(o.get<double>());
1601 break;
1602 case json::value_t::null:
1603 case json::value_t::discarded:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001604 // default:
Selmar Kokfa7022f2018-04-04 18:10:20 +02001605 break;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001606 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001607 if (ret) *ret = val;
1608
Selmar Kokfa7022f2018-04-04 18:10:20 +02001609 return val.Type() != NULL_TYPE;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001610}
1611
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001612static bool ParseExtrasProperty(Value *ret, const json &o) {
1613 json::const_iterator it = o.find("extras");
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001614 if (it == o.end()) {
1615 return false;
1616 }
1617
Selmar Kokfa7022f2018-04-04 18:10:20 +02001618 return ParseJsonAsValue(ret, it.value());
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001619}
1620
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001621static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09001622 const std::string &property,
1623 const bool required,
1624 const std::string &parent_node = "") {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001625 json::const_iterator it = o.find(property);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001626 if (it == o.end()) {
1627 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001628 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09001629 (*err) += "'" + property + "' property is missing";
1630 if (!parent_node.empty()) {
1631 (*err) += " in " + parent_node;
1632 }
1633 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001634 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001635 }
1636 return false;
1637 }
1638
Syoyo Fujita83675312017-12-02 21:14:13 +09001639 if (!it.value().is_boolean()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001640 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001641 if (err) {
1642 (*err) += "'" + property + "' property is not a bool type.\n";
1643 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001644 }
1645 return false;
1646 }
1647
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001648 if (ret) {
Syoyo Fujita83675312017-12-02 21:14:13 +09001649 (*ret) = it.value().get<bool>();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001650 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001651
1652 return true;
1653}
1654
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001655static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09001656 const std::string &property,
1657 const bool required,
1658 const std::string &parent_node = "") {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001659 json::const_iterator it = o.find(property);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001660 if (it == o.end()) {
1661 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001662 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001663 (*err) += "'" + property + "' property is missing";
1664 if (!parent_node.empty()) {
1665 (*err) += " in " + parent_node;
1666 }
1667 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001668 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001669 }
1670 return false;
1671 }
1672
Syoyo Fujita83675312017-12-02 21:14:13 +09001673 if (!it.value().is_number()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001674 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001675 if (err) {
1676 (*err) += "'" + property + "' property is not a number type.\n";
1677 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001678 }
1679 return false;
1680 }
1681
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001682 if (ret) {
Syoyo Fujita83675312017-12-02 21:14:13 +09001683 (*ret) = it.value().get<double>();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001684 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001685
1686 return true;
1687}
1688
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001689static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001690 const json &o, const std::string &property,
1691 bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001692 const std::string &parent_node = "") {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001693 json::const_iterator it = o.find(property);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001694 if (it == o.end()) {
1695 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001696 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001697 (*err) += "'" + property + "' property is missing";
1698 if (!parent_node.empty()) {
1699 (*err) += " in " + parent_node;
1700 }
1701 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001702 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001703 }
1704 return false;
1705 }
1706
Syoyo Fujita83675312017-12-02 21:14:13 +09001707 if (!it.value().is_array()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001708 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001709 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001710 (*err) += "'" + property + "' property is not an array";
1711 if (!parent_node.empty()) {
1712 (*err) += " in " + parent_node;
1713 }
1714 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001715 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001716 }
1717 return false;
1718 }
1719
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001720 ret->clear();
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001721 for (json::const_iterator i = it.value().begin(); i != it.value().end();
1722 i++) {
Syoyo Fujita83675312017-12-02 21:14:13 +09001723 if (!i.value().is_number()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001724 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001725 if (err) {
1726 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001727 if (!parent_node.empty()) {
1728 (*err) += " in " + parent_node;
1729 }
1730 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001731 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001732 }
1733 return false;
1734 }
Syoyo Fujita83675312017-12-02 21:14:13 +09001735 ret->push_back(i.value());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001736 }
1737
1738 return true;
1739}
1740
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001741static bool ParseStringProperty(
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001742 std::string *ret, std::string *err, const json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001743 const std::string &property, bool required,
1744 const std::string &parent_node = std::string()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001745 json::const_iterator it = o.find(property);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001746 if (it == o.end()) {
1747 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001748 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001749 (*err) += "'" + property + "' property is missing";
1750 if (parent_node.empty()) {
1751 (*err) += ".\n";
1752 } else {
1753 (*err) += " in `" + parent_node + "'.\n";
1754 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001755 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001756 }
1757 return false;
1758 }
1759
Syoyo Fujita83675312017-12-02 21:14:13 +09001760 if (!it.value().is_string()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001761 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001762 if (err) {
1763 (*err) += "'" + property + "' property is not a string type.\n";
1764 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001765 }
1766 return false;
1767 }
1768
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001769 if (ret) {
Syoyo Fujita83675312017-12-02 21:14:13 +09001770 (*ret) = it.value();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001771 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001772
1773 return true;
1774}
1775
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001776static bool ParseStringIntProperty(std::map<std::string, int> *ret,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001777 std::string *err, const json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001778 const std::string &property, bool required,
1779 const std::string &parent = "") {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001780 json::const_iterator it = o.find(property);
Luke San Antonio19894c72016-06-14 21:19:51 -04001781 if (it == o.end()) {
1782 if (required) {
1783 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09001784 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001785 (*err) +=
1786 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09001787 } else {
1788 (*err) += "'" + property + "' property is missing.\n";
1789 }
Luke San Antonio19894c72016-06-14 21:19:51 -04001790 }
1791 }
1792 return false;
1793 }
1794
1795 // Make sure we are dealing with an object / dictionary.
Syoyo Fujita83675312017-12-02 21:14:13 +09001796 if (!it.value().is_object()) {
Luke San Antonio19894c72016-06-14 21:19:51 -04001797 if (required) {
1798 if (err) {
1799 (*err) += "'" + property + "' property is not an object.\n";
1800 }
1801 }
1802 return false;
1803 }
1804
1805 ret->clear();
Syoyo Fujita83675312017-12-02 21:14:13 +09001806 const json &dict = it.value();
Luke San Antonio19894c72016-06-14 21:19:51 -04001807
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001808 json::const_iterator dictIt(dict.begin());
1809 json::const_iterator dictItEnd(dict.end());
Luke San Antonio19894c72016-06-14 21:19:51 -04001810
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001811 for (; dictIt != dictItEnd; ++dictIt) {
Syoyo Fujita83675312017-12-02 21:14:13 +09001812 if (!dictIt.value().is_number()) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001813 if (required) {
1814 if (err) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001815 (*err) += "'" + property + "' value is not an int.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04001816 }
1817 }
1818 return false;
1819 }
1820
1821 // Insert into the list.
Syoyo Fujita83675312017-12-02 21:14:13 +09001822 (*ret)[dictIt.key()] = static_cast<int>(dictIt.value());
Luke San Antonio19894c72016-06-14 21:19:51 -04001823 }
1824 return true;
1825}
1826
Syoyo Fujita5b407452017-06-04 17:42:41 +09001827static bool ParseJSONProperty(std::map<std::string, double> *ret,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001828 std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09001829 const std::string &property, bool required) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001830 json::const_iterator it = o.find(property);
Syoyo Fujita5b407452017-06-04 17:42:41 +09001831 if (it == o.end()) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001832 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09001833 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001834 (*err) += "'" + property + "' property is missing. \n'";
1835 }
1836 }
1837 return false;
1838 }
1839
Syoyo Fujita83675312017-12-02 21:14:13 +09001840 if (!it.value().is_object()) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001841 if (required) {
1842 if (err) {
1843 (*err) += "'" + property + "' property is not a JSON object.\n";
1844 }
1845 }
1846 return false;
1847 }
1848
1849 ret->clear();
Syoyo Fujita83675312017-12-02 21:14:13 +09001850 const json &obj = it.value();
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001851 json::const_iterator it2(obj.begin());
1852 json::const_iterator itEnd(obj.end());
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001853 for (; it2 != itEnd; it2++) {
Syoyo Fujita83675312017-12-02 21:14:13 +09001854 if (it2.value().is_number())
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001855 ret->insert(std::pair<std::string, double>(it2.key(), it2.value()));
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001856 }
1857
1858 return true;
1859}
1860
Selmar09d2ff12018-03-15 17:30:42 +01001861static bool ParseParameterProperty(Parameter *param, std::string *err,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001862 const json &o, const std::string &prop,
1863 bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01001864 // A parameter value can either be a string or an array of either a boolean or
1865 // a number. Booleans of any kind aren't supported here. Granted, it
1866 // complicates the Parameter structure and breaks it semantically in the sense
1867 // that the client probably works off the assumption that if the string is
1868 // empty the vector is used, etc. Would a tagged union work?
1869 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
1870 // Found string property.
1871 return true;
1872 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
1873 false)) {
1874 // Found a number array.
1875 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07001876 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
1877 return param->has_number_value = true;
Selmar09d2ff12018-03-15 17:30:42 +01001878 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
1879 false)) {
1880 return true;
1881 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
1882 return true;
1883 } else {
1884 if (required) {
1885 if (err) {
1886 (*err) += "parameter must be a string or number / number array.\n";
1887 }
1888 }
1889 return false;
1890 }
1891}
1892
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001893static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
1894 const json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09001895 (void)err;
1896
Selmar09d2ff12018-03-15 17:30:42 +01001897 json::const_iterator it = o.find("extensions");
1898 if (it == o.end()) {
1899 return false;
1900 }
1901 if (!it.value().is_object()) {
1902 return false;
1903 }
1904 ExtensionMap extensions;
1905 json::const_iterator extIt = it.value().begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001906 for (; extIt != it.value().end(); extIt++) {
1907 if (!extIt.value().is_object()) continue;
Selmar Kokfa7022f2018-04-04 18:10:20 +02001908 ParseJsonAsValue(&extensions[extIt.key()], extIt.value());
Selmar09d2ff12018-03-15 17:30:42 +01001909 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001910 if (ret) {
Selmar09d2ff12018-03-15 17:30:42 +01001911 (*ret) = extensions;
1912 }
1913 return true;
1914}
1915
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001916static bool ParseAsset(Asset *asset, std::string *err, const json &o) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09001917 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
1918 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
1919 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001920
Selmar09d2ff12018-03-15 17:30:42 +01001921 ParseExtensionsProperty(&asset->extensions, err, o);
1922
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001923 // Unity exporter version is added as extra here
1924 ParseExtrasProperty(&(asset->extras), o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001925
1926 return true;
1927}
1928
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001929static bool ParseImage(Image *image, std::string *err, const json &o,
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001930 const std::string &basedir,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001931 LoadImageDataFunction *LoadImageData = nullptr,
Squareysff644d82018-03-13 22:36:18 +01001932 void *user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001933 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001934
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09001935 // schema says oneOf [`bufferView`, `uri`]
1936 // TODO(syoyo): Check the type of each parameters.
1937 bool hasBufferView = (o.find("bufferView") != o.end());
1938 bool hasURI = (o.find("uri") != o.end());
1939
1940 if (hasBufferView && hasURI) {
1941 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001942 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001943 (*err) +=
1944 "Only one of `bufferView` or `uri` should be defined, but both are "
1945 "defined for Image.\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09001946 }
1947 return false;
1948 }
1949
1950 if (!hasBufferView && !hasURI) {
1951 if (err) {
1952 (*err) += "Neither required `bufferView` nor `uri` defined for Image.\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001953 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001954 return false;
1955 }
1956
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001957 ParseStringProperty(&image->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001958
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09001959 if (hasBufferView) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09001960 double bufferView = -1;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09001961 if (!ParseNumberProperty(&bufferView, err, o, "bufferView", true)) {
1962 if (err) {
Syoyo Fujitaba28ddc2018-03-21 20:05:11 +09001963 (*err) += "Failed to parse `bufferView` for Image.\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09001964 }
1965 return false;
1966 }
1967
1968 std::string mime_type;
1969 ParseStringProperty(&mime_type, err, o, "mimeType", false);
1970
1971 double width = 0.0;
1972 ParseNumberProperty(&width, err, o, "width", false);
1973
1974 double height = 0.0;
1975 ParseNumberProperty(&height, err, o, "height", false);
1976
1977 // Just only save some information here. Loading actual image data from
1978 // bufferView is done after this `ParseImage` function.
1979 image->bufferView = static_cast<int>(bufferView);
1980 image->mimeType = mime_type;
1981 image->width = static_cast<int>(width);
1982 image->height = static_cast<int>(height);
1983
1984 return true;
1985 }
1986
Syoyo Fujita246654a2018-03-21 20:32:22 +09001987 // Parse URI & Load image data.
1988
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09001989 std::string uri;
1990 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09001991 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
1992 if (err) {
1993 (*err) += "Failed to parse `uri` for Image.\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09001994 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09001995 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09001996 }
1997
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001998 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001999
Syoyo Fujita246654a2018-03-21 20:32:22 +09002000 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02002001 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09002002 if (err) {
2003 (*err) += "Failed to decode 'uri' for image parameter.\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002004 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09002005 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002006 }
2007 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09002008 // Assume external file
2009 // Keep texture path (for textures that cannot be decoded)
2010 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01002011#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09002012 return true;
Selmar67af3c92018-03-16 11:48:19 +01002013#endif
Syoyo Fujita246654a2018-03-21 20:32:22 +09002014 if (!LoadExternalFile(&img, err, uri, basedir, 0, false)) {
2015 if (err) {
2016 (*err) += "Failed to load external 'uri' for image parameter\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002017 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09002018 // If the image cannot be loaded, keep uri as image->uri.
2019 return true;
2020 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09002021
Syoyo Fujita246654a2018-03-21 20:32:22 +09002022 if (img.empty()) {
2023 if (err) {
2024 (*err) += "Image is empty.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002025 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09002026 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002027 }
2028 }
2029
Squareysff644d82018-03-13 22:36:18 +01002030 if (*LoadImageData == nullptr) {
2031 if (err) {
2032 (*err) += "No LoadImageData callback specified.\n";
2033 }
2034 return false;
2035 }
2036 return (*LoadImageData)(image, err, 0, 0, &img.at(0),
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002037 static_cast<int>(img.size()), user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002038}
2039
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002040static bool ParseTexture(Texture *texture, std::string *err, const json &o,
Syoyo Fujitabeded612016-05-01 20:03:43 +09002041 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002042 (void)basedir;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002043 double sampler = -1.0;
2044 double source = -1.0;
2045 ParseNumberProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002046
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +00002047 ParseNumberProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09002048
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002049 texture->sampler = static_cast<int>(sampler);
2050 texture->source = static_cast<int>(source);
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002051
Selmar Kokfa7022f2018-04-04 18:10:20 +02002052 ParseExtensionsProperty(&texture->extensions, err, o);
2053 ParseExtrasProperty(&texture->extras, o);
Syoyo Fujitabde70212016-02-07 17:38:17 +09002054
2055 return true;
2056}
2057
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002058static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
2059 const std::string &basedir, bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09002060 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09002061 size_t bin_size = 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002062 double byteLength;
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09002063 if (!ParseNumberProperty(&byteLength, err, o, "byteLength", true, "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002064 return false;
2065 }
2066
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00002067 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05002068 buffer->uri.clear();
2069 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002070
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00002071 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05002072 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002073 if (err) {
2074 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
2075 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00002076 }
2077
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002078 json::const_iterator type = o.find("type");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002079 if (type != o.end()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002080 if (type.value().is_string()) {
2081 const std::string &ty = type.value();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002082 if (ty.compare("arraybuffer") == 0) {
2083 // buffer.type = "arraybuffer";
2084 }
2085 }
2086 }
2087
2088 size_t bytes = static_cast<size_t>(byteLength);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002089 if (is_binary) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09002090 // Still binary glTF accepts external dataURI. First try external resources.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002091
Syoyo Fujita20244e12018-03-15 11:01:05 -05002092 if (!buffer->uri.empty()) {
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00002093 // External .bin file.
Syoyo Fujita20244e12018-03-15 11:01:05 -05002094 LoadExternalFile(&buffer->data, err, buffer->uri, basedir, bytes, true);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002095 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09002096 // load data from (embedded) binary data
2097
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09002098 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09002099 if (err) {
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09002100 (*err) += "Invalid binary data in `Buffer'.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002101 }
2102 return false;
2103 }
2104
2105 if (byteLength > bin_size) {
2106 if (err) {
2107 std::stringstream ss;
2108 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002109 "`byteLength' = "
2110 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002111 (*err) += ss.str();
2112 }
2113 return false;
2114 }
2115
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00002116 // Read buffer data
2117 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002118 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09002119 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002120
2121 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002122 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02002123 std::string mime_type;
2124 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, bytes, true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002125 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002126 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002127 }
2128 return false;
2129 }
2130 } else {
2131 // Assume external .bin file.
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002132 if (!LoadExternalFile(&buffer->data, err, buffer->uri, basedir, bytes,
2133 true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002134 return false;
2135 }
2136 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002137 }
2138
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002139 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002140
2141 return true;
2142}
2143
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002144static bool ParseBufferView(BufferView *bufferView, std::string *err,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002145 const json &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002146 double buffer = -1.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002147 if (!ParseNumberProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002148 return false;
2149 }
2150
Syoyo Fujitad17ff662017-05-29 02:53:12 +09002151 double byteOffset = 0.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002152 ParseNumberProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002153
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002154 double byteLength = 1.0;
Syoyo Fujita5b407452017-06-04 17:42:41 +09002155 if (!ParseNumberProperty(&byteLength, err, o, "byteLength", true,
2156 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002157 return false;
2158 }
2159
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09002160 size_t byteStride = 0;
2161 double byteStrideValue = 0.0;
2162 if (!ParseNumberProperty(&byteStrideValue, err, o, "byteStride", false)) {
2163 // Spec says: When byteStride of referenced bufferView is not defined, it
2164 // means that accessor elements are tightly packed, i.e., effective stride
2165 // equals the size of the element.
2166 // We cannot determine the actual byteStride until Accessor are parsed, thus
2167 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
2168 byteStride = 0;
2169 } else {
2170 byteStride = static_cast<size_t>(byteStrideValue);
2171 }
2172
2173 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
2174 if (err) {
2175 std::stringstream ss;
2176 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
2177 "4 : "
2178 << byteStride << std::endl;
2179
2180 (*err) += ss.str();
2181 }
2182 return false;
2183 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002184
2185 double target = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002186 ParseNumberProperty(&target, err, o, "target", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002187 int targetValue = static_cast<int>(target);
2188 if ((targetValue == TINYGLTF_TARGET_ARRAY_BUFFER) ||
2189 (targetValue == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
2190 // OK
2191 } else {
2192 targetValue = 0;
2193 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002194 bufferView->target = targetValue;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002195
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002196 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002197
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002198 bufferView->buffer = static_cast<int>(buffer);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002199 bufferView->byteOffset = static_cast<size_t>(byteOffset);
2200 bufferView->byteLength = static_cast<size_t>(byteLength);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002201 bufferView->byteStride = static_cast<size_t>(byteStride);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002202
2203 return true;
2204}
2205
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002206static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002207 double bufferView = -1.0;
Syoyo Fujita5b407452017-06-04 17:42:41 +09002208 if (!ParseNumberProperty(&bufferView, err, o, "bufferView", true,
2209 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002210 return false;
2211 }
2212
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002213 double byteOffset = 0.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002214 ParseNumberProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002215
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09002216 bool normalized = false;
2217 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
2218
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002219 double componentType = 0.0;
Syoyo Fujita5b407452017-06-04 17:42:41 +09002220 if (!ParseNumberProperty(&componentType, err, o, "componentType", true,
2221 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002222 return false;
2223 }
2224
2225 double count = 0.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002226 if (!ParseNumberProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002227 return false;
2228 }
2229
2230 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002231 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002232 return false;
2233 }
2234
2235 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002236 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002237 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002238 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002239 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002240 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002241 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002242 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002243 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002244 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002245 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002246 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002247 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002248 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002249 } else {
2250 std::stringstream ss;
2251 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002252 if (err) {
2253 (*err) += ss.str();
2254 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002255 return false;
2256 }
2257
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002258 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002259
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002260 accessor->minValues.clear();
2261 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09002262 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
2263 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002264
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09002265 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
2266 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002267
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002268 accessor->count = static_cast<size_t>(count);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002269 accessor->bufferView = static_cast<int>(bufferView);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002270 accessor->byteOffset = static_cast<size_t>(byteOffset);
jianghaosen4748ad62017-08-29 20:08:37 +08002271 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002272 {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002273 int comp = static_cast<int>(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002274 if (comp >= TINYGLTF_COMPONENT_TYPE_BYTE &&
2275 comp <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
2276 // OK
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002277 accessor->componentType = comp;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002278 } else {
2279 std::stringstream ss;
2280 ss << "Invalid `componentType` in accessor. Got " << comp << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002281 if (err) {
2282 (*err) += ss.str();
2283 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002284 return false;
2285 }
2286 }
2287
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002288 ParseExtrasProperty(&(accessor->extras), o);
2289
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002290 return true;
2291}
2292
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002293static bool ParsePrimitive(Primitive *primitive, std::string *err,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002294 const json &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002295 double material = -1.0;
2296 ParseNumberProperty(&material, err, o, "material", false);
2297 primitive->material = static_cast<int>(material);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002298
2299 double mode = static_cast<double>(TINYGLTF_MODE_TRIANGLES);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002300 ParseNumberProperty(&mode, err, o, "mode", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002301
2302 int primMode = static_cast<int>(mode);
Syoyo Fujita5b407452017-06-04 17:42:41 +09002303 primitive->mode = primMode; // Why only triangled were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002304
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002305 double indices = -1.0;
2306 ParseNumberProperty(&indices, err, o, "indices", false);
2307 primitive->indices = static_cast<int>(indices);
2308 if (!ParseStringIntProperty(&primitive->attributes, err, o, "attributes",
Syoyo Fujita57c10182017-10-19 18:48:26 +09002309 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002310 return false;
2311 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002312
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00002313 // Look for morph targets
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002314 json::const_iterator targetsObject = o.find("targets");
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002315 if ((targetsObject != o.end()) && targetsObject.value().is_array()) {
2316 for (json::const_iterator i = targetsObject.value().begin();
2317 i != targetsObject.value().end(); i++) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00002318 std::map<std::string, int> targetAttribues;
2319
Syoyo Fujita83675312017-12-02 21:14:13 +09002320 const json &dict = i.value();
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002321 json::const_iterator dictIt(dict.begin());
2322 json::const_iterator dictItEnd(dict.end());
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00002323
2324 for (; dictIt != dictItEnd; ++dictIt) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002325 targetAttribues[dictIt.key()] = static_cast<int>(dictIt.value());
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00002326 }
2327 primitive->targets.push_back(targetAttribues);
2328 }
2329 }
2330
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002331 ParseExtrasProperty(&(primitive->extras), o);
2332
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002333 return true;
2334}
2335
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002336static bool ParseMesh(Mesh *mesh, std::string *err, const json &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002337 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002338
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002339 mesh->primitives.clear();
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002340 json::const_iterator primObject = o.find("primitives");
Syoyo Fujita83675312017-12-02 21:14:13 +09002341 if ((primObject != o.end()) && primObject.value().is_array()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002342 for (json::const_iterator i = primObject.value().begin();
2343 i != primObject.value().end(); i++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002344 Primitive primitive;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002345 if (ParsePrimitive(&primitive, err, i.value())) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04002346 // Only add the primitive if the parsing succeeds.
2347 mesh->primitives.push_back(primitive);
2348 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002349 }
2350 }
2351
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00002352 // Look for morph targets
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002353 json::const_iterator targetsObject = o.find("targets");
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002354 if ((targetsObject != o.end()) && targetsObject.value().is_array()) {
2355 for (json::const_iterator i = targetsObject.value().begin();
2356 i != targetsObject.value().end(); i++) {
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00002357 std::map<std::string, int> targetAttribues;
2358
Syoyo Fujita83675312017-12-02 21:14:13 +09002359 const json &dict = i.value();
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002360 json::const_iterator dictIt(dict.begin());
2361 json::const_iterator dictItEnd(dict.end());
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00002362
2363 for (; dictIt != dictItEnd; ++dictIt) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002364 targetAttribues[dictIt.key()] = static_cast<int>(dictIt.value());
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00002365 }
2366 mesh->targets.push_back(targetAttribues);
2367 }
2368 }
2369
2370 // Should probably check if has targets and if dimensions fit
2371 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
2372
Selmar09d2ff12018-03-15 17:30:42 +01002373 ParseExtensionsProperty(&mesh->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002374 ParseExtrasProperty(&(mesh->extras), o);
2375
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002376 return true;
2377}
2378
Syoyo Fujita83675312017-12-02 21:14:13 +09002379static bool ParseLight(Light *light, std::string *err, const json &o) {
Emanuel Schrade186322b2017-11-06 11:14:41 +01002380 ParseStringProperty(&light->name, err, o, "name", false);
2381 ParseNumberArrayProperty(&light->color, err, o, "color", false);
2382 ParseStringProperty(&light->type, err, o, "type", false);
2383 return true;
2384}
2385
Syoyo Fujita83675312017-12-02 21:14:13 +09002386static bool ParseNode(Node *node, std::string *err, const json &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002387 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002388
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002389 double skin = -1.0;
2390 ParseNumberProperty(&skin, err, o, "skin", false);
2391 node->skin = static_cast<int>(skin);
2392
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002393 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09002394 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002395 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
2396 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
2397 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
2398 }
2399
2400 double camera = -1.0;
2401 ParseNumberProperty(&camera, err, o, "camera", false);
2402 node->camera = static_cast<int>(camera);
2403
2404 double mesh = -1.0;
2405 ParseNumberProperty(&mesh, err, o, "mesh", false);
Syoyo Fujitaf6120152017-05-27 23:51:23 +09002406 node->mesh = int(mesh);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002407
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002408 node->children.clear();
Syoyo Fujita83675312017-12-02 21:14:13 +09002409 json::const_iterator childrenObject = o.find("children");
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002410 if ((childrenObject != o.end()) && childrenObject.value().is_array()) {
2411 for (json::const_iterator i = childrenObject.value().begin();
2412 i != childrenObject.value().end(); i++) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002413 if (!i.value().is_number()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002414 if (err) {
2415 (*err) += "Invalid `children` array.\n";
2416 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002417 return false;
2418 }
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002419 const int &childrenNode = static_cast<int>(i.value());
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002420 node->children.push_back(childrenNode);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002421 }
2422 }
2423
Selmar09d2ff12018-03-15 17:30:42 +01002424 ParseExtensionsProperty(&node->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002425 ParseExtrasProperty(&(node->extras), o);
2426
Emanuel Schrade186322b2017-11-06 11:14:41 +01002427 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002428}
2429
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002430static bool ParseMaterial(Material *material, std::string *err, const json &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002431 material->values.clear();
Selmar09d2ff12018-03-15 17:30:42 +01002432 material->extensions.clear();
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002433 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002434
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002435 json::const_iterator it(o.begin());
2436 json::const_iterator itEnd(o.end());
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002437
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002438 for (; it != itEnd; it++) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002439 if (it.key() == "pbrMetallicRoughness") {
Syoyo Fujita83675312017-12-02 21:14:13 +09002440 if (it.value().is_object()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002441 const json &values_object = it.value();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002442
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002443 json::const_iterator itVal(values_object.begin());
2444 json::const_iterator itValEnd(values_object.end());
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002445
Syoyo Fujitaf6120152017-05-27 23:51:23 +09002446 for (; itVal != itValEnd; itVal++) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002447 Parameter param;
Syoyo Fujita83675312017-12-02 21:14:13 +09002448 if (ParseParameterProperty(&param, err, values_object, itVal.key(),
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002449 false)) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002450 material->values[itVal.key()] = param;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002451 }
2452 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002453 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002454 } else if (it.key() == "extensions" || it.key() == "extras") {
2455 // done later, skip, otherwise poorly parsed contents will be saved in the
2456 // parametermap and serialized again later
2457 } else {
Syoyo Fujita5b407452017-06-04 17:42:41 +09002458 Parameter param;
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002459 if (ParseParameterProperty(&param, err, o, it.key(), false)) {
2460 material->additionalValues[it.key()] = param;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00002461 }
2462 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09002463 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002464
Selmar09d2ff12018-03-15 17:30:42 +01002465 ParseExtensionsProperty(&material->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002466 ParseExtrasProperty(&(material->extras), o);
2467
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002468 return true;
2469}
2470
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002471static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002472 const json &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002473 double samplerIndex = -1.0;
2474 double targetIndex = -1.0;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002475 if (!ParseNumberProperty(&samplerIndex, err, o, "sampler", true,
2476 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002477 if (err) {
2478 (*err) += "`sampler` field is missing in animation channels\n";
2479 }
2480 return false;
2481 }
2482
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002483 json::const_iterator targetIt = o.find("target");
Syoyo Fujita83675312017-12-02 21:14:13 +09002484 if ((targetIt != o.end()) && targetIt.value().is_object()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002485 const json &target_object = targetIt.value();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002486
Syoyo Fujita5b407452017-06-04 17:42:41 +09002487 if (!ParseNumberProperty(&targetIndex, err, target_object, "node", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002488 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09002489 (*err) += "`node` field is missing in animation.channels.target\n";
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002490 }
2491 return false;
2492 }
2493
2494 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
2495 true)) {
2496 if (err) {
2497 (*err) += "`path` field is missing in animation.channels.target\n";
2498 }
2499 return false;
2500 }
2501 }
2502
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002503 channel->sampler = static_cast<int>(samplerIndex);
2504 channel->target_node = static_cast<int>(targetIndex);
2505
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002506 ParseExtrasProperty(&(channel->extras), o);
2507
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002508 return true;
2509}
2510
2511static bool ParseAnimation(Animation *animation, std::string *err,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002512 const json &o) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002513 {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002514 json::const_iterator channelsIt = o.find("channels");
Syoyo Fujita83675312017-12-02 21:14:13 +09002515 if ((channelsIt != o.end()) && channelsIt.value().is_array()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002516 for (json::const_iterator i = channelsIt.value().begin();
2517 i != channelsIt.value().end(); i++) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002518 AnimationChannel channel;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002519 if (ParseAnimationChannel(&channel, err, i.value())) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002520 // Only add the channel if the parsing succeeds.
2521 animation->channels.push_back(channel);
2522 }
2523 }
2524 }
2525 }
2526
2527 {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002528 json::const_iterator samplerIt = o.find("samplers");
Syoyo Fujita83675312017-12-02 21:14:13 +09002529 if ((samplerIt != o.end()) && samplerIt.value().is_array()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002530 const json &sampler_array = samplerIt.value();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002531
Syoyo Fujita83675312017-12-02 21:14:13 +09002532 json::const_iterator it = sampler_array.begin();
2533 json::const_iterator itEnd = sampler_array.end();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002534
2535 for (; it != itEnd; it++) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002536 const json &s = it->get<json>();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002537
2538 AnimationSampler sampler;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002539 double inputIndex = -1.0;
2540 double outputIndex = -1.0;
2541 if (!ParseNumberProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002542 if (err) {
2543 (*err) += "`input` field is missing in animation.sampler\n";
2544 }
2545 return false;
2546 }
2547 if (!ParseStringProperty(&sampler.interpolation, err, s,
2548 "interpolation", true)) {
2549 if (err) {
2550 (*err) += "`interpolation` field is missing in animation.sampler\n";
2551 }
2552 return false;
2553 }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002554 if (!ParseNumberProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002555 if (err) {
2556 (*err) += "`output` field is missing in animation.sampler\n";
2557 }
2558 return false;
2559 }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002560 sampler.input = static_cast<int>(inputIndex);
2561 sampler.output = static_cast<int>(outputIndex);
2562 animation->samplers.push_back(sampler);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002563 }
2564 }
2565 }
2566
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002567 ParseStringProperty(&animation->name, err, o, "name", false);
2568
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002569 ParseExtrasProperty(&(animation->extras), o);
2570
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002571 return true;
2572}
2573
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002574static bool ParseSampler(Sampler *sampler, std::string *err, const json &o) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09002575 ParseStringProperty(&sampler->name, err, o, "name", false);
2576
2577 double minFilter =
2578 static_cast<double>(TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR);
2579 double magFilter = static_cast<double>(TINYGLTF_TEXTURE_FILTER_LINEAR);
Cemalettin Dervis246d8662017-12-07 20:29:51 +01002580 double wrapS = static_cast<double>(TINYGLTF_TEXTURE_WRAP_REPEAT);
2581 double wrapT = static_cast<double>(TINYGLTF_TEXTURE_WRAP_REPEAT);
Syoyo Fujitac2615632016-06-19 21:56:06 +09002582 ParseNumberProperty(&minFilter, err, o, "minFilter", false);
2583 ParseNumberProperty(&magFilter, err, o, "magFilter", false);
2584 ParseNumberProperty(&wrapS, err, o, "wrapS", false);
2585 ParseNumberProperty(&wrapT, err, o, "wrapT", false);
2586
2587 sampler->minFilter = static_cast<int>(minFilter);
2588 sampler->magFilter = static_cast<int>(magFilter);
2589 sampler->wrapS = static_cast<int>(wrapS);
2590 sampler->wrapT = static_cast<int>(wrapT);
2591
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002592 ParseExtrasProperty(&(sampler->extras), o);
2593
Syoyo Fujitac2615632016-06-19 21:56:06 +09002594 return true;
2595}
2596
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002597static bool ParseSkin(Skin *skin, std::string *err, const json &o) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002598 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002599
2600 std::vector<double> joints;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002601 if (!ParseNumberArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002602 return false;
2603 }
2604
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +00002605 double skeleton = -1.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002606 ParseNumberProperty(&skeleton, err, o, "skeleton", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002607 skin->skeleton = static_cast<int>(skeleton);
2608
Syoyo Fujita73cd7b92017-06-20 03:02:57 +09002609 skin->joints.resize(joints.size());
2610 for (size_t i = 0; i < joints.size(); i++) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002611 skin->joints[i] = static_cast<int>(joints[i]);
Syoyo Fujita73cd7b92017-06-20 03:02:57 +09002612 }
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002613
2614 double invBind = -1.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002615 ParseNumberProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002616 skin->inverseBindMatrices = static_cast<int>(invBind);
2617
2618 return true;
2619}
2620
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002621static bool ParsePerspectiveCamera(PerspectiveCamera *camera, std::string *err,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09002622 const json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002623 double yfov = 0.0;
2624 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
2625 return false;
2626 }
2627
2628 double znear = 0.0;
2629 if (!ParseNumberProperty(&znear, err, o, "znear", true,
2630 "PerspectiveCamera")) {
2631 return false;
2632 }
2633
2634 double aspectRatio = 0.0; // = invalid
2635 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
2636 "PerspectiveCamera");
2637
2638 double zfar = 0.0; // = invalid
2639 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
2640
2641 camera->aspectRatio = float(aspectRatio);
2642 camera->zfar = float(zfar);
2643 camera->yfov = float(yfov);
2644 camera->znear = float(znear);
2645
Selmar09d2ff12018-03-15 17:30:42 +01002646 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002647 ParseExtrasProperty(&(camera->extras), o);
2648
2649 // TODO(syoyo): Validate parameter values.
2650
2651 return true;
2652}
2653
2654static bool ParseOrthographicCamera(OrthographicCamera *camera,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002655 std::string *err, const json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002656 double xmag = 0.0;
2657 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
2658 return false;
2659 }
2660
2661 double ymag = 0.0;
2662 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
2663 return false;
2664 }
2665
2666 double zfar = 0.0;
2667 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
2668 return false;
2669 }
2670
2671 double znear = 0.0;
2672 if (!ParseNumberProperty(&znear, err, o, "znear", true,
2673 "OrthographicCamera")) {
2674 return false;
2675 }
2676
Selmar09d2ff12018-03-15 17:30:42 +01002677 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002678 ParseExtrasProperty(&(camera->extras), o);
2679
2680 camera->xmag = float(xmag);
2681 camera->ymag = float(ymag);
2682 camera->zfar = float(zfar);
2683 camera->znear = float(znear);
2684
2685 // TODO(syoyo): Validate parameter values.
2686
2687 return true;
2688}
2689
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002690static bool ParseCamera(Camera *camera, std::string *err, const json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002691 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
2692 return false;
2693 }
2694
2695 if (camera->type.compare("orthographic") == 0) {
2696 if (o.find("orthographic") == o.end()) {
2697 if (err) {
2698 std::stringstream ss;
2699 ss << "Orhographic camera description not found." << std::endl;
2700 (*err) += ss.str();
2701 }
2702 return false;
2703 }
2704
Syoyo Fujita83675312017-12-02 21:14:13 +09002705 const json &v = o.find("orthographic").value();
2706 if (!v.is_object()) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002707 if (err) {
2708 std::stringstream ss;
2709 ss << "\"orthographic\" is not a JSON object." << std::endl;
2710 (*err) += ss.str();
2711 }
2712 return false;
2713 }
2714
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002715 if (!ParseOrthographicCamera(&camera->orthographic, err, v.get<json>())) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002716 return false;
2717 }
2718 } else if (camera->type.compare("perspective") == 0) {
2719 if (o.find("perspective") == o.end()) {
2720 if (err) {
2721 std::stringstream ss;
2722 ss << "Perspective camera description not found." << std::endl;
2723 (*err) += ss.str();
2724 }
2725 return false;
2726 }
2727
Syoyo Fujita83675312017-12-02 21:14:13 +09002728 const json &v = o.find("perspective").value();
2729 if (!v.is_object()) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002730 if (err) {
2731 std::stringstream ss;
2732 ss << "\"perspective\" is not a JSON object." << std::endl;
2733 (*err) += ss.str();
2734 }
2735 return false;
2736 }
2737
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002738 if (!ParsePerspectiveCamera(&camera->perspective, err, v.get<json>())) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002739 return false;
2740 }
2741 } else {
2742 if (err) {
2743 std::stringstream ss;
2744 ss << "Invalid camera type: \"" << camera->type
2745 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
2746 (*err) += ss.str();
2747 }
2748 return false;
2749 }
2750
2751 ParseStringProperty(&camera->name, err, o, "name", false);
2752
Selmar09d2ff12018-03-15 17:30:42 +01002753 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002754 ParseExtrasProperty(&(camera->extras), o);
2755
2756 return true;
2757}
2758
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002759bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str,
2760 unsigned int length, const std::string &base_dir,
2761 unsigned int check_sections) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002762 if (length < 4) {
2763 if (err) {
2764 (*err) = "JSON string too short.\n";
2765 }
2766 return false;
2767 }
2768
Syoyo Fujita83675312017-12-02 21:14:13 +09002769 json v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09002770
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002771#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
2772 defined(_CPPUNWIND)) && \
2773 not defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09002774 try {
Syoyo Fujita83675312017-12-02 21:14:13 +09002775 v = json::parse(str, str + length);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002776
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09002777 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002778 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09002779 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002780 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002781 return false;
2782 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09002783#else
2784 {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002785 v = json::parse(str, str + length, nullptr, /* exception */ false);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09002786
2787 if (!v.is_object()) {
2788 // Assume parsing was failed.
2789 if (err) {
2790 (*err) = "Failed to parse JSON object\n";
2791 }
2792 return false;
2793 }
2794 }
2795#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002796
Syoyo Fujita83675312017-12-02 21:14:13 +09002797 if (!v.is_object()) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002798 // root is not an object.
2799 if (err) {
2800 (*err) = "Root element is not a JSON object\n";
2801 }
2802 return false;
2803 }
2804
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002805 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09002806 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002807
Syoyo Fujita83675312017-12-02 21:14:13 +09002808 {
Squareys43374632018-03-13 22:20:48 +01002809 json::const_iterator it = v.find("scenes");
Syoyo Fujita83675312017-12-02 21:14:13 +09002810 if ((it != v.end()) && it.value().is_array()) {
2811 // OK
2812 } else if (check_sections & REQUIRE_SCENES) {
2813 if (err) {
2814 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
2815 }
2816 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002817 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002818 }
2819
Syoyo Fujita83675312017-12-02 21:14:13 +09002820 {
Squareys43374632018-03-13 22:20:48 +01002821 json::const_iterator it = v.find("nodes");
Syoyo Fujita83675312017-12-02 21:14:13 +09002822 if ((it != v.end()) && it.value().is_array()) {
2823 // OK
2824 } else if (check_sections & REQUIRE_NODES) {
2825 if (err) {
2826 (*err) += "\"nodes\" object not found in .gltf\n";
2827 }
2828 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002829 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002830 }
2831
Syoyo Fujita83675312017-12-02 21:14:13 +09002832 {
Squareys43374632018-03-13 22:20:48 +01002833 json::const_iterator it = v.find("accessors");
Syoyo Fujita83675312017-12-02 21:14:13 +09002834 if ((it != v.end()) && it.value().is_array()) {
2835 // OK
2836 } else if (check_sections & REQUIRE_ACCESSORS) {
2837 if (err) {
2838 (*err) += "\"accessors\" object not found in .gltf\n";
2839 }
2840 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002841 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002842 }
2843
Syoyo Fujita83675312017-12-02 21:14:13 +09002844 {
Squareys43374632018-03-13 22:20:48 +01002845 json::const_iterator it = v.find("buffers");
Syoyo Fujita83675312017-12-02 21:14:13 +09002846 if ((it != v.end()) && it.value().is_array()) {
2847 // OK
2848 } else if (check_sections & REQUIRE_BUFFERS) {
2849 if (err) {
2850 (*err) += "\"buffers\" object not found in .gltf\n";
2851 }
2852 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002853 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002854 }
2855
Syoyo Fujita83675312017-12-02 21:14:13 +09002856 {
Squareys43374632018-03-13 22:20:48 +01002857 json::const_iterator it = v.find("bufferViews");
Syoyo Fujita83675312017-12-02 21:14:13 +09002858 if ((it != v.end()) && it.value().is_array()) {
2859 // OK
2860 } else if (check_sections & REQUIRE_BUFFER_VIEWS) {
2861 if (err) {
2862 (*err) += "\"bufferViews\" object not found in .gltf\n";
2863 }
2864 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002865 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002866 }
2867
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002868 model->buffers.clear();
2869 model->bufferViews.clear();
2870 model->accessors.clear();
2871 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002872 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002873 model->nodes.clear();
2874 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00002875 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01002876 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002877 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002878
Syoyo Fujita83675312017-12-02 21:14:13 +09002879 // 1. Parse Asset
2880 {
Squareys43374632018-03-13 22:20:48 +01002881 json::const_iterator it = v.find("asset");
Syoyo Fujita83675312017-12-02 21:14:13 +09002882 if ((it != v.end()) && it.value().is_object()) {
2883 const json &root = it.value();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002884
Syoyo Fujita83675312017-12-02 21:14:13 +09002885 ParseAsset(&model->asset, err, root);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00002886 }
2887 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002888
Syoyo Fujita83675312017-12-02 21:14:13 +09002889 // 2. Parse extensionUsed
2890 {
Squareys43374632018-03-13 22:20:48 +01002891 json::const_iterator it = v.find("extensionsUsed");
Syoyo Fujita83675312017-12-02 21:14:13 +09002892 if ((it != v.end()) && it.value().is_array()) {
2893 const json &root = it.value();
2894 for (unsigned int i = 0; i < root.size(); ++i) {
2895 model->extensionsUsed.push_back(root[i].get<std::string>());
Syoyo Fujita59130d12017-08-01 20:23:11 +09002896 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002897 }
2898 }
2899
Syoyo Fujita83675312017-12-02 21:14:13 +09002900 {
Squareys43374632018-03-13 22:20:48 +01002901 json::const_iterator it = v.find("extensionsRequired");
Syoyo Fujita83675312017-12-02 21:14:13 +09002902 if ((it != v.end()) && it.value().is_array()) {
2903 const json &root = it.value();
2904 for (unsigned int i = 0; i < root.size(); ++i) {
2905 model->extensionsRequired.push_back(root[i].get<std::string>());
Syoyo Fujita59130d12017-08-01 20:23:11 +09002906 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002907 }
2908 }
2909
Syoyo Fujita83675312017-12-02 21:14:13 +09002910 // 3. Parse Buffer
2911 {
Squareys43374632018-03-13 22:20:48 +01002912 json::const_iterator rootIt = v.find("buffers");
Syoyo Fujita83675312017-12-02 21:14:13 +09002913 if ((rootIt != v.end()) && rootIt.value().is_array()) {
2914 const json &root = rootIt.value();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002915
Syoyo Fujita83675312017-12-02 21:14:13 +09002916 json::const_iterator it(root.begin());
2917 json::const_iterator itEnd(root.end());
2918 for (; it != itEnd; it++) {
2919 if (!it.value().is_object()) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09002920 if (err) {
Syoyo Fujita83675312017-12-02 21:14:13 +09002921 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002922 }
2923 return false;
2924 }
Syoyo Fujita83675312017-12-02 21:14:13 +09002925 Buffer buffer;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002926 if (!ParseBuffer(&buffer, err, it->get<json>(), base_dir, is_binary_,
2927 bin_data_, bin_size_)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09002928 return false;
2929 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09002930
Syoyo Fujita83675312017-12-02 21:14:13 +09002931 model->buffers.push_back(buffer);
2932 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002933 }
2934 }
Squareys43374632018-03-13 22:20:48 +01002935
Syoyo Fujita83675312017-12-02 21:14:13 +09002936 // 4. Parse BufferView
2937 {
Squareys43374632018-03-13 22:20:48 +01002938 json::const_iterator rootIt = v.find("bufferViews");
Syoyo Fujita83675312017-12-02 21:14:13 +09002939 if ((rootIt != v.end()) && rootIt.value().is_array()) {
2940 const json &root = rootIt.value();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002941
Syoyo Fujita83675312017-12-02 21:14:13 +09002942 json::const_iterator it(root.begin());
2943 json::const_iterator itEnd(root.end());
2944 for (; it != itEnd; it++) {
2945 if (!it.value().is_object()) {
2946 if (err) {
2947 (*err) += "`bufferViews' does not contain an JSON object.";
2948 }
2949 return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09002950 }
Syoyo Fujita83675312017-12-02 21:14:13 +09002951 BufferView bufferView;
2952 if (!ParseBufferView(&bufferView, err, it->get<json>())) {
2953 return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09002954 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002955
Syoyo Fujita83675312017-12-02 21:14:13 +09002956 model->bufferViews.push_back(bufferView);
2957 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002958 }
2959 }
2960
Syoyo Fujita83675312017-12-02 21:14:13 +09002961 // 5. Parse Accessor
2962 {
Squareys43374632018-03-13 22:20:48 +01002963 json::const_iterator rootIt = v.find("accessors");
Syoyo Fujita83675312017-12-02 21:14:13 +09002964 if ((rootIt != v.end()) && rootIt.value().is_array()) {
2965 const json &root = rootIt.value();
Syoyo Fujitac2615632016-06-19 21:56:06 +09002966
Syoyo Fujita83675312017-12-02 21:14:13 +09002967 json::const_iterator it(root.begin());
2968 json::const_iterator itEnd(root.end());
2969 for (; it != itEnd; it++) {
2970 if (!it.value().is_object()) {
2971 if (err) {
2972 (*err) += "`accessors' does not contain an JSON object.";
2973 }
2974 return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09002975 }
Syoyo Fujita83675312017-12-02 21:14:13 +09002976 Accessor accessor;
2977 if (!ParseAccessor(&accessor, err, it->get<json>())) {
2978 return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09002979 }
Syoyo Fujitac2615632016-06-19 21:56:06 +09002980
Syoyo Fujita83675312017-12-02 21:14:13 +09002981 model->accessors.push_back(accessor);
2982 }
Syoyo Fujitac2615632016-06-19 21:56:06 +09002983 }
2984 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002985
Syoyo Fujita83675312017-12-02 21:14:13 +09002986 // 6. Parse Mesh
2987 {
Squareys43374632018-03-13 22:20:48 +01002988 json::const_iterator rootIt = v.find("meshes");
Syoyo Fujita83675312017-12-02 21:14:13 +09002989 if ((rootIt != v.end()) && rootIt.value().is_array()) {
2990 const json &root = rootIt.value();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002991
Syoyo Fujita83675312017-12-02 21:14:13 +09002992 json::const_iterator it(root.begin());
2993 json::const_iterator itEnd(root.end());
2994 for (; it != itEnd; it++) {
2995 if (!it.value().is_object()) {
2996 if (err) {
2997 (*err) += "`meshes' does not contain an JSON object.";
2998 }
2999 return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09003000 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003001 Mesh mesh;
3002 if (!ParseMesh(&mesh, err, it->get<json>())) {
3003 return false;
3004 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003005
Syoyo Fujita83675312017-12-02 21:14:13 +09003006 model->meshes.push_back(mesh);
3007 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003008 }
3009 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01003010
Syoyo Fujita83675312017-12-02 21:14:13 +09003011 // 7. Parse Node
3012 {
Squareys43374632018-03-13 22:20:48 +01003013 json::const_iterator rootIt = v.find("nodes");
Syoyo Fujita83675312017-12-02 21:14:13 +09003014 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3015 const json &root = rootIt.value();
Emanuel Schrade186322b2017-11-06 11:14:41 +01003016
Syoyo Fujita83675312017-12-02 21:14:13 +09003017 json::const_iterator it(root.begin());
3018 json::const_iterator itEnd(root.end());
3019 for (; it != itEnd; it++) {
3020 if (!it.value().is_object()) {
3021 if (err) {
3022 (*err) += "`nodes' does not contain an JSON object.";
3023 }
3024 return false;
3025 }
3026 Node node;
3027 if (!ParseNode(&node, err, it->get<json>())) {
3028 return false;
3029 }
3030
3031 model->nodes.push_back(node);
3032 }
3033 }
3034 }
3035
3036 // 8. Parse scenes.
3037 {
Squareys43374632018-03-13 22:20:48 +01003038 json::const_iterator rootIt = v.find("scenes");
Syoyo Fujita83675312017-12-02 21:14:13 +09003039 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3040 const json &root = rootIt.value();
3041
Syoyo Fujita83675312017-12-02 21:14:13 +09003042 json::const_iterator it(root.begin());
3043 json::const_iterator itEnd(root.end());
3044 for (; it != itEnd; it++) {
3045 if (!(it.value().is_object())) {
3046 if (err) {
3047 (*err) += "`scenes' does not contain an JSON object.";
3048 }
3049 return false;
3050 }
3051 const json &o = it->get<json>();
3052 std::vector<double> nodes;
3053 if (!ParseNumberArrayProperty(&nodes, err, o, "nodes", false)) {
3054 return false;
3055 }
3056
3057 Scene scene;
3058 ParseStringProperty(&scene.name, err, o, "name", false);
3059 std::vector<int> nodesIds;
3060 for (size_t i = 0; i < nodes.size(); i++) {
3061 nodesIds.push_back(static_cast<int>(nodes[i]));
3062 }
3063 scene.nodes = nodesIds;
3064
Selmar09d2ff12018-03-15 17:30:42 +01003065 ParseExtensionsProperty(&scene.extensions, err, o);
3066 ParseExtrasProperty(&scene.extras, o);
3067
Syoyo Fujita83675312017-12-02 21:14:13 +09003068 model->scenes.push_back(scene);
3069 }
3070 }
3071 }
3072
3073 // 9. Parse default scenes.
3074 {
Squareys43374632018-03-13 22:20:48 +01003075 json::const_iterator rootIt = v.find("scene");
Syoyo Fujita580d7c82018-03-15 22:09:01 -05003076 if ((rootIt != v.end()) && rootIt.value().is_number()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09003077 const int defaultScene = rootIt.value();
3078
3079 model->defaultScene = static_cast<int>(defaultScene);
3080 }
3081 }
3082
3083 // 10. Parse Material
3084 {
Squareys43374632018-03-13 22:20:48 +01003085 json::const_iterator rootIt = v.find("materials");
Syoyo Fujita83675312017-12-02 21:14:13 +09003086 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3087 const json &root = rootIt.value();
3088
3089 json::const_iterator it(root.begin());
3090 json::const_iterator itEnd(root.end());
3091 for (; it != itEnd; it++) {
3092 if (!it.value().is_object()) {
3093 if (err) {
3094 (*err) += "`materials' does not contain an JSON object.";
3095 }
3096 return false;
3097 }
3098 json jsonMaterial = it->get<json>();
3099
3100 Material material;
3101 ParseStringProperty(&material.name, err, jsonMaterial, "name", false);
3102
3103 if (!ParseMaterial(&material, err, jsonMaterial)) {
3104 return false;
3105 }
3106
3107 model->materials.push_back(material);
3108 }
3109 }
3110 }
3111
3112 // 11. Parse Image
3113 {
Squareys43374632018-03-13 22:20:48 +01003114 json::const_iterator rootIt = v.find("images");
Syoyo Fujita83675312017-12-02 21:14:13 +09003115 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3116 const json &root = rootIt.value();
3117
3118 json::const_iterator it(root.begin());
3119 json::const_iterator itEnd(root.end());
3120 for (; it != itEnd; it++) {
3121 if (!it.value().is_object()) {
3122 if (err) {
3123 (*err) += "`images' does not contain an JSON object.";
3124 }
3125 return false;
3126 }
3127 Image image;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09003128 if (!ParseImage(&image, err, it.value(), base_dir, &this->LoadImageData,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003129 load_image_user_data_)) {
Syoyo Fujita83675312017-12-02 21:14:13 +09003130 return false;
3131 }
3132
3133 if (image.bufferView != -1) {
3134 // Load image from the buffer view.
3135 if (size_t(image.bufferView) >= model->bufferViews.size()) {
3136 if (err) {
3137 std::stringstream ss;
3138 ss << "bufferView \"" << image.bufferView
3139 << "\" not found in the scene." << std::endl;
3140 (*err) += ss.str();
3141 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01003142 return false;
3143 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003144
3145 const BufferView &bufferView =
3146 model->bufferViews[size_t(image.bufferView)];
3147 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
3148
Squareysff644d82018-03-13 22:36:18 +01003149 if (*LoadImageData == nullptr) {
3150 if (err) {
3151 (*err) += "No LoadImageData callback specified.\n";
3152 }
3153 return false;
3154 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003155 bool ret = LoadImageData(&image, err, image.width, image.height,
3156 &buffer.data[bufferView.byteOffset],
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003157 static_cast<int>(bufferView.byteLength),
3158 load_image_user_data_);
Syoyo Fujita83675312017-12-02 21:14:13 +09003159 if (!ret) {
3160 return false;
3161 }
3162 }
3163
3164 model->images.push_back(image);
3165 }
3166 }
3167 }
3168
3169 // 12. Parse Texture
3170 {
Squareys43374632018-03-13 22:20:48 +01003171 json::const_iterator rootIt = v.find("textures");
Syoyo Fujita83675312017-12-02 21:14:13 +09003172 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3173 const json &root = rootIt.value();
3174
3175 json::const_iterator it(root.begin());
3176 json::const_iterator itEnd(root.end());
3177 for (; it != itEnd; it++) {
3178 if (!it.value().is_object()) {
3179 if (err) {
3180 (*err) += "`textures' does not contain an JSON object.";
3181 }
3182 return false;
3183 }
3184 Texture texture;
3185 if (!ParseTexture(&texture, err, it->get<json>(), base_dir)) {
3186 return false;
3187 }
3188
3189 model->textures.push_back(texture);
3190 }
3191 }
3192 }
3193
3194 // 13. Parse Animation
3195 {
Squareys43374632018-03-13 22:20:48 +01003196 json::const_iterator rootIt = v.find("animations");
Syoyo Fujita83675312017-12-02 21:14:13 +09003197 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3198 const json &root = rootIt.value();
3199
3200 json::const_iterator it(root.begin());
3201 json::const_iterator itEnd(root.end());
3202 for (; it != itEnd; ++it) {
3203 if (!it.value().is_object()) {
3204 if (err) {
3205 (*err) += "`animations' does not contain an JSON object.";
3206 }
3207 return false;
3208 }
3209 Animation animation;
3210 if (!ParseAnimation(&animation, err, it->get<json>())) {
3211 return false;
3212 }
3213
3214 model->animations.push_back(animation);
3215 }
3216 }
3217 }
3218
3219 // 14. Parse Skin
3220 {
Squareys43374632018-03-13 22:20:48 +01003221 json::const_iterator rootIt = v.find("skins");
Syoyo Fujita83675312017-12-02 21:14:13 +09003222 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3223 const json &root = rootIt.value();
3224
3225 json::const_iterator it(root.begin());
3226 json::const_iterator itEnd(root.end());
3227 for (; it != itEnd; ++it) {
3228 if (!it.value().is_object()) {
3229 if (err) {
3230 (*err) += "`skins' does not contain an JSON object.";
3231 }
3232 return false;
3233 }
3234 Skin skin;
3235 if (!ParseSkin(&skin, err, it->get<json>())) {
3236 return false;
3237 }
3238
3239 model->skins.push_back(skin);
3240 }
3241 }
3242 }
3243
3244 // 15. Parse Sampler
3245 {
Squareys43374632018-03-13 22:20:48 +01003246 json::const_iterator rootIt = v.find("samplers");
Syoyo Fujita83675312017-12-02 21:14:13 +09003247 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3248 const json &root = rootIt.value();
3249
3250 json::const_iterator it(root.begin());
3251 json::const_iterator itEnd(root.end());
3252 for (; it != itEnd; ++it) {
3253 if (!it.value().is_object()) {
3254 if (err) {
3255 (*err) += "`samplers' does not contain an JSON object.";
3256 }
3257 return false;
3258 }
3259 Sampler sampler;
3260 if (!ParseSampler(&sampler, err, it->get<json>())) {
3261 return false;
3262 }
3263
3264 model->samplers.push_back(sampler);
3265 }
3266 }
3267 }
3268
3269 // 16. Parse Camera
3270 {
Squareys43374632018-03-13 22:20:48 +01003271 json::const_iterator rootIt = v.find("cameras");
Syoyo Fujita83675312017-12-02 21:14:13 +09003272 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3273 const json &root = rootIt.value();
3274
3275 json::const_iterator it(root.begin());
3276 json::const_iterator itEnd(root.end());
3277 for (; it != itEnd; ++it) {
3278 if (!it.value().is_object()) {
3279 if (err) {
3280 (*err) += "`cameras' does not contain an JSON object.";
3281 }
3282 return false;
3283 }
3284 Camera camera;
3285 if (!ParseCamera(&camera, err, it->get<json>())) {
3286 return false;
3287 }
3288
3289 model->cameras.push_back(camera);
3290 }
3291 }
3292 }
3293
3294 // 17. Parse Extensions
Selmar09d2ff12018-03-15 17:30:42 +01003295 ParseExtensionsProperty(&model->extensions, err, v);
3296
3297 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09003298 {
Squareys43374632018-03-13 22:20:48 +01003299 json::const_iterator rootIt = v.find("extensions");
Syoyo Fujita83675312017-12-02 21:14:13 +09003300 if ((rootIt != v.end()) && rootIt.value().is_object()) {
3301 const json &root = rootIt.value();
3302
3303 json::const_iterator it(root.begin());
3304 json::const_iterator itEnd(root.end());
3305 for (; it != itEnd; ++it) {
3306 // parse KHR_lights_cmn extension
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003307 if ((it.key().compare("KHR_lights_cmn") == 0) &&
3308 it.value().is_object()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09003309 const json &object = it.value();
3310 json::const_iterator itLight(object.find("lights"));
3311 json::const_iterator itLightEnd(object.end());
3312 if (itLight == itLightEnd) {
3313 continue;
3314 }
3315
3316 if (!itLight.value().is_array()) {
3317 continue;
3318 }
3319
3320 const json &lights = itLight.value();
3321 json::const_iterator arrayIt(lights.begin());
3322 json::const_iterator arrayItEnd(lights.end());
3323 for (; arrayIt != arrayItEnd; ++arrayIt) {
3324 Light light;
3325 if (!ParseLight(&light, err, arrayIt.value())) {
3326 return false;
3327 }
3328 model->lights.push_back(light);
3329 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01003330 }
3331 }
3332 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01003333 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003334
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003335 return true;
3336}
3337
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003338bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
3339 const char *str, unsigned int length,
3340 const std::string &base_dir,
3341 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003342 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09003343 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003344 bin_size_ = 0;
3345
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003346 return LoadFromString(model, err, str, length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003347}
3348
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003349bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
3350 const std::string &filename,
3351 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09003352 std::stringstream ss;
3353
3354 std::ifstream f(filename.c_str());
3355 if (!f) {
3356 ss << "Failed to open file: " << filename << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003357 if (err) {
3358 (*err) = ss.str();
3359 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09003360 return false;
3361 }
3362
3363 f.seekg(0, f.end);
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +09003364 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09003365 std::vector<char> buf(sz);
3366
Luke San Antoniob310b4a2016-06-23 14:17:37 -04003367 if (sz == 0) {
3368 if (err) {
3369 (*err) = "Empty file.";
3370 }
3371 return false;
3372 }
3373
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09003374 f.seekg(0, f.beg);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003375 f.read(&buf.at(0), static_cast<std::streamsize>(sz));
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09003376 f.close();
3377
3378 std::string basedir = GetBaseDir(filename);
3379
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003380 bool ret = LoadASCIIFromString(model, err, &buf.at(0),
Luke San Antonio9e36b612016-06-23 14:10:51 -04003381 static_cast<unsigned int>(buf.size()), basedir,
3382 check_sections);
3383
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003384 return ret;
3385}
3386
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003387bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
3388 const unsigned char *bytes,
3389 unsigned int size,
3390 const std::string &base_dir,
3391 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003392 if (size < 20) {
3393 if (err) {
3394 (*err) = "Too short data size for glTF Binary.";
3395 }
3396 return false;
3397 }
3398
Syoyo Fujitabeded612016-05-01 20:03:43 +09003399 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
3400 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003401 // ok
3402 } else {
3403 if (err) {
3404 (*err) = "Invalid magic.";
3405 }
3406 return false;
3407 }
3408
Syoyo Fujitabeded612016-05-01 20:03:43 +09003409 unsigned int version; // 4 bytes
3410 unsigned int length; // 4 bytes
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003411 unsigned int model_length; // 4 bytes
3412 unsigned int model_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003413
3414 // @todo { Endian swap for big endian machine. }
Syoyo Fujitabeded612016-05-01 20:03:43 +09003415 memcpy(&version, bytes + 4, 4);
3416 swap4(&version);
3417 memcpy(&length, bytes + 8, 4);
3418 swap4(&length);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003419 memcpy(&model_length, bytes + 12, 4);
3420 swap4(&model_length);
3421 memcpy(&model_format, bytes + 16, 4);
3422 swap4(&model_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003423
Syoyo Fujitae69069d2018-03-15 22:01:18 -05003424 // In case the Bin buffer is not present, the size is exactly 20 + size of
3425 // JSON contents,
3426 // so use "greater than" operator.
3427 if ((20 + model_length > size) || (model_length < 1) ||
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09003428 (model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003429 if (err) {
3430 (*err) = "Invalid glTF binary.";
3431 }
3432 return false;
3433 }
3434
3435 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09003436 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003437 model_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003438
3439 is_binary_ = true;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003440 bin_data_ = bytes + 20 + model_length +
3441 8; // 4 bytes (buffer_length) + 4 bytes(buffer_format)
Syoyo Fujitabeded612016-05-01 20:03:43 +09003442 bin_size_ =
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003443 length - (20 + model_length); // extract header + JSON scene data.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003444
Syoyo Fujitabeded612016-05-01 20:03:43 +09003445 bool ret =
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003446 LoadFromString(model, err, reinterpret_cast<const char *>(&bytes[20]),
3447 model_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003448 if (!ret) {
3449 return ret;
3450 }
3451
3452 return true;
3453}
3454
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003455bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
3456 const std::string &filename,
3457 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003458 std::stringstream ss;
3459
r-lyeh66c10632016-08-29 16:56:18 +02003460 std::ifstream f(filename.c_str(), std::ios::binary);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003461 if (!f) {
3462 ss << "Failed to open file: " << filename << std::endl;
3463 if (err) {
3464 (*err) = ss.str();
3465 }
3466 return false;
3467 }
3468
3469 f.seekg(0, f.end);
3470 size_t sz = static_cast<size_t>(f.tellg());
3471 std::vector<char> buf(sz);
3472
3473 f.seekg(0, f.beg);
3474 f.read(&buf.at(0), static_cast<std::streamsize>(sz));
3475 f.close();
3476
Syoyo Fujitabeded612016-05-01 20:03:43 +09003477 std::string basedir = GetBaseDir(filename);
3478
3479 bool ret = LoadBinaryFromMemory(
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003480 model, err, reinterpret_cast<unsigned char *>(&buf.at(0)),
Luke San Antonio9e36b612016-06-23 14:10:51 -04003481 static_cast<unsigned int>(buf.size()), basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09003482
3483 return ret;
3484}
3485
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003486///////////////////////
3487// GLTF Serialization
3488///////////////////////
3489
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003490// typedef std::pair<std::string, json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003491
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003492template <typename T>
3493static void SerializeNumberProperty(const std::string &key, T number,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003494 json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003495 // obj.insert(
Syoyo Fujita83675312017-12-02 21:14:13 +09003496 // json_object_pair(key, json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003497 // obj[key] = static_cast<double>(number);
3498 obj[key] = number;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003499}
3500
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003501template <typename T>
3502static void SerializeNumberArrayProperty(const std::string &key,
3503 const std::vector<T> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003504 json &obj) {
3505 json o;
Syoyo Fujita83675312017-12-02 21:14:13 +09003506 json vals;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003507
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003508 for (unsigned int i = 0; i < value.size(); ++i) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003509 vals.push_back(static_cast<T>(value[i]));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003510 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02003511 if (!vals.is_null()) {
3512 obj[key] = vals;
3513 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003514}
3515
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003516static void SerializeStringProperty(const std::string &key,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003517 const std::string &value, json &obj) {
Syoyo Fujita83675312017-12-02 21:14:13 +09003518 obj[key] = value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003519}
3520
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003521static void SerializeStringArrayProperty(const std::string &key,
3522 const std::vector<std::string> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003523 json &obj) {
3524 json o;
Syoyo Fujita83675312017-12-02 21:14:13 +09003525 json vals;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003526
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003527 for (unsigned int i = 0; i < value.size(); ++i) {
Syoyo Fujita83675312017-12-02 21:14:13 +09003528 vals.push_back(value[i]);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003529 }
3530
Syoyo Fujita83675312017-12-02 21:14:13 +09003531 obj[key] = vals;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003532}
3533
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003534static bool ValueToJson(const Value &value, json *ret) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003535 json obj;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003536 switch (value.Type()) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003537 case NUMBER_TYPE:
3538 obj = json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003539 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02003540 case INT_TYPE:
3541 obj = json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003542 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02003543 case BOOL_TYPE:
3544 obj = json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003545 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02003546 case STRING_TYPE:
3547 obj = json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003548 break;
3549 case ARRAY_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003550 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
3551 Value elementValue = value.Get(int(i));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003552 json elementJson;
3553 if (ValueToJson(value.Get(int(i)), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02003554 obj.push_back(elementJson);
3555 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003556 break;
3557 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02003558 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003559 // TODO
3560 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02003561 return false;
3562 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003563 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003564 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003565 for (auto &it : objMap) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003566 json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003567 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02003568 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003569 break;
3570 }
3571 case NULL_TYPE:
3572 default:
3573 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02003574 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003575 if (ret) *ret = obj;
Selmar Kokfa7022f2018-04-04 18:10:20 +02003576 return true;
3577}
3578
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003579static void SerializeValue(const std::string &key, const Value &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003580 json &obj) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003581 json ret;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003582 if (ValueToJson(value, &ret)) obj[key] = ret;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003583}
3584
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003585static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
johan bowald30c53472018-03-30 11:49:36 +02003586 json &o) {
3587 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita719d7e42018-03-30 19:26:35 +09003588 std::string encodedData =
3589 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
johan bowald30c53472018-03-30 11:49:36 +02003590 SerializeStringProperty("uri", header + encodedData, o);
3591}
3592
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003593static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003594 const std::string &binFilename) {
3595 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003596 output.write(reinterpret_cast<const char *>(&data[0]),
3597 std::streamsize(data.size()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003598 output.close();
3599}
3600
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003601static void SerializeParameterMap(ParameterMap &param, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003602 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
3603 ++paramIt) {
3604 if (paramIt->second.number_array.size()) {
3605 SerializeNumberArrayProperty<double>(paramIt->first,
3606 paramIt->second.number_array, o);
3607 } else if (paramIt->second.json_double_value.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003608 json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003609 for (std::map<std::string, double>::iterator it =
3610 paramIt->second.json_double_value.begin();
3611 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003612 if (it->first == "index") {
3613 json_double_value[it->first] = paramIt->second.TextureIndex();
3614 } else {
3615 json_double_value[it->first] = it->second;
3616 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003617 }
3618
Syoyo Fujita83675312017-12-02 21:14:13 +09003619 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003620 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003621 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07003622 } else if (paramIt->second.has_number_value) {
3623 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003624 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09003625 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003626 }
3627 }
3628}
3629
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003630static void SerializeExtensionMap(ExtensionMap &extensions, json &o) {
3631 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01003632
3633 json extMap;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003634 for (ExtensionMap::iterator extIt = extensions.begin();
3635 extIt != extensions.end(); ++extIt) {
Selmar09d2ff12018-03-15 17:30:42 +01003636 json extension_values;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003637 SerializeValue(extIt->first, extIt->second, extMap);
Selmar09d2ff12018-03-15 17:30:42 +01003638 }
3639 o["extensions"] = extMap;
3640}
3641
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003642static void SerializeGltfAccessor(Accessor &accessor, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003643 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003644
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003645 if (accessor.byteOffset != 0.0)
3646 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003647
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003648 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
3649 SerializeNumberProperty<size_t>("count", accessor.count, o);
3650 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
3651 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
3652 std::string type;
3653 switch (accessor.type) {
3654 case TINYGLTF_TYPE_SCALAR:
3655 type = "SCALAR";
3656 break;
3657 case TINYGLTF_TYPE_VEC2:
3658 type = "VEC2";
3659 break;
3660 case TINYGLTF_TYPE_VEC3:
3661 type = "VEC3";
3662 break;
3663 case TINYGLTF_TYPE_VEC4:
3664 type = "VEC4";
3665 break;
3666 case TINYGLTF_TYPE_MAT2:
3667 type = "MAT2";
3668 break;
3669 case TINYGLTF_TYPE_MAT3:
3670 type = "MAT3";
3671 break;
3672 case TINYGLTF_TYPE_MAT4:
3673 type = "MAT4";
3674 break;
3675 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003676
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003677 SerializeStringProperty("type", type, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003678}
3679
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003680static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003681 SerializeNumberProperty("sampler", channel.sampler, o);
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003682 json target;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003683 SerializeNumberProperty("node", channel.target_node, target);
3684 SerializeStringProperty("path", channel.target_path, target);
3685
Syoyo Fujita83675312017-12-02 21:14:13 +09003686 o["target"] = target;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003687}
3688
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003689static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003690 SerializeNumberProperty("input", sampler.input, o);
3691 SerializeNumberProperty("output", sampler.output, o);
3692 SerializeStringProperty("interpolation", sampler.interpolation, o);
3693}
3694
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003695static void SerializeGltfAnimation(Animation &animation, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003696 SerializeStringProperty("name", animation.name, o);
Syoyo Fujita83675312017-12-02 21:14:13 +09003697 json channels;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003698 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003699 json channel;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003700 AnimationChannel gltfChannel = animation.channels[i];
3701 SerializeGltfAnimationChannel(gltfChannel, channel);
Syoyo Fujita83675312017-12-02 21:14:13 +09003702 channels.push_back(channel);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003703 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003704 o["channels"] = channels;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003705
Syoyo Fujita83675312017-12-02 21:14:13 +09003706 json samplers;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003707 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003708 json sampler;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003709 AnimationSampler gltfSampler = animation.samplers[i];
3710 SerializeGltfAnimationSampler(gltfSampler, sampler);
Syoyo Fujita83675312017-12-02 21:14:13 +09003711 samplers.push_back(sampler);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003712 }
3713
Syoyo Fujita83675312017-12-02 21:14:13 +09003714 o["samplers"] = samplers;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003715}
3716
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003717static void SerializeGltfAsset(Asset &asset, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003718 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003719 SerializeStringProperty("generator", asset.generator, o);
3720 }
3721
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003722 if (!asset.version.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003723 SerializeStringProperty("version", asset.version, o);
3724 }
3725
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003726 if (asset.extras.Keys().size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003727 SerializeValue("extras", asset.extras, o);
3728 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003729
Selmar09d2ff12018-03-15 17:30:42 +01003730 SerializeExtensionMap(asset.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003731}
3732
johan bowald30c53472018-03-30 11:49:36 +02003733static void SerializeGltfBuffer(Buffer &buffer, json &o) {
3734 SerializeNumberProperty("byteLength", buffer.data.size(), o);
3735 SerializeGltfBufferData(buffer.data, o);
3736
3737 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
3738}
3739
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003740static void SerializeGltfBuffer(Buffer &buffer, json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003741 const std::string &binFilename,
3742 const std::string &binBaseFilename) {
3743 SerializeGltfBufferData(buffer.data, binFilename);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003744 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003745 SerializeStringProperty("uri", binBaseFilename, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003746
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003747 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003748}
3749
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003750static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003751 SerializeNumberProperty("buffer", bufferView.buffer, o);
3752 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003753
Johan Bowaldfaa27222018-03-28 14:44:45 +02003754 // byteStride is optional, minimum allowed is 4
3755 if (bufferView.byteStride >= 4) {
3756 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
3757 }
3758 // byteOffset is optional, default is 0
3759 if (bufferView.byteOffset > 0) {
3760 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
3761 }
3762 // Target is optional, check if it contains a valid value
3763 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
3764 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
3765 SerializeNumberProperty("target", bufferView.target, o);
3766 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003767 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003768 SerializeStringProperty("name", bufferView.name, o);
3769 }
3770}
3771
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003772static void SerializeGltfImage(Image &image, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003773 SerializeStringProperty("uri", image.uri, o);
3774
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003775 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003776 SerializeStringProperty("name", image.name, o);
3777 }
3778}
3779
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003780static void SerializeGltfMaterial(Material &material, json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003781 if (material.extras.Size()) SerializeValue("extras", material.extras, o);
Selmar09d2ff12018-03-15 17:30:42 +01003782 SerializeExtensionMap(material.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003783
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003784 if (material.values.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003785 json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003786 SerializeParameterMap(material.values, pbrMetallicRoughness);
Syoyo Fujita83675312017-12-02 21:14:13 +09003787 o["pbrMetallicRoughness"] = pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003788 }
3789
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003790 SerializeParameterMap(material.additionalValues, o);
3791
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003792 if (material.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003793 SerializeStringProperty("name", material.name, o);
3794 }
3795}
3796
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003797static void SerializeGltfMesh(Mesh &mesh, json &o) {
Syoyo Fujita83675312017-12-02 21:14:13 +09003798 json primitives;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003799 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003800 json primitive;
3801 json attributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003802 Primitive gltfPrimitive = mesh.primitives[i];
3803 for (std::map<std::string, int>::iterator attrIt =
3804 gltfPrimitive.attributes.begin();
3805 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
3806 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
3807 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003808
Syoyo Fujita83675312017-12-02 21:14:13 +09003809 primitive["attributes"] = attributes;
Johan Bowaldfaa27222018-03-28 14:44:45 +02003810
3811 // Indicies is optional
3812 if (gltfPrimitive.indices > -1) {
3813 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
3814 }
3815 // Material is optional
3816 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09003817 SerializeNumberProperty<int>("material", gltfPrimitive.material,
3818 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02003819 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003820 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003821
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003822 // Morph targets
3823 if (gltfPrimitive.targets.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09003824 json targets;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003825 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003826 json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003827 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
3828 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
3829 attrIt != targetData.end(); ++attrIt) {
3830 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
3831 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003832 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003833
Syoyo Fujita83675312017-12-02 21:14:13 +09003834 targets.push_back(targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003835 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003836 primitive["targets"] = targets;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003837 }
3838
Syoyo Fujita83675312017-12-02 21:14:13 +09003839 primitives.push_back(primitive);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003840 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003841
Syoyo Fujita83675312017-12-02 21:14:13 +09003842 o["primitives"] = primitives;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003843 if (mesh.weights.size()) {
3844 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
3845 }
3846
3847 if (mesh.name.size()) {
3848 SerializeStringProperty("name", mesh.name, o);
3849 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003850}
3851
Syoyo Fujita83675312017-12-02 21:14:13 +09003852static void SerializeGltfLight(Light &light, json &o) {
Emanuel Schrade186322b2017-11-06 11:14:41 +01003853 SerializeStringProperty("name", light.name, o);
3854 SerializeNumberArrayProperty("color", light.color, o);
3855 SerializeStringProperty("type", light.type, o);
3856}
3857
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003858static void SerializeGltfNode(Node &node, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003859 if (node.translation.size() > 0) {
3860 SerializeNumberArrayProperty<double>("translation", node.translation, o);
3861 }
3862 if (node.rotation.size() > 0) {
3863 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
3864 }
3865 if (node.scale.size() > 0) {
3866 SerializeNumberArrayProperty<double>("scale", node.scale, o);
3867 }
3868 if (node.matrix.size() > 0) {
3869 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
3870 }
3871 if (node.mesh != -1) {
3872 SerializeNumberProperty<int>("mesh", node.mesh, o);
3873 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003874
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003875 if (node.skin != -1) {
3876 SerializeNumberProperty<int>("skin", node.skin, o);
3877 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003878
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003879 if (node.camera != -1) {
3880 SerializeNumberProperty<int>("camera", node.camera, o);
3881 }
3882
Jens Olssonb96f6962018-05-24 15:29:54 +02003883 if (node.extras.Type() != NULL_TYPE)
3884 SerializeValue("extras", node.extras, o);
3885 }
3886
Selmar09d2ff12018-03-15 17:30:42 +01003887 SerializeExtensionMap(node.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003888 SerializeStringProperty("name", node.name, o);
3889 SerializeNumberArrayProperty<int>("children", node.children, o);
3890}
3891
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003892static void SerializeGltfSampler(Sampler &sampler, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003893 SerializeNumberProperty("magFilter", sampler.magFilter, o);
3894 SerializeNumberProperty("minFilter", sampler.minFilter, o);
3895 SerializeNumberProperty("wrapS", sampler.wrapS, o);
3896 SerializeNumberProperty("wrapT", sampler.wrapT, o);
3897}
3898
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003899static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003900 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003901 SerializeNumberProperty("zfar", camera.zfar, o);
3902 SerializeNumberProperty("znear", camera.znear, o);
3903 SerializeNumberProperty("xmag", camera.xmag, o);
3904 SerializeNumberProperty("ymag", camera.ymag, o);
3905}
3906
3907static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003908 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003909 SerializeNumberProperty("zfar", camera.zfar, o);
3910 SerializeNumberProperty("znear", camera.znear, o);
3911 if (camera.aspectRatio > 0) {
3912 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
3913 }
3914
3915 if (camera.yfov > 0) {
3916 SerializeNumberProperty("yfov", camera.yfov, o);
3917 }
3918}
3919
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003920static void SerializeGltfCamera(const Camera &camera, json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003921 SerializeStringProperty("type", camera.type, o);
3922 if (!camera.name.empty()) {
3923 SerializeStringProperty("name", camera.type, o);
3924 }
3925
3926 if (camera.type.compare("orthographic") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003927 json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003928 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
Syoyo Fujita83675312017-12-02 21:14:13 +09003929 o["orthographic"] = orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003930 } else if (camera.type.compare("perspective") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003931 json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003932 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
Syoyo Fujita83675312017-12-02 21:14:13 +09003933 o["perspective"] = perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003934 } else {
3935 // ???
3936 }
3937}
3938
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003939static void SerializeGltfScene(Scene &scene, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003940 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
3941
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003942 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003943 SerializeStringProperty("name", scene.name, o);
3944 }
Selmar09d2ff12018-03-15 17:30:42 +01003945 if (scene.extras.Keys().size()) {
3946 SerializeValue("extras", scene.extras, o);
3947 }
3948 SerializeExtensionMap(scene.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003949}
3950
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003951static void SerializeGltfSkin(Skin &skin, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003952 if (skin.inverseBindMatrices != -1)
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003953 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
3954
3955 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
3956 SerializeNumberProperty("skeleton", skin.skeleton, o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003957 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003958 SerializeStringProperty("name", skin.name, o);
3959 }
3960}
3961
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003962static void SerializeGltfTexture(Texture &texture, json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02003963 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02003964 SerializeNumberProperty("sampler", texture.sampler, o);
3965 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003966 SerializeNumberProperty("source", texture.source, o);
3967
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003968 if (texture.extras.Size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003969 json extras;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003970 SerializeValue("extras", texture.extras, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003971 }
Selmar Kok9eae1102018-04-04 18:10:37 +02003972 SerializeExtensionMap(texture.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003973}
3974
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003975static void WriteGltfFile(const std::string &output,
3976 const std::string &content) {
3977 std::ofstream gltfFile(output.c_str());
3978 gltfFile << content << std::endl;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003979}
3980
johan bowald642a3432018-04-01 12:37:18 +02003981bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
3982 bool embedImages = false,
3983 bool embedBuffers = false
3984 /*, bool writeBinary*/) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003985 json output;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003986
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003987 // ACCESSORS
Syoyo Fujita83675312017-12-02 21:14:13 +09003988 json accessors;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003989 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003990 json accessor;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003991 SerializeGltfAccessor(model->accessors[i], accessor);
Syoyo Fujita83675312017-12-02 21:14:13 +09003992 accessors.push_back(accessor);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003993 }
Syoyo Fujita83675312017-12-02 21:14:13 +09003994 output["accessors"] = accessors;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003995
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003996 // ANIMATIONS
3997 if (model->animations.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09003998 json animations;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003999 for (unsigned int i = 0; i < model->animations.size(); ++i) {
4000 if (model->animations[i].channels.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004001 json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004002 SerializeGltfAnimation(model->animations[i], animation);
Syoyo Fujita83675312017-12-02 21:14:13 +09004003 animations.push_back(animation);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004004 }
4005 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004006 output["animations"] = animations;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004007 }
4008
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004009 // ASSET
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004010 json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004011 SerializeGltfAsset(model->asset, asset);
Syoyo Fujita83675312017-12-02 21:14:13 +09004012 output["asset"] = asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004013
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004014 std::string binFilename = GetBaseFilename(filename);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004015 std::string ext = ".bin";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004016 std::string::size_type pos = binFilename.rfind('.', binFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004017
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004018 if (pos != std::string::npos) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004019 binFilename = binFilename.substr(0, pos) + ext;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004020 } else {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004021 binFilename = binFilename + ".bin";
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004022 }
johan bowald642a3432018-04-01 12:37:18 +02004023 std::string baseDir = GetBaseDir(filename);
4024 if (baseDir.empty()) {
4025 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004026 }
4027
johan bowald642a3432018-04-01 12:37:18 +02004028 std::string binSaveFilePath = JoinPath(baseDir, binFilename);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004029
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004030 // BUFFERS (We expect only one buffer here)
Syoyo Fujita83675312017-12-02 21:14:13 +09004031 json buffers;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004032 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004033 json buffer;
johan bowald30c53472018-03-30 11:49:36 +02004034 if (embedBuffers) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09004035 SerializeGltfBuffer(model->buffers[i], buffer);
4036 } else {
johan bowald30c53472018-03-30 11:49:36 +02004037 SerializeGltfBuffer(model->buffers[i], buffer, binSaveFilePath,
4038 binFilename);
4039 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004040 buffers.push_back(buffer);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004041 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004042 output["buffers"] = buffers;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004043
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004044 // BUFFERVIEWS
Syoyo Fujita83675312017-12-02 21:14:13 +09004045 json bufferViews;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004046 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004047 json bufferView;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004048 SerializeGltfBufferView(model->bufferViews[i], bufferView);
Syoyo Fujita83675312017-12-02 21:14:13 +09004049 bufferViews.push_back(bufferView);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004050 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004051 output["bufferViews"] = bufferViews;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004052
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004053 // Extensions used
4054 if (model->extensionsUsed.size()) {
4055 SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed,
4056 output);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004057 }
4058
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004059 // Extensions required
4060 if (model->extensionsRequired.size()) {
4061 SerializeStringArrayProperty("extensionsRequired",
4062 model->extensionsRequired, output);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004063 }
4064
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004065 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02004066 if (model->images.size()) {
4067 json images;
4068 for (unsigned int i = 0; i < model->images.size(); ++i) {
4069 json image;
johan bowald642a3432018-04-01 12:37:18 +02004070
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09004071 UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
johan bowald642a3432018-04-01 12:37:18 +02004072 &this->WriteImageData, &this->write_image_user_data_);
Johan Bowaldfaa27222018-03-28 14:44:45 +02004073 SerializeGltfImage(model->images[i], image);
4074 images.push_back(image);
4075 }
4076 output["images"] = images;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004077 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004078
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004079 // MATERIALS
Johan Bowaldfaa27222018-03-28 14:44:45 +02004080 if (model->materials.size()) {
4081 json materials;
4082 for (unsigned int i = 0; i < model->materials.size(); ++i) {
4083 json material;
4084 SerializeGltfMaterial(model->materials[i], material);
4085 materials.push_back(material);
4086 }
4087 output["materials"] = materials;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004088 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004089
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004090 // MESHES
Johan Bowaldfaa27222018-03-28 14:44:45 +02004091 if (model->meshes.size()) {
4092 json meshes;
4093 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
4094 json mesh;
4095 SerializeGltfMesh(model->meshes[i], mesh);
4096 meshes.push_back(mesh);
4097 }
4098 output["meshes"] = meshes;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004099 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004100
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004101 // NODES
Johan Bowaldfaa27222018-03-28 14:44:45 +02004102 if (model->nodes.size()) {
4103 json nodes;
4104 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
4105 json node;
4106 SerializeGltfNode(model->nodes[i], node);
4107 nodes.push_back(node);
4108 }
4109 output["nodes"] = nodes;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004110 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004111
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004112 // SCENE
Johan Bowaldfaa27222018-03-28 14:44:45 +02004113 if (model->defaultScene > -1) {
4114 SerializeNumberProperty<int>("scene", model->defaultScene, output);
4115 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004116
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004117 // SCENES
Johan Bowaldfaa27222018-03-28 14:44:45 +02004118 if (model->scenes.size()) {
4119 json scenes;
4120 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
4121 json currentScene;
4122 SerializeGltfScene(model->scenes[i], currentScene);
4123 scenes.push_back(currentScene);
4124 }
4125 output["scenes"] = scenes;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004126 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004127
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004128 // SKINS
4129 if (model->skins.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09004130 json skins;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004131 for (unsigned int i = 0; i < model->skins.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09004132 json skin;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004133 SerializeGltfSkin(model->skins[i], skin);
Syoyo Fujita83675312017-12-02 21:14:13 +09004134 skins.push_back(skin);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004135 }
Syoyo Fujita83675312017-12-02 21:14:13 +09004136 output["skins"] = skins;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004137 }
4138
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004139 // TEXTURES
Johan Bowaldfaa27222018-03-28 14:44:45 +02004140 if (model->textures.size()) {
4141 json textures;
4142 for (unsigned int i = 0; i < model->textures.size(); ++i) {
4143 json texture;
4144 SerializeGltfTexture(model->textures[i], texture);
4145 textures.push_back(texture);
4146 }
4147 output["textures"] = textures;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004148 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004149
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004150 // SAMPLERS
Johan Bowaldfaa27222018-03-28 14:44:45 +02004151 if (model->samplers.size()) {
4152 json samplers;
4153 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
4154 json sampler;
4155 SerializeGltfSampler(model->samplers[i], sampler);
4156 samplers.push_back(sampler);
4157 }
4158 output["samplers"] = samplers;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004159 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004160
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004161 // CAMERAS
Johan Bowaldfaa27222018-03-28 14:44:45 +02004162 if (model->cameras.size()) {
4163 json cameras;
4164 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
4165 json camera;
4166 SerializeGltfCamera(model->cameras[i], camera);
4167 cameras.push_back(camera);
4168 }
4169 output["cameras"] = cameras;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004170 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004171
Selmar09d2ff12018-03-15 17:30:42 +01004172 // EXTENSIONS
4173 SerializeExtensionMap(model->extensions, output);
4174
Syoyo Fujitad5b02442018-03-17 16:12:42 -05004175 // LIGHTS as KHR_lights_cmn
Johan Bowaldfaa27222018-03-28 14:44:45 +02004176 if (model->lights.size()) {
4177 json lights;
4178 for (unsigned int i = 0; i < model->lights.size(); ++i) {
4179 json light;
4180 SerializeGltfLight(model->lights[i], light);
4181 lights.push_back(light);
4182 }
Syoyo Fujitad5b02442018-03-17 16:12:42 -05004183 json khr_lights_cmn;
4184 khr_lights_cmn["lights"] = lights;
4185 json ext_j;
4186
4187 if (output.find("extensions") != output.end()) {
4188 ext_j = output["extensions"];
4189 }
4190
4191 ext_j["KHR_lights_cmn"] = khr_lights_cmn;
4192
4193 output["extensions"] = ext_j;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004194 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01004195
Syoyo Fujita83675312017-12-02 21:14:13 +09004196 WriteGltfFile(filename, output.dump());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00004197 return true;
4198}
4199
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09004200} // namespace tinygltf
4201
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004202#ifdef __clang__
4203#pragma clang diagnostic pop
4204#endif
4205
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004206#endif // TINYGLTF_IMPLEMENTATION