blob: 629c79185db3c112a38a6dabf95dc91b2c231275 [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 Fujita5b407452017-06-04 17:42:41 +09007// Copyright (c) 2015 - 2017 Syoyo Fujita, Aurélien Chatelain and many
8// 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//
33// - picojson: C++ JSON library.
34// - 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 Fujitaf696c6f2016-10-17 03:48:55 +090040#include <cassert>
Syoyo Fujita7680abf2016-10-17 18:02:45 +090041#include <cstring>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090042#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090043#include <string>
44#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090045
46namespace tinygltf {
47
48#define TINYGLTF_MODE_POINTS (0)
49#define TINYGLTF_MODE_LINE (1)
50#define TINYGLTF_MODE_LINE_LOOP (2)
51#define TINYGLTF_MODE_TRIANGLES (4)
52#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
53#define TINYGLTF_MODE_TRIANGLE_FAN (6)
54
55#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
56#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
57#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
58#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
59#define TINYGLTF_COMPONENT_TYPE_INT (5124)
60#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
61#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
62#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5127)
63
Syoyo Fujitac2615632016-06-19 21:56:06 +090064#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
65#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
66#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
67#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
68#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
69#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
70
71#define TINYGLTF_TEXTURE_WRAP_RPEAT (10497)
72#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -040073#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +090074
Luke San Antoniocdf4cb72016-06-14 21:32:11 -040075// Redeclarations of the above for technique.parameters.
76#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
77#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
78#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
79#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
80#define TINYGLTF_PARAMETER_TYPE_INT (5124)
81#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
82#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
83
84#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
85#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
86#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
87
88#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
89#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
90#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
91
92#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
93#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
94#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
95#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
96
97#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
98#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
99#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
100
101#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
102
103// End parameter types
104
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900105#define TINYGLTF_TYPE_VEC2 (2)
106#define TINYGLTF_TYPE_VEC3 (3)
107#define TINYGLTF_TYPE_VEC4 (4)
108#define TINYGLTF_TYPE_MAT2 (32 + 2)
109#define TINYGLTF_TYPE_MAT3 (32 + 3)
110#define TINYGLTF_TYPE_MAT4 (32 + 4)
111#define TINYGLTF_TYPE_SCALAR (64 + 1)
112#define TINYGLTF_TYPE_VECTOR (64 + 4)
113#define TINYGLTF_TYPE_MATRIX (64 + 16)
114
115#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
116#define TINYGLTF_IMAGE_FORMAT_PNG (1)
117#define TINYGLTF_IMAGE_FORMAT_BMP (2)
118#define TINYGLTF_IMAGE_FORMAT_GIF (3)
119
Luke San Antonio6d616f52016-06-23 14:09:23 -0400120#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
121#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900122#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400123#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
124#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
125
Syoyo Fujitabde70212016-02-07 17:38:17 +0900126#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
127#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
128
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900129#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
130#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
131
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400132#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
133#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
134
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900135typedef enum {
136 NULL_TYPE = 0,
137 NUMBER_TYPE = 1,
138 INT_TYPE = 2,
139 BOOL_TYPE = 3,
140 STRING_TYPE = 4,
141 ARRAY_TYPE = 5,
142 BINARY_TYPE = 6,
143 OBJECT_TYPE = 7
144} Type;
145
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900146#ifdef __clang__
147#pragma clang diagnostic push
148// Suppress warning for : static Value null_value
149// https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang
150#pragma clang diagnostic ignored "-Wexit-time-destructors"
151#endif
152
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900153// Simple class to represent JSON object
154class Value {
155 public:
156 typedef std::vector<Value> Array;
157 typedef std::map<std::string, Value> Object;
158
159 Value() : type_(NULL_TYPE) {}
160
161 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900162 explicit Value(int i) : type_(INT_TYPE) { int_value_ = i; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900163 explicit Value(double n) : type_(NUMBER_TYPE) { number_value_ = n; }
164 explicit Value(const std::string &s) : type_(STRING_TYPE) {
165 string_value_ = s;
166 }
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900167 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900168 binary_value_.resize(n);
169 memcpy(binary_value_.data(), p, n);
170 }
171 explicit Value(const Array &a) : type_(ARRAY_TYPE) {
172 array_value_ = Array(a);
173 }
174 explicit Value(const Object &o) : type_(OBJECT_TYPE) {
175 object_value_ = Object(o);
176 }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900177
178 char Type() const { return static_cast<const char>(type_); }
179
180 bool IsBool() const { return (type_ == BOOL_TYPE); }
181
182 bool IsInt() const { return (type_ == INT_TYPE); }
183
184 bool IsNumber() const { return (type_ == NUMBER_TYPE); }
185
186 bool IsString() const { return (type_ == STRING_TYPE); }
187
188 bool IsBinary() const { return (type_ == BINARY_TYPE); }
189
190 bool IsArray() const { return (type_ == ARRAY_TYPE); }
191
192 bool IsObject() const { return (type_ == OBJECT_TYPE); }
193
194 // Accessor
195 template <typename T>
196 const T &Get() const;
197 template <typename T>
198 T &Get();
199
200 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900201 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900202 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900203 assert(IsArray());
204 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900205 return (static_cast<size_t>(idx) < array_value_.size())
206 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900207 : null_value;
208 }
209
210 // Lookup value from a key-value pair
211 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900212 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900213 assert(IsObject());
214 Object::const_iterator it = object_value_.find(key);
215 return (it != object_value_.end()) ? it->second : null_value;
216 }
217
218 size_t ArrayLen() const {
219 if (!IsArray()) return 0;
220 return array_value_.size();
221 }
222
223 // Valid only for object type.
224 bool Has(const std::string &key) const {
225 if (!IsObject()) return false;
226 Object::const_iterator it = object_value_.find(key);
227 return (it != object_value_.end()) ? true : false;
228 }
229
230 // List keys
231 std::vector<std::string> Keys() const {
232 std::vector<std::string> keys;
233 if (!IsObject()) return keys; // empty
234
235 for (Object::const_iterator it = object_value_.begin();
236 it != object_value_.end(); ++it) {
237 keys.push_back(it->first);
238 }
239
240 return keys;
241 }
242
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900243 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000244
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900245 protected:
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900246 int type_;
247
248 int int_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900249 double number_value_;
250 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900251 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900252 Array array_value_;
253 Object object_value_;
254 bool boolean_value_;
255 char pad[3];
256
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900257 int pad0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900258};
259
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900260#ifdef __clang__
261#pragma clang diagnostic pop
262#endif
263
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900264#define TINYGLTF_VALUE_GET(ctype, var) \
265 template <> \
266 inline const ctype &Value::Get<ctype>() const { \
267 return var; \
268 } \
269 template <> \
270 inline ctype &Value::Get<ctype>() { \
271 return var; \
272 }
273TINYGLTF_VALUE_GET(bool, boolean_value_)
274TINYGLTF_VALUE_GET(double, number_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900275TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900276TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900277TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900278TINYGLTF_VALUE_GET(Value::Array, array_value_)
279TINYGLTF_VALUE_GET(Value::Object, object_value_)
280#undef TINYGLTF_VALUE_GET
281
Syoyo Fujitabde70212016-02-07 17:38:17 +0900282typedef struct {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000283 bool bool_value;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900284 std::string string_value;
285 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000286 std::map<std::string, double> json_double_value;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900287} Parameter;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900288
Syoyo Fujitabde70212016-02-07 17:38:17 +0900289typedef std::map<std::string, Parameter> ParameterMap;
290
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000291struct AnimationChannel {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900292 int sampler; // required
293 int target_node; // required (index of the node to target)
294 std::string target_path; // required in ["translation", "rotation", "scale",
295 // "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900296 Value extras;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900297
Syoyo Fujita5b407452017-06-04 17:42:41 +0900298 AnimationChannel() : sampler(-1), target_node(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000299};
300
301struct AnimationSampler {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900302 int input; // required
303 int output; // required
304 std::string interpolation; // in ["LINEAR", "STEP", "CATMULLROMSPLINE",
305 // "CUBICSPLINE"], default "LINEAR"
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000306
Syoyo Fujita5b407452017-06-04 17:42:41 +0900307 AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000308};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900309
310typedef struct {
311 std::string name;
312 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000313 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900314 Value extras;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900315} Animation;
316
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000317struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900318 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900319 int inverseBindMatrices; // required here but not in the spec
320 int skeleton; // The index of the node used as a skeleton root
321 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000322
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900323 Skin() {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000324 inverseBindMatrices = -1;
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +0000325 skeleton = -1;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000326 }
327};
328
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000329struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900330 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900331 int minFilter; // ["NEAREST", "LINEAR", "NEAREST_MIPMAP_LINEAR",
332 // "LINEAR_MIPMAP_NEAREST", "NEAREST_MIPMAP_LINEAR",
333 // "LINEAR_MIPMAP_LINEAR"]
334 int magFilter; // ["NEAREST", "LINEAR"]
335 int wrapS; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default
336 // "REPEAT"
337 int wrapT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default
338 // "REPEAT"
339 int wrapR; // TinyGLTF extension
Syoyo Fujitacf6e07b2016-06-21 21:18:12 +0900340 int pad0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900341 Value extras;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900342
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000343 Sampler()
Syoyo Fujita5b407452017-06-04 17:42:41 +0900344 : wrapS(TINYGLTF_TEXTURE_WRAP_RPEAT),
345 wrapT(TINYGLTF_TEXTURE_WRAP_RPEAT) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000346};
347
Syoyo Fujita5b407452017-06-04 17:42:41 +0900348struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900349 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900350 int width;
351 int height;
352 int component;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900353 int pad0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900354 std::vector<unsigned char> image;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900355 int bufferView; // (required if no uri)
356 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png"]
357 std::string uri; // (reqiored if no mimeType)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900358 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900359
Syoyo Fujita5b407452017-06-04 17:42:41 +0900360 Image() { bufferView = -1; }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000361};
362
363struct Texture {
364 int sampler;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900365 int source; // Required (not specified in the spec ?)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900366 Value extras;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900367
Syoyo Fujita5b407452017-06-04 17:42:41 +0900368 Texture() : sampler(-1), source(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000369};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900370
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000371// Each extension should be stored in a ParameterMap.
372// members not in the values could be included in the ParameterMap
373// to keep a single material model
374struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900375 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900376
Syoyo Fujita5b407452017-06-04 17:42:41 +0900377 ParameterMap values; // PBR metal/roughness workflow
378 ParameterMap additionalValues; // normal/occlusion/emissive values
379 ParameterMap extCommonValues; // KHR_common_material extension
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000380 ParameterMap extPBRValues;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900381 Value extras;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000382};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900383
Syoyo Fujita5b407452017-06-04 17:42:41 +0900384struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900385 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900386 int buffer; // Required
387 size_t byteOffset; // minimum 0, default 0
388 size_t byteLength; // required, minimum 1
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900389 size_t byteStride; // minimum 4, maximum 252 (multiple of 4), default 0 =
390 // understood to be tightly packed
Syoyo Fujita5b407452017-06-04 17:42:41 +0900391 int target; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"]
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900392 int pad0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900393 Value extras;
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900394
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900395 BufferView() : byteOffset(0), byteStride(0) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000396};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900397
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000398struct Accessor {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900399 int bufferView; // optional in spec but required here since sparse accessor
400 // are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900401 std::string name;
402 size_t byteOffset;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900403 bool normalized; // optinal.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000404 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900405 size_t count; // required
406 int type; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900407 Value extras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000408
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900409 std::vector<double> minValues; // optional
410 std::vector<double> maxValues; // optional
411
412 // TODO(syoyo): "sparse"
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000413
Syoyo Fujita5b407452017-06-04 17:42:41 +0900414 Accessor() { bufferView = -1; }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000415};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900416
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900417struct PerspectiveCamera {
418 float aspectRatio; // min > 0
419 float yfov; // required. min > 0
420 float zfar; // min > 0
421 float znear; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900422
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900423 PerspectiveCamera()
424 : aspectRatio(0.0f),
425 yfov(0.0f),
426 zfar(0.0f) // 0 = use infinite projecton matrix
427 ,
428 znear(0.0f) {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900429
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900430 ParameterMap extensions;
431 Value extras;
432};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000433
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900434struct OrthographicCamera {
435 float xmag; // required. must not be zero.
436 float ymag; // required. must not be zero.
437 float zfar; // required. `zfar` must be greater than `znear`.
Syoyo Fujita5b407452017-06-04 17:42:41 +0900438 float znear; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000439
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900440 OrthographicCamera() : xmag(0.0f), ymag(0.0f), zfar(0.0f), znear(0.0f) {}
441
442 ParameterMap extensions;
443 Value extras;
444};
445
446struct Camera {
447 std::string type; // required. "perspective" or "orthographic"
448 std::string name;
449
450 PerspectiveCamera perspective;
451 OrthographicCamera orthographic;
452
453 Camera() {}
454
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000455 ParameterMap extensions;
456 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900457};
458
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000459struct Primitive {
460 std::map<std::string, int> attributes; // (required) A dictionary object of
461 // integer, where each integer
462 // is the index of the accessor
463 // containing an attribute.
464 int material; // The index of the material to apply to this primitive
Syoyo Fujita5b407452017-06-04 17:42:41 +0900465 // when rendering.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000466 int indices; // The index of the accessor that contains the indices.
467 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900468 std::vector<std::map<std::string, int> > targets; // array of morph targets,
469 // where each target is a dict with attribues in ["POSITION, "NORMAL",
470 // "TANGENT"] pointing
471 // to their corresponding accessors
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000472 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900473
Syoyo Fujita5b407452017-06-04 17:42:41 +0900474 Primitive() {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000475 material = -1;
476 indices = -1;
477 }
478};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900479
480typedef struct {
481 std::string name;
482 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900483 std::vector<double> weights; // weights to be applied to the Morph Targets
484 std::vector<std::map<std::string, int> > targets;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000485 ParameterMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900486 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900487} Mesh;
488
489class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900490 public:
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900491 Node() : camera(-1), skin(-1), mesh(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000492
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900493 ~Node() {}
494
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000495 int camera; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900496
497 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000498 int skin;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000499 int mesh;
500 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900501 std::vector<double> rotation; // length must be 0 or 4
502 std::vector<double> scale; // length must be 0 or 3
503 std::vector<double> translation; // length must be 0 or 3
504 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +0900505 std::vector<double> weights; // The weights of the instantiated Morph Target
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900506
507 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900508};
509
510typedef struct {
511 std::string name;
512 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900513 std::string
514 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900515 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900516} Buffer;
517
518typedef struct {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900519 std::string version; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900520 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +0000521 std::string minVersion;
522 std::string copyright;
523 ParameterMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900524 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900525} Asset;
526
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000527struct Scene {
528 std::string name;
529 std::vector<int> nodes;
530
531 ParameterMap extensions;
532 ParameterMap extras;
533};
534
535class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900536 public:
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000537 Model() {}
538 ~Model() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900539
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000540 std::vector<Accessor> accessors;
541 std::vector<Animation> animations;
542 std::vector<Buffer> buffers;
543 std::vector<BufferView> bufferViews;
544 std::vector<Material> materials;
545 std::vector<Mesh> meshes;
546 std::vector<Node> nodes;
547 std::vector<Texture> textures;
548 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000549 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000550 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900551 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000552 std::vector<Scene> scenes;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900553
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000554 int defaultScene;
555 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +0000556 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900557
558 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900559
560 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900561};
562
Syoyo Fujita0614eb82016-10-14 18:50:14 +0900563enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -0400564 NO_REQUIRE = 0x00,
565 REQUIRE_SCENE = 0x01,
566 REQUIRE_SCENES = 0x02,
567 REQUIRE_NODES = 0x04,
568 REQUIRE_ACCESSORS = 0x08,
569 REQUIRE_BUFFERS = 0x10,
570 REQUIRE_BUFFER_VIEWS = 0x20,
571 REQUIRE_ALL = 0x3f
572};
573
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900574class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900575 public:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900576 TinyGLTF() : bin_data_(NULL), bin_size_(0), is_binary_(false) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900577 pad[0] = pad[1] = pad[2] = pad[3] = pad[4] = pad[5] = pad[6] = 0;
578 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900579 ~TinyGLTF() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900580
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900581 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900582 /// Loads glTF ASCII asset from a file.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900583 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900584 ///
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000585 bool LoadASCIIFromFile(Model *model, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400586 const std::string &filename,
587 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900588
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900589 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900590 /// Loads glTF ASCII asset from string(memory).
591 /// `length` = strlen(str);
592 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900593 ///
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000594 bool LoadASCIIFromString(Model *model, std::string *err, const char *str,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900595 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400596 const std::string &base_dir,
597 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900598
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900599 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900600 /// Loads glTF binary asset from a file.
601 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900602 ///
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000603 bool LoadBinaryFromFile(Model *model, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400604 const std::string &filename,
605 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900606
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900607 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900608 /// Loads glTF binary asset from memory.
609 /// `length` = strlen(str);
610 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900611 ///
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000612 bool LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900613 const unsigned char *bytes,
614 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400615 const std::string &base_dir = "",
616 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900617
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900618 ///
619 /// Write glTF to file.
620 ///
621 bool WriteGltfSceneToFile(
622 Model *model,
623 const std::string &
624 filename /*, bool embedImages, bool embedBuffers, bool writeBinary*/);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000625
Syoyo Fujitabeded612016-05-01 20:03:43 +0900626 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900627 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900628 /// Loads glTF asset from string(memory).
629 /// `length` = strlen(str);
630 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900631 ///
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000632 bool LoadFromString(Model *model, std::string *err, const char *str,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400633 const unsigned int length, const std::string &base_dir,
634 unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900635
Syoyo Fujitabeded612016-05-01 20:03:43 +0900636 const unsigned char *bin_data_;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900637 size_t bin_size_;
638 bool is_binary_;
639 char pad[7];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900640};
641
Syoyo Fujita7c877972016-03-08 01:31:49 +0900642} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900643
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900644#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900645
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900646#ifdef TINYGLTF_IMPLEMENTATION
Syoyo Fujita7c877972016-03-08 01:31:49 +0900647#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900648//#include <cassert>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900649#include <fstream>
650#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +0900651
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900652#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +0900653// Disable some warnings for external files.
654#pragma clang diagnostic push
655#pragma clang diagnostic ignored "-Wfloat-equal"
656#pragma clang diagnostic ignored "-Wexit-time-destructors"
657#pragma clang diagnostic ignored "-Wconversion"
658#pragma clang diagnostic ignored "-Wold-style-cast"
659#pragma clang diagnostic ignored "-Wdouble-promotion"
660#pragma clang diagnostic ignored "-Wglobal-constructors"
661#pragma clang diagnostic ignored "-Wreserved-id-macro"
662#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
663#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900664#ifdef __APPLE__
665#if __clang_major__ >= 8 && __clang_minor__ >= 1
666#pragma clang diagnostic ignored "-Wcomma"
667#endif
668#else // __APPLE__
669#if (__clang_major__ >= 4) || (__clang_major__ >= 3 && __clang_minor__ > 8)
670#pragma clang diagnostic ignored "-Wcomma"
671#endif
672#endif // __APPLE__
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900673#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900674
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900675#define PICOJSON_USE_INT64
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +0900676#include "./picojson.h"
677#include "./stb_image.h"
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900678#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +0900679#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900680#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900681
682#ifdef _WIN32
683#include <Windows.h>
684#else
685#include <wordexp.h>
686#endif
687
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900688#if defined(__sparcv9)
689// Big endian
690#else
691#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
692#define TINYGLTF_LITTLE_ENDIAN 1
693#endif
694#endif
695
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +0900696namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900697
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900698static void swap4(unsigned int *val) {
699#ifdef TINYGLTF_LITTLE_ENDIAN
700 (void)val;
701#else
702 unsigned int tmp = *val;
703 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
704 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
705
706 dst[0] = src[3];
707 dst[1] = src[2];
708 dst[2] = src[1];
709 dst[3] = src[0];
710#endif
711}
712
Syoyo Fujita643ce102016-05-01 17:19:37 +0900713static bool FileExists(const std::string &abs_filename) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900714 bool ret;
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900715#ifdef _WIN32
716 FILE *fp;
717 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
718 if (err != 0) {
Syoyo Fujitabeded612016-05-01 20:03:43 +0900719 return false;
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900720 }
721#else
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900722 FILE *fp = fopen(abs_filename.c_str(), "rb");
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900723#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900724 if (fp) {
725 ret = true;
726 fclose(fp);
727 } else {
728 ret = false;
729 }
730
731 return ret;
732}
733
Syoyo Fujita643ce102016-05-01 17:19:37 +0900734static std::string ExpandFilePath(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900735#ifdef _WIN32
736 DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
737 char *str = new char[len];
738 ExpandEnvironmentStringsA(filepath.c_str(), str, len);
739
740 std::string s(str);
741
742 delete[] str;
743
744 return s;
745#else
746
Syoyo Fujita643ce102016-05-01 17:19:37 +0900747#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900748 // no expansion
749 std::string s = filepath;
750#else
751 std::string s;
752 wordexp_t p;
753
754 if (filepath.empty()) {
755 return "";
756 }
757
758 // char** w;
759 int ret = wordexp(filepath.c_str(), &p, 0);
760 if (ret) {
761 // err
762 s = filepath;
763 return s;
764 }
765
766 // Use first element only.
767 if (p.we_wordv) {
768 s = std::string(p.we_wordv[0]);
769 wordfree(&p);
770 } else {
771 s = filepath;
772 }
773
774#endif
775
776 return s;
777#endif
778}
779
Syoyo Fujitabeded612016-05-01 20:03:43 +0900780static std::string JoinPath(const std::string &path0,
781 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900782 if (path0.empty()) {
783 return path1;
784 } else {
785 // check '/'
786 char lastChar = *path0.rbegin();
787 if (lastChar != '/') {
788 return path0 + std::string("/") + path1;
789 } else {
790 return path0 + path1;
791 }
792 }
793}
794
Syoyo Fujita643ce102016-05-01 17:19:37 +0900795static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900796 const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900797 for (size_t i = 0; i < paths.size(); i++) {
798 std::string absPath = ExpandFilePath(JoinPath(paths[i], filepath));
799 if (FileExists(absPath)) {
800 return absPath;
801 }
802 }
803
804 return std::string();
805}
806
807// std::string GetFilePathExtension(const std::string& FileName)
808//{
809// if(FileName.find_last_of(".") != std::string::npos)
810// return FileName.substr(FileName.find_last_of(".")+1);
811// return "";
812//}
813
Syoyo Fujita643ce102016-05-01 17:19:37 +0900814static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900815 if (filepath.find_last_of("/\\") != std::string::npos)
816 return filepath.substr(0, filepath.find_last_of("/\\"));
817 return "";
818}
819
820// std::string base64_encode(unsigned char const* , unsigned int len);
821std::string base64_decode(std::string const &s);
822
823/*
824 base64.cpp and base64.h
825
826 Copyright (C) 2004-2008 René Nyffenegger
827
828 This source code is provided 'as-is', without any express or implied
829 warranty. In no event will the author be held liable for any damages
830 arising from the use of this software.
831
832 Permission is granted to anyone to use this software for any purpose,
833 including commercial applications, and to alter it and redistribute it
834 freely, subject to the following restrictions:
835
836 1. The origin of this source code must not be misrepresented; you must not
837 claim that you wrote the original source code. If you use this source code
838 in a product, an acknowledgment in the product documentation would be
839 appreciated but is not required.
840
841 2. Altered source versions must be plainly marked as such, and must not be
842 misrepresented as being the original source code.
843
844 3. This notice may not be removed or altered from any source distribution.
845
846 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
847
848*/
849
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900850#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +0900851#pragma clang diagnostic push
852#pragma clang diagnostic ignored "-Wexit-time-destructors"
853#pragma clang diagnostic ignored "-Wglobal-constructors"
854#pragma clang diagnostic ignored "-Wsign-conversion"
855#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900856#endif
Syoyo Fujita7c877972016-03-08 01:31:49 +0900857static const std::string base64_chars =
858 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
859 "abcdefghijklmnopqrstuvwxyz"
860 "0123456789+/";
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900861
862static inline bool is_base64(unsigned char c) {
863 return (isalnum(c) || (c == '+') || (c == '/'));
864}
865
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900866std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +0900867 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900868 int i = 0;
869 int j = 0;
870 int in_ = 0;
871 unsigned char char_array_4[4], char_array_3[3];
872 std::string ret;
873
874 while (in_len-- && (encoded_string[in_] != '=') &&
875 is_base64(encoded_string[in_])) {
876 char_array_4[i++] = encoded_string[in_];
877 in_++;
878 if (i == 4) {
879 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +0900880 char_array_4[i] =
881 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900882
883 char_array_3[0] =
884 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
885 char_array_3[1] =
886 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
887 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
888
Syoyo Fujita7c877972016-03-08 01:31:49 +0900889 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900890 i = 0;
891 }
892 }
893
894 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900895 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900896
897 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +0900898 char_array_4[j] =
899 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900900
901 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
902 char_array_3[1] =
903 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
904 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
905
Syoyo Fujita7c877972016-03-08 01:31:49 +0900906 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900907 }
908
909 return ret;
910}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900911#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +0900912#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900913#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900914
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900915static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900916 const std::string &filename,
917 const std::string &basedir, size_t reqBytes,
918 bool checkSize) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900919 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900920
921 std::vector<std::string> paths;
922 paths.push_back(basedir);
923 paths.push_back(".");
924
925 std::string filepath = FindFile(paths, filename);
926 if (filepath.empty()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900927 if (err) {
Luke San Antonioda8b5d12016-06-14 22:17:40 -0400928 (*err) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900929 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900930 return false;
931 }
932
933 std::ifstream f(filepath.c_str(), std::ifstream::binary);
934 if (!f) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900935 if (err) {
Luke San Antonioda8b5d12016-06-14 22:17:40 -0400936 (*err) += "File open error : " + filepath + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900937 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900938 return false;
939 }
940
941 f.seekg(0, f.end);
Syoyo Fujita643ce102016-05-01 17:19:37 +0900942 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +0900943 if (int(sz) < 0) {
944 // Looks reading directory, not a file.
945 return false;
946 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +0900947
948 if (sz == 0) {
949 // Invalid file size.
950 return false;
951 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900952 std::vector<unsigned char> buf(sz);
953
954 f.seekg(0, f.beg);
Syoyo Fujitabeded612016-05-01 20:03:43 +0900955 f.read(reinterpret_cast<char *>(&buf.at(0)),
956 static_cast<std::streamsize>(sz));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900957 f.close();
958
959 if (checkSize) {
960 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900961 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900962 return true;
963 } else {
964 std::stringstream ss;
965 ss << "File size mismatch : " << filepath << ", requestedBytes "
966 << reqBytes << ", but got " << sz << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900967 if (err) {
968 (*err) += ss.str();
969 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900970 return false;
971 }
972 }
973
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900974 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900975 return true;
976}
977
Syoyo Fujitabeded612016-05-01 20:03:43 +0900978static bool LoadImageData(Image *image, std::string *err, int req_width,
979 int req_height, const unsigned char *bytes,
980 int size) {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900981 std::cout << "size " << size << std::endl;
982
Syoyo Fujitabeded612016-05-01 20:03:43 +0900983 int w, h, comp;
Aurélien Chatelain5cb43462017-05-24 09:55:14 +0000984 // if image cannot be decoded, ignore parsing and keep it by its path
985 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +0900986 // FIXME we should only enter this function if the image is embedded. If
987 // image->uri references
988 // an image file, it should be left as it is. Image loading should not be
989 // mandatory (to support other formats)
Syoyo Fujitabeded612016-05-01 20:03:43 +0900990 unsigned char *data = stbi_load_from_memory(bytes, size, &w, &h, &comp, 0);
991 if (!data) {
992 if (err) {
993 (*err) += "Unknown image format.\n";
994 }
Aurélien Chatelain5cb43462017-05-24 09:55:14 +0000995 return true;
Syoyo Fujitabeded612016-05-01 20:03:43 +0900996 }
997
998 if (w < 1 || h < 1) {
Syoyo Fujita4be2f882016-11-24 16:20:34 +0900999 free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001000 if (err) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09001001 (*err) += "Invalid image data.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001002 }
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00001003 return true;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001004 }
1005
1006 if (req_width > 0) {
1007 if (req_width != w) {
Syoyo Fujita4be2f882016-11-24 16:20:34 +09001008 free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001009 if (err) {
1010 (*err) += "Image width mismatch.\n";
1011 }
1012 return false;
1013 }
1014 }
1015
1016 if (req_height > 0) {
1017 if (req_height != h) {
Syoyo Fujita4be2f882016-11-24 16:20:34 +09001018 free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001019 if (err) {
1020 (*err) += "Image height mismatch.\n";
1021 }
1022 return false;
1023 }
1024 }
1025
1026 image->width = w;
1027 image->height = h;
1028 image->component = comp;
1029 image->image.resize(static_cast<size_t>(w * h * comp));
1030 std::copy(data, data + w * h * comp, image->image.begin());
1031
Syoyo Fujita4be2f882016-11-24 16:20:34 +09001032 free(data);
1033
Syoyo Fujitabeded612016-05-01 20:03:43 +09001034 return true;
1035}
1036
Syoyo Fujita643ce102016-05-01 17:19:37 +09001037static bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001038 std::string header = "data:application/octet-stream;base64,";
1039 if (in.find(header) == 0) {
1040 return true;
1041 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001042
1043 header = "data:image/png;base64,";
1044 if (in.find(header) == 0) {
1045 return true;
1046 }
1047
1048 header = "data:image/jpeg;base64,";
1049 if (in.find(header) == 0) {
1050 return true;
1051 }
1052
Luke San Antoniocaa24b02016-06-15 02:23:09 -04001053 header = "data:text/plain;base64,";
1054 if (in.find(header) == 0) {
1055 return true;
1056 }
1057
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001058 return false;
1059}
1060
Syoyo Fujitabeded612016-05-01 20:03:43 +09001061static bool DecodeDataURI(std::vector<unsigned char> *out,
1062 const std::string &in, size_t reqBytes,
1063 bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001064 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09001065 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001066 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001067 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001068 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001069
1070 if (data.empty()) {
1071 header = "data:image/jpeg;base64,";
1072 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001073 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09001074 }
1075 }
1076
1077 if (data.empty()) {
1078 header = "data:image/png;base64,";
1079 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001080 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09001081 }
1082 }
1083
1084 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04001085 header = "data:text/plain;base64,";
1086 if (in.find(header) == 0) {
1087 data = base64_decode(in.substr(header.size()));
1088 }
1089 }
1090
1091 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09001092 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09001093 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001094
1095 if (checkSize) {
1096 if (data.size() != reqBytes) {
1097 return false;
1098 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001099 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09001100 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001101 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09001102 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001103 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09001104 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001105}
1106
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001107static void ParseObjectProperty(Value *ret, const picojson::object &o) {
1108 tinygltf::Value::Object vo;
1109 picojson::object::const_iterator it(o.begin());
1110 picojson::object::const_iterator itEnd(o.end());
1111
1112 for (; it != itEnd; it++) {
1113 picojson::value v = it->second;
1114
1115 if (v.is<bool>()) {
1116 vo[it->first] = tinygltf::Value(v.get<bool>());
1117 } else if (v.is<double>()) {
1118 vo[it->first] = tinygltf::Value(v.get<double>());
1119 } else if (v.is<int64_t>()) {
Syoyo Fujita7680abf2016-10-17 18:02:45 +09001120 vo[it->first] =
1121 tinygltf::Value(static_cast<int>(v.get<int64_t>())); // truncate
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001122 } else if (v.is<std::string>()) {
1123 vo[it->first] = tinygltf::Value(v.get<std::string>());
1124 } else if (v.is<picojson::object>()) {
1125 tinygltf::Value child_value;
1126 ParseObjectProperty(&child_value, v.get<picojson::object>());
Layla1dbfe0e2017-05-07 16:56:55 -04001127 vo[it->first] = child_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001128 }
1129 // TODO(syoyo) binary, array
1130 }
1131
1132 (*ret) = tinygltf::Value(vo);
1133}
1134
1135static bool ParseExtrasProperty(Value *ret, const picojson::object &o) {
1136 picojson::object::const_iterator it = o.find("extras");
1137 if (it == o.end()) {
1138 return false;
1139 }
1140
1141 // FIXME(syoyo) Currently we only support `object` type for extras property.
1142 if (!it->second.is<picojson::object>()) {
1143 return false;
1144 }
1145
1146 ParseObjectProperty(ret, it->second.get<picojson::object>());
1147
1148 return true;
1149}
1150
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001151static bool ParseBooleanProperty(bool *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001152 const picojson::object &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09001153 const std::string &property,
1154 const bool required,
1155 const std::string &parent_node = "") {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001156 picojson::object::const_iterator it = o.find(property);
1157 if (it == o.end()) {
1158 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001159 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09001160 (*err) += "'" + property + "' property is missing";
1161 if (!parent_node.empty()) {
1162 (*err) += " in " + parent_node;
1163 }
1164 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001165 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001166 }
1167 return false;
1168 }
1169
1170 if (!it->second.is<bool>()) {
1171 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001172 if (err) {
1173 (*err) += "'" + property + "' property is not a bool type.\n";
1174 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001175 }
1176 return false;
1177 }
1178
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001179 if (ret) {
1180 (*ret) = it->second.get<bool>();
1181 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001182
1183 return true;
1184}
1185
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001186static bool ParseNumberProperty(double *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001187 const picojson::object &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09001188 const std::string &property,
1189 const bool required,
1190 const std::string &parent_node = "") {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001191 picojson::object::const_iterator it = o.find(property);
1192 if (it == o.end()) {
1193 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001194 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001195 (*err) += "'" + property + "' property is missing";
1196 if (!parent_node.empty()) {
1197 (*err) += " in " + parent_node;
1198 }
1199 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001200 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001201 }
1202 return false;
1203 }
1204
1205 if (!it->second.is<double>()) {
1206 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001207 if (err) {
1208 (*err) += "'" + property + "' property is not a number type.\n";
1209 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001210 }
1211 return false;
1212 }
1213
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001214 if (ret) {
1215 (*ret) = it->second.get<double>();
1216 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001217
1218 return true;
1219}
1220
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001221static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001222 const picojson::object &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09001223 const std::string &property, bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001224 const std::string &parent_node = "") {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001225 picojson::object::const_iterator it = o.find(property);
1226 if (it == o.end()) {
1227 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001228 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001229 (*err) += "'" + property + "' property is missing";
1230 if (!parent_node.empty()) {
1231 (*err) += " in " + parent_node;
1232 }
1233 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001234 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001235 }
1236 return false;
1237 }
1238
1239 if (!it->second.is<picojson::array>()) {
1240 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001241 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001242 (*err) += "'" + property + "' property is not an array";
1243 if (!parent_node.empty()) {
1244 (*err) += " in " + parent_node;
1245 }
1246 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001247 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001248 }
1249 return false;
1250 }
1251
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001252 ret->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001253 const picojson::array &arr = it->second.get<picojson::array>();
1254 for (size_t i = 0; i < arr.size(); i++) {
1255 if (!arr[i].is<double>()) {
1256 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001257 if (err) {
1258 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001259 if (!parent_node.empty()) {
1260 (*err) += " in " + parent_node;
1261 }
1262 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001263 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001264 }
1265 return false;
1266 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001267 ret->push_back(arr[i].get<double>());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001268 }
1269
1270 return true;
1271}
1272
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001273static bool ParseStringProperty(
1274 std::string *ret, std::string *err, const picojson::object &o,
1275 const std::string &property, bool required,
1276 const std::string &parent_node = std::string()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001277 picojson::object::const_iterator it = o.find(property);
1278 if (it == o.end()) {
1279 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001280 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001281 (*err) += "'" + property + "' property is missing";
1282 if (parent_node.empty()) {
1283 (*err) += ".\n";
1284 } else {
1285 (*err) += " in `" + parent_node + "'.\n";
1286 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001287 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001288 }
1289 return false;
1290 }
1291
1292 if (!it->second.is<std::string>()) {
1293 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001294 if (err) {
1295 (*err) += "'" + property + "' property is not a string type.\n";
1296 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001297 }
1298 return false;
1299 }
1300
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001301 if (ret) {
1302 (*ret) = it->second.get<std::string>();
1303 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001304
1305 return true;
1306}
1307
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001308static bool ParseStringIntProperty(std::map<std::string, int> *ret,
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001309 std::string *err, const picojson::object &o,
1310 const std::string &property, bool required) {
Luke San Antonio19894c72016-06-14 21:19:51 -04001311 picojson::object::const_iterator it = o.find(property);
1312 if (it == o.end()) {
1313 if (required) {
1314 if (err) {
1315 (*err) += "'" + property + "' property is missing.\n";
1316 }
1317 }
1318 return false;
1319 }
1320
1321 // Make sure we are dealing with an object / dictionary.
1322 if (!it->second.is<picojson::object>()) {
1323 if (required) {
1324 if (err) {
1325 (*err) += "'" + property + "' property is not an object.\n";
1326 }
1327 }
1328 return false;
1329 }
1330
1331 ret->clear();
1332 const picojson::object &dict = it->second.get<picojson::object>();
1333
1334 picojson::object::const_iterator dictIt(dict.begin());
1335 picojson::object::const_iterator dictItEnd(dict.end());
1336
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001337 for (; dictIt != dictItEnd; ++dictIt) {
Syoyo Fujitaf6120152017-05-27 23:51:23 +09001338 if (!dictIt->second.is<double>()) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001339 if (required) {
1340 if (err) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001341 (*err) += "'" + property + "' value is not an int.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04001342 }
1343 }
1344 return false;
1345 }
1346
1347 // Insert into the list.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001348 (*ret)[dictIt->first] = static_cast<int>(dictIt->second.get<double>());
Luke San Antonio19894c72016-06-14 21:19:51 -04001349 }
1350 return true;
1351}
1352
Syoyo Fujita5b407452017-06-04 17:42:41 +09001353static bool ParseJSONProperty(std::map<std::string, double> *ret,
1354 std::string *err, const picojson::object &o,
1355 const std::string &property, bool required) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001356 picojson::object::const_iterator it = o.find(property);
Syoyo Fujita5b407452017-06-04 17:42:41 +09001357 if (it == o.end()) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001358 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09001359 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001360 (*err) += "'" + property + "' property is missing. \n'";
1361 }
1362 }
1363 return false;
1364 }
1365
Syoyo Fujita5b407452017-06-04 17:42:41 +09001366 if (!it->second.is<picojson::object>()) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001367 if (required) {
1368 if (err) {
1369 (*err) += "'" + property + "' property is not a JSON object.\n";
1370 }
1371 }
1372 return false;
1373 }
1374
1375 ret->clear();
1376 const picojson::object &obj = it->second.get<picojson::object>();
1377 picojson::object::const_iterator it2(obj.begin());
1378 picojson::object::const_iterator itEnd(obj.end());
1379 for (; it2 != itEnd; it2++) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09001380 if (it2->second.is<double>())
1381 ret->insert(std::pair<std::string, double>(it2->first,
1382 it2->second.get<double>()));
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001383 }
1384
1385 return true;
1386}
1387
Syoyo Fujitabeded612016-05-01 20:03:43 +09001388static bool ParseAsset(Asset *asset, std::string *err,
1389 const picojson::object &o) {
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001390 ParseStringProperty(&asset->version, err, o, "version", true);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001391 ParseStringProperty(&asset->generator, err, o, "generator", false);
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001392 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001393
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001394 // Unity exporter version is added as extra here
1395 ParseExtrasProperty(&(asset->extras), o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001396
1397 return true;
1398}
1399
Syoyo Fujitabeded612016-05-01 20:03:43 +09001400static bool ParseImage(Image *image, std::string *err,
1401 const picojson::object &o, const std::string &basedir,
1402 bool is_binary, const unsigned char *bin_data,
1403 size_t bin_size) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001404 // A glTF image must either reference a bufferView or an image uri
1405 double bufferView = -1;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001406 bool isEmbedded =
1407 ParseNumberProperty(&bufferView, err, o, "bufferView", false);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001408
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001409 std::string uri;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001410 std::string tmp_err;
1411 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", false) && !isEmbedded) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001412 if (err) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09001413 (*err) += "`bufferView` or `uri` required for Image.\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001414 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001415 return false;
1416 }
1417
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001418 ParseStringProperty(&image->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001419
1420 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001421
1422 if (is_binary) {
1423 // Still binary glTF accepts external dataURI. First try external resources.
1424 bool loaded = false;
1425 if (IsDataURI(uri)) {
1426 loaded = DecodeDataURI(&img, uri, 0, false);
1427 } else {
1428 // Assume external .bin file.
1429 loaded = LoadExternalFile(&img, err, uri, basedir, 0, false);
1430 }
1431
1432 if (!loaded) {
1433 // load data from (embedded) binary data
1434
1435 if ((bin_size == 0) || (bin_data == NULL)) {
1436 if (err) {
1437 (*err) += "Invalid binary data.\n";
1438 }
1439 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001440 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001441
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001442 double buffer_view = -1.0;
Aurélien Chatelain6d3bfd72017-05-30 15:04:32 +00001443 if (!ParseNumberProperty(&buffer_view, err, o, "bufferView", true)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001444 return false;
1445 }
1446
Aurélien Chatelain6d3bfd72017-05-30 15:04:32 +00001447 std::string mime_type;
1448 ParseStringProperty(&mime_type, err, o, "mimeType", false);
1449
1450 double width = 0.0;
1451 ParseNumberProperty(&width, err, o, "width", false);
1452
1453 double height = 0.0;
1454 ParseNumberProperty(&height, err, o, "height", false);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001455
1456 // Just only save some information here. Loading actual image data from
1457 // bufferView is done in other place.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001458 image->bufferView = static_cast<int>(buffer_view);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001459 image->mimeType = mime_type;
Aurélien Chatelain6d3bfd72017-05-30 15:04:32 +00001460 image->width = static_cast<int>(width);
1461 image->height = static_cast<int>(height);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001462
1463 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001464 }
1465 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001466 if (IsDataURI(uri)) {
1467 if (!DecodeDataURI(&img, uri, 0, false)) {
1468 if (err) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001469 (*err) += "Failed to decode 'uri' for image parameter.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001470 }
1471 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001472 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001473 } else {
1474 // Assume external file
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00001475
1476 // Keep texture path (for textures that cannot be decoded)
1477 image->uri = uri;
1478
Syoyo Fujitabeded612016-05-01 20:03:43 +09001479 if (!LoadExternalFile(&img, err, uri, basedir, 0, false)) {
1480 if (err) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001481 (*err) += "Failed to load external 'uri'. for image parameter\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001482 }
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00001483 // If the image cannot be loaded, keep uri as image->uri.
1484 return true;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001485 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001486 if (img.empty()) {
1487 if (err) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001488 (*err) += "Image is empty.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001489 }
1490 return false;
1491 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001492 }
1493 }
1494
Syoyo Fujitabeded612016-05-01 20:03:43 +09001495 return LoadImageData(image, err, 0, 0, &img.at(0),
1496 static_cast<int>(img.size()));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001497}
1498
Syoyo Fujitabeded612016-05-01 20:03:43 +09001499static bool ParseTexture(Texture *texture, std::string *err,
1500 const picojson::object &o,
1501 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001502 (void)basedir;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001503 double sampler = -1.0;
1504 double source = -1.0;
1505 ParseNumberProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001506
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +00001507 ParseNumberProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001508
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001509 texture->sampler = static_cast<int>(sampler);
1510 texture->source = static_cast<int>(source);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001511
1512 return true;
1513}
1514
Syoyo Fujitabeded612016-05-01 20:03:43 +09001515static bool ParseBuffer(Buffer *buffer, std::string *err,
1516 const picojson::object &o, const std::string &basedir,
1517 bool is_binary = false,
1518 const unsigned char *bin_data = NULL,
1519 size_t bin_size = 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001520 double byteLength;
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09001521 if (!ParseNumberProperty(&byteLength, err, o, "byteLength", true, "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001522 return false;
1523 }
1524
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00001525 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001526 std::string uri;
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09001527 ParseStringProperty(&uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001528
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00001529 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001530 if (!is_binary && uri.empty()) {
1531 if (err) {
1532 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
1533 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00001534 }
1535
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001536 picojson::object::const_iterator type = o.find("type");
1537 if (type != o.end()) {
1538 if (type->second.is<std::string>()) {
1539 const std::string &ty = (type->second).get<std::string>();
1540 if (ty.compare("arraybuffer") == 0) {
1541 // buffer.type = "arraybuffer";
1542 }
1543 }
1544 }
1545
1546 size_t bytes = static_cast<size_t>(byteLength);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001547 if (is_binary) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001548 // Still binary glTF accepts external dataURI. First try external resources.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001549
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001550 if (!uri.empty()) {
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00001551 // External .bin file.
1552 LoadExternalFile(&buffer->data, err, uri, basedir, bytes, true);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001553 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001554 // load data from (embedded) binary data
1555
1556 if ((bin_size == 0) || (bin_data == NULL)) {
1557 if (err) {
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09001558 (*err) += "Invalid binary data in `Buffer'.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001559 }
1560 return false;
1561 }
1562
1563 if (byteLength > bin_size) {
1564 if (err) {
1565 std::stringstream ss;
1566 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001567 "`byteLength' = "
1568 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001569 (*err) += ss.str();
1570 }
1571 return false;
1572 }
1573
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00001574 // Read buffer data
1575 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001576 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09001577 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001578
1579 } else {
1580 if (IsDataURI(uri)) {
1581 if (!DecodeDataURI(&buffer->data, uri, bytes, true)) {
1582 if (err) {
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09001583 (*err) += "Failed to decode 'uri' : " + uri + "\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001584 }
1585 return false;
1586 }
1587 } else {
1588 // Assume external .bin file.
1589 if (!LoadExternalFile(&buffer->data, err, uri, basedir, bytes, true)) {
1590 return false;
1591 }
1592 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001593 }
1594
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001595 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001596
1597 return true;
1598}
1599
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001600static bool ParseBufferView(BufferView *bufferView, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001601 const picojson::object &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001602 double buffer = -1.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001603 if (!ParseNumberProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001604 return false;
1605 }
1606
Syoyo Fujitad17ff662017-05-29 02:53:12 +09001607 double byteOffset = 0.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001608 ParseNumberProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001609
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001610 double byteLength = 1.0;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001611 if (!ParseNumberProperty(&byteLength, err, o, "byteLength", true,
1612 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001613 return false;
1614 }
1615
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09001616 size_t byteStride = 0;
1617 double byteStrideValue = 0.0;
1618 if (!ParseNumberProperty(&byteStrideValue, err, o, "byteStride", false)) {
1619 // Spec says: When byteStride of referenced bufferView is not defined, it
1620 // means that accessor elements are tightly packed, i.e., effective stride
1621 // equals the size of the element.
1622 // We cannot determine the actual byteStride until Accessor are parsed, thus
1623 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
1624 byteStride = 0;
1625 } else {
1626 byteStride = static_cast<size_t>(byteStrideValue);
1627 }
1628
1629 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
1630 if (err) {
1631 std::stringstream ss;
1632 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
1633 "4 : "
1634 << byteStride << std::endl;
1635
1636 (*err) += ss.str();
1637 }
1638 return false;
1639 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001640
1641 double target = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001642 ParseNumberProperty(&target, err, o, "target", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001643 int targetValue = static_cast<int>(target);
1644 if ((targetValue == TINYGLTF_TARGET_ARRAY_BUFFER) ||
1645 (targetValue == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
1646 // OK
1647 } else {
1648 targetValue = 0;
1649 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001650 bufferView->target = targetValue;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001651
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001652 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001653
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001654 bufferView->buffer = static_cast<int>(buffer);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001655 bufferView->byteOffset = static_cast<size_t>(byteOffset);
1656 bufferView->byteLength = static_cast<size_t>(byteLength);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001657 bufferView->byteStride = static_cast<size_t>(byteStride);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001658
1659 return true;
1660}
1661
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001662static bool ParseAccessor(Accessor *accessor, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001663 const picojson::object &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001664 double bufferView = -1.0;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001665 if (!ParseNumberProperty(&bufferView, err, o, "bufferView", true,
1666 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001667 return false;
1668 }
1669
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001670 double byteOffset = 0.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001671 ParseNumberProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001672
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09001673 bool normalized = false;
1674 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
1675
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001676 double componentType = 0.0;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001677 if (!ParseNumberProperty(&componentType, err, o, "componentType", true,
1678 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001679 return false;
1680 }
1681
1682 double count = 0.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001683 if (!ParseNumberProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001684 return false;
1685 }
1686
1687 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001688 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001689 return false;
1690 }
1691
1692 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001693 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001694 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001695 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001696 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001697 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001698 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001699 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001700 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001701 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001702 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001703 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001704 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001705 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001706 } else {
1707 std::stringstream ss;
1708 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001709 if (err) {
1710 (*err) += ss.str();
1711 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001712 return false;
1713 }
1714
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001715 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001716
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001717 accessor->minValues.clear();
1718 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09001719 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
1720 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001721
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09001722 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
1723 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001724
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001725 accessor->count = static_cast<size_t>(count);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001726 accessor->bufferView = static_cast<int>(bufferView);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001727 accessor->byteOffset = static_cast<size_t>(byteOffset);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001728
1729 {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001730 int comp = static_cast<int>(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001731 if (comp >= TINYGLTF_COMPONENT_TYPE_BYTE &&
1732 comp <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
1733 // OK
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001734 accessor->componentType = comp;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001735 } else {
1736 std::stringstream ss;
1737 ss << "Invalid `componentType` in accessor. Got " << comp << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001738 if (err) {
1739 (*err) += ss.str();
1740 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001741 return false;
1742 }
1743 }
1744
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001745 ParseExtrasProperty(&(accessor->extras), o);
1746
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001747 return true;
1748}
1749
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001750static bool ParsePrimitive(Primitive *primitive, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001751 const picojson::object &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001752 double material = -1.0;
1753 ParseNumberProperty(&material, err, o, "material", false);
1754 primitive->material = static_cast<int>(material);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001755
1756 double mode = static_cast<double>(TINYGLTF_MODE_TRIANGLES);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001757 ParseNumberProperty(&mode, err, o, "mode", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001758
1759 int primMode = static_cast<int>(mode);
Syoyo Fujita5b407452017-06-04 17:42:41 +09001760 primitive->mode = primMode; // Why only triangled were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001761
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001762 double indices = -1.0;
1763 ParseNumberProperty(&indices, err, o, "indices", false);
1764 primitive->indices = static_cast<int>(indices);
1765 if (!ParseStringIntProperty(&primitive->attributes, err, o, "attributes",
1766 true)) {
1767 return false;
1768 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001769
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00001770 // Look for morph targets
1771 picojson::object::const_iterator targetsObject = o.find("targets");
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001772 if ((targetsObject != o.end()) &&
1773 (targetsObject->second).is<picojson::array>()) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00001774 const picojson::array &targetArray =
1775 (targetsObject->second).get<picojson::array>();
1776 for (size_t i = 0; i < targetArray.size(); i++) {
1777 std::map<std::string, int> targetAttribues;
1778
1779 const picojson::object &dict = targetArray[i].get<picojson::object>();
1780 picojson::object::const_iterator dictIt(dict.begin());
1781 picojson::object::const_iterator dictItEnd(dict.end());
1782
1783 for (; dictIt != dictItEnd; ++dictIt) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001784 targetAttribues[dictIt->first] =
1785 static_cast<int>(dictIt->second.get<double>());
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00001786 }
1787 primitive->targets.push_back(targetAttribues);
1788 }
1789 }
1790
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001791 ParseExtrasProperty(&(primitive->extras), o);
1792
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001793 return true;
1794}
1795
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001796static bool ParseMesh(Mesh *mesh, std::string *err, const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001797 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001798
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001799 mesh->primitives.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001800 picojson::object::const_iterator primObject = o.find("primitives");
1801 if ((primObject != o.end()) && (primObject->second).is<picojson::array>()) {
1802 const picojson::array &primArray =
1803 (primObject->second).get<picojson::array>();
1804 for (size_t i = 0; i < primArray.size(); i++) {
1805 Primitive primitive;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001806 if (ParsePrimitive(&primitive, err,
1807 primArray[i].get<picojson::object>())) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04001808 // Only add the primitive if the parsing succeeds.
1809 mesh->primitives.push_back(primitive);
1810 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001811 }
1812 }
1813
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00001814 // Look for morph targets
1815 picojson::object::const_iterator targetsObject = o.find("targets");
Syoyo Fujita5b407452017-06-04 17:42:41 +09001816 if ((targetsObject != o.end()) &&
1817 (targetsObject->second).is<picojson::array>()) {
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00001818 const picojson::array &targetArray =
1819 (targetsObject->second).get<picojson::array>();
1820 for (size_t i = 0; i < targetArray.size(); i++) {
1821 std::map<std::string, int> targetAttribues;
1822
1823 const picojson::object &dict = targetArray[i].get<picojson::object>();
1824 picojson::object::const_iterator dictIt(dict.begin());
1825 picojson::object::const_iterator dictItEnd(dict.end());
1826
1827 for (; dictIt != dictItEnd; ++dictIt) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09001828 targetAttribues[dictIt->first] =
1829 static_cast<int>(dictIt->second.get<double>());
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00001830 }
1831 mesh->targets.push_back(targetAttribues);
1832 }
1833 }
1834
1835 // Should probably check if has targets and if dimensions fit
1836 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
1837
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001838 ParseExtrasProperty(&(mesh->extras), o);
1839
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001840 return true;
1841}
1842
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001843static bool ParseNode(Node *node, std::string *err, const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001844 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001845
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001846 double skin = -1.0;
1847 ParseNumberProperty(&skin, err, o, "skin", false);
1848 node->skin = static_cast<int>(skin);
1849
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001850 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09001851 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001852 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
1853 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
1854 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
1855 }
1856
1857 double camera = -1.0;
1858 ParseNumberProperty(&camera, err, o, "camera", false);
1859 node->camera = static_cast<int>(camera);
1860
1861 double mesh = -1.0;
1862 ParseNumberProperty(&mesh, err, o, "mesh", false);
Syoyo Fujitaf6120152017-05-27 23:51:23 +09001863 node->mesh = int(mesh);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001864
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001865 node->children.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001866 picojson::object::const_iterator childrenObject = o.find("children");
1867 if ((childrenObject != o.end()) &&
1868 (childrenObject->second).is<picojson::array>()) {
1869 const picojson::array &childrenArray =
1870 (childrenObject->second).get<picojson::array>();
1871 for (size_t i = 0; i < childrenArray.size(); i++) {
Syoyo Fujitaf6120152017-05-27 23:51:23 +09001872 if (!childrenArray[i].is<double>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001873 if (err) {
1874 (*err) += "Invalid `children` array.\n";
1875 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001876 return false;
1877 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09001878 const int &childrenNode =
1879 static_cast<int>(childrenArray[i].get<double>());
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001880 node->children.push_back(childrenNode);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001881 }
1882 }
1883
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001884 ParseExtrasProperty(&(node->extras), o);
1885
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001886 return true;
1887}
1888
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001889static bool ParseParameterProperty(Parameter *param, std::string *err,
1890 const picojson::object &o,
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001891 const std::string &prop, bool required) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001892 double num_val;
1893
1894 // A parameter value can either be a string or an array of either a boolean or
1895 // a number. Booleans of any kind aren't supported here. Granted, it
1896 // complicates the Parameter structure and breaks it semantically in the sense
1897 // that the client probably works off the assumption that if the string is
1898 // empty the vector is used, etc. Would a tagged union work?
1899 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
1900 // Found string property.
1901 return true;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001902 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
1903 false)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001904 // Found a number array.
1905 return true;
1906 } else if (ParseNumberProperty(&num_val, err, o, prop, false)) {
1907 param->number_array.push_back(num_val);
1908 return true;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001909 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
1910 false)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001911 return true;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001912 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001913 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001914 } else {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001915 if (required) {
1916 if (err) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001917 (*err) += "parameter must be a string or number / number array.\n";
1918 }
1919 }
1920 return false;
1921 }
1922}
1923
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001924static bool ParseMaterial(Material *material, std::string *err,
Syoyo Fujita5b407452017-06-04 17:42:41 +09001925 const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001926 material->values.clear();
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001927 material->extPBRValues.clear();
1928 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001929
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001930 picojson::object::const_iterator it(o.begin());
1931 picojson::object::const_iterator itEnd(o.end());
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001932
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001933 for (; it != itEnd; it++) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09001934 if (it->first == "pbrMetallicRoughness") {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001935 if ((it->second).is<picojson::object>()) {
1936 const picojson::object &values_object =
1937 (it->second).get<picojson::object>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001938
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001939 picojson::object::const_iterator itVal(values_object.begin());
Syoyo Fujitaf6120152017-05-27 23:51:23 +09001940 picojson::object::const_iterator itValEnd(values_object.end());
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001941
Syoyo Fujitaf6120152017-05-27 23:51:23 +09001942 for (; itVal != itValEnd; itVal++) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001943 Parameter param;
1944 if (ParseParameterProperty(&param, err, values_object, itVal->first,
1945 false)) {
1946 material->values[itVal->first] = param;
1947 }
1948 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001949 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09001950 } else if (it->first == "extensions") {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001951 if ((it->second).is<picojson::object>()) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09001952 const picojson::object &extension =
1953 (it->second).get<picojson::object>();
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001954
Syoyo Fujita5b407452017-06-04 17:42:41 +09001955 picojson::object::const_iterator extIt = extension.begin();
1956 if (!extIt->second.is<picojson::object>()) continue;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001957
Syoyo Fujita5b407452017-06-04 17:42:41 +09001958 const picojson::object &values_object =
1959 (extIt->second).get<picojson::object>();
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001960
Syoyo Fujita5b407452017-06-04 17:42:41 +09001961 picojson::object::const_iterator itVal(values_object.begin());
1962 picojson::object::const_iterator itValEnd(values_object.end());
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001963
Syoyo Fujita5b407452017-06-04 17:42:41 +09001964 for (; itVal != itValEnd; itVal++) {
1965 Parameter param;
1966 if (ParseParameterProperty(&param, err, values_object, itVal->first,
1967 false)) {
1968 material->extPBRValues[itVal->first] = param;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001969 }
1970 }
1971 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09001972 } else {
1973 Parameter param;
1974 if (ParseParameterProperty(&param, err, o, it->first, false)) {
1975 material->additionalValues[it->first] = param;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001976 }
1977 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09001978 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001979
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001980 ParseExtrasProperty(&(material->extras), o);
1981
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001982 return true;
1983}
1984
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001985static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err,
1986 const picojson::object &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001987 double samplerIndex = -1.0;
1988 double targetIndex = -1.0;
1989 if (!ParseNumberProperty(&samplerIndex, err, o, "sampler", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001990 if (err) {
1991 (*err) += "`sampler` field is missing in animation channels\n";
1992 }
1993 return false;
1994 }
1995
1996 picojson::object::const_iterator targetIt = o.find("target");
1997 if ((targetIt != o.end()) && (targetIt->second).is<picojson::object>()) {
1998 const picojson::object &target_object =
1999 (targetIt->second).get<picojson::object>();
2000
Syoyo Fujita5b407452017-06-04 17:42:41 +09002001 if (!ParseNumberProperty(&targetIndex, err, target_object, "node", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002002 if (err) {
2003 (*err) += "`id` field is missing in animation.channels.target\n";
2004 }
2005 return false;
2006 }
2007
2008 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
2009 true)) {
2010 if (err) {
2011 (*err) += "`path` field is missing in animation.channels.target\n";
2012 }
2013 return false;
2014 }
2015 }
2016
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002017 channel->sampler = static_cast<int>(samplerIndex);
2018 channel->target_node = static_cast<int>(targetIndex);
2019
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002020 ParseExtrasProperty(&(channel->extras), o);
2021
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002022 return true;
2023}
2024
2025static bool ParseAnimation(Animation *animation, std::string *err,
2026 const picojson::object &o) {
2027 {
2028 picojson::object::const_iterator channelsIt = o.find("channels");
2029 if ((channelsIt != o.end()) && (channelsIt->second).is<picojson::array>()) {
2030 const picojson::array &channelArray =
2031 (channelsIt->second).get<picojson::array>();
2032 for (size_t i = 0; i < channelArray.size(); i++) {
2033 AnimationChannel channel;
2034 if (ParseAnimationChannel(&channel, err,
2035 channelArray[i].get<picojson::object>())) {
2036 // Only add the channel if the parsing succeeds.
2037 animation->channels.push_back(channel);
2038 }
2039 }
2040 }
2041 }
2042
2043 {
2044 picojson::object::const_iterator samplerIt = o.find("samplers");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002045 if ((samplerIt != o.end()) && (samplerIt->second).is<picojson::array>()) {
2046 const picojson::array &sampler_array =
2047 (samplerIt->second).get<picojson::array>();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002048
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002049 picojson::array::const_iterator it = sampler_array.begin();
2050 picojson::array::const_iterator itEnd = sampler_array.end();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002051
2052 for (; it != itEnd; it++) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002053 const picojson::object &s = it->get<picojson::object>();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002054
2055 AnimationSampler sampler;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002056 double inputIndex = -1.0;
2057 double outputIndex = -1.0;
2058 if (!ParseNumberProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002059 if (err) {
2060 (*err) += "`input` field is missing in animation.sampler\n";
2061 }
2062 return false;
2063 }
2064 if (!ParseStringProperty(&sampler.interpolation, err, s,
2065 "interpolation", true)) {
2066 if (err) {
2067 (*err) += "`interpolation` field is missing in animation.sampler\n";
2068 }
2069 return false;
2070 }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002071 if (!ParseNumberProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002072 if (err) {
2073 (*err) += "`output` field is missing in animation.sampler\n";
2074 }
2075 return false;
2076 }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002077 sampler.input = static_cast<int>(inputIndex);
2078 sampler.output = static_cast<int>(outputIndex);
2079 animation->samplers.push_back(sampler);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002080 }
2081 }
2082 }
2083
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002084 ParseStringProperty(&animation->name, err, o, "name", false);
2085
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002086 ParseExtrasProperty(&(animation->extras), o);
2087
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002088 return true;
2089}
2090
Syoyo Fujitac2615632016-06-19 21:56:06 +09002091static bool ParseSampler(Sampler *sampler, std::string *err,
2092 const picojson::object &o) {
2093 ParseStringProperty(&sampler->name, err, o, "name", false);
2094
2095 double minFilter =
2096 static_cast<double>(TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR);
2097 double magFilter = static_cast<double>(TINYGLTF_TEXTURE_FILTER_LINEAR);
2098 double wrapS = static_cast<double>(TINYGLTF_TEXTURE_WRAP_RPEAT);
2099 double wrapT = static_cast<double>(TINYGLTF_TEXTURE_WRAP_RPEAT);
2100 ParseNumberProperty(&minFilter, err, o, "minFilter", false);
2101 ParseNumberProperty(&magFilter, err, o, "magFilter", false);
2102 ParseNumberProperty(&wrapS, err, o, "wrapS", false);
2103 ParseNumberProperty(&wrapT, err, o, "wrapT", false);
2104
2105 sampler->minFilter = static_cast<int>(minFilter);
2106 sampler->magFilter = static_cast<int>(magFilter);
2107 sampler->wrapS = static_cast<int>(wrapS);
2108 sampler->wrapT = static_cast<int>(wrapT);
2109
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002110 ParseExtrasProperty(&(sampler->extras), o);
2111
Syoyo Fujitac2615632016-06-19 21:56:06 +09002112 return true;
2113}
2114
Syoyo Fujita5b407452017-06-04 17:42:41 +09002115static bool ParseSkin(Skin *skin, std::string *err, const picojson::object &o) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002116 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002117
2118 std::vector<double> joints;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002119 if (!ParseNumberArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002120 return false;
2121 }
2122
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +00002123 double skeleton = -1.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002124 ParseNumberProperty(&skeleton, err, o, "skeleton", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002125 skin->skeleton = static_cast<int>(skeleton);
2126
Syoyo Fujita73cd7b92017-06-20 03:02:57 +09002127 skin->joints.resize(joints.size());
2128 for (size_t i = 0; i < joints.size(); i++) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002129 skin->joints[i] = static_cast<int>(joints[i]);
Syoyo Fujita73cd7b92017-06-20 03:02:57 +09002130 }
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002131
2132 double invBind = -1.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002133 ParseNumberProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002134 skin->inverseBindMatrices = static_cast<int>(invBind);
2135
2136 return true;
2137}
2138
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002139static bool ParsePerspectiveCamera(PerspectiveCamera *camera, std::string *err,
2140 const picojson::object &o) {
2141 double yfov = 0.0;
2142 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
2143 return false;
2144 }
2145
2146 double znear = 0.0;
2147 if (!ParseNumberProperty(&znear, err, o, "znear", true,
2148 "PerspectiveCamera")) {
2149 return false;
2150 }
2151
2152 double aspectRatio = 0.0; // = invalid
2153 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
2154 "PerspectiveCamera");
2155
2156 double zfar = 0.0; // = invalid
2157 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
2158
2159 camera->aspectRatio = float(aspectRatio);
2160 camera->zfar = float(zfar);
2161 camera->yfov = float(yfov);
2162 camera->znear = float(znear);
2163
2164 ParseExtrasProperty(&(camera->extras), o);
2165
2166 // TODO(syoyo): Validate parameter values.
2167
2168 return true;
2169}
2170
2171static bool ParseOrthographicCamera(OrthographicCamera *camera,
2172 std::string *err,
2173 const picojson::object &o) {
2174 double xmag = 0.0;
2175 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
2176 return false;
2177 }
2178
2179 double ymag = 0.0;
2180 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
2181 return false;
2182 }
2183
2184 double zfar = 0.0;
2185 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
2186 return false;
2187 }
2188
2189 double znear = 0.0;
2190 if (!ParseNumberProperty(&znear, err, o, "znear", true,
2191 "OrthographicCamera")) {
2192 return false;
2193 }
2194
2195 ParseExtrasProperty(&(camera->extras), o);
2196
2197 camera->xmag = float(xmag);
2198 camera->ymag = float(ymag);
2199 camera->zfar = float(zfar);
2200 camera->znear = float(znear);
2201
2202 // TODO(syoyo): Validate parameter values.
2203
2204 return true;
2205}
2206
2207static bool ParseCamera(Camera *camera, std::string *err,
2208 const picojson::object &o) {
2209 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
2210 return false;
2211 }
2212
2213 if (camera->type.compare("orthographic") == 0) {
2214 if (o.find("orthographic") == o.end()) {
2215 if (err) {
2216 std::stringstream ss;
2217 ss << "Orhographic camera description not found." << std::endl;
2218 (*err) += ss.str();
2219 }
2220 return false;
2221 }
2222
2223 const picojson::value &v = o.find("orthographic")->second;
2224 if (!v.is<picojson::object>()) {
2225 if (err) {
2226 std::stringstream ss;
2227 ss << "\"orthographic\" is not a JSON object." << std::endl;
2228 (*err) += ss.str();
2229 }
2230 return false;
2231 }
2232
2233 if (!ParseOrthographicCamera(&camera->orthographic, err,
2234 v.get<picojson::object>())) {
2235 return false;
2236 }
2237 } else if (camera->type.compare("perspective") == 0) {
2238 if (o.find("perspective") == o.end()) {
2239 if (err) {
2240 std::stringstream ss;
2241 ss << "Perspective camera description not found." << std::endl;
2242 (*err) += ss.str();
2243 }
2244 return false;
2245 }
2246
2247 const picojson::value &v = o.find("perspective")->second;
2248 if (!v.is<picojson::object>()) {
2249 if (err) {
2250 std::stringstream ss;
2251 ss << "\"perspective\" is not a JSON object." << std::endl;
2252 (*err) += ss.str();
2253 }
2254 return false;
2255 }
2256
2257 if (!ParsePerspectiveCamera(&camera->perspective, err,
2258 v.get<picojson::object>())) {
2259 return false;
2260 }
2261 } else {
2262 if (err) {
2263 std::stringstream ss;
2264 ss << "Invalid camera type: \"" << camera->type
2265 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
2266 (*err) += ss.str();
2267 }
2268 return false;
2269 }
2270
2271 ParseStringProperty(&camera->name, err, o, "name", false);
2272
2273 ParseExtrasProperty(&(camera->extras), o);
2274
2275 return true;
2276}
2277
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002278bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str,
2279 unsigned int length, const std::string &base_dir,
2280 unsigned int check_sections) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002281 if (length < 4) {
2282 if (err) {
2283 (*err) = "JSON string too short.\n";
2284 }
2285 return false;
2286 }
2287
Syoyo Fujitacdf84902017-08-01 20:12:08 +09002288 // TODO(syoyo): Add feature not using exception handling.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002289 picojson::value v;
Syoyo Fujitacdf84902017-08-01 20:12:08 +09002290 try {
2291 std::string perr = picojson::parse(v, str, str + length);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002292
Syoyo Fujitacdf84902017-08-01 20:12:08 +09002293 if (!perr.empty()) {
2294 if (err) {
2295 (*err) = "JSON parsing error: " + perr;
2296 }
2297 return false;
2298 }
2299 } catch (std::exception e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002300 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09002301 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002302 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002303 return false;
2304 }
2305
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002306 if (!v.is<picojson::object>()) {
2307 // root is not an object.
2308 if (err) {
2309 (*err) = "Root element is not a JSON object\n";
2310 }
2311 return false;
2312 }
2313
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002314 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09002315 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002316
Syoyo Fujita5b407452017-06-04 17:42:41 +09002317 if (v.contains("scenes") && v.get("scenes").is<picojson::array>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002318 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04002319 } else if (check_sections & REQUIRE_SCENES) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002320 if (err) {
2321 (*err) += "\"scenes\" object not found in .gltf\n";
2322 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002323 return false;
2324 }
2325
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002326 if (v.contains("nodes") && v.get("nodes").is<picojson::array>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002327 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04002328 } else if (check_sections & REQUIRE_NODES) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002329 if (err) {
2330 (*err) += "\"nodes\" object not found in .gltf\n";
2331 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002332 return false;
2333 }
2334
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002335 if (v.contains("accessors") && v.get("accessors").is<picojson::array>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002336 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04002337 } else if (check_sections & REQUIRE_ACCESSORS) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002338 if (err) {
2339 (*err) += "\"accessors\" object not found in .gltf\n";
2340 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002341 return false;
2342 }
2343
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002344 if (v.contains("buffers") && v.get("buffers").is<picojson::array>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002345 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04002346 } else if (check_sections & REQUIRE_BUFFERS) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002347 if (err) {
2348 (*err) += "\"buffers\" object not found in .gltf\n";
2349 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002350 return false;
2351 }
2352
Syoyo Fujita5b407452017-06-04 17:42:41 +09002353 if (v.contains("bufferViews") && v.get("bufferViews").is<picojson::array>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002354 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04002355 } else if (check_sections & REQUIRE_BUFFER_VIEWS) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002356 if (err) {
2357 (*err) += "\"bufferViews\" object not found in .gltf\n";
2358 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002359 return false;
2360 }
2361
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002362 model->buffers.clear();
2363 model->bufferViews.clear();
2364 model->accessors.clear();
2365 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002366 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002367 model->nodes.clear();
2368 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00002369 model->extensionsRequired.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002370 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002371
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002372 // 0. Parse Asset
2373 if (v.contains("asset") && v.get("asset").is<picojson::object>()) {
2374 const picojson::object &root = v.get("asset").get<picojson::object>();
2375
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002376 ParseAsset(&model->asset, err, root);
2377 }
2378
2379 // 0. Parse extensionUsed
Syoyo Fujita5b407452017-06-04 17:42:41 +09002380 if (v.contains("extensionsUsed") &&
2381 v.get("extensionsUsed").is<picojson::array>()) {
2382 const picojson::array &root =
2383 v.get("extensionsUsed").get<picojson::array>();
2384 for (unsigned int i = 0; i < root.size(); ++i) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002385 model->extensionsUsed.push_back(root[i].get<std::string>());
2386 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002387 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002388 if (v.contains("extensionsRequired") &&
2389 v.get("extensionsRequired").is<picojson::array>()) {
2390 const picojson::array &root =
2391 v.get("extensionsRequired").get<picojson::array>();
2392 for (unsigned int i = 0; i < root.size(); ++i) {
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00002393 model->extensionsRequired.push_back(root[i].get<std::string>());
2394 }
2395 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002396
2397 // 1. Parse Buffer
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002398 if (v.contains("buffers") && v.get("buffers").is<picojson::array>()) {
2399 const picojson::array &root = v.get("buffers").get<picojson::array>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002400
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002401 picojson::array::const_iterator it(root.begin());
2402 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002403 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002404 Buffer buffer;
Syoyo Fujita5b407452017-06-04 17:42:41 +09002405 if (!ParseBuffer(&buffer, err, it->get<picojson::object>(), base_dir,
2406 is_binary_, bin_data_, bin_size_)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002407 return false;
2408 }
2409
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002410 model->buffers.push_back(buffer);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002411 }
2412 }
2413
2414 // 2. Parse BufferView
Syoyo Fujita5b407452017-06-04 17:42:41 +09002415 if (v.contains("bufferViews") && v.get("bufferViews").is<picojson::array>()) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002416 const picojson::array &root = v.get("bufferViews").get<picojson::array>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002417
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002418 picojson::array::const_iterator it(root.begin());
2419 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002420 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002421 BufferView bufferView;
Syoyo Fujita5b407452017-06-04 17:42:41 +09002422 if (!ParseBufferView(&bufferView, err, it->get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002423 return false;
2424 }
2425
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002426 model->bufferViews.push_back(bufferView);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002427 }
2428 }
2429
2430 // 3. Parse Accessor
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002431 if (v.contains("accessors") && v.get("accessors").is<picojson::array>()) {
2432 const picojson::array &root = v.get("accessors").get<picojson::array>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002433
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002434 picojson::array::const_iterator it(root.begin());
2435 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002436 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002437 Accessor accessor;
Syoyo Fujita5b407452017-06-04 17:42:41 +09002438 if (!ParseAccessor(&accessor, err, it->get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002439 return false;
2440 }
2441
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002442 model->accessors.push_back(accessor);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002443 }
2444 }
2445
2446 // 4. Parse Mesh
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002447 if (v.contains("meshes") && v.get("meshes").is<picojson::array>()) {
2448 const picojson::array &root = v.get("meshes").get<picojson::array>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002449
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002450 picojson::array::const_iterator it(root.begin());
2451 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002452 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002453 Mesh mesh;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002454 if (!ParseMesh(&mesh, err, it->get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002455 return false;
2456 }
2457
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002458 model->meshes.push_back(mesh);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002459 }
2460 }
2461
2462 // 5. Parse Node
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002463 if (v.contains("nodes") && v.get("nodes").is<picojson::array>()) {
2464 const picojson::array &root = v.get("nodes").get<picojson::array>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002465
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002466 picojson::array::const_iterator it(root.begin());
2467 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002468 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002469 Node node;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002470 if (!ParseNode(&node, err, it->get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002471 return false;
2472 }
2473
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002474 model->nodes.push_back(node);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002475 }
2476 }
2477
2478 // 6. Parse scenes.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002479 if (v.contains("scenes") && v.get("scenes").is<picojson::array>()) {
2480 const picojson::array &root = v.get("scenes").get<picojson::array>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002481
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002482 picojson::array::const_iterator it(root.begin());
2483 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002484 for (; it != itEnd; it++) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002485 if (!(it->is<picojson::object>())) {
Syoyo Fujitae04b1b52016-06-05 22:31:20 +09002486 if (err) {
2487 (*err) += "`scenes' does not contain an object.";
2488 }
2489 return false;
2490 }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002491 const picojson::object &o = it->get<picojson::object>();
2492 std::vector<double> nodes;
2493 if (!ParseNumberArrayProperty(&nodes, err, o, "nodes", false)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002494 return false;
2495 }
2496
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002497 Scene scene;
2498 ParseStringProperty(&scene.name, err, o, "name", false);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002499 std::vector<int> nodesIds;
2500 for (size_t i = 0; i < nodes.size(); i++) {
2501 nodesIds.push_back(static_cast<int>(nodes[i]));
2502 }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002503 scene.nodes = nodesIds;
2504
2505 model->scenes.push_back(scene);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002506 }
2507 }
2508
2509 // 7. Parse default scenes.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002510 if (v.contains("scene") && v.get("scene").is<double>()) {
Syoyo Fujitaf6120152017-05-27 23:51:23 +09002511 const int defaultScene = int(v.get("scene").get<double>());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002512
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002513 model->defaultScene = static_cast<int>(defaultScene);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002514 }
2515
2516 // 8. Parse Material
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002517 if (v.contains("materials") && v.get("materials").is<picojson::array>()) {
2518 const picojson::array &root = v.get("materials").get<picojson::array>();
2519 picojson::array::const_iterator it(root.begin());
2520 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002521 for (; it != itEnd; it++) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002522 picojson::object jsonMaterial = it->get<picojson::object>();
2523
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002524 Material material;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002525 ParseStringProperty(&material.name, err, jsonMaterial, "name", false);
2526
2527 if (!ParseMaterial(&material, err, jsonMaterial)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002528 return false;
2529 }
2530
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002531 model->materials.push_back(material);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002532 }
2533 }
2534
2535 // 9. Parse Image
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002536 if (v.contains("images") && v.get("images").is<picojson::array>()) {
2537 const picojson::array &root = v.get("images").get<picojson::array>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002538
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002539 picojson::array::const_iterator it(root.begin());
2540 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002541 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002542 Image image;
Syoyo Fujita5b407452017-06-04 17:42:41 +09002543 if (!ParseImage(&image, err, it->get<picojson::object>(), base_dir,
2544 is_binary_, bin_data_, bin_size_)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002545 return false;
2546 }
2547
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002548 if (image.bufferView != -1) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09002549 // Load image from the buffer view.
Syoyo Fujitaf6120152017-05-27 23:51:23 +09002550 if (size_t(image.bufferView) >= model->bufferViews.size()) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09002551 if (err) {
2552 std::stringstream ss;
2553 ss << "bufferView \"" << image.bufferView
2554 << "\" not found in the scene." << std::endl;
2555 (*err) += ss.str();
2556 }
2557 return false;
2558 }
2559
Syoyo Fujita5b407452017-06-04 17:42:41 +09002560 const BufferView &bufferView =
2561 model->bufferViews[size_t(image.bufferView)];
Syoyo Fujitaf6120152017-05-27 23:51:23 +09002562 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
Syoyo Fujitabeded612016-05-01 20:03:43 +09002563
2564 bool ret = LoadImageData(&image, err, image.width, image.height,
2565 &buffer.data[bufferView.byteOffset],
2566 static_cast<int>(bufferView.byteLength));
2567 if (!ret) {
2568 return false;
2569 }
2570 }
2571
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002572 model->images.push_back(image);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002573 }
2574 }
2575
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002576 // 10. Parse Texture
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002577 if (v.contains("textures") && v.get("textures").is<picojson::array>()) {
2578 const picojson::array &root = v.get("textures").get<picojson::array>();
Syoyo Fujitabde70212016-02-07 17:38:17 +09002579
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002580 picojson::array::const_iterator it(root.begin());
2581 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitabde70212016-02-07 17:38:17 +09002582 for (; it != itEnd; it++) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09002583 Texture texture;
Syoyo Fujita5b407452017-06-04 17:42:41 +09002584 if (!ParseTexture(&texture, err, it->get<picojson::object>(), base_dir)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09002585 return false;
2586 }
2587
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002588 model->textures.push_back(texture);
Syoyo Fujitabde70212016-02-07 17:38:17 +09002589 }
2590 }
2591
Aurélien Chatelainf4f4ae42017-05-24 12:54:17 +00002592 // 11. Parse Animation
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002593 if (v.contains("animations") && v.get("animations").is<picojson::array>()) {
2594 const picojson::array &root = v.get("animations").get<picojson::array>();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002595
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002596 picojson::array::const_iterator it(root.begin());
2597 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002598 for (; it != itEnd; ++it) {
2599 Animation animation;
Syoyo Fujita5b407452017-06-04 17:42:41 +09002600 if (!ParseAnimation(&animation, err, it->get<picojson::object>())) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002601 return false;
2602 }
2603
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002604 model->animations.push_back(animation);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002605 }
2606 }
2607
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002608 // 12. Parse Skin
2609 if (v.contains("skins") && v.get("skins").is<picojson::array>()) {
2610 const picojson::array &root = v.get("skins").get<picojson::array>();
Syoyo Fujitac2615632016-06-19 21:56:06 +09002611
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002612 picojson::array::const_iterator it(root.begin());
2613 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitac2615632016-06-19 21:56:06 +09002614 for (; it != itEnd; ++it) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002615 Skin skin;
Syoyo Fujita5b407452017-06-04 17:42:41 +09002616 if (!ParseSkin(&skin, err, it->get<picojson::object>())) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09002617 return false;
2618 }
2619
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002620 model->skins.push_back(skin);
2621 }
2622 }
2623
2624 // 13. Parse Sampler
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002625 if (v.contains("samplers") && v.get("samplers").is<picojson::array>()) {
2626 const picojson::array &root = v.get("samplers").get<picojson::array>();
Syoyo Fujitac2615632016-06-19 21:56:06 +09002627
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002628 picojson::array::const_iterator it(root.begin());
2629 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitac2615632016-06-19 21:56:06 +09002630 for (; it != itEnd; ++it) {
2631 Sampler sampler;
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002632 if (!(it->is<picojson::object>())) {
2633 continue;
2634 }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002635 if (!ParseSampler(&sampler, err, it->get<picojson::object>())) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09002636 return false;
2637 }
2638
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002639 model->samplers.push_back(sampler);
Syoyo Fujitac2615632016-06-19 21:56:06 +09002640 }
2641 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09002642
2643 // 14. Parse Camera
2644 if (v.contains("cameras") && v.get("cameras").is<picojson::array>()) {
2645 const picojson::array &root = v.get("cameras").get<picojson::array>();
2646
2647 picojson::array::const_iterator it(root.begin());
2648 picojson::array::const_iterator itEnd(root.end());
2649 for (; it != itEnd; ++it) {
2650 Camera camera;
2651 if (!ParseCamera(&camera, err, it->get<picojson::object>())) {
2652 return false;
2653 }
2654
2655 model->cameras.push_back(camera);
2656 }
2657 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002658 return true;
2659}
2660
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002661bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
2662 const char *str, unsigned int length,
2663 const std::string &base_dir,
2664 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002665 is_binary_ = false;
2666 bin_data_ = NULL;
2667 bin_size_ = 0;
2668
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002669 return LoadFromString(model, err, str, length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002670}
2671
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002672bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
2673 const std::string &filename,
2674 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002675 std::stringstream ss;
2676
2677 std::ifstream f(filename.c_str());
2678 if (!f) {
2679 ss << "Failed to open file: " << filename << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002680 if (err) {
2681 (*err) = ss.str();
2682 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002683 return false;
2684 }
2685
2686 f.seekg(0, f.end);
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +09002687 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002688 std::vector<char> buf(sz);
2689
Luke San Antoniob310b4a2016-06-23 14:17:37 -04002690 if (sz == 0) {
2691 if (err) {
2692 (*err) = "Empty file.";
2693 }
2694 return false;
2695 }
2696
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002697 f.seekg(0, f.beg);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002698 f.read(&buf.at(0), static_cast<std::streamsize>(sz));
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002699 f.close();
2700
2701 std::string basedir = GetBaseDir(filename);
2702
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002703 bool ret = LoadASCIIFromString(model, err, &buf.at(0),
Luke San Antonio9e36b612016-06-23 14:10:51 -04002704 static_cast<unsigned int>(buf.size()), basedir,
2705 check_sections);
2706
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002707 return ret;
2708}
2709
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002710bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
2711 const unsigned char *bytes,
2712 unsigned int size,
2713 const std::string &base_dir,
2714 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002715 if (size < 20) {
2716 if (err) {
2717 (*err) = "Too short data size for glTF Binary.";
2718 }
2719 return false;
2720 }
2721
Syoyo Fujitabeded612016-05-01 20:03:43 +09002722 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
2723 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002724 // ok
2725 } else {
2726 if (err) {
2727 (*err) = "Invalid magic.";
2728 }
2729 return false;
2730 }
2731
Syoyo Fujitabeded612016-05-01 20:03:43 +09002732 unsigned int version; // 4 bytes
2733 unsigned int length; // 4 bytes
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002734 unsigned int model_length; // 4 bytes
2735 unsigned int model_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002736
2737 // @todo { Endian swap for big endian machine. }
Syoyo Fujitabeded612016-05-01 20:03:43 +09002738 memcpy(&version, bytes + 4, 4);
2739 swap4(&version);
2740 memcpy(&length, bytes + 8, 4);
2741 swap4(&length);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002742 memcpy(&model_length, bytes + 12, 4);
2743 swap4(&model_length);
2744 memcpy(&model_format, bytes + 16, 4);
2745 swap4(&model_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002746
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002747 if ((20 + model_length >= size) || (model_length < 1) ||
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09002748 (model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002749 if (err) {
2750 (*err) = "Invalid glTF binary.";
2751 }
2752 return false;
2753 }
2754
2755 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09002756 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002757 model_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002758
2759 is_binary_ = true;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002760 bin_data_ = bytes + 20 + model_length +
2761 8; // 4 bytes (buffer_length) + 4 bytes(buffer_format)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002762 bin_size_ =
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002763 length - (20 + model_length); // extract header + JSON scene data.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002764
Syoyo Fujitabeded612016-05-01 20:03:43 +09002765 bool ret =
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002766 LoadFromString(model, err, reinterpret_cast<const char *>(&bytes[20]),
2767 model_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002768 if (!ret) {
2769 return ret;
2770 }
2771
2772 return true;
2773}
2774
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002775bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
2776 const std::string &filename,
2777 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002778 std::stringstream ss;
2779
r-lyeh66c10632016-08-29 16:56:18 +02002780 std::ifstream f(filename.c_str(), std::ios::binary);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002781 if (!f) {
2782 ss << "Failed to open file: " << filename << std::endl;
2783 if (err) {
2784 (*err) = ss.str();
2785 }
2786 return false;
2787 }
2788
2789 f.seekg(0, f.end);
2790 size_t sz = static_cast<size_t>(f.tellg());
2791 std::vector<char> buf(sz);
2792
2793 f.seekg(0, f.beg);
2794 f.read(&buf.at(0), static_cast<std::streamsize>(sz));
2795 f.close();
2796
Syoyo Fujitabeded612016-05-01 20:03:43 +09002797 std::string basedir = GetBaseDir(filename);
2798
2799 bool ret = LoadBinaryFromMemory(
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002800 model, err, reinterpret_cast<unsigned char *>(&buf.at(0)),
Luke San Antonio9e36b612016-06-23 14:10:51 -04002801 static_cast<unsigned int>(buf.size()), basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002802
2803 return ret;
2804}
2805
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002806///////////////////////
2807// GLTF Serialization
2808///////////////////////
2809
2810typedef std::pair<std::string, picojson::value> json_object_pair;
2811
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002812template <typename T>
2813static void SerializeNumberProperty(const std::string &key, T number,
2814 picojson::object &obj) {
2815 obj.insert(
2816 json_object_pair(key, picojson::value(static_cast<double>(number))));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002817}
2818
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002819template <typename T>
2820static void SerializeNumberArrayProperty(const std::string &key,
2821 const std::vector<T> &value,
2822 picojson::object &obj) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002823 picojson::object o;
2824 picojson::array vals;
2825
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002826 for (unsigned int i = 0; i < value.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002827 vals.push_back(picojson::value(static_cast<double>(value[i])));
2828 }
2829
2830 obj.insert(json_object_pair(key, picojson::value(vals)));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002831}
2832
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002833static void SerializeStringProperty(const std::string &key,
2834 const std::string &value,
2835 picojson::object &obj) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002836 picojson::value strVal(value);
2837 obj.insert(json_object_pair(key, strVal));
2838}
2839
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002840static void SerializeStringArrayProperty(const std::string &key,
2841 const std::vector<std::string> &value,
2842 picojson::object &obj) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002843 picojson::object o;
2844 picojson::array vals;
2845
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002846 for (unsigned int i = 0; i < value.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002847 vals.push_back(picojson::value(value[i]));
2848 }
2849
2850 obj.insert(json_object_pair(key, picojson::value(vals)));
2851}
2852
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002853static void SerializeValue(const std::string &key, const Value &value,
2854 picojson::object &obj) {
2855 if (value.IsArray()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002856 picojson::array jsonValue;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002857 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
Syoyo Fujita9a1ea7e2017-06-20 02:17:28 +09002858 Value elementValue = value.Get(int(i));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002859 if (elementValue.IsString())
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002860 jsonValue.push_back(picojson::value(elementValue.Get<std::string>()));
2861 }
2862 obj.insert(json_object_pair(key, picojson::value(jsonValue)));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002863 } else {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002864 picojson::object jsonValue;
2865 std::vector<std::string> valueKeys;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002866 for (unsigned int i = 0; i < valueKeys.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002867 Value elementValue = value.Get(valueKeys[i]);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002868 if (elementValue.IsInt())
2869 jsonValue.insert(json_object_pair(
2870 valueKeys[i],
2871 picojson::value(static_cast<double>(elementValue.Get<int>()))));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002872 }
2873
2874 obj.insert(json_object_pair(key, picojson::value(jsonValue)));
2875 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002876}
2877
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002878static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
2879 const std::string &binFilePath) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002880 std::ofstream output(binFilePath.c_str(), std::ofstream::binary);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002881 output.write(reinterpret_cast<const char *>(&data[0]),
2882 std::streamsize(data.size()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002883 output.close();
2884}
2885
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002886static void SerializeParameterMap(ParameterMap &param, picojson::object &o) {
2887 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
2888 ++paramIt) {
2889 if (paramIt->second.number_array.size()) {
2890 SerializeNumberArrayProperty<double>(paramIt->first,
2891 paramIt->second.number_array, o);
2892 } else if (paramIt->second.json_double_value.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002893 picojson::object json_double_value;
2894
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002895 for (std::map<std::string, double>::iterator it =
2896 paramIt->second.json_double_value.begin();
2897 it != paramIt->second.json_double_value.end(); ++it) {
2898 json_double_value.insert(
2899 json_object_pair(it->first, picojson::value(it->second)));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002900 }
2901
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002902 o.insert(
2903 json_object_pair(paramIt->first, picojson::value(json_double_value)));
2904 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002905 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002906 } else {
2907 o.insert(json_object_pair(paramIt->first,
2908 picojson::value(paramIt->second.bool_value)));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002909 }
2910 }
2911}
2912
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002913static void SerializeGltfAccessor(Accessor &accessor, picojson::object &o) {
2914 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002915
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002916 if (accessor.byteOffset != 0.0)
2917 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002918
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002919 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
2920 SerializeNumberProperty<size_t>("count", accessor.count, o);
2921 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
2922 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
2923 std::string type;
2924 switch (accessor.type) {
2925 case TINYGLTF_TYPE_SCALAR:
2926 type = "SCALAR";
2927 break;
2928 case TINYGLTF_TYPE_VEC2:
2929 type = "VEC2";
2930 break;
2931 case TINYGLTF_TYPE_VEC3:
2932 type = "VEC3";
2933 break;
2934 case TINYGLTF_TYPE_VEC4:
2935 type = "VEC4";
2936 break;
2937 case TINYGLTF_TYPE_MAT2:
2938 type = "MAT2";
2939 break;
2940 case TINYGLTF_TYPE_MAT3:
2941 type = "MAT3";
2942 break;
2943 case TINYGLTF_TYPE_MAT4:
2944 type = "MAT4";
2945 break;
2946 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002947
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002948 SerializeStringProperty("type", type, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002949}
2950
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002951static void SerializeGltfAnimationChannel(AnimationChannel &channel,
2952 picojson::object &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002953 SerializeNumberProperty("sampler", channel.sampler, o);
2954 picojson::object target;
2955 SerializeNumberProperty("node", channel.target_node, target);
2956 SerializeStringProperty("path", channel.target_path, target);
2957
2958 o.insert(json_object_pair("target", picojson::value(target)));
2959}
2960
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002961static void SerializeGltfAnimationSampler(AnimationSampler &sampler,
2962 picojson::object &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002963 SerializeNumberProperty("input", sampler.input, o);
2964 SerializeNumberProperty("output", sampler.output, o);
2965 SerializeStringProperty("interpolation", sampler.interpolation, o);
2966}
2967
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002968static void SerializeGltfAnimation(Animation &animation, picojson::object &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002969 SerializeStringProperty("name", animation.name, o);
2970 picojson::array channels;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002971 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002972 picojson::object channel;
2973 AnimationChannel gltfChannel = animation.channels[i];
2974 SerializeGltfAnimationChannel(gltfChannel, channel);
2975 channels.push_back(picojson::value(channel));
2976 }
2977 o.insert(json_object_pair("channels", picojson::value(channels)));
2978
2979 picojson::array samplers;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002980 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002981 picojson::object sampler;
2982 AnimationSampler gltfSampler = animation.samplers[i];
2983 SerializeGltfAnimationSampler(gltfSampler, sampler);
2984 samplers.push_back(picojson::value(sampler));
2985 }
2986
2987 o.insert(json_object_pair("samplers", picojson::value(samplers)));
2988}
2989
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002990static void SerializeGltfAsset(Asset &asset, picojson::object &o) {
2991 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002992 SerializeStringProperty("generator", asset.generator, o);
2993 }
2994
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002995 if (!asset.version.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00002996 SerializeStringProperty("version", asset.version, o);
2997 }
2998
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09002999 if (asset.extras.Keys().size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003000 SerializeValue("extras", asset.extras, o);
3001 }
3002}
3003
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003004static void SerializeGltfBuffer(Buffer &buffer, picojson::object &o,
3005 const std::string &binFilePath) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003006 SerializeGltfBufferData(buffer.data, binFilePath);
3007 SerializeNumberProperty("byteLength", buffer.data.size(), o);
3008 SerializeStringProperty("uri", binFilePath, o);
3009
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003010 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003011}
3012
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003013static void SerializeGltfBufferView(BufferView &bufferView,
3014 picojson::object &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003015 SerializeNumberProperty("buffer", bufferView.buffer, o);
3016 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
3017 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
3018 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
3019 SerializeNumberProperty("target", bufferView.target, o);
3020
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003021 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003022 SerializeStringProperty("name", bufferView.name, o);
3023 }
3024}
3025
3026// Only external textures are serialized for now
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003027static void SerializeGltfImage(Image &image, picojson::object &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003028 SerializeStringProperty("uri", image.uri, o);
3029
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003030 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003031 SerializeStringProperty("name", image.name, o);
3032 }
3033}
3034
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003035static void SerializeGltfMaterial(Material &material, picojson::object &o) {
3036 if (material.extPBRValues.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003037 // Serialize PBR specular/glossiness material
3038 picojson::object values;
3039 SerializeParameterMap(material.extPBRValues, values);
3040
3041 picojson::object extension;
3042 o.insert(json_object_pair("extensions", picojson::value(extension)));
3043 }
3044
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003045 if (material.values.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003046 picojson::object pbrMetallicRoughness;
3047 SerializeParameterMap(material.values, pbrMetallicRoughness);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003048 o.insert(json_object_pair("pbrMetallicRoughness",
3049 picojson::value(pbrMetallicRoughness)));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003050 }
3051
3052 picojson::object additionalValues;
3053 SerializeParameterMap(material.additionalValues, o);
3054
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003055 if (material.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003056 SerializeStringProperty("name", material.name, o);
3057 }
3058}
3059
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003060static void SerializeGltfMesh(Mesh &mesh, picojson::object &o) {
3061 picojson::array primitives;
3062 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
3063 picojson::object primitive;
3064 picojson::object attributes;
3065 Primitive gltfPrimitive = mesh.primitives[i];
3066 for (std::map<std::string, int>::iterator attrIt =
3067 gltfPrimitive.attributes.begin();
3068 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
3069 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
3070 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003071
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003072 primitive.insert(
3073 json_object_pair("attributes", picojson::value(attributes)));
3074 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
3075 SerializeNumberProperty<int>("material", gltfPrimitive.material, primitive);
3076 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003077
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003078 // Morph targets
3079 if (gltfPrimitive.targets.size()) {
3080 picojson::array targets;
3081 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
3082 picojson::object targetAttributes;
3083 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
3084 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
3085 attrIt != targetData.end(); ++attrIt) {
3086 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
3087 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003088 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003089
3090 targets.push_back(picojson::value(targetAttributes));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003091 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003092 primitive.insert(json_object_pair("targets", picojson::value(targets)));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003093 }
3094
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003095 primitives.push_back(picojson::value(primitive));
3096 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003097
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003098 o.insert(json_object_pair("primitives", picojson::value(primitives)));
3099 if (mesh.weights.size()) {
3100 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
3101 }
3102
3103 if (mesh.name.size()) {
3104 SerializeStringProperty("name", mesh.name, o);
3105 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003106}
3107
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003108static void SerializeGltfNode(Node &node, picojson::object &o) {
3109 if (node.translation.size() > 0) {
3110 SerializeNumberArrayProperty<double>("translation", node.translation, o);
3111 }
3112 if (node.rotation.size() > 0) {
3113 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
3114 }
3115 if (node.scale.size() > 0) {
3116 SerializeNumberArrayProperty<double>("scale", node.scale, o);
3117 }
3118 if (node.matrix.size() > 0) {
3119 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
3120 }
3121 if (node.mesh != -1) {
3122 SerializeNumberProperty<int>("mesh", node.mesh, o);
3123 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003124
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003125 if (node.skin != -1) {
3126 SerializeNumberProperty<int>("skin", node.skin, o);
3127 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003128
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003129 if (node.camera != -1) {
3130 SerializeNumberProperty<int>("camera", node.camera, o);
3131 }
3132
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003133 SerializeStringProperty("name", node.name, o);
3134 SerializeNumberArrayProperty<int>("children", node.children, o);
3135}
3136
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003137static void SerializeGltfSampler(Sampler &sampler, picojson::object &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003138 SerializeNumberProperty("magFilter", sampler.magFilter, o);
3139 SerializeNumberProperty("minFilter", sampler.minFilter, o);
3140 SerializeNumberProperty("wrapS", sampler.wrapS, o);
3141 SerializeNumberProperty("wrapT", sampler.wrapT, o);
3142}
3143
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003144static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
3145 picojson::object &o) {
3146 SerializeNumberProperty("zfar", camera.zfar, o);
3147 SerializeNumberProperty("znear", camera.znear, o);
3148 SerializeNumberProperty("xmag", camera.xmag, o);
3149 SerializeNumberProperty("ymag", camera.ymag, o);
3150}
3151
3152static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
3153 picojson::object &o) {
3154 SerializeNumberProperty("zfar", camera.zfar, o);
3155 SerializeNumberProperty("znear", camera.znear, o);
3156 if (camera.aspectRatio > 0) {
3157 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
3158 }
3159
3160 if (camera.yfov > 0) {
3161 SerializeNumberProperty("yfov", camera.yfov, o);
3162 }
3163}
3164
3165static void SerializeGltfCamera(const Camera &camera, picojson::object &o) {
3166 SerializeStringProperty("type", camera.type, o);
3167 if (!camera.name.empty()) {
3168 SerializeStringProperty("name", camera.type, o);
3169 }
3170
3171 if (camera.type.compare("orthographic") == 0) {
3172 picojson::object orthographic;
3173 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
3174 o.insert(json_object_pair("orthographic", picojson::value(orthographic)));
3175 } else if (camera.type.compare("perspective") == 0) {
3176 picojson::object perspective;
3177 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
3178 o.insert(json_object_pair("perspective", picojson::value(perspective)));
3179 } else {
3180 // ???
3181 }
3182}
3183
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003184static void SerializeGltfScene(Scene &scene, picojson::object &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003185 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
3186
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003187 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003188 SerializeStringProperty("name", scene.name, o);
3189 }
3190}
3191
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003192static void SerializeGltfSkin(Skin &skin, picojson::object &o) {
3193 if (skin.inverseBindMatrices != -1)
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003194 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
3195
3196 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
3197 SerializeNumberProperty("skeleton", skin.skeleton, o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003198 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003199 SerializeStringProperty("name", skin.name, o);
3200 }
3201}
3202
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003203static void SerializeGltfTexture(Texture &texture, picojson::object &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003204 SerializeNumberProperty("sampler", texture.sampler, o);
3205 SerializeNumberProperty("source", texture.source, o);
3206
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003207 if (texture.extras.Size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003208 picojson::object extras;
3209 SerializeValue("extras", texture.extras, o);
3210 o.insert(json_object_pair("extras", picojson::value(extras)));
3211 }
3212}
3213
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003214static void WriteGltfFile(const std::string &output,
3215 const std::string &content) {
3216 std::ofstream gltfFile(output.c_str());
3217 gltfFile << content << std::endl;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003218}
3219
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003220bool TinyGLTF::WriteGltfSceneToFile(
3221 Model *model,
3222 const std::string
3223 &filename /*, bool embedImages, bool embedBuffers, bool writeBinary*/) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003224 picojson::object output;
3225
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003226 // ACCESSORS
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003227 picojson::array accessors;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003228 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003229 picojson::object accessor;
3230 SerializeGltfAccessor(model->accessors[i], accessor);
3231 accessors.push_back(picojson::value(accessor));
3232 }
3233 output.insert(json_object_pair("accessors", picojson::value(accessors)));
3234
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003235 // ANIMATIONS
3236 if (model->animations.size()) {
3237 picojson::array animations;
3238 for (unsigned int i = 0; i < model->animations.size(); ++i) {
3239 if (model->animations[i].channels.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003240 picojson::object animation;
3241 SerializeGltfAnimation(model->animations[i], animation);
3242 animations.push_back(picojson::value(animation));
3243 }
3244 }
3245 output.insert(json_object_pair("animations", picojson::value(animations)));
3246 }
3247
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003248 // ASSET
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003249 picojson::object asset;
3250 SerializeGltfAsset(model->asset, asset);
3251 output.insert(json_object_pair("asset", picojson::value(asset)));
3252
3253 std::string binFilePath = filename;
3254 std::string ext = ".bin";
Syoyo Fujita9a1ea7e2017-06-20 02:17:28 +09003255 std::string::size_type pos = binFilePath.rfind('.', binFilePath.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003256
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003257 if (pos != std::string::npos) {
3258 binFilePath = binFilePath.substr(0, pos) + ext;
3259 } else {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003260 binFilePath = "./" + binFilePath + ".bin";
3261 }
3262
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003263 // BUFFERS (We expect only one buffer here)
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003264 picojson::array buffers;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003265 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003266 picojson::object buffer;
3267 SerializeGltfBuffer(model->buffers[i], buffer, binFilePath);
3268 buffers.push_back(picojson::value(buffer));
3269 }
3270 output.insert(json_object_pair("buffers", picojson::value(buffers)));
3271
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003272 // BUFFERVIEWS
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003273 picojson::array bufferViews;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003274 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003275 picojson::object bufferView;
3276 SerializeGltfBufferView(model->bufferViews[i], bufferView);
3277 bufferViews.push_back(picojson::value(bufferView));
3278 }
3279 output.insert(json_object_pair("bufferViews", picojson::value(bufferViews)));
3280
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003281 // Extensions used
3282 if (model->extensionsUsed.size()) {
3283 SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed,
3284 output);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003285 }
3286
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003287 // Extensions required
3288 if (model->extensionsRequired.size()) {
3289 SerializeStringArrayProperty("extensionsRequired",
3290 model->extensionsRequired, output);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003291 }
3292
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003293 // IMAGES
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003294 picojson::array images;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003295 for (unsigned int i = 0; i < model->images.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003296 picojson::object image;
3297 SerializeGltfImage(model->images[i], image);
3298 images.push_back(picojson::value(image));
3299 }
3300 output.insert(json_object_pair("images", picojson::value(images)));
3301
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003302 // MATERIALS
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003303 picojson::array materials;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003304 for (unsigned int i = 0; i < model->materials.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003305 picojson::object material;
3306 SerializeGltfMaterial(model->materials[i], material);
3307 materials.push_back(picojson::value(material));
3308 }
3309 output.insert(json_object_pair("materials", picojson::value(materials)));
3310
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003311 // MESHES
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003312 picojson::array meshes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003313 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003314 picojson::object mesh;
3315 SerializeGltfMesh(model->meshes[i], mesh);
3316 meshes.push_back(picojson::value(mesh));
3317 }
3318 output.insert(json_object_pair("meshes", picojson::value(meshes)));
3319
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003320 // NODES
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003321 picojson::array nodes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003322 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003323 picojson::object node;
3324 SerializeGltfNode(model->nodes[i], node);
3325 nodes.push_back(picojson::value(node));
3326 }
3327 output.insert(json_object_pair("nodes", picojson::value(nodes)));
3328
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003329 // SCENE
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003330 SerializeNumberProperty<int>("scene", model->defaultScene, output);
3331
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003332 // SCENES
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003333 picojson::array scenes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003334 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003335 picojson::object currentScene;
3336 SerializeGltfScene(model->scenes[i], currentScene);
3337 scenes.push_back(picojson::value(currentScene));
3338 }
3339 output.insert(json_object_pair("scenes", picojson::value(scenes)));
3340
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003341 // SKINS
3342 if (model->skins.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003343 picojson::array skins;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003344 for (unsigned int i = 0; i < model->skins.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003345 picojson::object skin;
3346 SerializeGltfSkin(model->skins[i], skin);
3347 skins.push_back(picojson::value(picojson::value(skin)));
3348 }
3349 output.insert(json_object_pair("skins", picojson::value(skins)));
3350 }
3351
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003352 // TEXTURES
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003353 picojson::array textures;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003354 for (unsigned int i = 0; i < model->textures.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003355 picojson::object texture;
3356 SerializeGltfTexture(model->textures[i], texture);
3357 textures.push_back(picojson::value(texture));
3358 }
3359 output.insert(json_object_pair("textures", picojson::value(textures)));
3360
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003361 // SAMPLERS
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003362 picojson::array samplers;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003363 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003364 picojson::object sampler;
3365 SerializeGltfSampler(model->samplers[i], sampler);
3366 samplers.push_back(picojson::value(sampler));
3367 }
3368 output.insert(json_object_pair("samplers", picojson::value(samplers)));
3369
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09003370 // CAMERAS
3371 picojson::array cameras;
3372 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
3373 picojson::object camera;
3374 SerializeGltfCamera(model->cameras[i], camera);
3375 cameras.push_back(picojson::value(camera));
3376 }
3377 output.insert(json_object_pair("cameras", picojson::value(cameras)));
3378
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00003379 WriteGltfFile(filename, picojson::value(output).serialize());
3380 return true;
3381}
3382
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09003383} // namespace tinygltf
3384
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003385#endif // TINYGLTF_IMPLEMENTATION