blob: 651e9d7fdd4b144267d9d992d5f1fbd546d107c0 [file] [log] [blame]
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001//
Syoyo Fujitaf6120152017-05-27 23:51:23 +09002// Header-only tiny glTF 2.0 loader.
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 Fujitaf6120152017-05-27 23:51:23 +09007// Copyright (c) 2015 - 2017 Syoyo Fujita, Aurélien Chatelain and many contributors.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09008//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09009// Permission is hereby granted, free of charge, to any person obtaining a copy
10// of this software and associated documentation files (the "Software"), to deal
11// in the Software without restriction, including without limitation the rights
12// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13// copies of the Software, and to permit persons to whom the Software is
14// furnished to do so, subject to the following conditions:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090015//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090016// The above copyright notice and this permission notice shall be included in
17// all copies or substantial portions of the Software.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090018//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090019// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25// THE SOFTWARE.
26
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090027// Version:
Syoyo Fujitaf6120152017-05-27 23:51:23 +090028// - v2.0.0 glTF 2.0!.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090029//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090030// Tiny glTF loader is using following third party libraries:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090031//
32// - picojson: C++ JSON library.
33// - base64: base64 decode/encode library.
34// - stb_image: Image loading library.
35//
Syoyo Fujita7c877972016-03-08 01:31:49 +090036#ifndef TINY_GLTF_LOADER_H_
37#define TINY_GLTF_LOADER_H_
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090038
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090039#include <cassert>
Syoyo Fujita7680abf2016-10-17 18:02:45 +090040#include <cstring>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090041#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090042#include <string>
43#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090044
45namespace tinygltf {
46
47#define TINYGLTF_MODE_POINTS (0)
48#define TINYGLTF_MODE_LINE (1)
49#define TINYGLTF_MODE_LINE_LOOP (2)
50#define TINYGLTF_MODE_TRIANGLES (4)
51#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
52#define TINYGLTF_MODE_TRIANGLE_FAN (6)
53
54#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
55#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
56#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
57#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
58#define TINYGLTF_COMPONENT_TYPE_INT (5124)
59#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
60#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
61#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5127)
62
Syoyo Fujitac2615632016-06-19 21:56:06 +090063#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
64#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
65#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
66#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
67#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
68#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
69
70#define TINYGLTF_TEXTURE_WRAP_RPEAT (10497)
71#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -040072#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +090073
Luke San Antoniocdf4cb72016-06-14 21:32:11 -040074// Redeclarations of the above for technique.parameters.
75#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
76#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
77#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
78#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
79#define TINYGLTF_PARAMETER_TYPE_INT (5124)
80#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
81#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
82
83#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
84#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
85#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
86
87#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
88#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
89#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
90
91#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
92#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
93#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
94#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
95
96#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
97#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
98#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
99
100#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
101
102// End parameter types
103
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900104#define TINYGLTF_TYPE_VEC2 (2)
105#define TINYGLTF_TYPE_VEC3 (3)
106#define TINYGLTF_TYPE_VEC4 (4)
107#define TINYGLTF_TYPE_MAT2 (32 + 2)
108#define TINYGLTF_TYPE_MAT3 (32 + 3)
109#define TINYGLTF_TYPE_MAT4 (32 + 4)
110#define TINYGLTF_TYPE_SCALAR (64 + 1)
111#define TINYGLTF_TYPE_VECTOR (64 + 4)
112#define TINYGLTF_TYPE_MATRIX (64 + 16)
113
114#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
115#define TINYGLTF_IMAGE_FORMAT_PNG (1)
116#define TINYGLTF_IMAGE_FORMAT_BMP (2)
117#define TINYGLTF_IMAGE_FORMAT_GIF (3)
118
Luke San Antonio6d616f52016-06-23 14:09:23 -0400119#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
120#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900121#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400122#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
123#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
124
Syoyo Fujitabde70212016-02-07 17:38:17 +0900125#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
126#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
127
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900128#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
129#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
130
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400131#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
132#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
133
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900134typedef enum {
135 NULL_TYPE = 0,
136 NUMBER_TYPE = 1,
137 INT_TYPE = 2,
138 BOOL_TYPE = 3,
139 STRING_TYPE = 4,
140 ARRAY_TYPE = 5,
141 BINARY_TYPE = 6,
142 OBJECT_TYPE = 7
143} Type;
144
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900145#ifdef __clang__
146#pragma clang diagnostic push
147// Suppress warning for : static Value null_value
148// https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang
149#pragma clang diagnostic ignored "-Wexit-time-destructors"
150#endif
151
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900152// Simple class to represent JSON object
153class Value {
154 public:
155 typedef std::vector<Value> Array;
156 typedef std::map<std::string, Value> Object;
157
158 Value() : type_(NULL_TYPE) {}
159
160 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900161 explicit Value(int i) : type_(INT_TYPE) { int_value_ = i; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900162 explicit Value(double n) : type_(NUMBER_TYPE) { number_value_ = n; }
163 explicit Value(const std::string &s) : type_(STRING_TYPE) {
164 string_value_ = s;
165 }
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900166 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900167 binary_value_.resize(n);
168 memcpy(binary_value_.data(), p, n);
169 }
170 explicit Value(const Array &a) : type_(ARRAY_TYPE) {
171 array_value_ = Array(a);
172 }
173 explicit Value(const Object &o) : type_(OBJECT_TYPE) {
174 object_value_ = Object(o);
175 }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900176
177 char Type() const { return static_cast<const char>(type_); }
178
179 bool IsBool() const { return (type_ == BOOL_TYPE); }
180
181 bool IsInt() const { return (type_ == INT_TYPE); }
182
183 bool IsNumber() const { return (type_ == NUMBER_TYPE); }
184
185 bool IsString() const { return (type_ == STRING_TYPE); }
186
187 bool IsBinary() const { return (type_ == BINARY_TYPE); }
188
189 bool IsArray() const { return (type_ == ARRAY_TYPE); }
190
191 bool IsObject() const { return (type_ == OBJECT_TYPE); }
192
193 // Accessor
194 template <typename T>
195 const T &Get() const;
196 template <typename T>
197 T &Get();
198
199 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900200 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900201 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900202 assert(IsArray());
203 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900204 return (static_cast<size_t>(idx) < array_value_.size())
205 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900206 : null_value;
207 }
208
209 // Lookup value from a key-value pair
210 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900211 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900212 assert(IsObject());
213 Object::const_iterator it = object_value_.find(key);
214 return (it != object_value_.end()) ? it->second : null_value;
215 }
216
217 size_t ArrayLen() const {
218 if (!IsArray()) return 0;
219 return array_value_.size();
220 }
221
222 // Valid only for object type.
223 bool Has(const std::string &key) const {
224 if (!IsObject()) return false;
225 Object::const_iterator it = object_value_.find(key);
226 return (it != object_value_.end()) ? true : false;
227 }
228
229 // List keys
230 std::vector<std::string> Keys() const {
231 std::vector<std::string> keys;
232 if (!IsObject()) return keys; // empty
233
234 for (Object::const_iterator it = object_value_.begin();
235 it != object_value_.end(); ++it) {
236 keys.push_back(it->first);
237 }
238
239 return keys;
240 }
241
242 protected:
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900243 int type_;
244
245 int int_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900246 double number_value_;
247 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900248 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900249 Array array_value_;
250 Object object_value_;
251 bool boolean_value_;
252 char pad[3];
253
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900254 int pad0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900255};
256
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900257#ifdef __clang__
258#pragma clang diagnostic pop
259#endif
260
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900261#define TINYGLTF_VALUE_GET(ctype, var) \
262 template <> \
263 inline const ctype &Value::Get<ctype>() const { \
264 return var; \
265 } \
266 template <> \
267 inline ctype &Value::Get<ctype>() { \
268 return var; \
269 }
270TINYGLTF_VALUE_GET(bool, boolean_value_)
271TINYGLTF_VALUE_GET(double, number_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900272TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900273TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900274TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900275TINYGLTF_VALUE_GET(Value::Array, array_value_)
276TINYGLTF_VALUE_GET(Value::Object, object_value_)
277#undef TINYGLTF_VALUE_GET
278
Syoyo Fujitabde70212016-02-07 17:38:17 +0900279typedef struct {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000280 bool bool_value;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900281 std::string string_value;
282 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000283 std::map<std::string, double> json_double_value;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900284} Parameter;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900285
Syoyo Fujitabde70212016-02-07 17:38:17 +0900286typedef std::map<std::string, Parameter> ParameterMap;
287
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000288struct AnimationChannel {
289 int sampler; // required
290 int target_node; // required (index of the node to target)
291 std::string target_path; // required in ["translation", "rotation", "scale", "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900292 Value extras;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900293
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000294 AnimationChannel()
Syoyo Fujitaf6120152017-05-27 23:51:23 +0900295 : sampler(-1)
296 , target_node(-1)
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000297 {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000298 }
299};
300
301struct AnimationSampler {
302 int input; // required
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000303 int output; // required
Syoyo Fujitaf6120152017-05-27 23:51:23 +0900304 std::string interpolation; // in ["LINEAR", "STEP", "CATMULLROMSPLINE", "CUBICSPLINE"], default "LINEAR"
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000305
306 AnimationSampler()
Syoyo Fujitaf6120152017-05-27 23:51:23 +0900307 : input(-1)
308 , output(-1)
309 , interpolation("LINEAR")
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000310 {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000311 }
312};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900313
314typedef struct {
315 std::string name;
316 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000317 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900318 Value extras;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900319} Animation;
320
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000321struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900322 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000323 int inverseBindMatrices; // required here but not in the spec
324 int skeleton; // The index of the node used as a skeleton root
325 std::vector<int> joints; // Indices of skeleton nodes
326
327 Skin()
328 {
329 inverseBindMatrices = -1;
330 }
331};
332
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000333struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900334 std::string name;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000335 int minFilter; // ["NEAREST", "LINEAR", "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_NEAREST", "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
336 int magFilter; // ["NEAREST", "LINEAR"]
337 int wrapS; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default "REPEAT"
338 int wrapT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default "REPEAT"
Syoyo Fujitac2615632016-06-19 21:56:06 +0900339 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 Fujitaf6120152017-05-27 23:51:23 +0900344 : wrapS(TINYGLTF_TEXTURE_WRAP_RPEAT)
345 , wrapT(TINYGLTF_TEXTURE_WRAP_RPEAT)
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000346 {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000347 }
348};
349
350struct Image{
Syoyo Fujitac2615632016-06-19 21:56:06 +0900351 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900352 int width;
353 int height;
354 int component;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900355 int pad0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900356 std::vector<unsigned char> image;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000357 int bufferView; // (required if no uri)
358 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png"]
359 std::string uri; // (reqiored if no mimeType)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900360 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900361
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000362 Image()
363 {
364 bufferView = -1;
365 }
366};
367
368struct Texture {
369 int sampler;
370 int source; // Required (not specified in the spec ?)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900371 Value extras;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900372
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000373 Texture()
Syoyo Fujitaf6120152017-05-27 23:51:23 +0900374 : sampler(-1)
375 , source(-1)
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000376 {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000377 }
378};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900379
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000380// Each extension should be stored in a ParameterMap.
381// members not in the values could be included in the ParameterMap
382// to keep a single material model
383struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900384 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900385
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000386 ParameterMap values; // PBR metal/roughness workflow
387 ParameterMap additionalValues; // normal/occlusion/emissive values
388 ParameterMap extCommonValues; // KHR_common_material extension
389 ParameterMap extPBRValues;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900390 Value extras;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000391};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900392
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000393struct BufferView{
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900394 std::string name;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000395 int buffer; // Required
396 size_t byteOffset; // minimum 0, default 0
397 size_t byteLength; // required, minimum 1
398 size_t byteStride; // minimum 4, maximum 252 (multiple of 4)
399 int target; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"]
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900400 int pad0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900401 Value extras;
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900402
403 BufferView()
404 : byteOffset(0)
405 , byteStride(4)
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +0000406 {}
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900407
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000408};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900409
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000410struct Accessor {
411 int bufferView; // optional in spec but required here since sparse accessor are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900412 std::string name;
413 size_t byteOffset;
414 size_t byteStride;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000415 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
416 size_t count; // required
417 int type; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900418 Value extras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000419
420 std::vector<double> minValues; // required
421 std::vector<double> maxValues; // required
422
423 Accessor()
424 {
425 bufferView = -1;
426 }
427};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900428
429class Camera {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900430 public:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900431 Camera() {}
432 ~Camera() {}
433
434 std::string name;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900435 bool isOrthographic; // false = perspective.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900436
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000437 // Orthographic properties
438 float xMag; // required
439 float yMag; // required
440 float zFar; // required
441 float zNear; //required
442
443 // Perspective properties
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900444 float aspectRatio;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000445 float yfov; // required
446 float zfar;
447 float znear; // required
448
449 ParameterMap extensions;
450 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900451};
452
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000453struct Primitive {
454 std::map<std::string, int> attributes; // (required) A dictionary object of
455 // integer, where each integer
456 // is the index of the accessor
457 // containing an attribute.
458 int material; // The index of the material to apply to this primitive
459 // when rendering.
460 int indices; // The index of the accessor that contains the indices.
461 int mode; // one of TINYGLTF_MODE_***
Aurélien Chatelain38a6a752017-05-24 09:54:55 +0000462 std::vector<std::map<std::string, int> > targets; // array of morph targets,
463 //where each target is a dict with attribues in ["POSITION, "NORMAL", "TANGENT"] pointing
464 // to their corresponding accessors
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000465 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900466
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000467 Primitive()
468 {
469 material = -1;
470 indices = -1;
471 }
472};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900473
474typedef struct {
475 std::string name;
476 std::vector<Primitive> primitives;
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +0000477 std::vector<double> weights; // weights to be applied to the Morph Targets
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000478 ParameterMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900479 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900480} Mesh;
481
482class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900483 public:
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000484 Node()
Syoyo Fujitaf6120152017-05-27 23:51:23 +0900485 : skin(-1)
486 , mesh(-1)
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000487 {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000488 }
489
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900490 ~Node() {}
491
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000492 int camera; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900493
494 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000495 int skin;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000496 int mesh;
497 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900498 std::vector<double> rotation; // length must be 0 or 4
499 std::vector<double> scale; // length must be 0 or 3
500 std::vector<double> translation; // length must be 0 or 3
501 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900502
503 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900504};
505
506typedef struct {
507 std::string name;
508 std::vector<unsigned char> data;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000509 std::string uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900510 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900511} Buffer;
512
513typedef struct {
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +0000514 std::string version; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900515 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +0000516 std::string minVersion;
517 std::string copyright;
518 ParameterMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900519 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900520} Asset;
521
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000522struct Scene {
523 std::string name;
524 std::vector<int> nodes;
525
526 ParameterMap extensions;
527 ParameterMap extras;
528};
529
530class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900531 public:
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000532 Model() {}
533 ~Model() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900534
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000535 std::vector<Accessor> accessors;
536 std::vector<Animation> animations;
537 std::vector<Buffer> buffers;
538 std::vector<BufferView> bufferViews;
539 std::vector<Material> materials;
540 std::vector<Mesh> meshes;
541 std::vector<Node> nodes;
542 std::vector<Texture> textures;
543 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000544 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000545 std::vector<Sampler> samplers;
546 std::vector<Scene> scenes;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900547
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000548 int defaultScene;
549 std::vector<std::string> extensionsUsed;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900550
551 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900552
553 Value extras;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900554};
555
Syoyo Fujita0614eb82016-10-14 18:50:14 +0900556enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -0400557 NO_REQUIRE = 0x00,
558 REQUIRE_SCENE = 0x01,
559 REQUIRE_SCENES = 0x02,
560 REQUIRE_NODES = 0x04,
561 REQUIRE_ACCESSORS = 0x08,
562 REQUIRE_BUFFERS = 0x10,
563 REQUIRE_BUFFER_VIEWS = 0x20,
564 REQUIRE_ALL = 0x3f
565};
566
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900567class TinyGLTFLoader {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900568 public:
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900569 TinyGLTFLoader() : bin_data_(NULL), bin_size_(0), is_binary_(false) {
570 pad[0] = pad[1] = pad[2] = pad[3] = pad[4] = pad[5] = pad[6] = 0;
571 }
Syoyo Fujita7c877972016-03-08 01:31:49 +0900572 ~TinyGLTFLoader() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900573
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900574 /// Loads glTF ASCII asset from a file.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900575 /// Returns false and set error string to `err` if there's an error.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000576 bool LoadASCIIFromFile(Model *model, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400577 const std::string &filename,
578 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900579
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900580 /// Loads glTF ASCII asset from string(memory).
581 /// `length` = strlen(str);
582 /// Returns false and set error string to `err` if there's an error.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000583 bool LoadASCIIFromString(Model *model, std::string *err, const char *str,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900584 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400585 const std::string &base_dir,
586 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900587
588 /// Loads glTF binary asset from a file.
589 /// Returns false and set error string to `err` if there's an error.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000590 bool LoadBinaryFromFile(Model *model, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400591 const std::string &filename,
592 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900593
594 /// Loads glTF binary asset from memory.
595 /// `length` = strlen(str);
596 /// Returns false and set error string to `err` if there's an error.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000597 bool LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900598 const unsigned char *bytes,
599 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400600 const std::string &base_dir = "",
601 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900602
Syoyo Fujitabeded612016-05-01 20:03:43 +0900603 private:
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900604 /// Loads glTF asset from string(memory).
605 /// `length` = strlen(str);
606 /// Returns false and set error string to `err` if there's an error.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000607 bool LoadFromString(Model *model, std::string *err, const char *str,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400608 const unsigned int length, const std::string &base_dir,
609 unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900610
Syoyo Fujitabeded612016-05-01 20:03:43 +0900611 const unsigned char *bin_data_;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900612 size_t bin_size_;
613 bool is_binary_;
614 char pad[7];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900615};
616
Syoyo Fujita7c877972016-03-08 01:31:49 +0900617} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900618
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900619#endif // TINY_GLTF_LOADER_H_
620
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900621#ifdef TINYGLTF_LOADER_IMPLEMENTATION
Syoyo Fujita7c877972016-03-08 01:31:49 +0900622#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900623//#include <cassert>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900624#include <fstream>
625#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +0900626
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900627#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +0900628// Disable some warnings for external files.
629#pragma clang diagnostic push
630#pragma clang diagnostic ignored "-Wfloat-equal"
631#pragma clang diagnostic ignored "-Wexit-time-destructors"
632#pragma clang diagnostic ignored "-Wconversion"
633#pragma clang diagnostic ignored "-Wold-style-cast"
634#pragma clang diagnostic ignored "-Wdouble-promotion"
635#pragma clang diagnostic ignored "-Wglobal-constructors"
636#pragma clang diagnostic ignored "-Wreserved-id-macro"
637#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
638#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900639#ifdef __APPLE__
640#if __clang_major__ >= 8 && __clang_minor__ >= 1
641#pragma clang diagnostic ignored "-Wcomma"
642#endif
643#else // __APPLE__
644#if (__clang_major__ >= 4) || (__clang_major__ >= 3 && __clang_minor__ > 8)
645#pragma clang diagnostic ignored "-Wcomma"
646#endif
647#endif // __APPLE__
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900648#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900649
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900650#define PICOJSON_USE_INT64
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +0900651#include "./picojson.h"
652#include "./stb_image.h"
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900653#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +0900654#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900655#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900656
657#ifdef _WIN32
658#include <Windows.h>
659#else
660#include <wordexp.h>
661#endif
662
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900663#if defined(__sparcv9)
664// Big endian
665#else
666#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
667#define TINYGLTF_LITTLE_ENDIAN 1
668#endif
669#endif
670
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +0900671namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900672
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900673static void swap4(unsigned int *val) {
674#ifdef TINYGLTF_LITTLE_ENDIAN
675 (void)val;
676#else
677 unsigned int tmp = *val;
678 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
679 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
680
681 dst[0] = src[3];
682 dst[1] = src[2];
683 dst[2] = src[1];
684 dst[3] = src[0];
685#endif
686}
687
Syoyo Fujita643ce102016-05-01 17:19:37 +0900688static bool FileExists(const std::string &abs_filename) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900689 bool ret;
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900690#ifdef _WIN32
691 FILE *fp;
692 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
693 if (err != 0) {
Syoyo Fujitabeded612016-05-01 20:03:43 +0900694 return false;
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900695 }
696#else
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900697 FILE *fp = fopen(abs_filename.c_str(), "rb");
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900698#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900699 if (fp) {
700 ret = true;
701 fclose(fp);
702 } else {
703 ret = false;
704 }
705
706 return ret;
707}
708
Syoyo Fujita643ce102016-05-01 17:19:37 +0900709static std::string ExpandFilePath(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900710#ifdef _WIN32
711 DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
712 char *str = new char[len];
713 ExpandEnvironmentStringsA(filepath.c_str(), str, len);
714
715 std::string s(str);
716
717 delete[] str;
718
719 return s;
720#else
721
Syoyo Fujita643ce102016-05-01 17:19:37 +0900722#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900723 // no expansion
724 std::string s = filepath;
725#else
726 std::string s;
727 wordexp_t p;
728
729 if (filepath.empty()) {
730 return "";
731 }
732
733 // char** w;
734 int ret = wordexp(filepath.c_str(), &p, 0);
735 if (ret) {
736 // err
737 s = filepath;
738 return s;
739 }
740
741 // Use first element only.
742 if (p.we_wordv) {
743 s = std::string(p.we_wordv[0]);
744 wordfree(&p);
745 } else {
746 s = filepath;
747 }
748
749#endif
750
751 return s;
752#endif
753}
754
Syoyo Fujitabeded612016-05-01 20:03:43 +0900755static std::string JoinPath(const std::string &path0,
756 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900757 if (path0.empty()) {
758 return path1;
759 } else {
760 // check '/'
761 char lastChar = *path0.rbegin();
762 if (lastChar != '/') {
763 return path0 + std::string("/") + path1;
764 } else {
765 return path0 + path1;
766 }
767 }
768}
769
Syoyo Fujita643ce102016-05-01 17:19:37 +0900770static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900771 const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900772 for (size_t i = 0; i < paths.size(); i++) {
773 std::string absPath = ExpandFilePath(JoinPath(paths[i], filepath));
774 if (FileExists(absPath)) {
775 return absPath;
776 }
777 }
778
779 return std::string();
780}
781
782// std::string GetFilePathExtension(const std::string& FileName)
783//{
784// if(FileName.find_last_of(".") != std::string::npos)
785// return FileName.substr(FileName.find_last_of(".")+1);
786// return "";
787//}
788
Syoyo Fujita643ce102016-05-01 17:19:37 +0900789static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900790 if (filepath.find_last_of("/\\") != std::string::npos)
791 return filepath.substr(0, filepath.find_last_of("/\\"));
792 return "";
793}
794
795// std::string base64_encode(unsigned char const* , unsigned int len);
796std::string base64_decode(std::string const &s);
797
798/*
799 base64.cpp and base64.h
800
801 Copyright (C) 2004-2008 René Nyffenegger
802
803 This source code is provided 'as-is', without any express or implied
804 warranty. In no event will the author be held liable for any damages
805 arising from the use of this software.
806
807 Permission is granted to anyone to use this software for any purpose,
808 including commercial applications, and to alter it and redistribute it
809 freely, subject to the following restrictions:
810
811 1. The origin of this source code must not be misrepresented; you must not
812 claim that you wrote the original source code. If you use this source code
813 in a product, an acknowledgment in the product documentation would be
814 appreciated but is not required.
815
816 2. Altered source versions must be plainly marked as such, and must not be
817 misrepresented as being the original source code.
818
819 3. This notice may not be removed or altered from any source distribution.
820
821 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
822
823*/
824
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900825#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +0900826#pragma clang diagnostic push
827#pragma clang diagnostic ignored "-Wexit-time-destructors"
828#pragma clang diagnostic ignored "-Wglobal-constructors"
829#pragma clang diagnostic ignored "-Wsign-conversion"
830#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900831#endif
Syoyo Fujita7c877972016-03-08 01:31:49 +0900832static const std::string base64_chars =
833 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
834 "abcdefghijklmnopqrstuvwxyz"
835 "0123456789+/";
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900836
837static inline bool is_base64(unsigned char c) {
838 return (isalnum(c) || (c == '+') || (c == '/'));
839}
840
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900841std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +0900842 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900843 int i = 0;
844 int j = 0;
845 int in_ = 0;
846 unsigned char char_array_4[4], char_array_3[3];
847 std::string ret;
848
849 while (in_len-- && (encoded_string[in_] != '=') &&
850 is_base64(encoded_string[in_])) {
851 char_array_4[i++] = encoded_string[in_];
852 in_++;
853 if (i == 4) {
854 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +0900855 char_array_4[i] =
856 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900857
858 char_array_3[0] =
859 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
860 char_array_3[1] =
861 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
862 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
863
Syoyo Fujita7c877972016-03-08 01:31:49 +0900864 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900865 i = 0;
866 }
867 }
868
869 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900870 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900871
872 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +0900873 char_array_4[j] =
874 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900875
876 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
877 char_array_3[1] =
878 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
879 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
880
Syoyo Fujita7c877972016-03-08 01:31:49 +0900881 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900882 }
883
884 return ret;
885}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900886#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +0900887#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900888#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900889
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900890static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900891 const std::string &filename,
892 const std::string &basedir, size_t reqBytes,
893 bool checkSize) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900894 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900895
896 std::vector<std::string> paths;
897 paths.push_back(basedir);
898 paths.push_back(".");
899
900 std::string filepath = FindFile(paths, filename);
901 if (filepath.empty()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900902 if (err) {
Luke San Antonioda8b5d12016-06-14 22:17:40 -0400903 (*err) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900904 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900905 return false;
906 }
907
908 std::ifstream f(filepath.c_str(), std::ifstream::binary);
909 if (!f) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900910 if (err) {
Luke San Antonioda8b5d12016-06-14 22:17:40 -0400911 (*err) += "File open error : " + filepath + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900912 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900913 return false;
914 }
915
916 f.seekg(0, f.end);
Syoyo Fujita643ce102016-05-01 17:19:37 +0900917 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +0900918 if (int(sz) < 0) {
919 // Looks reading directory, not a file.
920 return false;
921 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900922 std::vector<unsigned char> buf(sz);
923
924 f.seekg(0, f.beg);
Syoyo Fujitabeded612016-05-01 20:03:43 +0900925 f.read(reinterpret_cast<char *>(&buf.at(0)),
926 static_cast<std::streamsize>(sz));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900927 f.close();
928
929 if (checkSize) {
930 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900931 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900932 return true;
933 } else {
934 std::stringstream ss;
935 ss << "File size mismatch : " << filepath << ", requestedBytes "
936 << reqBytes << ", but got " << sz << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900937 if (err) {
938 (*err) += ss.str();
939 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900940 return false;
941 }
942 }
943
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900944 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900945 return true;
946}
947
Syoyo Fujitabeded612016-05-01 20:03:43 +0900948static bool LoadImageData(Image *image, std::string *err, int req_width,
949 int req_height, const unsigned char *bytes,
950 int size) {
951 int w, h, comp;
Aurélien Chatelain5cb43462017-05-24 09:55:14 +0000952 // if image cannot be decoded, ignore parsing and keep it by its path
953 // don't break in this case
954 //FIXME we should only enter this function if the image is embedded. If image->uri references
955 // an image file, it should be left as it is. Image loading should not be mandatory (to support other formats)
Syoyo Fujitabeded612016-05-01 20:03:43 +0900956 unsigned char *data = stbi_load_from_memory(bytes, size, &w, &h, &comp, 0);
957 if (!data) {
958 if (err) {
959 (*err) += "Unknown image format.\n";
960 }
Aurélien Chatelain5cb43462017-05-24 09:55:14 +0000961 return true;
Syoyo Fujitabeded612016-05-01 20:03:43 +0900962 }
963
964 if (w < 1 || h < 1) {
Syoyo Fujita4be2f882016-11-24 16:20:34 +0900965 free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +0900966 if (err) {
967 (*err) += "Unknown image format.\n";
968 }
Aurélien Chatelain5cb43462017-05-24 09:55:14 +0000969 return true;
Syoyo Fujitabeded612016-05-01 20:03:43 +0900970 }
971
972 if (req_width > 0) {
973 if (req_width != w) {
Syoyo Fujita4be2f882016-11-24 16:20:34 +0900974 free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +0900975 if (err) {
976 (*err) += "Image width mismatch.\n";
977 }
978 return false;
979 }
980 }
981
982 if (req_height > 0) {
983 if (req_height != h) {
Syoyo Fujita4be2f882016-11-24 16:20:34 +0900984 free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +0900985 if (err) {
986 (*err) += "Image height mismatch.\n";
987 }
988 return false;
989 }
990 }
991
992 image->width = w;
993 image->height = h;
994 image->component = comp;
995 image->image.resize(static_cast<size_t>(w * h * comp));
996 std::copy(data, data + w * h * comp, image->image.begin());
997
Syoyo Fujita4be2f882016-11-24 16:20:34 +0900998 free(data);
999
Syoyo Fujitabeded612016-05-01 20:03:43 +09001000 return true;
1001}
1002
Syoyo Fujita643ce102016-05-01 17:19:37 +09001003static bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001004 std::string header = "data:application/octet-stream;base64,";
1005 if (in.find(header) == 0) {
1006 return true;
1007 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001008
1009 header = "data:image/png;base64,";
1010 if (in.find(header) == 0) {
1011 return true;
1012 }
1013
1014 header = "data:image/jpeg;base64,";
1015 if (in.find(header) == 0) {
1016 return true;
1017 }
1018
Luke San Antoniocaa24b02016-06-15 02:23:09 -04001019 header = "data:text/plain;base64,";
1020 if (in.find(header) == 0) {
1021 return true;
1022 }
1023
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001024 return false;
1025}
1026
Syoyo Fujitabeded612016-05-01 20:03:43 +09001027static bool DecodeDataURI(std::vector<unsigned char> *out,
1028 const std::string &in, size_t reqBytes,
1029 bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001030 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09001031 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001032 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001033 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001034 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001035
1036 if (data.empty()) {
1037 header = "data:image/jpeg;base64,";
1038 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001039 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09001040 }
1041 }
1042
1043 if (data.empty()) {
1044 header = "data:image/png;base64,";
1045 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001046 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09001047 }
1048 }
1049
1050 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04001051 header = "data:text/plain;base64,";
1052 if (in.find(header) == 0) {
1053 data = base64_decode(in.substr(header.size()));
1054 }
1055 }
1056
1057 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09001058 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09001059 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09001060
1061 if (checkSize) {
1062 if (data.size() != reqBytes) {
1063 return false;
1064 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001065 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09001066 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001067 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09001068 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001069 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09001070 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001071}
1072
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001073static void ParseObjectProperty(Value *ret, const picojson::object &o) {
1074 tinygltf::Value::Object vo;
1075 picojson::object::const_iterator it(o.begin());
1076 picojson::object::const_iterator itEnd(o.end());
1077
1078 for (; it != itEnd; it++) {
1079 picojson::value v = it->second;
1080
1081 if (v.is<bool>()) {
1082 vo[it->first] = tinygltf::Value(v.get<bool>());
1083 } else if (v.is<double>()) {
1084 vo[it->first] = tinygltf::Value(v.get<double>());
1085 } else if (v.is<int64_t>()) {
Syoyo Fujita7680abf2016-10-17 18:02:45 +09001086 vo[it->first] =
1087 tinygltf::Value(static_cast<int>(v.get<int64_t>())); // truncate
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001088 } else if (v.is<std::string>()) {
1089 vo[it->first] = tinygltf::Value(v.get<std::string>());
1090 } else if (v.is<picojson::object>()) {
1091 tinygltf::Value child_value;
1092 ParseObjectProperty(&child_value, v.get<picojson::object>());
Layla1dbfe0e2017-05-07 16:56:55 -04001093 vo[it->first] = child_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001094 }
1095 // TODO(syoyo) binary, array
1096 }
1097
1098 (*ret) = tinygltf::Value(vo);
1099}
1100
1101static bool ParseExtrasProperty(Value *ret, const picojson::object &o) {
1102 picojson::object::const_iterator it = o.find("extras");
1103 if (it == o.end()) {
1104 return false;
1105 }
1106
1107 // FIXME(syoyo) Currently we only support `object` type for extras property.
1108 if (!it->second.is<picojson::object>()) {
1109 return false;
1110 }
1111
1112 ParseObjectProperty(ret, it->second.get<picojson::object>());
1113
1114 return true;
1115}
1116
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001117static bool ParseBooleanProperty(bool *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001118 const picojson::object &o,
1119 const std::string &property, bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001120 picojson::object::const_iterator it = o.find(property);
1121 if (it == o.end()) {
1122 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001123 if (err) {
1124 (*err) += "'" + property + "' property is missing.\n";
1125 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001126 }
1127 return false;
1128 }
1129
1130 if (!it->second.is<bool>()) {
1131 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001132 if (err) {
1133 (*err) += "'" + property + "' property is not a bool type.\n";
1134 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001135 }
1136 return false;
1137 }
1138
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001139 if (ret) {
1140 (*ret) = it->second.get<bool>();
1141 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001142
1143 return true;
1144}
1145
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001146static bool ParseNumberProperty(double *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001147 const picojson::object &o,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001148 const std::string &property, const bool required, const std::string &parent_node = "") {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001149 picojson::object::const_iterator it = o.find(property);
1150 if (it == o.end()) {
1151 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001152 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001153 (*err) += "'" + property + "' property is missing";
1154 if (!parent_node.empty()) {
1155 (*err) += " in " + parent_node;
1156 }
1157 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001158 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001159 }
1160 return false;
1161 }
1162
1163 if (!it->second.is<double>()) {
1164 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001165 if (err) {
1166 (*err) += "'" + property + "' property is not a number type.\n";
1167 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001168 }
1169 return false;
1170 }
1171
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001172 if (ret) {
1173 (*ret) = it->second.get<double>();
1174 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001175
1176 return true;
1177}
1178
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001179static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001180 const picojson::object &o,
1181 const std::string &property,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001182 bool required,
1183 const std::string &parent_node = "") {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001184 picojson::object::const_iterator it = o.find(property);
1185 if (it == o.end()) {
1186 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001187 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001188 (*err) += "'" + property + "' property is missing";
1189 if (!parent_node.empty()) {
1190 (*err) += " in " + parent_node;
1191 }
1192 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001193 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001194 }
1195 return false;
1196 }
1197
1198 if (!it->second.is<picojson::array>()) {
1199 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001200 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001201 (*err) += "'" + property + "' property is not an array";
1202 if (!parent_node.empty()) {
1203 (*err) += " in " + parent_node;
1204 }
1205 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001206 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001207 }
1208 return false;
1209 }
1210
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001211 ret->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001212 const picojson::array &arr = it->second.get<picojson::array>();
1213 for (size_t i = 0; i < arr.size(); i++) {
1214 if (!arr[i].is<double>()) {
1215 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001216 if (err) {
1217 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001218 if (!parent_node.empty()) {
1219 (*err) += " in " + parent_node;
1220 }
1221 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001222 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001223 }
1224 return false;
1225 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001226 ret->push_back(arr[i].get<double>());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001227 }
1228
1229 return true;
1230}
1231
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001232static bool ParseStringProperty(
1233 std::string *ret, std::string *err, const picojson::object &o,
1234 const std::string &property, bool required,
1235 const std::string &parent_node = std::string()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001236 picojson::object::const_iterator it = o.find(property);
1237 if (it == o.end()) {
1238 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001239 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001240 (*err) += "'" + property + "' property is missing";
1241 if (parent_node.empty()) {
1242 (*err) += ".\n";
1243 } else {
1244 (*err) += " in `" + parent_node + "'.\n";
1245 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001246 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001247 }
1248 return false;
1249 }
1250
1251 if (!it->second.is<std::string>()) {
1252 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001253 if (err) {
1254 (*err) += "'" + property + "' property is not a string type.\n";
1255 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001256 }
1257 return false;
1258 }
1259
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001260 if (ret) {
1261 (*ret) = it->second.get<std::string>();
1262 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001263
1264 return true;
1265}
1266
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001267static bool ParseStringIntProperty(std::map<std::string, int> *ret,
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001268 std::string *err, const picojson::object &o,
1269 const std::string &property, bool required) {
Luke San Antonio19894c72016-06-14 21:19:51 -04001270 picojson::object::const_iterator it = o.find(property);
1271 if (it == o.end()) {
1272 if (required) {
1273 if (err) {
1274 (*err) += "'" + property + "' property is missing.\n";
1275 }
1276 }
1277 return false;
1278 }
1279
1280 // Make sure we are dealing with an object / dictionary.
1281 if (!it->second.is<picojson::object>()) {
1282 if (required) {
1283 if (err) {
1284 (*err) += "'" + property + "' property is not an object.\n";
1285 }
1286 }
1287 return false;
1288 }
1289
1290 ret->clear();
1291 const picojson::object &dict = it->second.get<picojson::object>();
1292
1293 picojson::object::const_iterator dictIt(dict.begin());
1294 picojson::object::const_iterator dictItEnd(dict.end());
1295
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001296 for (; dictIt != dictItEnd; ++dictIt) {
Syoyo Fujitaf6120152017-05-27 23:51:23 +09001297 if (!dictIt->second.is<double>()) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001298 if (required) {
1299 if (err) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001300 (*err) += "'" + property + "' value is not an int.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04001301 }
1302 }
1303 return false;
1304 }
1305
1306 // Insert into the list.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001307 (*ret)[dictIt->first] = static_cast<int>(dictIt->second.get<double>());
Luke San Antonio19894c72016-06-14 21:19:51 -04001308 }
1309 return true;
1310}
1311
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001312static bool ParseJSONProperty(std::map<std::string, double> *ret, std::string *err,
1313 const picojson::object &o,
1314 const std::string &property,
1315 bool required)
1316{
1317 picojson::object::const_iterator it = o.find(property);
1318 if(it == o.end())
1319 {
1320 if (required) {
1321 if(err) {
1322 (*err) += "'" + property + "' property is missing. \n'";
1323 }
1324 }
1325 return false;
1326 }
1327
1328 if(!it->second.is<picojson::object>()) {
1329 if (required) {
1330 if (err) {
1331 (*err) += "'" + property + "' property is not a JSON object.\n";
1332 }
1333 }
1334 return false;
1335 }
1336
1337 ret->clear();
1338 const picojson::object &obj = it->second.get<picojson::object>();
1339 picojson::object::const_iterator it2(obj.begin());
1340 picojson::object::const_iterator itEnd(obj.end());
1341 for (; it2 != itEnd; it2++) {
1342 if(it2->second.is<double>())
1343 ret->insert(std::pair<std::string, double>(it2->first, it2->second.get<double>()));
1344 }
1345
1346 return true;
1347}
1348
Syoyo Fujitabeded612016-05-01 20:03:43 +09001349static bool ParseAsset(Asset *asset, std::string *err,
1350 const picojson::object &o) {
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001351 ParseStringProperty(&asset->version, err, o, "version", true);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001352 ParseStringProperty(&asset->generator, err, o, "generator", false);
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001353 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001354
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001355 // Unity exporter version is added as extra here
1356 ParseExtrasProperty(&(asset->extras), o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001357
1358 return true;
1359}
1360
Syoyo Fujitabeded612016-05-01 20:03:43 +09001361static bool ParseImage(Image *image, std::string *err,
1362 const picojson::object &o, const std::string &basedir,
1363 bool is_binary, const unsigned char *bin_data,
1364 size_t bin_size) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001365 // A glTF image must either reference a bufferView or an image uri
1366 double bufferView = -1;
1367 bool isEmbedded = ParseNumberProperty(&bufferView, err, o, "bufferView", true);
1368 isEmbedded = isEmbedded && static_cast<int>(bufferView) != -1;
1369
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001370 std::string uri;
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09001371 if (!ParseStringProperty(&uri, err, o, "uri", true, "Image") && !isEmbedded) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001372 if (err) {
1373 (*err) += "Invalid image data (required data is missing).\n";
1374 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001375 return false;
1376 }
1377
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001378 ParseStringProperty(&image->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001379
1380 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001381
1382 if (is_binary) {
1383 // Still binary glTF accepts external dataURI. First try external resources.
1384 bool loaded = false;
1385 if (IsDataURI(uri)) {
1386 loaded = DecodeDataURI(&img, uri, 0, false);
1387 } else {
1388 // Assume external .bin file.
1389 loaded = LoadExternalFile(&img, err, uri, basedir, 0, false);
1390 }
1391
1392 if (!loaded) {
1393 // load data from (embedded) binary data
1394
1395 if ((bin_size == 0) || (bin_data == NULL)) {
1396 if (err) {
1397 (*err) += "Invalid binary data.\n";
1398 }
1399 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001400 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001401
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001402 double buffer_view = -1.0;
Aurélien Chatelain6d3bfd72017-05-30 15:04:32 +00001403 if (!ParseNumberProperty(&buffer_view, err, o, "bufferView", true)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001404 return false;
1405 }
1406
Aurélien Chatelain6d3bfd72017-05-30 15:04:32 +00001407 std::string mime_type;
1408 ParseStringProperty(&mime_type, err, o, "mimeType", false);
1409
1410 double width = 0.0;
1411 ParseNumberProperty(&width, err, o, "width", false);
1412
1413 double height = 0.0;
1414 ParseNumberProperty(&height, err, o, "height", false);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001415
1416 // Just only save some information here. Loading actual image data from
1417 // bufferView is done in other place.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001418 image->bufferView = static_cast<int>(buffer_view);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001419 image->mimeType = mime_type;
Aurélien Chatelain6d3bfd72017-05-30 15:04:32 +00001420 image->width = static_cast<int>(width);
1421 image->height = static_cast<int>(height);
Syoyo Fujitabeded612016-05-01 20:03:43 +09001422
1423 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001424 }
1425 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001426 if (IsDataURI(uri)) {
1427 if (!DecodeDataURI(&img, uri, 0, false)) {
1428 if (err) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001429 (*err) += "Failed to decode 'uri' for image parameter.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001430 }
1431 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001432 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001433 } else {
1434 // Assume external file
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00001435
1436 // Keep texture path (for textures that cannot be decoded)
1437 image->uri = uri;
1438
Syoyo Fujitabeded612016-05-01 20:03:43 +09001439 if (!LoadExternalFile(&img, err, uri, basedir, 0, false)) {
1440 if (err) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001441 (*err) += "Failed to load external 'uri'. for image parameter\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001442 }
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00001443 // If the image cannot be loaded, keep uri as image->uri.
1444 return true;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001445 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001446 if (img.empty()) {
1447 if (err) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001448 (*err) += "Image is empty.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001449 }
1450 return false;
1451 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001452 }
1453 }
1454
Syoyo Fujitabeded612016-05-01 20:03:43 +09001455 return LoadImageData(image, err, 0, 0, &img.at(0),
1456 static_cast<int>(img.size()));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001457}
1458
Syoyo Fujitabeded612016-05-01 20:03:43 +09001459static bool ParseTexture(Texture *texture, std::string *err,
1460 const picojson::object &o,
1461 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001462 (void)basedir;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001463 double sampler = -1.0;
1464 double source = -1.0;
1465 ParseNumberProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001466
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001467 if (!ParseNumberProperty(&source, err, o, "source", true)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001468 return false;
1469 }
1470
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001471 texture->sampler = static_cast<int>(sampler);
1472 texture->source = static_cast<int>(source);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001473
1474 return true;
1475}
1476
Syoyo Fujitabeded612016-05-01 20:03:43 +09001477static bool ParseBuffer(Buffer *buffer, std::string *err,
1478 const picojson::object &o, const std::string &basedir,
1479 bool is_binary = false,
1480 const unsigned char *bin_data = NULL,
1481 size_t bin_size = 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001482 double byteLength;
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09001483 if (!ParseNumberProperty(&byteLength, err, o, "byteLength", true, "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001484 return false;
1485 }
1486
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00001487 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001488 std::string uri;
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09001489 ParseStringProperty(&uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001490
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00001491 // having an empty uri for a non embedded image should not be valid
1492 if(!is_binary && uri.empty())
1493 {
1494 if (err) {
1495 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
1496 }
1497 }
1498
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001499 picojson::object::const_iterator type = o.find("type");
1500 if (type != o.end()) {
1501 if (type->second.is<std::string>()) {
1502 const std::string &ty = (type->second).get<std::string>();
1503 if (ty.compare("arraybuffer") == 0) {
1504 // buffer.type = "arraybuffer";
1505 }
1506 }
1507 }
1508
1509 size_t bytes = static_cast<size_t>(byteLength);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001510 if (is_binary) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001511 // Still binary glTF accepts external dataURI. First try external resources.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001512
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00001513 if(!uri.empty())
1514 {
1515 // External .bin file.
1516 LoadExternalFile(&buffer->data, err, uri, basedir, bytes, true);
1517 }
1518 else
1519 {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001520 // load data from (embedded) binary data
1521
1522 if ((bin_size == 0) || (bin_data == NULL)) {
1523 if (err) {
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09001524 (*err) += "Invalid binary data in `Buffer'.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001525 }
1526 return false;
1527 }
1528
1529 if (byteLength > bin_size) {
1530 if (err) {
1531 std::stringstream ss;
1532 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001533 "`byteLength' = "
1534 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001535 (*err) += ss.str();
1536 }
1537 return false;
1538 }
1539
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00001540 // Read buffer data
1541 buffer->data.resize(static_cast<size_t>(byteLength));
1542 memcpy(&(buffer->data.at(0)), bin_data,
1543 static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09001544 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001545
1546 } else {
1547 if (IsDataURI(uri)) {
1548 if (!DecodeDataURI(&buffer->data, uri, bytes, true)) {
1549 if (err) {
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09001550 (*err) += "Failed to decode 'uri' : " + uri + "\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001551 }
1552 return false;
1553 }
1554 } else {
1555 // Assume external .bin file.
1556 if (!LoadExternalFile(&buffer->data, err, uri, basedir, bytes, true)) {
1557 return false;
1558 }
1559 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001560 }
1561
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001562 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001563
1564 return true;
1565}
1566
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001567static bool ParseBufferView(BufferView *bufferView, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001568 const picojson::object &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001569 double buffer = -1.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001570 if (!ParseNumberProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001571 return false;
1572 }
1573
Syoyo Fujitad17ff662017-05-29 02:53:12 +09001574 double byteOffset = 0.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001575 ParseNumberProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001576
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001577 double byteLength = 1.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001578 if(!ParseNumberProperty(&byteLength, err, o, "byteLength", true, "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001579 return false;
1580 }
1581
1582 double byteStride = 4.0;
1583 ParseNumberProperty(&byteLength, err, o, "byteStride", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001584
1585 double target = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001586 ParseNumberProperty(&target, err, o, "target", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001587 int targetValue = static_cast<int>(target);
1588 if ((targetValue == TINYGLTF_TARGET_ARRAY_BUFFER) ||
1589 (targetValue == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
1590 // OK
1591 } else {
1592 targetValue = 0;
1593 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001594 bufferView->target = targetValue;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001595
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001596 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001597
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001598 bufferView->buffer = static_cast<int>(buffer);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001599 bufferView->byteOffset = static_cast<size_t>(byteOffset);
1600 bufferView->byteLength = static_cast<size_t>(byteLength);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001601 bufferView->byteStride = static_cast<size_t>(byteStride);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001602
1603 return true;
1604}
1605
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001606static bool ParseAccessor(Accessor *accessor, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001607 const picojson::object &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001608 double bufferView = -1.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001609 if (!ParseNumberProperty(&bufferView, err, o, "bufferView", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001610 return false;
1611 }
1612
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001613 double byteOffset = 0.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001614 ParseNumberProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001615
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001616 double componentType = 0.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001617 if (!ParseNumberProperty(&componentType, err, o, "componentType", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001618 return false;
1619 }
1620
1621 double count = 0.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001622 if (!ParseNumberProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001623 return false;
1624 }
1625
1626 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001627 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001628 return false;
1629 }
1630
1631 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001632 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001633 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001634 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001635 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001636 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001637 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001638 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001639 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001640 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001641 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001642 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001643 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001644 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001645 } else {
1646 std::stringstream ss;
1647 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001648 if (err) {
1649 (*err) += ss.str();
1650 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001651 return false;
1652 }
1653
1654 double byteStride = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001655 ParseNumberProperty(&byteStride, err, o, "byteStride", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001656
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001657 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001658
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001659 accessor->minValues.clear();
1660 accessor->maxValues.clear();
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001661 if(!ParseNumberArrayProperty(&accessor->minValues, err, o, "min", true, "Accessor")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001662 return false;
1663 }
1664
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09001665 if(!ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", true, "Accessor")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001666 return false;
1667 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001668
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001669 accessor->count = static_cast<size_t>(count);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001670 accessor->bufferView = static_cast<int>(bufferView);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001671 accessor->byteOffset = static_cast<size_t>(byteOffset);
1672 accessor->byteStride = static_cast<size_t>(byteStride);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001673
1674 {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001675 int comp = static_cast<int>(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001676 if (comp >= TINYGLTF_COMPONENT_TYPE_BYTE &&
1677 comp <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
1678 // OK
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001679 accessor->componentType = comp;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001680 } else {
1681 std::stringstream ss;
1682 ss << "Invalid `componentType` in accessor. Got " << comp << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001683 if (err) {
1684 (*err) += ss.str();
1685 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001686 return false;
1687 }
1688 }
1689
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001690 ParseExtrasProperty(&(accessor->extras), o);
1691
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001692 return true;
1693}
1694
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001695static bool ParsePrimitive(Primitive *primitive, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001696 const picojson::object &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001697 double material = -1.0;
1698 ParseNumberProperty(&material, err, o, "material", false);
1699 primitive->material = static_cast<int>(material);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001700
1701 double mode = static_cast<double>(TINYGLTF_MODE_TRIANGLES);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001702 ParseNumberProperty(&mode, err, o, "mode", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001703
1704 int primMode = static_cast<int>(mode);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001705 primitive->mode = primMode; // Why only triangled were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001706
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001707 double indices = -1.0;
1708 ParseNumberProperty(&indices, err, o, "indices", false);
1709 primitive->indices = static_cast<int>(indices);
1710 if (!ParseStringIntProperty(&primitive->attributes, err, o, "attributes",
1711 true)) {
1712 return false;
1713 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001714
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00001715 // Look for morph targets
1716 picojson::object::const_iterator targetsObject = o.find("targets");
1717 if ((targetsObject != o.end()) && (targetsObject->second).is<picojson::array>()) {
1718 const picojson::array &targetArray =
1719 (targetsObject->second).get<picojson::array>();
1720 for (size_t i = 0; i < targetArray.size(); i++) {
1721 std::map<std::string, int> targetAttribues;
1722
1723 const picojson::object &dict = targetArray[i].get<picojson::object>();
1724 picojson::object::const_iterator dictIt(dict.begin());
1725 picojson::object::const_iterator dictItEnd(dict.end());
1726
1727 for (; dictIt != dictItEnd; ++dictIt) {
1728 targetAttribues[dictIt->first] = static_cast<int>(dictIt->second.get<double>());
1729 }
1730 primitive->targets.push_back(targetAttribues);
1731 }
1732 }
1733
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001734 ParseExtrasProperty(&(primitive->extras), o);
1735
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001736 return true;
1737}
1738
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001739static bool ParseMesh(Mesh *mesh, std::string *err, const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001740 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001741
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001742 mesh->primitives.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001743 picojson::object::const_iterator primObject = o.find("primitives");
1744 if ((primObject != o.end()) && (primObject->second).is<picojson::array>()) {
1745 const picojson::array &primArray =
1746 (primObject->second).get<picojson::array>();
1747 for (size_t i = 0; i < primArray.size(); i++) {
1748 Primitive primitive;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001749 if (ParsePrimitive(&primitive, err,
1750 primArray[i].get<picojson::object>())) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04001751 // Only add the primitive if the parsing succeeds.
1752 mesh->primitives.push_back(primitive);
1753 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001754 }
1755 }
1756
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00001757 // Should probably check if has targets and if dimensions fit
1758 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
1759
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001760 ParseExtrasProperty(&(mesh->extras), o);
1761
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001762 return true;
1763}
1764
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001765static bool ParseNode(Node *node, std::string *err, const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001766 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001767
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001768 double skin = -1.0;
1769 ParseNumberProperty(&skin, err, o, "skin", false);
1770 node->skin = static_cast<int>(skin);
1771
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001772 // Matrix and T/R/S are exclusive
1773 if(!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
1774
1775 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
1776 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
1777 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
1778 }
1779
1780 double camera = -1.0;
1781 ParseNumberProperty(&camera, err, o, "camera", false);
1782 node->camera = static_cast<int>(camera);
1783
1784 double mesh = -1.0;
1785 ParseNumberProperty(&mesh, err, o, "mesh", false);
Syoyo Fujitaf6120152017-05-27 23:51:23 +09001786 node->mesh = int(mesh);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001787
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001788 node->children.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001789 picojson::object::const_iterator childrenObject = o.find("children");
1790 if ((childrenObject != o.end()) &&
1791 (childrenObject->second).is<picojson::array>()) {
1792 const picojson::array &childrenArray =
1793 (childrenObject->second).get<picojson::array>();
1794 for (size_t i = 0; i < childrenArray.size(); i++) {
Syoyo Fujitaf6120152017-05-27 23:51:23 +09001795 if (!childrenArray[i].is<double>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001796 if (err) {
1797 (*err) += "Invalid `children` array.\n";
1798 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001799 return false;
1800 }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001801 const int &childrenNode = static_cast<int>(childrenArray[i].get<double>());
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001802 node->children.push_back(childrenNode);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001803 }
1804 }
1805
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001806 ParseExtrasProperty(&(node->extras), o);
1807
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001808 return true;
1809}
1810
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001811static bool ParseParameterProperty(Parameter *param, std::string *err,
1812 const picojson::object &o,
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001813 const std::string &prop, bool required) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001814 double num_val;
1815
1816 // A parameter value can either be a string or an array of either a boolean or
1817 // a number. Booleans of any kind aren't supported here. Granted, it
1818 // complicates the Parameter structure and breaks it semantically in the sense
1819 // that the client probably works off the assumption that if the string is
1820 // empty the vector is used, etc. Would a tagged union work?
1821 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
1822 // Found string property.
1823 return true;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001824 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
1825 false)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001826 // Found a number array.
1827 return true;
1828 } else if (ParseNumberProperty(&num_val, err, o, prop, false)) {
1829 param->number_array.push_back(num_val);
1830 return true;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001831 } else if(ParseJSONProperty(&param->json_double_value, err, o, prop, false)) {
1832 return true;
1833 } else if(ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
1834 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001835 } else {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001836 if (required) {
1837 if (err) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001838 (*err) += "parameter must be a string or number / number array.\n";
1839 }
1840 }
1841 return false;
1842 }
1843}
1844
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001845static bool ParseMaterial(Material *material, std::string *err,
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001846 const picojson::object &o) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001847
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001848 material->values.clear();
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001849 material->extPBRValues.clear();
1850 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001851
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001852 picojson::object::const_iterator it(o.begin());
1853 picojson::object::const_iterator itEnd(o.end());
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001854
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001855 for (; it != itEnd; it++) {
1856 if(it->first == "pbrMetallicRoughness")
1857 {
1858 if ((it->second).is<picojson::object>()) {
1859 const picojson::object &values_object =
1860 (it->second).get<picojson::object>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001861
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001862 picojson::object::const_iterator itVal(values_object.begin());
Syoyo Fujitaf6120152017-05-27 23:51:23 +09001863 picojson::object::const_iterator itValEnd(values_object.end());
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001864
Syoyo Fujitaf6120152017-05-27 23:51:23 +09001865 for (; itVal != itValEnd; itVal++) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001866 Parameter param;
1867 if (ParseParameterProperty(&param, err, values_object, itVal->first,
1868 false)) {
1869 material->values[itVal->first] = param;
1870 }
1871 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001872 }
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001873 }
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001874 else if(it->first == "extensions")
1875 {
1876 if ((it->second).is<picojson::object>()) {
1877 const picojson::object &extension = (it->second).get<picojson::object>();
1878
1879 picojson::object::const_iterator extIt = extension.begin();
1880 if(!extIt->second.is<picojson::object>())
1881 continue;
1882
1883 const picojson::object &values_object =
1884 (extIt->second).get<picojson::object>();
1885
1886 picojson::object::const_iterator itVal(values_object.begin());
Syoyo Fujitaf6120152017-05-27 23:51:23 +09001887 picojson::object::const_iterator itValEnd(values_object.end());
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001888
Syoyo Fujitaf6120152017-05-27 23:51:23 +09001889 for (; itVal != itValEnd; itVal++) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00001890 Parameter param;
1891 if (ParseParameterProperty(&param, err, values_object, itVal->first,
1892 false)) {
1893 material->extPBRValues[itVal->first] = param;
1894 }
1895 }
1896 }
1897 }
1898 else
1899 {
1900 Parameter param;
1901 if (ParseParameterProperty(&param, err, o, it->first,
1902 false)) {
1903 material->additionalValues[it->first] = param;
1904 }
1905 }
1906 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001907
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001908 ParseExtrasProperty(&(material->extras), o);
1909
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001910 return true;
1911}
1912
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001913static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err,
1914 const picojson::object &o) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001915 double samplerIndex = -1.0;
1916 double targetIndex = -1.0;
1917 if (!ParseNumberProperty(&samplerIndex, err, o, "sampler", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001918 if (err) {
1919 (*err) += "`sampler` field is missing in animation channels\n";
1920 }
1921 return false;
1922 }
1923
1924 picojson::object::const_iterator targetIt = o.find("target");
1925 if ((targetIt != o.end()) && (targetIt->second).is<picojson::object>()) {
1926 const picojson::object &target_object =
1927 (targetIt->second).get<picojson::object>();
1928
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001929 if (!ParseNumberProperty(&targetIndex, err, target_object, "node",
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001930 true)) {
1931 if (err) {
1932 (*err) += "`id` field is missing in animation.channels.target\n";
1933 }
1934 return false;
1935 }
1936
1937 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
1938 true)) {
1939 if (err) {
1940 (*err) += "`path` field is missing in animation.channels.target\n";
1941 }
1942 return false;
1943 }
1944 }
1945
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001946 channel->sampler = static_cast<int>(samplerIndex);
1947 channel->target_node = static_cast<int>(targetIndex);
1948
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001949 ParseExtrasProperty(&(channel->extras), o);
1950
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001951 return true;
1952}
1953
1954static bool ParseAnimation(Animation *animation, std::string *err,
1955 const picojson::object &o) {
1956 {
1957 picojson::object::const_iterator channelsIt = o.find("channels");
1958 if ((channelsIt != o.end()) && (channelsIt->second).is<picojson::array>()) {
1959 const picojson::array &channelArray =
1960 (channelsIt->second).get<picojson::array>();
1961 for (size_t i = 0; i < channelArray.size(); i++) {
1962 AnimationChannel channel;
1963 if (ParseAnimationChannel(&channel, err,
1964 channelArray[i].get<picojson::object>())) {
1965 // Only add the channel if the parsing succeeds.
1966 animation->channels.push_back(channel);
1967 }
1968 }
1969 }
1970 }
1971
1972 {
1973 picojson::object::const_iterator samplerIt = o.find("samplers");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001974 if ((samplerIt != o.end()) && (samplerIt->second).is<picojson::array>()) {
1975 const picojson::array &sampler_array =
1976 (samplerIt->second).get<picojson::array>();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001977
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001978 picojson::array::const_iterator it = sampler_array.begin();
1979 picojson::array::const_iterator itEnd = sampler_array.end();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001980
1981 for (; it != itEnd; it++) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001982 const picojson::object &s = it->get<picojson::object>();
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001983
1984 AnimationSampler sampler;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001985 double inputIndex = -1.0;
1986 double outputIndex = -1.0;
1987 if (!ParseNumberProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001988 if (err) {
1989 (*err) += "`input` field is missing in animation.sampler\n";
1990 }
1991 return false;
1992 }
1993 if (!ParseStringProperty(&sampler.interpolation, err, s,
1994 "interpolation", true)) {
1995 if (err) {
1996 (*err) += "`interpolation` field is missing in animation.sampler\n";
1997 }
1998 return false;
1999 }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002000 if (!ParseNumberProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002001 if (err) {
2002 (*err) += "`output` field is missing in animation.sampler\n";
2003 }
2004 return false;
2005 }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002006 sampler.input = static_cast<int>(inputIndex);
2007 sampler.output = static_cast<int>(outputIndex);
2008 animation->samplers.push_back(sampler);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002009 }
2010 }
2011 }
2012
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002013 ParseStringProperty(&animation->name, err, o, "name", false);
2014
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002015 ParseExtrasProperty(&(animation->extras), o);
2016
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002017 return true;
2018}
2019
Syoyo Fujitac2615632016-06-19 21:56:06 +09002020static bool ParseSampler(Sampler *sampler, std::string *err,
2021 const picojson::object &o) {
2022 ParseStringProperty(&sampler->name, err, o, "name", false);
2023
2024 double minFilter =
2025 static_cast<double>(TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR);
2026 double magFilter = static_cast<double>(TINYGLTF_TEXTURE_FILTER_LINEAR);
2027 double wrapS = static_cast<double>(TINYGLTF_TEXTURE_WRAP_RPEAT);
2028 double wrapT = static_cast<double>(TINYGLTF_TEXTURE_WRAP_RPEAT);
2029 ParseNumberProperty(&minFilter, err, o, "minFilter", false);
2030 ParseNumberProperty(&magFilter, err, o, "magFilter", false);
2031 ParseNumberProperty(&wrapS, err, o, "wrapS", false);
2032 ParseNumberProperty(&wrapT, err, o, "wrapT", false);
2033
2034 sampler->minFilter = static_cast<int>(minFilter);
2035 sampler->magFilter = static_cast<int>(magFilter);
2036 sampler->wrapS = static_cast<int>(wrapS);
2037 sampler->wrapT = static_cast<int>(wrapT);
2038
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002039 ParseExtrasProperty(&(sampler->extras), o);
2040
Syoyo Fujitac2615632016-06-19 21:56:06 +09002041 return true;
2042}
2043
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002044static bool ParseSkin(Skin *skin, std::string *err,
2045 const picojson::object &o) {
2046
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002047 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002048
2049 std::vector<double> joints;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002050 if (!ParseNumberArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002051 return false;
2052 }
2053
2054 double skeleton;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002055 ParseNumberProperty(&skeleton, err, o, "skeleton", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002056 skin->skeleton = static_cast<int>(skeleton);
2057
2058 skin->joints = std::vector<int>(joints.begin(), joints.end());
2059
2060 double invBind = -1.0;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002061 ParseNumberProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002062 skin->inverseBindMatrices = static_cast<int>(invBind);
2063
2064 return true;
2065}
2066
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002067bool TinyGLTFLoader::LoadFromString(Model *model, std::string *err,
Syoyo Fujitabde70212016-02-07 17:38:17 +09002068 const char *str, unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04002069 const std::string &base_dir,
2070 unsigned int check_sections) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002071 picojson::value v;
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002072 std::string perr = picojson::parse(v, str, str + length);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002073
2074 if (!perr.empty()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002075 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09002076 (*err) = "JSON parsing error: " + perr;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002077 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002078 return false;
2079 }
2080
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002081 // scene is not mandatory.
2082 //FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002083
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002084if (v.contains("scenes") && v.get("scenes").is<picojson::array>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002085 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04002086 } else if (check_sections & REQUIRE_SCENES) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002087 if (err) {
2088 (*err) += "\"scenes\" object not found in .gltf\n";
2089 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002090 return false;
2091 }
2092
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002093 if (v.contains("nodes") && v.get("nodes").is<picojson::array>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002094 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04002095 } else if (check_sections & REQUIRE_NODES) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002096 if (err) {
2097 (*err) += "\"nodes\" object not found in .gltf\n";
2098 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002099 return false;
2100 }
2101
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002102 if (v.contains("accessors") && v.get("accessors").is<picojson::array>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002103 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04002104 } else if (check_sections & REQUIRE_ACCESSORS) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002105 if (err) {
2106 (*err) += "\"accessors\" object not found in .gltf\n";
2107 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002108 return false;
2109 }
2110
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002111 if (v.contains("buffers") && v.get("buffers").is<picojson::array>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002112 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04002113 } else if (check_sections & REQUIRE_BUFFERS) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002114 if (err) {
2115 (*err) += "\"buffers\" object not found in .gltf\n";
2116 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002117 return false;
2118 }
2119
2120 if (v.contains("bufferViews") &&
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002121 v.get("bufferViews").is<picojson::array>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002122 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04002123 } else if (check_sections & REQUIRE_BUFFER_VIEWS) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002124 if (err) {
2125 (*err) += "\"bufferViews\" object not found in .gltf\n";
2126 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002127 return false;
2128 }
2129
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002130 model->buffers.clear();
2131 model->bufferViews.clear();
2132 model->accessors.clear();
2133 model->meshes.clear();
2134 model->nodes.clear();
2135 model->extensionsUsed.clear();
2136 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002137
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002138 // 0. Parse Asset
2139 if (v.contains("asset") && v.get("asset").is<picojson::object>()) {
2140 const picojson::object &root = v.get("asset").get<picojson::object>();
2141
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002142 ParseAsset(&model->asset, err, root);
2143 }
2144
2145 // 0. Parse extensionUsed
2146 if (v.contains("extensionsUsed") && v.get("extensionsUsed").is<picojson::array>()) {
2147 const picojson::array &root = v.get("extensionsUsed").get<picojson::array>();
2148 for(unsigned int i=0; i< root.size(); ++i)
2149 {
2150 model->extensionsUsed.push_back(root[i].get<std::string>());
2151 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002152 }
2153
2154 // 1. Parse Buffer
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002155 if (v.contains("buffers") && v.get("buffers").is<picojson::array>()) {
2156 const picojson::array &root = v.get("buffers").get<picojson::array>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002157
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002158 picojson::array::const_iterator it(root.begin());
2159 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002160 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002161 Buffer buffer;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002162 if (!ParseBuffer(&buffer, err, it->get<picojson::object>(),
Syoyo Fujitabeded612016-05-01 20:03:43 +09002163 base_dir, is_binary_, bin_data_, bin_size_)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002164 return false;
2165 }
2166
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002167 model->buffers.push_back(buffer);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002168 }
2169 }
2170
2171 // 2. Parse BufferView
2172 if (v.contains("bufferViews") &&
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002173 v.get("bufferViews").is<picojson::array>()) {
2174 const picojson::array &root = v.get("bufferViews").get<picojson::array>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002175
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002176 picojson::array::const_iterator it(root.begin());
2177 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002178 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002179 BufferView bufferView;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002180 if (!ParseBufferView(&bufferView, err,
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002181 it->get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002182 return false;
2183 }
2184
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002185 model->bufferViews.push_back(bufferView);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002186 }
2187 }
2188
2189 // 3. Parse Accessor
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002190 if (v.contains("accessors") && v.get("accessors").is<picojson::array>()) {
2191 const picojson::array &root = v.get("accessors").get<picojson::array>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002192
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002193 picojson::array::const_iterator it(root.begin());
2194 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002195 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002196 Accessor accessor;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002197 if (!ParseAccessor(&accessor, err,
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002198 it->get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002199 return false;
2200 }
2201
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002202 model->accessors.push_back(accessor);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002203 }
2204 }
2205
2206 // 4. Parse Mesh
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002207 if (v.contains("meshes") && v.get("meshes").is<picojson::array>()) {
2208 const picojson::array &root = v.get("meshes").get<picojson::array>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002209
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002210 picojson::array::const_iterator it(root.begin());
2211 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002212 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002213 Mesh mesh;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002214 if (!ParseMesh(&mesh, err, it->get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002215 return false;
2216 }
2217
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002218 model->meshes.push_back(mesh);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002219 }
2220 }
2221
2222 // 5. Parse Node
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002223 if (v.contains("nodes") && v.get("nodes").is<picojson::array>()) {
2224 const picojson::array &root = v.get("nodes").get<picojson::array>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002225
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002226 picojson::array::const_iterator it(root.begin());
2227 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002228 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002229 Node node;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002230 if (!ParseNode(&node, err, it->get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002231 return false;
2232 }
2233
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002234 model->nodes.push_back(node);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002235 }
2236 }
2237
2238 // 6. Parse scenes.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002239 if (v.contains("scenes") && v.get("scenes").is<picojson::array>()) {
2240 const picojson::array &root = v.get("scenes").get<picojson::array>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002241
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002242 picojson::array::const_iterator it(root.begin());
2243 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002244 for (; it != itEnd; it++) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002245 if (!(it->is<picojson::object>())) {
Syoyo Fujitae04b1b52016-06-05 22:31:20 +09002246 if (err) {
2247 (*err) += "`scenes' does not contain an object.";
2248 }
2249 return false;
2250 }
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002251 const picojson::object &o = it->get<picojson::object>();
2252 std::vector<double> nodes;
2253 if (!ParseNumberArrayProperty(&nodes, err, o, "nodes", false)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002254 return false;
2255 }
2256
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002257 Scene scene;
2258 ParseStringProperty(&scene.name, err, o, "name", false);
2259 std::vector<int> nodesIds(nodes.begin(), nodes.end());
2260 scene.nodes = nodesIds;
2261
2262 model->scenes.push_back(scene);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002263 }
2264 }
2265
2266 // 7. Parse default scenes.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002267 if (v.contains("scene") && v.get("scene").is<double>()) {
Syoyo Fujitaf6120152017-05-27 23:51:23 +09002268 const int defaultScene = int(v.get("scene").get<double>());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002269
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002270 model->defaultScene = static_cast<int>(defaultScene);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002271 }
2272
2273 // 8. Parse Material
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002274 if (v.contains("materials") && v.get("materials").is<picojson::array>()) {
2275 const picojson::array &root = v.get("materials").get<picojson::array>();
2276 picojson::array::const_iterator it(root.begin());
2277 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002278 for (; it != itEnd; it++) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002279 picojson::object jsonMaterial = it->get<picojson::object>();
2280
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002281 Material material;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002282 ParseStringProperty(&material.name, err, jsonMaterial, "name", false);
2283
2284 if (!ParseMaterial(&material, err, jsonMaterial)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002285 return false;
2286 }
2287
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002288 model->materials.push_back(material);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002289 }
2290 }
2291
2292 // 9. Parse Image
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002293 if (v.contains("images") && v.get("images").is<picojson::array>()) {
2294 const picojson::array &root = v.get("images").get<picojson::array>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002295
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002296 picojson::array::const_iterator it(root.begin());
2297 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002298 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002299 Image image;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002300 if (!ParseImage(&image, err, it->get<picojson::object>(),
Syoyo Fujitabeded612016-05-01 20:03:43 +09002301 base_dir, is_binary_, bin_data_, bin_size_)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002302 return false;
2303 }
2304
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002305 if (image.bufferView != -1) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09002306 // Load image from the buffer view.
Syoyo Fujitaf6120152017-05-27 23:51:23 +09002307 if (size_t(image.bufferView) >= model->bufferViews.size()) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09002308 if (err) {
2309 std::stringstream ss;
2310 ss << "bufferView \"" << image.bufferView
2311 << "\" not found in the scene." << std::endl;
2312 (*err) += ss.str();
2313 }
2314 return false;
2315 }
2316
Syoyo Fujitaf6120152017-05-27 23:51:23 +09002317 const BufferView &bufferView = model->bufferViews[size_t(image.bufferView)];
2318 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
Syoyo Fujitabeded612016-05-01 20:03:43 +09002319
2320 bool ret = LoadImageData(&image, err, image.width, image.height,
2321 &buffer.data[bufferView.byteOffset],
2322 static_cast<int>(bufferView.byteLength));
2323 if (!ret) {
2324 return false;
2325 }
2326 }
2327
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002328 model->images.push_back(image);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002329 }
2330 }
2331
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002332 // 10. Parse Texture
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002333 if (v.contains("textures") && v.get("textures").is<picojson::array>()) {
2334 const picojson::array &root = v.get("textures").get<picojson::array>();
Syoyo Fujitabde70212016-02-07 17:38:17 +09002335
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002336 picojson::array::const_iterator it(root.begin());
2337 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitabde70212016-02-07 17:38:17 +09002338 for (; it != itEnd; it++) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09002339 Texture texture;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002340 if (!ParseTexture(&texture, err, it->get<picojson::object>(),
Syoyo Fujitabeded612016-05-01 20:03:43 +09002341 base_dir)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09002342 return false;
2343 }
2344
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002345 model->textures.push_back(texture);
Syoyo Fujitabde70212016-02-07 17:38:17 +09002346 }
2347 }
2348
Aurélien Chatelainf4f4ae42017-05-24 12:54:17 +00002349 // 11. Parse Animation
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002350 if (v.contains("animations") && v.get("animations").is<picojson::array>()) {
2351 const picojson::array &root = v.get("animations").get<picojson::array>();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002352
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002353 picojson::array::const_iterator it(root.begin());
2354 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002355 for (; it != itEnd; ++it) {
2356 Animation animation;
2357 if (!ParseAnimation(&animation, err,
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002358 it->get<picojson::object>())) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002359 return false;
2360 }
2361
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002362 model->animations.push_back(animation);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002363 }
2364 }
2365
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002366 // 12. Parse Skin
2367 if (v.contains("skins") && v.get("skins").is<picojson::array>()) {
2368 const picojson::array &root = v.get("skins").get<picojson::array>();
Syoyo Fujitac2615632016-06-19 21:56:06 +09002369
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002370 picojson::array::const_iterator it(root.begin());
2371 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitac2615632016-06-19 21:56:06 +09002372 for (; it != itEnd; ++it) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002373 Skin skin;
2374 if (!ParseSkin(&skin, err,
2375 it->get<picojson::object>())) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09002376 return false;
2377 }
2378
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00002379 model->skins.push_back(skin);
2380 }
2381 }
2382
2383 // 13. Parse Sampler
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002384 if (v.contains("samplers") && v.get("samplers").is<picojson::array>()) {
2385 const picojson::array &root = v.get("samplers").get<picojson::array>();
Syoyo Fujitac2615632016-06-19 21:56:06 +09002386
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002387 picojson::array::const_iterator it(root.begin());
2388 picojson::array::const_iterator itEnd(root.end());
Syoyo Fujitac2615632016-06-19 21:56:06 +09002389 for (; it != itEnd; ++it) {
2390 Sampler sampler;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002391 if (!ParseSampler(&sampler, err, it->get<picojson::object>())) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09002392 return false;
2393 }
2394
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002395 model->samplers.push_back(sampler);
Syoyo Fujitac2615632016-06-19 21:56:06 +09002396 }
2397 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002398 return true;
2399}
2400
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002401bool TinyGLTFLoader::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09002402 const char *str, unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04002403 const std::string &base_dir,
2404 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002405 is_binary_ = false;
2406 bin_data_ = NULL;
2407 bin_size_ = 0;
2408
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002409 return LoadFromString(model, err, str, length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002410}
2411
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002412bool TinyGLTFLoader::LoadASCIIFromFile(Model *model, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -04002413 const std::string &filename,
2414 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002415 std::stringstream ss;
2416
2417 std::ifstream f(filename.c_str());
2418 if (!f) {
2419 ss << "Failed to open file: " << filename << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002420 if (err) {
2421 (*err) = ss.str();
2422 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002423 return false;
2424 }
2425
2426 f.seekg(0, f.end);
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +09002427 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002428 std::vector<char> buf(sz);
2429
Luke San Antoniob310b4a2016-06-23 14:17:37 -04002430 if (sz == 0) {
2431 if (err) {
2432 (*err) = "Empty file.";
2433 }
2434 return false;
2435 }
2436
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002437 f.seekg(0, f.beg);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002438 f.read(&buf.at(0), static_cast<std::streamsize>(sz));
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002439 f.close();
2440
2441 std::string basedir = GetBaseDir(filename);
2442
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002443 bool ret = LoadASCIIFromString(model, err, &buf.at(0),
Luke San Antonio9e36b612016-06-23 14:10:51 -04002444 static_cast<unsigned int>(buf.size()), basedir,
2445 check_sections);
2446
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002447 return ret;
2448}
2449
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002450bool TinyGLTFLoader::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09002451 const unsigned char *bytes,
2452 unsigned int size,
Luke San Antonio9e36b612016-06-23 14:10:51 -04002453 const std::string &base_dir,
2454 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002455 if (size < 20) {
2456 if (err) {
2457 (*err) = "Too short data size for glTF Binary.";
2458 }
2459 return false;
2460 }
2461
Syoyo Fujitabeded612016-05-01 20:03:43 +09002462 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
2463 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002464 // ok
2465 } else {
2466 if (err) {
2467 (*err) = "Invalid magic.";
2468 }
2469 return false;
2470 }
2471
Syoyo Fujitabeded612016-05-01 20:03:43 +09002472 unsigned int version; // 4 bytes
2473 unsigned int length; // 4 bytes
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002474 unsigned int model_length; // 4 bytes
2475 unsigned int model_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002476
2477 // @todo { Endian swap for big endian machine. }
Syoyo Fujitabeded612016-05-01 20:03:43 +09002478 memcpy(&version, bytes + 4, 4);
2479 swap4(&version);
2480 memcpy(&length, bytes + 8, 4);
2481 swap4(&length);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002482 memcpy(&model_length, bytes + 12, 4);
2483 swap4(&model_length);
2484 memcpy(&model_format, bytes + 16, 4);
2485 swap4(&model_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002486
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002487 if ((20 + model_length >= size) || (model_length < 1) ||
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09002488 (model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002489 if (err) {
2490 (*err) = "Invalid glTF binary.";
2491 }
2492 return false;
2493 }
2494
2495 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09002496 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002497 model_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002498
2499 is_binary_ = true;
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00002500 bin_data_ = bytes + 20 + model_length + 8; // 4 bytes (buffer_length) + 4 bytes(buffer_format)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002501 bin_size_ =
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002502 length - (20 + model_length); // extract header + JSON scene data.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002503
Syoyo Fujitabeded612016-05-01 20:03:43 +09002504 bool ret =
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002505 LoadFromString(model, err, reinterpret_cast<const char *>(&bytes[20]),
2506 model_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002507 if (!ret) {
2508 return ret;
2509 }
2510
2511 return true;
2512}
2513
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002514bool TinyGLTFLoader::LoadBinaryFromFile(Model *model, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -04002515 const std::string &filename,
2516 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002517 std::stringstream ss;
2518
r-lyeh66c10632016-08-29 16:56:18 +02002519 std::ifstream f(filename.c_str(), std::ios::binary);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002520 if (!f) {
2521 ss << "Failed to open file: " << filename << std::endl;
2522 if (err) {
2523 (*err) = ss.str();
2524 }
2525 return false;
2526 }
2527
2528 f.seekg(0, f.end);
2529 size_t sz = static_cast<size_t>(f.tellg());
2530 std::vector<char> buf(sz);
2531
2532 f.seekg(0, f.beg);
2533 f.read(&buf.at(0), static_cast<std::streamsize>(sz));
2534 f.close();
2535
Syoyo Fujitabeded612016-05-01 20:03:43 +09002536 std::string basedir = GetBaseDir(filename);
2537
2538 bool ret = LoadBinaryFromMemory(
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00002539 model, err, reinterpret_cast<unsigned char *>(&buf.at(0)),
Luke San Antonio9e36b612016-06-23 14:10:51 -04002540 static_cast<unsigned int>(buf.size()), basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002541
2542 return ret;
2543}
2544
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09002545} // namespace tinygltf
2546
Syoyo Fujita7c877972016-03-08 01:31:49 +09002547#endif // TINYGLTF_LOADER_IMPLEMENTATION