blob: 3746ac83ee00347b5346480ee4b76a704c9e9641 [file] [log] [blame]
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001//
2// Tiny glTF loader.
3//
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09004// Copyright (c) 2015-2016, Syoyo Fujita and many contributors.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005// All rights reserved.
6// (Licensed under 2-clause BSD liecense)
7//
8// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are met:
10//
11// 1. Redistributions of source code must retain the above copyright notice,
12// this
13// list of conditions and the following disclaimer.
14// 2. Redistributions in binary form must reproduce the above copyright notice,
15// this list of conditions and the following disclaimer in the documentation
16// and/or other materials provided with the distribution.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19// AND
20// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
23// FOR
24// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25// DAMAGES
26// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31//
32//
33// Version:
Syoyo Fujita8c5ab032016-06-19 18:15:32 +090034// - v0.9.4 Support parsing `shader`, `program` and `tecnique` thanks to
35// @lukesanantonio
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +090036// - v0.9.3 Support binary glTF
Syoyo Fujitabde70212016-02-07 17:38:17 +090037// - v0.9.2 Support parsing `texture`
38// - v0.9.1 Support loading glTF asset from memory
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090039// - v0.9.0 Initial
40//
41// Tiny glTF loader is using following libraries:
42//
43// - picojson: C++ JSON library.
44// - base64: base64 decode/encode library.
45// - stb_image: Image loading library.
46//
Syoyo Fujita7c877972016-03-08 01:31:49 +090047#ifndef TINY_GLTF_LOADER_H_
48#define TINY_GLTF_LOADER_H_
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090049
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090050#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090051#include <string>
52#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090053
54namespace tinygltf {
55
56#define TINYGLTF_MODE_POINTS (0)
57#define TINYGLTF_MODE_LINE (1)
58#define TINYGLTF_MODE_LINE_LOOP (2)
59#define TINYGLTF_MODE_TRIANGLES (4)
60#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
61#define TINYGLTF_MODE_TRIANGLE_FAN (6)
62
63#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
64#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
65#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
66#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
67#define TINYGLTF_COMPONENT_TYPE_INT (5124)
68#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
69#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
70#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5127)
71
Syoyo Fujitac2615632016-06-19 21:56:06 +090072#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
73#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
74#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
75#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
76#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
77#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
78
79#define TINYGLTF_TEXTURE_WRAP_RPEAT (10497)
80#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -040081#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +090082
Luke San Antoniocdf4cb72016-06-14 21:32:11 -040083// Redeclarations of the above for technique.parameters.
84#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
85#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
86#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
87#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
88#define TINYGLTF_PARAMETER_TYPE_INT (5124)
89#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
90#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
91
92#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
93#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
94#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
95
96#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
97#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
98#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
99
100#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
101#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
102#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
103#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
104
105#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
106#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
107#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
108
109#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
110
111// End parameter types
112
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900113#define TINYGLTF_TYPE_VEC2 (2)
114#define TINYGLTF_TYPE_VEC3 (3)
115#define TINYGLTF_TYPE_VEC4 (4)
116#define TINYGLTF_TYPE_MAT2 (32 + 2)
117#define TINYGLTF_TYPE_MAT3 (32 + 3)
118#define TINYGLTF_TYPE_MAT4 (32 + 4)
119#define TINYGLTF_TYPE_SCALAR (64 + 1)
120#define TINYGLTF_TYPE_VECTOR (64 + 4)
121#define TINYGLTF_TYPE_MATRIX (64 + 16)
122
123#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
124#define TINYGLTF_IMAGE_FORMAT_PNG (1)
125#define TINYGLTF_IMAGE_FORMAT_BMP (2)
126#define TINYGLTF_IMAGE_FORMAT_GIF (3)
127
Luke San Antonio6d616f52016-06-23 14:09:23 -0400128#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
129#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900130#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400131#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
132#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
133
Syoyo Fujitabde70212016-02-07 17:38:17 +0900134#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
135#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
136
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900137#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
138#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
139
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400140#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
141#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
142
Syoyo Fujitabde70212016-02-07 17:38:17 +0900143typedef struct {
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900144 std::string string_value;
145 std::vector<double> number_array;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900146} Parameter;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900147
Syoyo Fujitabde70212016-02-07 17:38:17 +0900148typedef std::map<std::string, Parameter> ParameterMap;
149
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900150typedef struct {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900151 std::string sampler;
152 std::string target_id;
153 std::string target_path;
154} AnimationChannel;
155
156typedef struct {
157 std::string input;
158 std::string interpolation;
159 std::string output;
160} AnimationSampler;
161
162typedef struct {
163 std::string name;
164 std::vector<AnimationChannel> channels;
165 std::map<std::string, AnimationSampler> samplers;
166 ParameterMap parameters;
167} Animation;
168
169typedef struct {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900170 std::string name;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900171 int minFilter;
172 int magFilter;
173 int wrapS;
174 int wrapT;
175 int wrapR; // TinyGLTF extension
Syoyo Fujitacf6e07b2016-06-21 21:18:12 +0900176 int pad0;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900177} Sampler;
178
179typedef struct {
180 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900181 int width;
182 int height;
183 int component;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900184 int pad0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900185 std::vector<unsigned char> image;
Syoyo Fujitabeded612016-05-01 20:03:43 +0900186
187 std::string bufferView; // KHR_binary_glTF extenstion.
188 std::string mimeType; // KHR_binary_glTF extenstion.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900189} Image;
190
191typedef struct {
Syoyo Fujitabde70212016-02-07 17:38:17 +0900192 int format;
193 int internalFormat;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900194 std::string sampler; // Required
195 std::string source; // Required
Syoyo Fujitabde70212016-02-07 17:38:17 +0900196 int target;
197 int type;
198 std::string name;
199} Texture;
200
201typedef struct {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900202 std::string name;
203 std::string technique;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900204 ParameterMap values;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900205} Material;
206
207typedef struct {
208 std::string name;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900209 std::string buffer; // Required
210 size_t byteOffset; // Required
211 size_t byteLength; // default: 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900212 int target;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900213 int pad0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900214} BufferView;
215
216typedef struct {
217 std::string bufferView;
218 std::string name;
219 size_t byteOffset;
220 size_t byteStride;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900221 int componentType; // One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900222 int pad0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900223 size_t count;
Syoyo Fujitabeded612016-05-01 20:03:43 +0900224 int type; // One of TINYGLTF_TYPE_***
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900225 int pad1;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900226 std::vector<double> minValues; // Optional
227 std::vector<double> maxValues; // Optional
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900228} Accessor;
229
230class Camera {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900231 public:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900232 Camera() {}
233 ~Camera() {}
234
235 std::string name;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900236 bool isOrthographic; // false = perspective.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900237
238 // Some common properties.
239 float aspectRatio;
240 float yFov;
241 float zFar;
242 float zNear;
243};
244
245typedef struct {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900246 std::map<std::string, std::string> attributes; // A dictionary object of
247 // strings, where each string
248 // is the ID of the accessor
249 // containing an attribute.
250 std::string material; // The ID of the material to apply to this primitive
251 // when rendering.
252 std::string indices; // The ID of the accessor that contains the indices.
253 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900254 int pad0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900255} Primitive;
256
257typedef struct {
258 std::string name;
259 std::vector<Primitive> primitives;
260} Mesh;
261
262class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900263 public:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900264 Node() {}
265 ~Node() {}
266
Syoyo Fujita7c877972016-03-08 01:31:49 +0900267 std::string camera; // camera object referenced by this node.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900268
269 std::string name;
270 std::vector<std::string> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900271 std::vector<double> rotation; // length must be 0 or 4
272 std::vector<double> scale; // length must be 0 or 3
273 std::vector<double> translation; // length must be 0 or 3
274 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900275 std::vector<std::string> meshes;
276};
277
278typedef struct {
279 std::string name;
280 std::vector<unsigned char> data;
281} Buffer;
282
283typedef struct {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400284 std::string name;
285 int type;
Syoyo Fujitacf6e07b2016-06-21 21:18:12 +0900286 int pad0;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400287 std::vector<unsigned char> source;
288} Shader;
289
290typedef struct {
291 std::string name;
292 std::string vertexShader;
293 std::string fragmentShader;
294 std::vector<std::string> attributes;
295} Program;
296
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +0900297typedef struct {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400298 int count;
Syoyo Fujitacf6e07b2016-06-21 21:18:12 +0900299 int pad0;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400300 std::string node;
301 std::string semantic;
302 int type;
Syoyo Fujitacf6e07b2016-06-21 21:18:12 +0900303 int pad1;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400304 Parameter value;
305} TechniqueParameter;
306
307typedef struct {
308 std::string name;
309 std::string program;
310 std::map<std::string, TechniqueParameter> parameters;
311 std::map<std::string, std::string> attributes;
312 std::map<std::string, std::string> uniforms;
313} Technique;
314
315typedef struct {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900316 std::string generator;
317 std::string version;
318 std::string profile_api;
319 std::string profile_version;
320 bool premultipliedAlpha;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900321 char pad[7];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900322} Asset;
323
324class Scene {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900325 public:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900326 Scene() {}
327 ~Scene() {}
328
329 std::map<std::string, Accessor> accessors;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900330 std::map<std::string, Animation> animations;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900331 std::map<std::string, Buffer> buffers;
332 std::map<std::string, BufferView> bufferViews;
333 std::map<std::string, Material> materials;
334 std::map<std::string, Mesh> meshes;
335 std::map<std::string, Node> nodes;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900336 std::map<std::string, Texture> textures;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900337 std::map<std::string, Image> images;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400338 std::map<std::string, Shader> shaders;
339 std::map<std::string, Program> programs;
340 std::map<std::string, Technique> techniques;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900341 std::map<std::string, Sampler> samplers;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900342 std::map<std::string, std::vector<std::string> > scenes; // list of nodes
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900343
344 std::string defaultScene;
345
346 Asset asset;
347};
348
Luke San Antonio9e36b612016-06-23 14:10:51 -0400349enum SectionCheck
350{
351 NO_REQUIRE = 0x00,
352 REQUIRE_SCENE = 0x01,
353 REQUIRE_SCENES = 0x02,
354 REQUIRE_NODES = 0x04,
355 REQUIRE_ACCESSORS = 0x08,
356 REQUIRE_BUFFERS = 0x10,
357 REQUIRE_BUFFER_VIEWS = 0x20,
358 REQUIRE_ALL = 0x3f
359};
360
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900361class TinyGLTFLoader {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900362 public:
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900363 TinyGLTFLoader() : bin_data_(NULL), bin_size_(0), is_binary_(false) {
364 pad[0] = pad[1] = pad[2] = pad[3] = pad[4] = pad[5] = pad[6] = 0;
365 }
Syoyo Fujita7c877972016-03-08 01:31:49 +0900366 ~TinyGLTFLoader() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900367
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900368 /// Loads glTF ASCII asset from a file.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900369 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900370 bool LoadASCIIFromFile(Scene *scene, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400371 const std::string &filename,
372 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900373
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900374 /// Loads glTF ASCII asset from string(memory).
375 /// `length` = strlen(str);
376 /// Returns false and set error string to `err` if there's an error.
377 bool LoadASCIIFromString(Scene *scene, std::string *err, const char *str,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900378 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400379 const std::string &base_dir,
380 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900381
382 /// Loads glTF binary asset from a file.
383 /// Returns false and set error string to `err` if there's an error.
384 bool LoadBinaryFromFile(Scene *scene, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400385 const std::string &filename,
386 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900387
388 /// Loads glTF binary asset from memory.
389 /// `length` = strlen(str);
390 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujitabeded612016-05-01 20:03:43 +0900391 bool LoadBinaryFromMemory(Scene *scene, std::string *err,
392 const unsigned char *bytes,
393 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400394 const std::string &base_dir = "",
395 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900396
Syoyo Fujitabeded612016-05-01 20:03:43 +0900397 private:
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900398 /// Loads glTF asset from string(memory).
399 /// `length` = strlen(str);
400 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900401 bool LoadFromString(Scene *scene, std::string *err, const char *str,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400402 const unsigned int length, const std::string &base_dir,
403 unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900404
Syoyo Fujitabeded612016-05-01 20:03:43 +0900405 const unsigned char *bin_data_;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900406 size_t bin_size_;
407 bool is_binary_;
408 char pad[7];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900409};
410
Syoyo Fujita7c877972016-03-08 01:31:49 +0900411} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900412
413#ifdef TINYGLTF_LOADER_IMPLEMENTATION
Syoyo Fujita7c877972016-03-08 01:31:49 +0900414#include <algorithm>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900415#include <cassert>
416#include <fstream>
417#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +0900418
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900419#ifdef TINYGLTF_APPLY_CLANG_WEVERYTHING
Syoyo Fujita643ce102016-05-01 17:19:37 +0900420// Disable some warnings for external files.
421#pragma clang diagnostic push
422#pragma clang diagnostic ignored "-Wfloat-equal"
423#pragma clang diagnostic ignored "-Wexit-time-destructors"
424#pragma clang diagnostic ignored "-Wconversion"
425#pragma clang diagnostic ignored "-Wold-style-cast"
426#pragma clang diagnostic ignored "-Wdouble-promotion"
427#pragma clang diagnostic ignored "-Wglobal-constructors"
428#pragma clang diagnostic ignored "-Wreserved-id-macro"
429#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
430#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900431#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900432
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +0900433#include "./picojson.h"
434#include "./stb_image.h"
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900435#ifdef TINYGLTF_APPLY_CLANG_WEVERYTHING
Syoyo Fujita643ce102016-05-01 17:19:37 +0900436#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900437#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900438
439#ifdef _WIN32
440#include <Windows.h>
441#else
442#include <wordexp.h>
443#endif
444
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900445#if defined(__sparcv9)
446// Big endian
447#else
448#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
449#define TINYGLTF_LITTLE_ENDIAN 1
450#endif
451#endif
452
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +0900453namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900454
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900455static void swap4(unsigned int *val) {
456#ifdef TINYGLTF_LITTLE_ENDIAN
457 (void)val;
458#else
459 unsigned int tmp = *val;
460 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
461 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
462
463 dst[0] = src[3];
464 dst[1] = src[2];
465 dst[2] = src[1];
466 dst[3] = src[0];
467#endif
468}
469
Syoyo Fujita643ce102016-05-01 17:19:37 +0900470static bool FileExists(const std::string &abs_filename) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900471 bool ret;
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900472#ifdef _WIN32
473 FILE *fp;
474 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
475 if (err != 0) {
Syoyo Fujitabeded612016-05-01 20:03:43 +0900476 return false;
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900477 }
478#else
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900479 FILE *fp = fopen(abs_filename.c_str(), "rb");
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900480#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900481 if (fp) {
482 ret = true;
483 fclose(fp);
484 } else {
485 ret = false;
486 }
487
488 return ret;
489}
490
Syoyo Fujita643ce102016-05-01 17:19:37 +0900491static std::string ExpandFilePath(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900492#ifdef _WIN32
493 DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
494 char *str = new char[len];
495 ExpandEnvironmentStringsA(filepath.c_str(), str, len);
496
497 std::string s(str);
498
499 delete[] str;
500
501 return s;
502#else
503
Syoyo Fujita643ce102016-05-01 17:19:37 +0900504#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900505 // no expansion
506 std::string s = filepath;
507#else
508 std::string s;
509 wordexp_t p;
510
511 if (filepath.empty()) {
512 return "";
513 }
514
515 // char** w;
516 int ret = wordexp(filepath.c_str(), &p, 0);
517 if (ret) {
518 // err
519 s = filepath;
520 return s;
521 }
522
523 // Use first element only.
524 if (p.we_wordv) {
525 s = std::string(p.we_wordv[0]);
526 wordfree(&p);
527 } else {
528 s = filepath;
529 }
530
531#endif
532
533 return s;
534#endif
535}
536
Syoyo Fujitabeded612016-05-01 20:03:43 +0900537static std::string JoinPath(const std::string &path0,
538 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900539 if (path0.empty()) {
540 return path1;
541 } else {
542 // check '/'
543 char lastChar = *path0.rbegin();
544 if (lastChar != '/') {
545 return path0 + std::string("/") + path1;
546 } else {
547 return path0 + path1;
548 }
549 }
550}
551
Syoyo Fujita643ce102016-05-01 17:19:37 +0900552static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900553 const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900554 for (size_t i = 0; i < paths.size(); i++) {
555 std::string absPath = ExpandFilePath(JoinPath(paths[i], filepath));
556 if (FileExists(absPath)) {
557 return absPath;
558 }
559 }
560
561 return std::string();
562}
563
564// std::string GetFilePathExtension(const std::string& FileName)
565//{
566// if(FileName.find_last_of(".") != std::string::npos)
567// return FileName.substr(FileName.find_last_of(".")+1);
568// return "";
569//}
570
Syoyo Fujita643ce102016-05-01 17:19:37 +0900571static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900572 if (filepath.find_last_of("/\\") != std::string::npos)
573 return filepath.substr(0, filepath.find_last_of("/\\"));
574 return "";
575}
576
577// std::string base64_encode(unsigned char const* , unsigned int len);
578std::string base64_decode(std::string const &s);
579
580/*
581 base64.cpp and base64.h
582
583 Copyright (C) 2004-2008 René Nyffenegger
584
585 This source code is provided 'as-is', without any express or implied
586 warranty. In no event will the author be held liable for any damages
587 arising from the use of this software.
588
589 Permission is granted to anyone to use this software for any purpose,
590 including commercial applications, and to alter it and redistribute it
591 freely, subject to the following restrictions:
592
593 1. The origin of this source code must not be misrepresented; you must not
594 claim that you wrote the original source code. If you use this source code
595 in a product, an acknowledgment in the product documentation would be
596 appreciated but is not required.
597
598 2. Altered source versions must be plainly marked as such, and must not be
599 misrepresented as being the original source code.
600
601 3. This notice may not be removed or altered from any source distribution.
602
603 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
604
605*/
606
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900607#ifdef TINYGLTF_APPLY_CLANG_WEVERYTHING
Syoyo Fujita643ce102016-05-01 17:19:37 +0900608#pragma clang diagnostic push
609#pragma clang diagnostic ignored "-Wexit-time-destructors"
610#pragma clang diagnostic ignored "-Wglobal-constructors"
611#pragma clang diagnostic ignored "-Wsign-conversion"
612#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900613#endif
Syoyo Fujita7c877972016-03-08 01:31:49 +0900614static const std::string base64_chars =
615 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
616 "abcdefghijklmnopqrstuvwxyz"
617 "0123456789+/";
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900618
619static inline bool is_base64(unsigned char c) {
620 return (isalnum(c) || (c == '+') || (c == '/'));
621}
622
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900623std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +0900624 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900625 int i = 0;
626 int j = 0;
627 int in_ = 0;
628 unsigned char char_array_4[4], char_array_3[3];
629 std::string ret;
630
631 while (in_len-- && (encoded_string[in_] != '=') &&
632 is_base64(encoded_string[in_])) {
633 char_array_4[i++] = encoded_string[in_];
634 in_++;
635 if (i == 4) {
636 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +0900637 char_array_4[i] =
638 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900639
640 char_array_3[0] =
641 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
642 char_array_3[1] =
643 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
644 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
645
Syoyo Fujita7c877972016-03-08 01:31:49 +0900646 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900647 i = 0;
648 }
649 }
650
651 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900652 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900653
654 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +0900655 char_array_4[j] =
656 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900657
658 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
659 char_array_3[1] =
660 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
661 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
662
Syoyo Fujita7c877972016-03-08 01:31:49 +0900663 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900664 }
665
666 return ret;
667}
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900668#ifdef TINYGLTF_APPLY_CLANG_WEVERYTHING
Syoyo Fujita643ce102016-05-01 17:19:37 +0900669#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900670#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900671
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900672static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900673 const std::string &filename,
674 const std::string &basedir, size_t reqBytes,
675 bool checkSize) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900676 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900677
678 std::vector<std::string> paths;
679 paths.push_back(basedir);
680 paths.push_back(".");
681
682 std::string filepath = FindFile(paths, filename);
683 if (filepath.empty()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900684 if (err) {
Luke San Antonioda8b5d12016-06-14 22:17:40 -0400685 (*err) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900686 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900687 return false;
688 }
689
690 std::ifstream f(filepath.c_str(), std::ifstream::binary);
691 if (!f) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900692 if (err) {
Luke San Antonioda8b5d12016-06-14 22:17:40 -0400693 (*err) += "File open error : " + filepath + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900694 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900695 return false;
696 }
697
698 f.seekg(0, f.end);
Syoyo Fujita643ce102016-05-01 17:19:37 +0900699 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900700 std::vector<unsigned char> buf(sz);
701
702 f.seekg(0, f.beg);
Syoyo Fujitabeded612016-05-01 20:03:43 +0900703 f.read(reinterpret_cast<char *>(&buf.at(0)),
704 static_cast<std::streamsize>(sz));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900705 f.close();
706
707 if (checkSize) {
708 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900709 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900710 return true;
711 } else {
712 std::stringstream ss;
713 ss << "File size mismatch : " << filepath << ", requestedBytes "
714 << reqBytes << ", but got " << sz << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900715 if (err) {
716 (*err) += ss.str();
717 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900718 return false;
719 }
720 }
721
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900722 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900723 return true;
724}
725
Syoyo Fujitabeded612016-05-01 20:03:43 +0900726static bool LoadImageData(Image *image, std::string *err, int req_width,
727 int req_height, const unsigned char *bytes,
728 int size) {
729 int w, h, comp;
730 unsigned char *data = stbi_load_from_memory(bytes, size, &w, &h, &comp, 0);
731 if (!data) {
732 if (err) {
733 (*err) += "Unknown image format.\n";
734 }
735 return false;
736 }
737
738 if (w < 1 || h < 1) {
739 if (err) {
740 (*err) += "Unknown image format.\n";
741 }
742 return false;
743 }
744
745 if (req_width > 0) {
746 if (req_width != w) {
747 if (err) {
748 (*err) += "Image width mismatch.\n";
749 }
750 return false;
751 }
752 }
753
754 if (req_height > 0) {
755 if (req_height != h) {
756 if (err) {
757 (*err) += "Image height mismatch.\n";
758 }
759 return false;
760 }
761 }
762
763 image->width = w;
764 image->height = h;
765 image->component = comp;
766 image->image.resize(static_cast<size_t>(w * h * comp));
767 std::copy(data, data + w * h * comp, image->image.begin());
768
769 return true;
770}
771
Syoyo Fujita643ce102016-05-01 17:19:37 +0900772static bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900773 std::string header = "data:application/octet-stream;base64,";
774 if (in.find(header) == 0) {
775 return true;
776 }
Syoyo Fujita620eed12016-01-02 23:37:12 +0900777
778 header = "data:image/png;base64,";
779 if (in.find(header) == 0) {
780 return true;
781 }
782
783 header = "data:image/jpeg;base64,";
784 if (in.find(header) == 0) {
785 return true;
786 }
787
Luke San Antoniocaa24b02016-06-15 02:23:09 -0400788 header = "data:text/plain;base64,";
789 if (in.find(header) == 0) {
790 return true;
791 }
792
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900793 return false;
794}
795
Syoyo Fujitabeded612016-05-01 20:03:43 +0900796static bool DecodeDataURI(std::vector<unsigned char> *out,
797 const std::string &in, size_t reqBytes,
798 bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900799 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +0900800 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900801 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900802 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900803 }
Syoyo Fujita620eed12016-01-02 23:37:12 +0900804
805 if (data.empty()) {
806 header = "data:image/jpeg;base64,";
807 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900808 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +0900809 }
810 }
811
812 if (data.empty()) {
813 header = "data:image/png;base64,";
814 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900815 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +0900816 }
817 }
818
819 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -0400820 header = "data:text/plain;base64,";
821 if (in.find(header) == 0) {
822 data = base64_decode(in.substr(header.size()));
823 }
824 }
825
826 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +0900827 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900828 }
Syoyo Fujita620eed12016-01-02 23:37:12 +0900829
830 if (checkSize) {
831 if (data.size() != reqBytes) {
832 return false;
833 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900834 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +0900835 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900836 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +0900837 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900838 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +0900839 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900840}
841
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900842static bool ParseBooleanProperty(bool *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900843 const picojson::object &o,
844 const std::string &property, bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900845 picojson::object::const_iterator it = o.find(property);
846 if (it == o.end()) {
847 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900848 if (err) {
849 (*err) += "'" + property + "' property is missing.\n";
850 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900851 }
852 return false;
853 }
854
855 if (!it->second.is<bool>()) {
856 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900857 if (err) {
858 (*err) += "'" + property + "' property is not a bool type.\n";
859 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900860 }
861 return false;
862 }
863
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900864 if (ret) {
865 (*ret) = it->second.get<bool>();
866 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900867
868 return true;
869}
870
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900871static bool ParseNumberProperty(double *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900872 const picojson::object &o,
873 const std::string &property, bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900874 picojson::object::const_iterator it = o.find(property);
875 if (it == o.end()) {
876 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900877 if (err) {
878 (*err) += "'" + property + "' property is missing.\n";
879 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900880 }
881 return false;
882 }
883
884 if (!it->second.is<double>()) {
885 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900886 if (err) {
887 (*err) += "'" + property + "' property is not a number type.\n";
888 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900889 }
890 return false;
891 }
892
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900893 if (ret) {
894 (*ret) = it->second.get<double>();
895 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900896
897 return true;
898}
899
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900900static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900901 const picojson::object &o,
902 const std::string &property,
903 bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900904 picojson::object::const_iterator it = o.find(property);
905 if (it == o.end()) {
906 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900907 if (err) {
908 (*err) += "'" + property + "' property is missing.\n";
909 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900910 }
911 return false;
912 }
913
914 if (!it->second.is<picojson::array>()) {
915 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900916 if (err) {
917 (*err) += "'" + property + "' property is not an array.\n";
918 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900919 }
920 return false;
921 }
922
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900923 ret->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900924 const picojson::array &arr = it->second.get<picojson::array>();
925 for (size_t i = 0; i < arr.size(); i++) {
926 if (!arr[i].is<double>()) {
927 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900928 if (err) {
929 (*err) += "'" + property + "' property is not a number.\n";
930 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900931 }
932 return false;
933 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900934 ret->push_back(arr[i].get<double>());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900935 }
936
937 return true;
938}
939
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900940static bool ParseStringProperty(std::string *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900941 const picojson::object &o,
942 const std::string &property, bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900943 picojson::object::const_iterator it = o.find(property);
944 if (it == o.end()) {
945 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900946 if (err) {
947 (*err) += "'" + property + "' property is missing.\n";
948 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900949 }
950 return false;
951 }
952
953 if (!it->second.is<std::string>()) {
954 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900955 if (err) {
956 (*err) += "'" + property + "' property is not a string type.\n";
957 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900958 }
959 return false;
960 }
961
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900962 if (ret) {
963 (*ret) = it->second.get<std::string>();
964 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900965
966 return true;
967}
968
Syoyo Fujitabeded612016-05-01 20:03:43 +0900969static bool ParseStringArrayProperty(std::vector<std::string> *ret,
970 std::string *err,
971 const picojson::object &o,
972 const std::string &property,
973 bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900974 picojson::object::const_iterator it = o.find(property);
975 if (it == o.end()) {
976 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900977 if (err) {
978 (*err) += "'" + property + "' property is missing.\n";
979 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900980 }
981 return false;
982 }
983
984 if (!it->second.is<picojson::array>()) {
985 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900986 if (err) {
987 (*err) += "'" + property + "' property is not an array.\n";
988 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900989 }
990 return false;
991 }
992
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900993 ret->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900994 const picojson::array &arr = it->second.get<picojson::array>();
995 for (size_t i = 0; i < arr.size(); i++) {
996 if (!arr[i].is<std::string>()) {
997 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900998 if (err) {
999 (*err) += "'" + property + "' property is not a string.\n";
1000 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001001 }
1002 return false;
1003 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001004 ret->push_back(arr[i].get<std::string>());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001005 }
1006
1007 return true;
1008}
1009
Luke San Antonio19894c72016-06-14 21:19:51 -04001010static bool ParseStringMapProperty(std::map<std::string, std::string> *ret,
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001011 std::string *err, const picojson::object &o,
1012 const std::string &property, bool required) {
Luke San Antonio19894c72016-06-14 21:19:51 -04001013 picojson::object::const_iterator it = o.find(property);
1014 if (it == o.end()) {
1015 if (required) {
1016 if (err) {
1017 (*err) += "'" + property + "' property is missing.\n";
1018 }
1019 }
1020 return false;
1021 }
1022
1023 // Make sure we are dealing with an object / dictionary.
1024 if (!it->second.is<picojson::object>()) {
1025 if (required) {
1026 if (err) {
1027 (*err) += "'" + property + "' property is not an object.\n";
1028 }
1029 }
1030 return false;
1031 }
1032
1033 ret->clear();
1034 const picojson::object &dict = it->second.get<picojson::object>();
1035
1036 picojson::object::const_iterator dictIt(dict.begin());
1037 picojson::object::const_iterator dictItEnd(dict.end());
1038
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001039 for (; dictIt != dictItEnd; ++dictIt) {
Luke San Antonio19894c72016-06-14 21:19:51 -04001040 // Check that the value is a string.
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001041 if (!dictIt->second.is<std::string>()) {
1042 if (required) {
1043 if (err) {
Luke San Antonio19894c72016-06-14 21:19:51 -04001044 (*err) += "'" + property + "' value is not a string.\n";
1045 }
1046 }
1047 return false;
1048 }
1049
1050 // Insert into the list.
1051 (*ret)[dictIt->first] = dictIt->second.get<std::string>();
1052 }
1053 return true;
1054}
1055
Syoyo Fujitabeded612016-05-01 20:03:43 +09001056static bool ParseKHRBinaryExtension(const picojson::object &o, std::string *err,
1057 std::string *buffer_view,
1058 std::string *mime_type, int *image_width,
1059 int *image_height) {
1060 picojson::object j = o;
1061
1062 if (j.find("extensions") == j.end()) {
1063 if (err) {
1064 (*err) += "`extensions' property is missing.\n";
1065 }
1066 return false;
1067 }
1068
1069 if (!(j["extensions"].is<picojson::object>())) {
1070 if (err) {
1071 (*err) += "Invalid `extensions' property.\n";
1072 }
1073 return false;
1074 }
1075
1076 picojson::object ext = j["extensions"].get<picojson::object>();
1077
1078 if (ext.find("KHR_binary_glTF") == ext.end()) {
1079 if (err) {
1080 (*err) +=
1081 "`KHR_binary_glTF' property is missing in extension property.\n";
1082 }
1083 return false;
1084 }
1085
1086 if (!(ext["KHR_binary_glTF"].is<picojson::object>())) {
1087 if (err) {
1088 (*err) += "Invalid `KHR_binary_glTF' property.\n";
1089 }
1090 return false;
1091 }
1092
1093 picojson::object k = ext["KHR_binary_glTF"].get<picojson::object>();
1094
1095 if (!ParseStringProperty(buffer_view, err, k, "bufferView", true)) {
1096 return false;
1097 }
1098
1099 if (mime_type) {
1100 ParseStringProperty(mime_type, err, k, "mimeType", false);
1101 }
1102
1103 if (image_width) {
1104 double width = 0.0;
1105 if (ParseNumberProperty(&width, err, k, "width", false)) {
1106 (*image_width) = static_cast<int>(width);
1107 }
1108 }
1109
1110 if (image_height) {
1111 double height = 0.0;
1112 if (ParseNumberProperty(&height, err, k, "height", false)) {
1113 (*image_height) = static_cast<int>(height);
1114 }
1115 }
1116
1117 return true;
1118}
1119
1120static bool ParseAsset(Asset *asset, std::string *err,
1121 const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001122 ParseStringProperty(&asset->generator, err, o, "generator", false);
1123 ParseBooleanProperty(&asset->premultipliedAlpha, err, o, "premultipliedAlpha",
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001124 false);
1125
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001126 ParseStringProperty(&asset->version, err, o, "version", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001127
1128 picojson::object::const_iterator profile = o.find("profile");
1129 if (profile != o.end()) {
1130 const picojson::value &v = profile->second;
1131 if (v.contains("api") & v.get("api").is<std::string>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001132 asset->profile_api = v.get("api").get<std::string>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001133 }
1134 if (v.contains("version") & v.get("version").is<std::string>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001135 asset->profile_version = v.get("version").get<std::string>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001136 }
1137 }
1138
1139 return true;
1140}
1141
Syoyo Fujitabeded612016-05-01 20:03:43 +09001142static bool ParseImage(Image *image, std::string *err,
1143 const picojson::object &o, const std::string &basedir,
1144 bool is_binary, const unsigned char *bin_data,
1145 size_t bin_size) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001146 std::string uri;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001147 if (!ParseStringProperty(&uri, err, o, "uri", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001148 return false;
1149 }
1150
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001151 ParseStringProperty(&image->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001152
1153 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001154
1155 if (is_binary) {
1156 // Still binary glTF accepts external dataURI. First try external resources.
1157 bool loaded = false;
1158 if (IsDataURI(uri)) {
1159 loaded = DecodeDataURI(&img, uri, 0, false);
1160 } else {
1161 // Assume external .bin file.
1162 loaded = LoadExternalFile(&img, err, uri, basedir, 0, false);
1163 }
1164
1165 if (!loaded) {
1166 // load data from (embedded) binary data
1167
1168 if ((bin_size == 0) || (bin_data == NULL)) {
1169 if (err) {
1170 (*err) += "Invalid binary data.\n";
1171 }
1172 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001173 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001174
1175 // There should be "extensions" property.
1176 // "extensions":{"KHR_binary_glTF":{"bufferView": "id", ...
1177
1178 std::string buffer_view;
1179 std::string mime_type;
1180 int image_width;
1181 int image_height;
1182 bool ret = ParseKHRBinaryExtension(o, err, &buffer_view, &mime_type,
1183 &image_width, &image_height);
1184 if (!ret) {
1185 return false;
1186 }
1187
1188 if (uri.compare("data:,") == 0) {
1189 // ok
1190 } else {
1191 if (err) {
1192 (*err) += "Invalid URI for binary data.\n";
1193 }
1194 return false;
1195 }
1196
1197 // Just only save some information here. Loading actual image data from
1198 // bufferView is done in other place.
1199 image->bufferView = buffer_view;
1200 image->mimeType = mime_type;
1201 image->width = image_width;
1202 image->height = image_height;
1203
1204 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001205 }
1206 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001207 if (IsDataURI(uri)) {
1208 if (!DecodeDataURI(&img, uri, 0, false)) {
1209 if (err) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001210 (*err) += "Failed to decode 'uri' for image parameter.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001211 }
1212 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001213 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001214 } else {
1215 // Assume external file
1216 if (!LoadExternalFile(&img, err, uri, basedir, 0, false)) {
1217 if (err) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001218 (*err) += "Failed to load external 'uri'. for image parameter\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001219 }
1220 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001221 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001222 if (img.empty()) {
1223 if (err) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001224 (*err) += "Image is empty.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001225 }
1226 return false;
1227 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001228 }
1229 }
1230
Syoyo Fujitabeded612016-05-01 20:03:43 +09001231 return LoadImageData(image, err, 0, 0, &img.at(0),
1232 static_cast<int>(img.size()));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001233}
1234
Syoyo Fujitabeded612016-05-01 20:03:43 +09001235static bool ParseTexture(Texture *texture, std::string *err,
1236 const picojson::object &o,
1237 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001238 (void)basedir;
1239
1240 if (!ParseStringProperty(&texture->sampler, err, o, "sampler", true)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001241 return false;
1242 }
1243
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001244 if (!ParseStringProperty(&texture->source, err, o, "source", true)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001245 return false;
1246 }
1247
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001248 ParseStringProperty(&texture->name, err, o, "name", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001249
1250 double format = TINYGLTF_TEXTURE_FORMAT_RGBA;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001251 ParseNumberProperty(&format, err, o, "format", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001252
1253 double internalFormat = TINYGLTF_TEXTURE_FORMAT_RGBA;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001254 ParseNumberProperty(&internalFormat, err, o, "internalFormat", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001255
1256 double target = TINYGLTF_TEXTURE_TARGET_TEXTURE2D;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001257 ParseNumberProperty(&target, err, o, "target", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001258
1259 double type = TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001260 ParseNumberProperty(&type, err, o, "type", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001261
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001262 texture->format = static_cast<int>(format);
1263 texture->internalFormat = static_cast<int>(internalFormat);
1264 texture->target = static_cast<int>(target);
1265 texture->type = static_cast<int>(type);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001266
1267 return true;
1268}
1269
Syoyo Fujitabeded612016-05-01 20:03:43 +09001270static bool ParseBuffer(Buffer *buffer, std::string *err,
1271 const picojson::object &o, const std::string &basedir,
1272 bool is_binary = false,
1273 const unsigned char *bin_data = NULL,
1274 size_t bin_size = 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001275 double byteLength;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001276 if (!ParseNumberProperty(&byteLength, err, o, "byteLength", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001277 return false;
1278 }
1279
1280 std::string uri;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001281 if (!ParseStringProperty(&uri, err, o, "uri", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001282 return false;
1283 }
1284
1285 picojson::object::const_iterator type = o.find("type");
1286 if (type != o.end()) {
1287 if (type->second.is<std::string>()) {
1288 const std::string &ty = (type->second).get<std::string>();
1289 if (ty.compare("arraybuffer") == 0) {
1290 // buffer.type = "arraybuffer";
1291 }
1292 }
1293 }
1294
1295 size_t bytes = static_cast<size_t>(byteLength);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001296 if (is_binary) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001297 // Still binary glTF accepts external dataURI. First try external resources.
1298 bool loaded = false;
1299 if (IsDataURI(uri)) {
1300 loaded = DecodeDataURI(&buffer->data, uri, bytes, true);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001301 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001302 // Assume external .bin file.
1303 loaded = LoadExternalFile(&buffer->data, err, uri, basedir, bytes, true);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001304 }
1305
Syoyo Fujitabeded612016-05-01 20:03:43 +09001306 if (!loaded) {
1307 // load data from (embedded) binary data
1308
1309 if ((bin_size == 0) || (bin_data == NULL)) {
1310 if (err) {
1311 (*err) += "Invalid binary data.\n";
1312 }
1313 return false;
1314 }
1315
1316 if (byteLength > bin_size) {
1317 if (err) {
1318 std::stringstream ss;
1319 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001320 "`byteLength' = "
1321 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001322 (*err) += ss.str();
1323 }
1324 return false;
1325 }
1326
1327 if (uri.compare("data:,") == 0) {
1328 // @todo { check uri }
1329 buffer->data.resize(static_cast<size_t>(byteLength));
1330 memcpy(&(buffer->data.at(0)), bin_data,
1331 static_cast<size_t>(byteLength));
1332
1333 } else {
1334 if (err) {
1335 (*err) += "Invalid URI for binary data.\n";
1336 }
1337 return false;
1338 }
1339 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001340
1341 } else {
1342 if (IsDataURI(uri)) {
1343 if (!DecodeDataURI(&buffer->data, uri, bytes, true)) {
1344 if (err) {
1345 (*err) += "Failed to decode 'uri'.\n";
1346 }
1347 return false;
1348 }
1349 } else {
1350 // Assume external .bin file.
1351 if (!LoadExternalFile(&buffer->data, err, uri, basedir, bytes, true)) {
1352 return false;
1353 }
1354 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001355 }
1356
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001357 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001358
1359 return true;
1360}
1361
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001362static bool ParseBufferView(BufferView *bufferView, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001363 const picojson::object &o) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001364 std::string buffer;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001365 if (!ParseStringProperty(&buffer, err, o, "buffer", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001366 return false;
1367 }
1368
1369 double byteOffset;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001370 if (!ParseNumberProperty(&byteOffset, err, o, "byteOffset", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001371 return false;
1372 }
1373
1374 double byteLength = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001375 ParseNumberProperty(&byteLength, err, o, "byteLength", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001376
1377 double target = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001378 ParseNumberProperty(&target, err, o, "target", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001379 int targetValue = static_cast<int>(target);
1380 if ((targetValue == TINYGLTF_TARGET_ARRAY_BUFFER) ||
1381 (targetValue == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
1382 // OK
1383 } else {
1384 targetValue = 0;
1385 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001386 bufferView->target = targetValue;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001387
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001388 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001389
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001390 bufferView->buffer = buffer;
1391 bufferView->byteOffset = static_cast<size_t>(byteOffset);
1392 bufferView->byteLength = static_cast<size_t>(byteLength);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001393
1394 return true;
1395}
1396
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001397static bool ParseAccessor(Accessor *accessor, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001398 const picojson::object &o) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001399 std::string bufferView;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001400 if (!ParseStringProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001401 return false;
1402 }
1403
1404 double byteOffset;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001405 if (!ParseNumberProperty(&byteOffset, err, o, "byteOffset", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001406 return false;
1407 }
1408
1409 double componentType;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001410 if (!ParseNumberProperty(&componentType, err, o, "componentType", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001411 return false;
1412 }
1413
1414 double count = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001415 if (!ParseNumberProperty(&count, err, o, "count", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001416 return false;
1417 }
1418
1419 std::string type;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001420 if (!ParseStringProperty(&type, err, o, "type", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001421 return false;
1422 }
1423
1424 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001425 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001426 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001427 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001428 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001429 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001430 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001431 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001432 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001433 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001434 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001435 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001436 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001437 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001438 } else {
1439 std::stringstream ss;
1440 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001441 if (err) {
1442 (*err) += ss.str();
1443 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001444 return false;
1445 }
1446
1447 double byteStride = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001448 ParseNumberProperty(&byteStride, err, o, "byteStride", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001449
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001450 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001451
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001452 accessor->minValues.clear();
1453 accessor->maxValues.clear();
1454 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false);
1455 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001456
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001457 accessor->count = static_cast<size_t>(count);
1458 accessor->bufferView = bufferView;
1459 accessor->byteOffset = static_cast<size_t>(byteOffset);
1460 accessor->byteStride = static_cast<size_t>(byteStride);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001461
1462 {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001463 int comp = static_cast<int>(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001464 if (comp >= TINYGLTF_COMPONENT_TYPE_BYTE &&
1465 comp <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
1466 // OK
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001467 accessor->componentType = comp;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001468 } else {
1469 std::stringstream ss;
1470 ss << "Invalid `componentType` in accessor. Got " << comp << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001471 if (err) {
1472 (*err) += ss.str();
1473 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001474 return false;
1475 }
1476 }
1477
1478 return true;
1479}
1480
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001481static bool ParsePrimitive(Primitive *primitive, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001482 const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001483 if (!ParseStringProperty(&primitive->material, err, o, "material", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001484 return false;
1485 }
1486
1487 double mode = static_cast<double>(TINYGLTF_MODE_TRIANGLES);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001488 ParseNumberProperty(&mode, err, o, "mode", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001489
1490 int primMode = static_cast<int>(mode);
1491 if (primMode != TINYGLTF_MODE_TRIANGLES) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001492 if (err) {
1493 (*err) +=
1494 "Currently TinyGLTFLoader doesn not support primitive mode other \n"
1495 "than TRIANGLES.\n";
1496 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001497 return false;
1498 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001499 primitive->mode = primMode;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001500
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001501 primitive->indices = "";
1502 ParseStringProperty(&primitive->indices, err, o, "indices", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001503
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001504 if (!ParseStringMapProperty(&primitive->attributes, err, o, "attributes",
1505 true)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04001506 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001507 }
1508
1509 return true;
1510}
1511
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001512static bool ParseMesh(Mesh *mesh, std::string *err, const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001513 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001514
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001515 mesh->primitives.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001516 picojson::object::const_iterator primObject = o.find("primitives");
1517 if ((primObject != o.end()) && (primObject->second).is<picojson::array>()) {
1518 const picojson::array &primArray =
1519 (primObject->second).get<picojson::array>();
1520 for (size_t i = 0; i < primArray.size(); i++) {
1521 Primitive primitive;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001522 if (ParsePrimitive(&primitive, err,
1523 primArray[i].get<picojson::object>())) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04001524 // Only add the primitive if the parsing succeeds.
1525 mesh->primitives.push_back(primitive);
1526 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001527 }
1528 }
1529
1530 return true;
1531}
1532
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001533static bool ParseNode(Node *node, std::string *err, const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001534 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001535
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001536 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
1537 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
1538 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
1539 ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false);
1540 ParseStringArrayProperty(&node->meshes, err, o, "meshes", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001541
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001542 node->children.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001543 picojson::object::const_iterator childrenObject = o.find("children");
1544 if ((childrenObject != o.end()) &&
1545 (childrenObject->second).is<picojson::array>()) {
1546 const picojson::array &childrenArray =
1547 (childrenObject->second).get<picojson::array>();
1548 for (size_t i = 0; i < childrenArray.size(); i++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001549 if (!childrenArray[i].is<std::string>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001550 if (err) {
1551 (*err) += "Invalid `children` array.\n";
1552 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001553 return false;
1554 }
1555 const std::string &childrenNode = childrenArray[i].get<std::string>();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001556 node->children.push_back(childrenNode);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001557 }
1558 }
1559
1560 return true;
1561}
1562
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001563static bool ParseParameterProperty(Parameter *param, std::string *err,
1564 const picojson::object &o,
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001565 const std::string &prop, bool required) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001566 double num_val;
1567
1568 // A parameter value can either be a string or an array of either a boolean or
1569 // a number. Booleans of any kind aren't supported here. Granted, it
1570 // complicates the Parameter structure and breaks it semantically in the sense
1571 // that the client probably works off the assumption that if the string is
1572 // empty the vector is used, etc. Would a tagged union work?
1573 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
1574 // Found string property.
1575 return true;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001576 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
1577 false)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001578 // Found a number array.
1579 return true;
1580 } else if (ParseNumberProperty(&num_val, err, o, prop, false)) {
1581 param->number_array.push_back(num_val);
1582 return true;
1583 } else {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001584 if (required) {
1585 if (err) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001586 (*err) += "parameter must be a string or number / number array.\n";
1587 }
1588 }
1589 return false;
1590 }
1591}
1592
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001593static bool ParseMaterial(Material *material, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001594 const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001595 ParseStringProperty(&material->name, err, o, "name", false);
1596 ParseStringProperty(&material->technique, err, o, "technique", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001597
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001598 material->values.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001599 picojson::object::const_iterator valuesIt = o.find("values");
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001600
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001601 if ((valuesIt != o.end()) && (valuesIt->second).is<picojson::object>()) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001602 const picojson::object &values_object =
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001603 (valuesIt->second).get<picojson::object>();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001604
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001605 picojson::object::const_iterator it(values_object.begin());
1606 picojson::object::const_iterator itEnd(values_object.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001607
1608 for (; it != itEnd; it++) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001609 Parameter param;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001610 if (ParseParameterProperty(&param, err, values_object, it->first,
1611 false)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001612 material->values[it->first] = param;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001613 }
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001614 }
1615 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001616
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001617 return true;
1618}
1619
1620static bool ParseShader(Shader *shader, std::string *err,
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001621 const picojson::object &o, const std::string &basedir,
1622 bool is_binary = false,
1623 const unsigned char *bin_data = NULL,
1624 size_t bin_size = 0) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001625 std::string uri;
1626 if (!ParseStringProperty(&uri, err, o, "uri", true)) {
1627 return false;
1628 }
1629
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001630 if (is_binary) {
1631 // Still binary glTF accepts external dataURI. First try external resources.
1632 bool loaded = false;
1633 if (IsDataURI(uri)) {
1634 loaded = DecodeDataURI(&shader->source, uri, 0, false);
1635 } else {
1636 // Assume external .bin file.
1637 loaded = LoadExternalFile(&shader->source, err, uri, basedir, 0, false);
1638 }
1639
1640 if (!loaded) {
1641 // load data from (embedded) binary data
1642
1643 if ((bin_size == 0) || (bin_data == NULL)) {
1644 if (err) {
1645 (*err) += "Invalid binary data.\n";
1646 }
1647 return false;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001648 }
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001649
1650 // There should be "extensions" property.
1651 // "extensions":{"KHR_binary_glTF":{"bufferView": "id", ...
1652
1653 std::string buffer_view;
1654 std::string mime_type;
1655 int image_width;
1656 int image_height;
1657 bool ret = ParseKHRBinaryExtension(o, err, &buffer_view, &mime_type,
1658 &image_width, &image_height);
1659 if (!ret) {
1660 return false;
1661 }
1662
1663 if (uri.compare("data:,") == 0) {
1664 // ok
1665 } else {
1666 if (err) {
1667 (*err) += "Invalid URI for binary data.\n";
1668 }
1669 return false;
1670 }
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001671 }
1672 } else {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001673 // Load shader source from data uri
1674 // TODO: Support ascii or utf-8 data uris.
1675 if (IsDataURI(uri)) {
1676 if (!DecodeDataURI(&shader->source, uri, 0, false)) {
1677 if (err) {
1678 (*err) += "Failed to decode 'uri' for shader parameter.\n";
1679 }
1680 return false;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001681 }
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001682 } else {
1683 // Assume external file
1684 if (!LoadExternalFile(&shader->source, err, uri, basedir, 0, false)) {
1685 if (err) {
1686 (*err) += "Failed to load external 'uri' for shader parameter.\n";
1687 }
1688 return false;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001689 }
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001690 if (shader->source.empty()) {
1691 if (err) {
1692 (*err) += "shader is empty.\n"; // This may be OK?
1693 }
1694 return false;
1695 }
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001696 }
1697 }
1698
1699 double type;
1700 if (!ParseNumberProperty(&type, err, o, "type", true)) {
1701 return false;
1702 }
1703
1704 shader->type = static_cast<int>(type);
1705
1706 return true;
1707}
1708
1709static bool ParseProgram(Program *program, std::string *err,
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001710 const picojson::object &o) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001711 ParseStringProperty(&program->name, err, o, "name", false);
1712
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001713 if (!ParseStringProperty(&program->vertexShader, err, o, "vertexShader",
1714 true)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001715 return false;
1716 }
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001717 if (!ParseStringProperty(&program->fragmentShader, err, o, "fragmentShader",
1718 true)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001719 return false;
1720 }
1721
1722 // I suppose the list of attributes isn't needed, but a technique doesn't
1723 // really make sense without it.
1724 ParseStringArrayProperty(&program->attributes, err, o, "attributes", false);
1725
1726 return true;
1727}
1728
1729static bool ParseTechniqueParameter(TechniqueParameter *param, std::string *err,
1730 const picojson::object &o) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001731 double count = 1;
1732 ParseNumberProperty(&count, err, o, "count", false);
1733
1734 double type;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001735 if (!ParseNumberProperty(&type, err, o, "type", true)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001736 return false;
1737 }
1738
1739 ParseStringProperty(&param->node, err, o, "node", false);
1740 ParseStringProperty(&param->semantic, err, o, "semantic", false);
1741
1742 ParseParameterProperty(&param->value, err, o, "value", false);
1743
1744 param->count = static_cast<int>(count);
1745 param->type = static_cast<int>(type);
1746
1747 return true;
1748}
1749
1750static bool ParseTechnique(Technique *technique, std::string *err,
1751 const picojson::object &o) {
1752 ParseStringProperty(&technique->name, err, o, "name", false);
1753
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001754 if (!ParseStringProperty(&technique->program, err, o, "program", true)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001755 return false;
1756 }
1757
1758 ParseStringMapProperty(&technique->attributes, err, o, "attributes", false);
1759 ParseStringMapProperty(&technique->uniforms, err, o, "uniforms", false);
1760
1761 technique->parameters.clear();
1762 picojson::object::const_iterator paramsIt = o.find("parameters");
1763
1764 // Verify parameters is an object
1765 if ((paramsIt != o.end()) && (paramsIt->second).is<picojson::object>()) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001766 // For each parameter in params_object.
1767 const picojson::object &params_object =
1768 (paramsIt->second).get<picojson::object>();
1769
1770 picojson::object::const_iterator it(params_object.begin());
1771 picojson::object::const_iterator itEnd(params_object.end());
1772
1773 for (; it != itEnd; it++) {
1774 TechniqueParameter param;
1775
1776 // Skip non-objects
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001777 if (!it->second.is<picojson::object>()) continue;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001778
1779 // Parse the technique parameter
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001780 const picojson::object &param_obj = it->second.get<picojson::object>();
1781 if (ParseTechniqueParameter(&param, err, param_obj)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001782 // Add if successful
1783 technique->parameters[it->first] = param;
1784 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001785 }
1786 }
1787
1788 return true;
1789}
Syoyo Fujita7c877972016-03-08 01:31:49 +09001790
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001791static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err,
1792 const picojson::object &o) {
1793 if (!ParseStringProperty(&channel->sampler, err, o, "sampler", true)) {
1794 if (err) {
1795 (*err) += "`sampler` field is missing in animation channels\n";
1796 }
1797 return false;
1798 }
1799
1800 picojson::object::const_iterator targetIt = o.find("target");
1801 if ((targetIt != o.end()) && (targetIt->second).is<picojson::object>()) {
1802 const picojson::object &target_object =
1803 (targetIt->second).get<picojson::object>();
1804
1805 if (!ParseStringProperty(&channel->target_id, err, target_object, "id",
1806 true)) {
1807 if (err) {
1808 (*err) += "`id` field is missing in animation.channels.target\n";
1809 }
1810 return false;
1811 }
1812
1813 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
1814 true)) {
1815 if (err) {
1816 (*err) += "`path` field is missing in animation.channels.target\n";
1817 }
1818 return false;
1819 }
1820 }
1821
1822 return true;
1823}
1824
1825static bool ParseAnimation(Animation *animation, std::string *err,
1826 const picojson::object &o) {
1827 {
1828 picojson::object::const_iterator channelsIt = o.find("channels");
1829 if ((channelsIt != o.end()) && (channelsIt->second).is<picojson::array>()) {
1830 const picojson::array &channelArray =
1831 (channelsIt->second).get<picojson::array>();
1832 for (size_t i = 0; i < channelArray.size(); i++) {
1833 AnimationChannel channel;
1834 if (ParseAnimationChannel(&channel, err,
1835 channelArray[i].get<picojson::object>())) {
1836 // Only add the channel if the parsing succeeds.
1837 animation->channels.push_back(channel);
1838 }
1839 }
1840 }
1841 }
1842
1843 {
1844 picojson::object::const_iterator samplerIt = o.find("samplers");
1845 if ((samplerIt != o.end()) && (samplerIt->second).is<picojson::object>()) {
1846 const picojson::object &sampler_object =
1847 (samplerIt->second).get<picojson::object>();
1848
1849 picojson::object::const_iterator it = sampler_object.begin();
1850 picojson::object::const_iterator itEnd = sampler_object.end();
1851
1852 for (; it != itEnd; it++) {
1853 // Skip non-objects
1854 if (!it->second.is<picojson::object>()) continue;
1855
1856 const picojson::object &s = it->second.get<picojson::object>();
1857
1858 AnimationSampler sampler;
1859 if (!ParseStringProperty(&sampler.input, err, s, "input", true)) {
1860 if (err) {
1861 (*err) += "`input` field is missing in animation.sampler\n";
1862 }
1863 return false;
1864 }
1865 if (!ParseStringProperty(&sampler.interpolation, err, s,
1866 "interpolation", true)) {
1867 if (err) {
1868 (*err) += "`interpolation` field is missing in animation.sampler\n";
1869 }
1870 return false;
1871 }
1872 if (!ParseStringProperty(&sampler.output, err, s, "output", true)) {
1873 if (err) {
1874 (*err) += "`output` field is missing in animation.sampler\n";
1875 }
1876 return false;
1877 }
1878
1879 animation->samplers[it->first] = sampler;
1880 }
1881 }
1882 }
1883
1884 picojson::object::const_iterator parametersIt = o.find("parameters");
1885 if ((parametersIt != o.end()) &&
1886 (parametersIt->second).is<picojson::object>()) {
1887 const picojson::object &parameters_object =
1888 (parametersIt->second).get<picojson::object>();
1889
1890 picojson::object::const_iterator it(parameters_object.begin());
1891 picojson::object::const_iterator itEnd(parameters_object.end());
1892
1893 for (; it != itEnd; it++) {
1894 Parameter param;
1895 if (ParseParameterProperty(&param, err, parameters_object, it->first,
1896 false)) {
1897 animation->parameters[it->first] = param;
1898 }
1899 }
1900 }
1901 ParseStringProperty(&animation->name, err, o, "name", false);
1902
1903 return true;
1904}
1905
Syoyo Fujitac2615632016-06-19 21:56:06 +09001906static bool ParseSampler(Sampler *sampler, std::string *err,
1907 const picojson::object &o) {
1908 ParseStringProperty(&sampler->name, err, o, "name", false);
1909
1910 double minFilter =
1911 static_cast<double>(TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR);
1912 double magFilter = static_cast<double>(TINYGLTF_TEXTURE_FILTER_LINEAR);
1913 double wrapS = static_cast<double>(TINYGLTF_TEXTURE_WRAP_RPEAT);
1914 double wrapT = static_cast<double>(TINYGLTF_TEXTURE_WRAP_RPEAT);
1915 ParseNumberProperty(&minFilter, err, o, "minFilter", false);
1916 ParseNumberProperty(&magFilter, err, o, "magFilter", false);
1917 ParseNumberProperty(&wrapS, err, o, "wrapS", false);
1918 ParseNumberProperty(&wrapT, err, o, "wrapT", false);
1919
1920 sampler->minFilter = static_cast<int>(minFilter);
1921 sampler->magFilter = static_cast<int>(magFilter);
1922 sampler->wrapS = static_cast<int>(wrapS);
1923 sampler->wrapT = static_cast<int>(wrapT);
1924
1925 return true;
1926}
1927
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001928bool TinyGLTFLoader::LoadFromString(Scene *scene, std::string *err,
Syoyo Fujitabde70212016-02-07 17:38:17 +09001929 const char *str, unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001930 const std::string &base_dir,
1931 unsigned int check_sections) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001932 picojson::value v;
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001933 std::string perr = picojson::parse(v, str, str + length);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001934
1935 if (!perr.empty()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001936 if (err) {
1937 (*err) = perr;
1938 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001939 return false;
1940 }
1941
1942 if (v.contains("scene") && v.get("scene").is<std::string>()) {
1943 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04001944 } else if (check_sections & REQUIRE_SCENE) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001945 if (err) {
1946 (*err) += "\"scene\" object not found in .gltf\n";
1947 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001948 return false;
1949 }
1950
1951 if (v.contains("scenes") && v.get("scenes").is<picojson::object>()) {
1952 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04001953 } else if (check_sections & REQUIRE_SCENES) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001954 if (err) {
1955 (*err) += "\"scenes\" object not found in .gltf\n";
1956 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001957 return false;
1958 }
1959
1960 if (v.contains("nodes") && v.get("nodes").is<picojson::object>()) {
1961 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04001962 } else if (check_sections & REQUIRE_NODES) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001963 if (err) {
1964 (*err) += "\"nodes\" object not found in .gltf\n";
1965 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001966 return false;
1967 }
1968
1969 if (v.contains("accessors") && v.get("accessors").is<picojson::object>()) {
1970 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04001971 } else if (check_sections & REQUIRE_ACCESSORS) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001972 if (err) {
1973 (*err) += "\"accessors\" object not found in .gltf\n";
1974 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001975 return false;
1976 }
1977
1978 if (v.contains("buffers") && v.get("buffers").is<picojson::object>()) {
1979 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04001980 } else if (check_sections & REQUIRE_BUFFERS) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001981 if (err) {
1982 (*err) += "\"buffers\" object not found in .gltf\n";
1983 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001984 return false;
1985 }
1986
1987 if (v.contains("bufferViews") &&
1988 v.get("bufferViews").is<picojson::object>()) {
1989 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04001990 } else if (check_sections & REQUIRE_BUFFER_VIEWS) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001991 if (err) {
1992 (*err) += "\"bufferViews\" object not found in .gltf\n";
1993 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001994 return false;
1995 }
1996
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001997 scene->buffers.clear();
1998 scene->bufferViews.clear();
1999 scene->accessors.clear();
2000 scene->meshes.clear();
2001 scene->nodes.clear();
2002 scene->defaultScene = "";
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002003
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002004 // 0. Parse Asset
2005 if (v.contains("asset") && v.get("asset").is<picojson::object>()) {
2006 const picojson::object &root = v.get("asset").get<picojson::object>();
2007
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002008 ParseAsset(&scene->asset, err, root);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002009 }
2010
2011 // 1. Parse Buffer
2012 if (v.contains("buffers") && v.get("buffers").is<picojson::object>()) {
2013 const picojson::object &root = v.get("buffers").get<picojson::object>();
2014
2015 picojson::object::const_iterator it(root.begin());
2016 picojson::object::const_iterator itEnd(root.end());
2017 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002018 Buffer buffer;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002019 if (!ParseBuffer(&buffer, err, (it->second).get<picojson::object>(),
Syoyo Fujitabeded612016-05-01 20:03:43 +09002020 base_dir, is_binary_, bin_data_, bin_size_)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002021 return false;
2022 }
2023
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002024 scene->buffers[it->first] = buffer;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002025 }
2026 }
2027
2028 // 2. Parse BufferView
2029 if (v.contains("bufferViews") &&
2030 v.get("bufferViews").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002031 const picojson::object &root = v.get("bufferViews").get<picojson::object>();
2032
2033 picojson::object::const_iterator it(root.begin());
2034 picojson::object::const_iterator itEnd(root.end());
2035 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002036 BufferView bufferView;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002037 if (!ParseBufferView(&bufferView, err,
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002038 (it->second).get<picojson::object>())) {
2039 return false;
2040 }
2041
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002042 scene->bufferViews[it->first] = bufferView;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002043 }
2044 }
2045
2046 // 3. Parse Accessor
2047 if (v.contains("accessors") && v.get("accessors").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002048 const picojson::object &root = v.get("accessors").get<picojson::object>();
2049
2050 picojson::object::const_iterator it(root.begin());
2051 picojson::object::const_iterator itEnd(root.end());
2052 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002053 Accessor accessor;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002054 if (!ParseAccessor(&accessor, err,
2055 (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002056 return false;
2057 }
2058
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002059 scene->accessors[it->first] = accessor;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002060 }
2061 }
2062
2063 // 4. Parse Mesh
2064 if (v.contains("meshes") && v.get("meshes").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002065 const picojson::object &root = v.get("meshes").get<picojson::object>();
2066
2067 picojson::object::const_iterator it(root.begin());
2068 picojson::object::const_iterator itEnd(root.end());
2069 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002070 Mesh mesh;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002071 if (!ParseMesh(&mesh, err, (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002072 return false;
2073 }
2074
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002075 scene->meshes[it->first] = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002076 }
2077 }
2078
2079 // 5. Parse Node
2080 if (v.contains("nodes") && v.get("nodes").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002081 const picojson::object &root = v.get("nodes").get<picojson::object>();
2082
2083 picojson::object::const_iterator it(root.begin());
2084 picojson::object::const_iterator itEnd(root.end());
2085 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002086 Node node;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002087 if (!ParseNode(&node, err, (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002088 return false;
2089 }
2090
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002091 scene->nodes[it->first] = node;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002092 }
2093 }
2094
2095 // 6. Parse scenes.
2096 if (v.contains("scenes") && v.get("scenes").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002097 const picojson::object &root = v.get("scenes").get<picojson::object>();
2098
2099 picojson::object::const_iterator it(root.begin());
2100 picojson::object::const_iterator itEnd(root.end());
2101 for (; it != itEnd; it++) {
Syoyo Fujitae04b1b52016-06-05 22:31:20 +09002102 if (!((it->second).is<picojson::object>())) {
2103 if (err) {
2104 (*err) += "`scenes' does not contain an object.";
2105 }
2106 return false;
2107 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002108 const picojson::object &o = (it->second).get<picojson::object>();
2109 std::vector<std::string> nodes;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002110 if (!ParseStringArrayProperty(&nodes, err, o, "nodes", false)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002111 return false;
2112 }
2113
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002114 scene->scenes[it->first] = nodes;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002115 }
2116 }
2117
2118 // 7. Parse default scenes.
2119 if (v.contains("scene") && v.get("scene").is<std::string>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002120 const std::string defaultScene = v.get("scene").get<std::string>();
2121
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002122 scene->defaultScene = defaultScene;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002123 }
2124
2125 // 8. Parse Material
2126 if (v.contains("materials") && v.get("materials").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002127 const picojson::object &root = v.get("materials").get<picojson::object>();
2128
2129 picojson::object::const_iterator it(root.begin());
2130 picojson::object::const_iterator itEnd(root.end());
2131 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002132 Material material;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002133 if (!ParseMaterial(&material, err,
2134 (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002135 return false;
2136 }
2137
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002138 scene->materials[it->first] = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002139 }
2140 }
2141
2142 // 9. Parse Image
2143 if (v.contains("images") && v.get("images").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002144 const picojson::object &root = v.get("images").get<picojson::object>();
2145
2146 picojson::object::const_iterator it(root.begin());
2147 picojson::object::const_iterator itEnd(root.end());
2148 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002149 Image image;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002150 if (!ParseImage(&image, err, (it->second).get<picojson::object>(),
Syoyo Fujitabeded612016-05-01 20:03:43 +09002151 base_dir, is_binary_, bin_data_, bin_size_)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002152 return false;
2153 }
2154
Syoyo Fujitabeded612016-05-01 20:03:43 +09002155 if (!image.bufferView.empty()) {
2156 // Load image from the buffer view.
2157 if (scene->bufferViews.find(image.bufferView) ==
2158 scene->bufferViews.end()) {
2159 if (err) {
2160 std::stringstream ss;
2161 ss << "bufferView \"" << image.bufferView
2162 << "\" not found in the scene." << std::endl;
2163 (*err) += ss.str();
2164 }
2165 return false;
2166 }
2167
2168 const BufferView &bufferView = scene->bufferViews[image.bufferView];
2169 const Buffer &buffer = scene->buffers[bufferView.buffer];
2170
2171 bool ret = LoadImageData(&image, err, image.width, image.height,
2172 &buffer.data[bufferView.byteOffset],
2173 static_cast<int>(bufferView.byteLength));
2174 if (!ret) {
2175 return false;
2176 }
2177 }
2178
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002179 scene->images[it->first] = image;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002180 }
2181 }
2182
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002183 // 10. Parse Texture
Syoyo Fujitabde70212016-02-07 17:38:17 +09002184 if (v.contains("textures") && v.get("textures").is<picojson::object>()) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09002185 const picojson::object &root = v.get("textures").get<picojson::object>();
2186
2187 picojson::object::const_iterator it(root.begin());
2188 picojson::object::const_iterator itEnd(root.end());
2189 for (; it != itEnd; it++) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09002190 Texture texture;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002191 if (!ParseTexture(&texture, err, (it->second).get<picojson::object>(),
Syoyo Fujitabeded612016-05-01 20:03:43 +09002192 base_dir)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09002193 return false;
2194 }
2195
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002196 scene->textures[it->first] = texture;
Syoyo Fujitabde70212016-02-07 17:38:17 +09002197 }
2198 }
2199
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002200 // 11. Parse Shader
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002201 if (v.contains("shaders") && v.get("shaders").is<picojson::object>()) {
2202 const picojson::object &root = v.get("shaders").get<picojson::object>();
2203
2204 picojson::object::const_iterator it(root.begin());
2205 picojson::object::const_iterator itEnd(root.end());
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002206 for (; it != itEnd; ++it) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002207 Shader shader;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002208 if (!ParseShader(&shader, err, (it->second).get<picojson::object>(),
2209 base_dir, is_binary_, bin_data_, bin_size_)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002210 return false;
2211 }
2212
2213 scene->shaders[it->first] = shader;
2214 }
2215 }
2216
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002217 // 12. Parse Program
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002218 if (v.contains("programs") && v.get("programs").is<picojson::object>()) {
2219 const picojson::object &root = v.get("programs").get<picojson::object>();
2220
2221 picojson::object::const_iterator it(root.begin());
2222 picojson::object::const_iterator itEnd(root.end());
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002223 for (; it != itEnd; ++it) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002224 Program program;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002225 if (!ParseProgram(&program, err, (it->second).get<picojson::object>())) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002226 return false;
2227 }
2228
2229 scene->programs[it->first] = program;
2230 }
2231 }
2232
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002233 // 13. Parse Technique
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002234 if (v.contains("techniques") && v.get("techniques").is<picojson::object>()) {
2235 const picojson::object &root = v.get("techniques").get<picojson::object>();
2236
2237 picojson::object::const_iterator it(root.begin());
2238 picojson::object::const_iterator itEnd(root.end());
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002239 for (; it != itEnd; ++it) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002240 Technique technique;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002241 if (!ParseTechnique(&technique, err,
2242 (it->second).get<picojson::object>())) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002243 return false;
2244 }
2245
2246 scene->techniques[it->first] = technique;
2247 }
2248 }
2249
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002250 // 14. Parse Animation
2251 if (v.contains("animations") && v.get("animations").is<picojson::object>()) {
2252 const picojson::object &root = v.get("animations").get<picojson::object>();
2253
2254 picojson::object::const_iterator it(root.begin());
2255 picojson::object::const_iterator itEnd(root.end());
2256 for (; it != itEnd; ++it) {
2257 Animation animation;
2258 if (!ParseAnimation(&animation, err,
2259 (it->second).get<picojson::object>())) {
2260 return false;
2261 }
2262
2263 scene->animations[it->first] = animation;
2264 }
2265 }
2266
Syoyo Fujitac2615632016-06-19 21:56:06 +09002267 // 15. Parse Sampler
2268 if (v.contains("samplers") && v.get("samplers").is<picojson::object>()) {
2269 const picojson::object &root = v.get("samplers").get<picojson::object>();
2270
2271 picojson::object::const_iterator it(root.begin());
2272 picojson::object::const_iterator itEnd(root.end());
2273 for (; it != itEnd; ++it) {
2274 Sampler sampler;
2275 if (!ParseSampler(&sampler, err, (it->second).get<picojson::object>())) {
2276 return false;
2277 }
2278
2279 scene->samplers[it->first] = sampler;
2280 }
2281 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002282 return true;
2283}
2284
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002285bool TinyGLTFLoader::LoadASCIIFromString(Scene *scene, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09002286 const char *str, unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04002287 const std::string &base_dir,
2288 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002289 is_binary_ = false;
2290 bin_data_ = NULL;
2291 bin_size_ = 0;
2292
Luke San Antonio9e36b612016-06-23 14:10:51 -04002293 return LoadFromString(scene, err, str, length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002294}
2295
2296bool TinyGLTFLoader::LoadASCIIFromFile(Scene *scene, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -04002297 const std::string &filename,
2298 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002299 std::stringstream ss;
2300
2301 std::ifstream f(filename.c_str());
2302 if (!f) {
2303 ss << "Failed to open file: " << filename << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002304 if (err) {
2305 (*err) = ss.str();
2306 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002307 return false;
2308 }
2309
2310 f.seekg(0, f.end);
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +09002311 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002312 std::vector<char> buf(sz);
2313
Luke San Antoniob310b4a2016-06-23 14:17:37 -04002314 if (sz == 0) {
2315 if (err) {
2316 (*err) = "Empty file.";
2317 }
2318 return false;
2319 }
2320
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002321 f.seekg(0, f.beg);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002322 f.read(&buf.at(0), static_cast<std::streamsize>(sz));
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002323 f.close();
2324
2325 std::string basedir = GetBaseDir(filename);
2326
Luke San Antonio9e36b612016-06-23 14:10:51 -04002327 bool ret = LoadASCIIFromString(scene, err, &buf.at(0),
2328 static_cast<unsigned int>(buf.size()), basedir,
2329 check_sections);
2330
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002331
2332 return ret;
2333}
2334
2335bool TinyGLTFLoader::LoadBinaryFromMemory(Scene *scene, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09002336 const unsigned char *bytes,
2337 unsigned int size,
Luke San Antonio9e36b612016-06-23 14:10:51 -04002338 const std::string &base_dir,
2339 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002340 if (size < 20) {
2341 if (err) {
2342 (*err) = "Too short data size for glTF Binary.";
2343 }
2344 return false;
2345 }
2346
Syoyo Fujitabeded612016-05-01 20:03:43 +09002347 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
2348 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002349 // ok
2350 } else {
2351 if (err) {
2352 (*err) = "Invalid magic.";
2353 }
2354 return false;
2355 }
2356
Syoyo Fujitabeded612016-05-01 20:03:43 +09002357 unsigned int version; // 4 bytes
2358 unsigned int length; // 4 bytes
2359 unsigned int scene_length; // 4 bytes
2360 unsigned int scene_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002361
2362 // @todo { Endian swap for big endian machine. }
Syoyo Fujitabeded612016-05-01 20:03:43 +09002363 memcpy(&version, bytes + 4, 4);
2364 swap4(&version);
2365 memcpy(&length, bytes + 8, 4);
2366 swap4(&length);
2367 memcpy(&scene_length, bytes + 12, 4);
2368 swap4(&scene_length);
2369 memcpy(&scene_format, bytes + 16, 4);
2370 swap4(&scene_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002371
Syoyo Fujitabeded612016-05-01 20:03:43 +09002372 if ((20 + scene_length >= size) || (scene_length < 1) ||
2373 (scene_format != 0)) { // 0 = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002374 if (err) {
2375 (*err) = "Invalid glTF binary.";
2376 }
2377 return false;
2378 }
2379
2380 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09002381 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
2382 scene_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002383
2384 is_binary_ = true;
2385 bin_data_ = bytes + 20 + scene_length;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002386 bin_size_ =
2387 length - (20 + scene_length); // extract header + JSON scene data.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002388
Syoyo Fujitabeded612016-05-01 20:03:43 +09002389 bool ret =
2390 LoadFromString(scene, err, reinterpret_cast<const char *>(&bytes[20]),
Luke San Antonio9e36b612016-06-23 14:10:51 -04002391 scene_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002392 if (!ret) {
2393 return ret;
2394 }
2395
2396 return true;
2397}
2398
2399bool TinyGLTFLoader::LoadBinaryFromFile(Scene *scene, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -04002400 const std::string &filename,
2401 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002402 std::stringstream ss;
2403
r-lyeh66c10632016-08-29 16:56:18 +02002404 std::ifstream f(filename.c_str(), std::ios::binary);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002405 if (!f) {
2406 ss << "Failed to open file: " << filename << std::endl;
2407 if (err) {
2408 (*err) = ss.str();
2409 }
2410 return false;
2411 }
2412
2413 f.seekg(0, f.end);
2414 size_t sz = static_cast<size_t>(f.tellg());
2415 std::vector<char> buf(sz);
2416
2417 f.seekg(0, f.beg);
2418 f.read(&buf.at(0), static_cast<std::streamsize>(sz));
2419 f.close();
2420
Syoyo Fujitabeded612016-05-01 20:03:43 +09002421 std::string basedir = GetBaseDir(filename);
2422
2423 bool ret = LoadBinaryFromMemory(
2424 scene, err, reinterpret_cast<unsigned char *>(&buf.at(0)),
Luke San Antonio9e36b612016-06-23 14:10:51 -04002425 static_cast<unsigned int>(buf.size()), basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002426
2427 return ret;
2428}
2429
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09002430} // namespace tinygltf
2431
Syoyo Fujita7c877972016-03-08 01:31:49 +09002432#endif // TINYGLTF_LOADER_IMPLEMENTATION
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002433
Syoyo Fujita7c877972016-03-08 01:31:49 +09002434#endif // TINY_GLTF_LOADER_H_