blob: 3f05a341d942b898e7ab3362cf1d9a549e505eb1 [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)
81#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33071)
82
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
Syoyo Fujita0614eb82016-10-14 18:50:14 +0900349enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -0400350 NO_REQUIRE = 0x00,
351 REQUIRE_SCENE = 0x01,
352 REQUIRE_SCENES = 0x02,
353 REQUIRE_NODES = 0x04,
354 REQUIRE_ACCESSORS = 0x08,
355 REQUIRE_BUFFERS = 0x10,
356 REQUIRE_BUFFER_VIEWS = 0x20,
357 REQUIRE_ALL = 0x3f
358};
359
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900360class TinyGLTFLoader {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900361 public:
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900362 TinyGLTFLoader() : bin_data_(NULL), bin_size_(0), is_binary_(false) {
363 pad[0] = pad[1] = pad[2] = pad[3] = pad[4] = pad[5] = pad[6] = 0;
364 }
Syoyo Fujita7c877972016-03-08 01:31:49 +0900365 ~TinyGLTFLoader() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900366
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900367 /// Loads glTF ASCII asset from a file.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900368 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900369 bool LoadASCIIFromFile(Scene *scene, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400370 const std::string &filename,
371 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900372
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900373 /// Loads glTF ASCII asset from string(memory).
374 /// `length` = strlen(str);
375 /// Returns false and set error string to `err` if there's an error.
376 bool LoadASCIIFromString(Scene *scene, std::string *err, const char *str,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900377 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400378 const std::string &base_dir,
379 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900380
381 /// Loads glTF binary asset from a file.
382 /// Returns false and set error string to `err` if there's an error.
383 bool LoadBinaryFromFile(Scene *scene, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400384 const std::string &filename,
385 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900386
387 /// Loads glTF binary asset from memory.
388 /// `length` = strlen(str);
389 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujitabeded612016-05-01 20:03:43 +0900390 bool LoadBinaryFromMemory(Scene *scene, std::string *err,
391 const unsigned char *bytes,
392 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400393 const std::string &base_dir = "",
394 unsigned int check_sections = REQUIRE_ALL);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900395
Syoyo Fujitabeded612016-05-01 20:03:43 +0900396 private:
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900397 /// Loads glTF asset from string(memory).
398 /// `length` = strlen(str);
399 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900400 bool LoadFromString(Scene *scene, std::string *err, const char *str,
Luke San Antonio9e36b612016-06-23 14:10:51 -0400401 const unsigned int length, const std::string &base_dir,
402 unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900403
Syoyo Fujitabeded612016-05-01 20:03:43 +0900404 const unsigned char *bin_data_;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900405 size_t bin_size_;
406 bool is_binary_;
407 char pad[7];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900408};
409
Syoyo Fujita7c877972016-03-08 01:31:49 +0900410} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900411
412#ifdef TINYGLTF_LOADER_IMPLEMENTATION
Syoyo Fujita7c877972016-03-08 01:31:49 +0900413#include <algorithm>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900414#include <cassert>
415#include <fstream>
416#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +0900417
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900418#ifdef TINYGLTF_APPLY_CLANG_WEVERYTHING
Syoyo Fujita643ce102016-05-01 17:19:37 +0900419// Disable some warnings for external files.
420#pragma clang diagnostic push
421#pragma clang diagnostic ignored "-Wfloat-equal"
422#pragma clang diagnostic ignored "-Wexit-time-destructors"
423#pragma clang diagnostic ignored "-Wconversion"
424#pragma clang diagnostic ignored "-Wold-style-cast"
425#pragma clang diagnostic ignored "-Wdouble-promotion"
426#pragma clang diagnostic ignored "-Wglobal-constructors"
427#pragma clang diagnostic ignored "-Wreserved-id-macro"
428#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
429#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900430#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900431
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +0900432#include "./picojson.h"
433#include "./stb_image.h"
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900434#ifdef TINYGLTF_APPLY_CLANG_WEVERYTHING
Syoyo Fujita643ce102016-05-01 17:19:37 +0900435#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900436#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900437
438#ifdef _WIN32
439#include <Windows.h>
440#else
441#include <wordexp.h>
442#endif
443
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900444#if defined(__sparcv9)
445// Big endian
446#else
447#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
448#define TINYGLTF_LITTLE_ENDIAN 1
449#endif
450#endif
451
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +0900452namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900453
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900454static void swap4(unsigned int *val) {
455#ifdef TINYGLTF_LITTLE_ENDIAN
456 (void)val;
457#else
458 unsigned int tmp = *val;
459 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
460 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
461
462 dst[0] = src[3];
463 dst[1] = src[2];
464 dst[2] = src[1];
465 dst[3] = src[0];
466#endif
467}
468
Syoyo Fujita643ce102016-05-01 17:19:37 +0900469static bool FileExists(const std::string &abs_filename) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900470 bool ret;
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900471#ifdef _WIN32
472 FILE *fp;
473 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
474 if (err != 0) {
Syoyo Fujitabeded612016-05-01 20:03:43 +0900475 return false;
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900476 }
477#else
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900478 FILE *fp = fopen(abs_filename.c_str(), "rb");
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900479#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900480 if (fp) {
481 ret = true;
482 fclose(fp);
483 } else {
484 ret = false;
485 }
486
487 return ret;
488}
489
Syoyo Fujita643ce102016-05-01 17:19:37 +0900490static std::string ExpandFilePath(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900491#ifdef _WIN32
492 DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
493 char *str = new char[len];
494 ExpandEnvironmentStringsA(filepath.c_str(), str, len);
495
496 std::string s(str);
497
498 delete[] str;
499
500 return s;
501#else
502
Syoyo Fujita643ce102016-05-01 17:19:37 +0900503#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900504 // no expansion
505 std::string s = filepath;
506#else
507 std::string s;
508 wordexp_t p;
509
510 if (filepath.empty()) {
511 return "";
512 }
513
514 // char** w;
515 int ret = wordexp(filepath.c_str(), &p, 0);
516 if (ret) {
517 // err
518 s = filepath;
519 return s;
520 }
521
522 // Use first element only.
523 if (p.we_wordv) {
524 s = std::string(p.we_wordv[0]);
525 wordfree(&p);
526 } else {
527 s = filepath;
528 }
529
530#endif
531
532 return s;
533#endif
534}
535
Syoyo Fujitabeded612016-05-01 20:03:43 +0900536static std::string JoinPath(const std::string &path0,
537 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900538 if (path0.empty()) {
539 return path1;
540 } else {
541 // check '/'
542 char lastChar = *path0.rbegin();
543 if (lastChar != '/') {
544 return path0 + std::string("/") + path1;
545 } else {
546 return path0 + path1;
547 }
548 }
549}
550
Syoyo Fujita643ce102016-05-01 17:19:37 +0900551static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900552 const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900553 for (size_t i = 0; i < paths.size(); i++) {
554 std::string absPath = ExpandFilePath(JoinPath(paths[i], filepath));
555 if (FileExists(absPath)) {
556 return absPath;
557 }
558 }
559
560 return std::string();
561}
562
563// std::string GetFilePathExtension(const std::string& FileName)
564//{
565// if(FileName.find_last_of(".") != std::string::npos)
566// return FileName.substr(FileName.find_last_of(".")+1);
567// return "";
568//}
569
Syoyo Fujita643ce102016-05-01 17:19:37 +0900570static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900571 if (filepath.find_last_of("/\\") != std::string::npos)
572 return filepath.substr(0, filepath.find_last_of("/\\"));
573 return "";
574}
575
576// std::string base64_encode(unsigned char const* , unsigned int len);
577std::string base64_decode(std::string const &s);
578
579/*
580 base64.cpp and base64.h
581
582 Copyright (C) 2004-2008 René Nyffenegger
583
584 This source code is provided 'as-is', without any express or implied
585 warranty. In no event will the author be held liable for any damages
586 arising from the use of this software.
587
588 Permission is granted to anyone to use this software for any purpose,
589 including commercial applications, and to alter it and redistribute it
590 freely, subject to the following restrictions:
591
592 1. The origin of this source code must not be misrepresented; you must not
593 claim that you wrote the original source code. If you use this source code
594 in a product, an acknowledgment in the product documentation would be
595 appreciated but is not required.
596
597 2. Altered source versions must be plainly marked as such, and must not be
598 misrepresented as being the original source code.
599
600 3. This notice may not be removed or altered from any source distribution.
601
602 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
603
604*/
605
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900606#ifdef TINYGLTF_APPLY_CLANG_WEVERYTHING
Syoyo Fujita643ce102016-05-01 17:19:37 +0900607#pragma clang diagnostic push
608#pragma clang diagnostic ignored "-Wexit-time-destructors"
609#pragma clang diagnostic ignored "-Wglobal-constructors"
610#pragma clang diagnostic ignored "-Wsign-conversion"
611#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900612#endif
Syoyo Fujita7c877972016-03-08 01:31:49 +0900613static const std::string base64_chars =
614 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
615 "abcdefghijklmnopqrstuvwxyz"
616 "0123456789+/";
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900617
618static inline bool is_base64(unsigned char c) {
619 return (isalnum(c) || (c == '+') || (c == '/'));
620}
621
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900622std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +0900623 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900624 int i = 0;
625 int j = 0;
626 int in_ = 0;
627 unsigned char char_array_4[4], char_array_3[3];
628 std::string ret;
629
630 while (in_len-- && (encoded_string[in_] != '=') &&
631 is_base64(encoded_string[in_])) {
632 char_array_4[i++] = encoded_string[in_];
633 in_++;
634 if (i == 4) {
635 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +0900636 char_array_4[i] =
637 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900638
639 char_array_3[0] =
640 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
641 char_array_3[1] =
642 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
643 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
644
Syoyo Fujita7c877972016-03-08 01:31:49 +0900645 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900646 i = 0;
647 }
648 }
649
650 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900651 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900652
653 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +0900654 char_array_4[j] =
655 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900656
657 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
658 char_array_3[1] =
659 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
660 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
661
Syoyo Fujita7c877972016-03-08 01:31:49 +0900662 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900663 }
664
665 return ret;
666}
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900667#ifdef TINYGLTF_APPLY_CLANG_WEVERYTHING
Syoyo Fujita643ce102016-05-01 17:19:37 +0900668#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900669#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900670
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900671static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900672 const std::string &filename,
673 const std::string &basedir, size_t reqBytes,
674 bool checkSize) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900675 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900676
677 std::vector<std::string> paths;
678 paths.push_back(basedir);
679 paths.push_back(".");
680
681 std::string filepath = FindFile(paths, filename);
682 if (filepath.empty()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900683 if (err) {
Luke San Antonioda8b5d12016-06-14 22:17:40 -0400684 (*err) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900685 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900686 return false;
687 }
688
689 std::ifstream f(filepath.c_str(), std::ifstream::binary);
690 if (!f) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900691 if (err) {
Luke San Antonioda8b5d12016-06-14 22:17:40 -0400692 (*err) += "File open error : " + filepath + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900693 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900694 return false;
695 }
696
697 f.seekg(0, f.end);
Syoyo Fujita643ce102016-05-01 17:19:37 +0900698 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900699 std::vector<unsigned char> buf(sz);
700
701 f.seekg(0, f.beg);
Syoyo Fujitabeded612016-05-01 20:03:43 +0900702 f.read(reinterpret_cast<char *>(&buf.at(0)),
703 static_cast<std::streamsize>(sz));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900704 f.close();
705
706 if (checkSize) {
707 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900708 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900709 return true;
710 } else {
711 std::stringstream ss;
712 ss << "File size mismatch : " << filepath << ", requestedBytes "
713 << reqBytes << ", but got " << sz << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900714 if (err) {
715 (*err) += ss.str();
716 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900717 return false;
718 }
719 }
720
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900721 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900722 return true;
723}
724
Syoyo Fujitabeded612016-05-01 20:03:43 +0900725static bool LoadImageData(Image *image, std::string *err, int req_width,
726 int req_height, const unsigned char *bytes,
727 int size) {
728 int w, h, comp;
729 unsigned char *data = stbi_load_from_memory(bytes, size, &w, &h, &comp, 0);
730 if (!data) {
731 if (err) {
732 (*err) += "Unknown image format.\n";
733 }
734 return false;
735 }
736
737 if (w < 1 || h < 1) {
738 if (err) {
739 (*err) += "Unknown image format.\n";
740 }
741 return false;
742 }
743
744 if (req_width > 0) {
745 if (req_width != w) {
746 if (err) {
747 (*err) += "Image width mismatch.\n";
748 }
749 return false;
750 }
751 }
752
753 if (req_height > 0) {
754 if (req_height != h) {
755 if (err) {
756 (*err) += "Image height mismatch.\n";
757 }
758 return false;
759 }
760 }
761
762 image->width = w;
763 image->height = h;
764 image->component = comp;
765 image->image.resize(static_cast<size_t>(w * h * comp));
766 std::copy(data, data + w * h * comp, image->image.begin());
767
768 return true;
769}
770
Syoyo Fujita643ce102016-05-01 17:19:37 +0900771static bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900772 std::string header = "data:application/octet-stream;base64,";
773 if (in.find(header) == 0) {
774 return true;
775 }
Syoyo Fujita620eed12016-01-02 23:37:12 +0900776
777 header = "data:image/png;base64,";
778 if (in.find(header) == 0) {
779 return true;
780 }
781
782 header = "data:image/jpeg;base64,";
783 if (in.find(header) == 0) {
784 return true;
785 }
786
Luke San Antoniocaa24b02016-06-15 02:23:09 -0400787 header = "data:text/plain;base64,";
788 if (in.find(header) == 0) {
789 return true;
790 }
791
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900792 return false;
793}
794
Syoyo Fujitabeded612016-05-01 20:03:43 +0900795static bool DecodeDataURI(std::vector<unsigned char> *out,
796 const std::string &in, size_t reqBytes,
797 bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900798 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +0900799 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900800 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900801 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900802 }
Syoyo Fujita620eed12016-01-02 23:37:12 +0900803
804 if (data.empty()) {
805 header = "data:image/jpeg;base64,";
806 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900807 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +0900808 }
809 }
810
811 if (data.empty()) {
812 header = "data:image/png;base64,";
813 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900814 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +0900815 }
816 }
817
818 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -0400819 header = "data:text/plain;base64,";
820 if (in.find(header) == 0) {
821 data = base64_decode(in.substr(header.size()));
822 }
823 }
824
825 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +0900826 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900827 }
Syoyo Fujita620eed12016-01-02 23:37:12 +0900828
829 if (checkSize) {
830 if (data.size() != reqBytes) {
831 return false;
832 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900833 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +0900834 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900835 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +0900836 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900837 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +0900838 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900839}
840
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900841static bool ParseBooleanProperty(bool *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900842 const picojson::object &o,
843 const std::string &property, bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900844 picojson::object::const_iterator it = o.find(property);
845 if (it == o.end()) {
846 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900847 if (err) {
848 (*err) += "'" + property + "' property is missing.\n";
849 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900850 }
851 return false;
852 }
853
854 if (!it->second.is<bool>()) {
855 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900856 if (err) {
857 (*err) += "'" + property + "' property is not a bool type.\n";
858 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900859 }
860 return false;
861 }
862
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900863 if (ret) {
864 (*ret) = it->second.get<bool>();
865 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900866
867 return true;
868}
869
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900870static bool ParseNumberProperty(double *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900871 const picojson::object &o,
872 const std::string &property, bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900873 picojson::object::const_iterator it = o.find(property);
874 if (it == o.end()) {
875 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900876 if (err) {
877 (*err) += "'" + property + "' property is missing.\n";
878 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900879 }
880 return false;
881 }
882
883 if (!it->second.is<double>()) {
884 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900885 if (err) {
886 (*err) += "'" + property + "' property is not a number type.\n";
887 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900888 }
889 return false;
890 }
891
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900892 if (ret) {
893 (*ret) = it->second.get<double>();
894 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900895
896 return true;
897}
898
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900899static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900900 const picojson::object &o,
901 const std::string &property,
902 bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900903 picojson::object::const_iterator it = o.find(property);
904 if (it == o.end()) {
905 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900906 if (err) {
907 (*err) += "'" + property + "' property is missing.\n";
908 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900909 }
910 return false;
911 }
912
913 if (!it->second.is<picojson::array>()) {
914 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900915 if (err) {
916 (*err) += "'" + property + "' property is not an array.\n";
917 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900918 }
919 return false;
920 }
921
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900922 ret->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900923 const picojson::array &arr = it->second.get<picojson::array>();
924 for (size_t i = 0; i < arr.size(); i++) {
925 if (!arr[i].is<double>()) {
926 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900927 if (err) {
928 (*err) += "'" + property + "' property is not a number.\n";
929 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900930 }
931 return false;
932 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900933 ret->push_back(arr[i].get<double>());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900934 }
935
936 return true;
937}
938
Syoyo Fujita0614eb82016-10-14 18:50:14 +0900939static bool ParseStringProperty(
940 std::string *ret, std::string *err, const picojson::object &o,
941 const std::string &property, bool required,
942 const std::string &parent_node = std::string()) {
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) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +0900947 (*err) += "'" + property + "' property is missing";
948 if (parent_node.empty()) {
949 (*err) += ".\n";
950 } else {
951 (*err) += " in `" + parent_node + "'.\n";
952 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900953 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900954 }
955 return false;
956 }
957
958 if (!it->second.is<std::string>()) {
959 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900960 if (err) {
961 (*err) += "'" + property + "' property is not a string type.\n";
962 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900963 }
964 return false;
965 }
966
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900967 if (ret) {
968 (*ret) = it->second.get<std::string>();
969 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900970
971 return true;
972}
973
Syoyo Fujitabeded612016-05-01 20:03:43 +0900974static bool ParseStringArrayProperty(std::vector<std::string> *ret,
975 std::string *err,
976 const picojson::object &o,
977 const std::string &property,
978 bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900979 picojson::object::const_iterator it = o.find(property);
980 if (it == o.end()) {
981 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900982 if (err) {
983 (*err) += "'" + property + "' property is missing.\n";
984 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900985 }
986 return false;
987 }
988
989 if (!it->second.is<picojson::array>()) {
990 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900991 if (err) {
992 (*err) += "'" + property + "' property is not an array.\n";
993 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900994 }
995 return false;
996 }
997
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900998 ret->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900999 const picojson::array &arr = it->second.get<picojson::array>();
1000 for (size_t i = 0; i < arr.size(); i++) {
1001 if (!arr[i].is<std::string>()) {
1002 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001003 if (err) {
1004 (*err) += "'" + property + "' property is not a string.\n";
1005 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001006 }
1007 return false;
1008 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001009 ret->push_back(arr[i].get<std::string>());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001010 }
1011
1012 return true;
1013}
1014
Luke San Antonio19894c72016-06-14 21:19:51 -04001015static bool ParseStringMapProperty(std::map<std::string, std::string> *ret,
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001016 std::string *err, const picojson::object &o,
1017 const std::string &property, bool required) {
Luke San Antonio19894c72016-06-14 21:19:51 -04001018 picojson::object::const_iterator it = o.find(property);
1019 if (it == o.end()) {
1020 if (required) {
1021 if (err) {
1022 (*err) += "'" + property + "' property is missing.\n";
1023 }
1024 }
1025 return false;
1026 }
1027
1028 // Make sure we are dealing with an object / dictionary.
1029 if (!it->second.is<picojson::object>()) {
1030 if (required) {
1031 if (err) {
1032 (*err) += "'" + property + "' property is not an object.\n";
1033 }
1034 }
1035 return false;
1036 }
1037
1038 ret->clear();
1039 const picojson::object &dict = it->second.get<picojson::object>();
1040
1041 picojson::object::const_iterator dictIt(dict.begin());
1042 picojson::object::const_iterator dictItEnd(dict.end());
1043
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001044 for (; dictIt != dictItEnd; ++dictIt) {
Luke San Antonio19894c72016-06-14 21:19:51 -04001045 // Check that the value is a string.
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001046 if (!dictIt->second.is<std::string>()) {
1047 if (required) {
1048 if (err) {
Luke San Antonio19894c72016-06-14 21:19:51 -04001049 (*err) += "'" + property + "' value is not a string.\n";
1050 }
1051 }
1052 return false;
1053 }
1054
1055 // Insert into the list.
1056 (*ret)[dictIt->first] = dictIt->second.get<std::string>();
1057 }
1058 return true;
1059}
1060
Syoyo Fujitabeded612016-05-01 20:03:43 +09001061static bool ParseKHRBinaryExtension(const picojson::object &o, std::string *err,
1062 std::string *buffer_view,
1063 std::string *mime_type, int *image_width,
1064 int *image_height) {
1065 picojson::object j = o;
1066
1067 if (j.find("extensions") == j.end()) {
1068 if (err) {
1069 (*err) += "`extensions' property is missing.\n";
1070 }
1071 return false;
1072 }
1073
1074 if (!(j["extensions"].is<picojson::object>())) {
1075 if (err) {
1076 (*err) += "Invalid `extensions' property.\n";
1077 }
1078 return false;
1079 }
1080
1081 picojson::object ext = j["extensions"].get<picojson::object>();
1082
1083 if (ext.find("KHR_binary_glTF") == ext.end()) {
1084 if (err) {
1085 (*err) +=
1086 "`KHR_binary_glTF' property is missing in extension property.\n";
1087 }
1088 return false;
1089 }
1090
1091 if (!(ext["KHR_binary_glTF"].is<picojson::object>())) {
1092 if (err) {
1093 (*err) += "Invalid `KHR_binary_glTF' property.\n";
1094 }
1095 return false;
1096 }
1097
1098 picojson::object k = ext["KHR_binary_glTF"].get<picojson::object>();
1099
1100 if (!ParseStringProperty(buffer_view, err, k, "bufferView", true)) {
1101 return false;
1102 }
1103
1104 if (mime_type) {
1105 ParseStringProperty(mime_type, err, k, "mimeType", false);
1106 }
1107
1108 if (image_width) {
1109 double width = 0.0;
1110 if (ParseNumberProperty(&width, err, k, "width", false)) {
1111 (*image_width) = static_cast<int>(width);
1112 }
1113 }
1114
1115 if (image_height) {
1116 double height = 0.0;
1117 if (ParseNumberProperty(&height, err, k, "height", false)) {
1118 (*image_height) = static_cast<int>(height);
1119 }
1120 }
1121
1122 return true;
1123}
1124
1125static bool ParseAsset(Asset *asset, std::string *err,
1126 const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001127 ParseStringProperty(&asset->generator, err, o, "generator", false);
1128 ParseBooleanProperty(&asset->premultipliedAlpha, err, o, "premultipliedAlpha",
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001129 false);
1130
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001131 ParseStringProperty(&asset->version, err, o, "version", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001132
1133 picojson::object::const_iterator profile = o.find("profile");
1134 if (profile != o.end()) {
1135 const picojson::value &v = profile->second;
1136 if (v.contains("api") & v.get("api").is<std::string>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001137 asset->profile_api = v.get("api").get<std::string>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001138 }
1139 if (v.contains("version") & v.get("version").is<std::string>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001140 asset->profile_version = v.get("version").get<std::string>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001141 }
1142 }
1143
1144 return true;
1145}
1146
Syoyo Fujitabeded612016-05-01 20:03:43 +09001147static bool ParseImage(Image *image, std::string *err,
1148 const picojson::object &o, const std::string &basedir,
1149 bool is_binary, const unsigned char *bin_data,
1150 size_t bin_size) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001151 std::string uri;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001152 if (!ParseStringProperty(&uri, err, o, "uri", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001153 return false;
1154 }
1155
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001156 ParseStringProperty(&image->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001157
1158 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001159
1160 if (is_binary) {
1161 // Still binary glTF accepts external dataURI. First try external resources.
1162 bool loaded = false;
1163 if (IsDataURI(uri)) {
1164 loaded = DecodeDataURI(&img, uri, 0, false);
1165 } else {
1166 // Assume external .bin file.
1167 loaded = LoadExternalFile(&img, err, uri, basedir, 0, false);
1168 }
1169
1170 if (!loaded) {
1171 // load data from (embedded) binary data
1172
1173 if ((bin_size == 0) || (bin_data == NULL)) {
1174 if (err) {
1175 (*err) += "Invalid binary data.\n";
1176 }
1177 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001178 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001179
1180 // There should be "extensions" property.
1181 // "extensions":{"KHR_binary_glTF":{"bufferView": "id", ...
1182
1183 std::string buffer_view;
1184 std::string mime_type;
1185 int image_width;
1186 int image_height;
1187 bool ret = ParseKHRBinaryExtension(o, err, &buffer_view, &mime_type,
1188 &image_width, &image_height);
1189 if (!ret) {
1190 return false;
1191 }
1192
1193 if (uri.compare("data:,") == 0) {
1194 // ok
1195 } else {
1196 if (err) {
1197 (*err) += "Invalid URI for binary data.\n";
1198 }
1199 return false;
1200 }
1201
1202 // Just only save some information here. Loading actual image data from
1203 // bufferView is done in other place.
1204 image->bufferView = buffer_view;
1205 image->mimeType = mime_type;
1206 image->width = image_width;
1207 image->height = image_height;
1208
1209 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001210 }
1211 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001212 if (IsDataURI(uri)) {
1213 if (!DecodeDataURI(&img, uri, 0, false)) {
1214 if (err) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001215 (*err) += "Failed to decode 'uri' for image parameter.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001216 }
1217 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001218 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001219 } else {
1220 // Assume external file
1221 if (!LoadExternalFile(&img, err, uri, basedir, 0, false)) {
1222 if (err) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001223 (*err) += "Failed to load external 'uri'. for image parameter\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001224 }
1225 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001226 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001227 if (img.empty()) {
1228 if (err) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001229 (*err) += "Image is empty.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09001230 }
1231 return false;
1232 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001233 }
1234 }
1235
Syoyo Fujitabeded612016-05-01 20:03:43 +09001236 return LoadImageData(image, err, 0, 0, &img.at(0),
1237 static_cast<int>(img.size()));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001238}
1239
Syoyo Fujitabeded612016-05-01 20:03:43 +09001240static bool ParseTexture(Texture *texture, std::string *err,
1241 const picojson::object &o,
1242 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001243 (void)basedir;
1244
1245 if (!ParseStringProperty(&texture->sampler, err, o, "sampler", true)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001246 return false;
1247 }
1248
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001249 if (!ParseStringProperty(&texture->source, err, o, "source", true)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001250 return false;
1251 }
1252
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001253 ParseStringProperty(&texture->name, err, o, "name", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001254
1255 double format = TINYGLTF_TEXTURE_FORMAT_RGBA;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001256 ParseNumberProperty(&format, err, o, "format", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001257
1258 double internalFormat = TINYGLTF_TEXTURE_FORMAT_RGBA;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001259 ParseNumberProperty(&internalFormat, err, o, "internalFormat", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001260
1261 double target = TINYGLTF_TEXTURE_TARGET_TEXTURE2D;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001262 ParseNumberProperty(&target, err, o, "target", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001263
1264 double type = TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001265 ParseNumberProperty(&type, err, o, "type", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001266
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001267 texture->format = static_cast<int>(format);
1268 texture->internalFormat = static_cast<int>(internalFormat);
1269 texture->target = static_cast<int>(target);
1270 texture->type = static_cast<int>(type);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001271
1272 return true;
1273}
1274
Syoyo Fujitabeded612016-05-01 20:03:43 +09001275static bool ParseBuffer(Buffer *buffer, std::string *err,
1276 const picojson::object &o, const std::string &basedir,
1277 bool is_binary = false,
1278 const unsigned char *bin_data = NULL,
1279 size_t bin_size = 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001280 double byteLength;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001281 if (!ParseNumberProperty(&byteLength, err, o, "byteLength", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001282 return false;
1283 }
1284
1285 std::string uri;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001286 if (!ParseStringProperty(&uri, err, o, "uri", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001287 return false;
1288 }
1289
1290 picojson::object::const_iterator type = o.find("type");
1291 if (type != o.end()) {
1292 if (type->second.is<std::string>()) {
1293 const std::string &ty = (type->second).get<std::string>();
1294 if (ty.compare("arraybuffer") == 0) {
1295 // buffer.type = "arraybuffer";
1296 }
1297 }
1298 }
1299
1300 size_t bytes = static_cast<size_t>(byteLength);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001301 if (is_binary) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001302 // Still binary glTF accepts external dataURI. First try external resources.
1303 bool loaded = false;
1304 if (IsDataURI(uri)) {
1305 loaded = DecodeDataURI(&buffer->data, uri, bytes, true);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001306 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001307 // Assume external .bin file.
1308 loaded = LoadExternalFile(&buffer->data, err, uri, basedir, bytes, true);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001309 }
1310
Syoyo Fujitabeded612016-05-01 20:03:43 +09001311 if (!loaded) {
1312 // load data from (embedded) binary data
1313
1314 if ((bin_size == 0) || (bin_data == NULL)) {
1315 if (err) {
1316 (*err) += "Invalid binary data.\n";
1317 }
1318 return false;
1319 }
1320
1321 if (byteLength > bin_size) {
1322 if (err) {
1323 std::stringstream ss;
1324 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001325 "`byteLength' = "
1326 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001327 (*err) += ss.str();
1328 }
1329 return false;
1330 }
1331
1332 if (uri.compare("data:,") == 0) {
1333 // @todo { check uri }
1334 buffer->data.resize(static_cast<size_t>(byteLength));
1335 memcpy(&(buffer->data.at(0)), bin_data,
1336 static_cast<size_t>(byteLength));
1337
1338 } else {
1339 if (err) {
1340 (*err) += "Invalid URI for binary data.\n";
1341 }
1342 return false;
1343 }
1344 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001345
1346 } else {
1347 if (IsDataURI(uri)) {
1348 if (!DecodeDataURI(&buffer->data, uri, bytes, true)) {
1349 if (err) {
1350 (*err) += "Failed to decode 'uri'.\n";
1351 }
1352 return false;
1353 }
1354 } else {
1355 // Assume external .bin file.
1356 if (!LoadExternalFile(&buffer->data, err, uri, basedir, bytes, true)) {
1357 return false;
1358 }
1359 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001360 }
1361
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001362 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001363
1364 return true;
1365}
1366
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001367static bool ParseBufferView(BufferView *bufferView, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001368 const picojson::object &o) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001369 std::string buffer;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001370 if (!ParseStringProperty(&buffer, err, o, "buffer", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001371 return false;
1372 }
1373
1374 double byteOffset;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001375 if (!ParseNumberProperty(&byteOffset, err, o, "byteOffset", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001376 return false;
1377 }
1378
1379 double byteLength = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001380 ParseNumberProperty(&byteLength, err, o, "byteLength", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001381
1382 double target = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001383 ParseNumberProperty(&target, err, o, "target", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001384 int targetValue = static_cast<int>(target);
1385 if ((targetValue == TINYGLTF_TARGET_ARRAY_BUFFER) ||
1386 (targetValue == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
1387 // OK
1388 } else {
1389 targetValue = 0;
1390 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001391 bufferView->target = targetValue;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001392
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001393 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001394
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001395 bufferView->buffer = buffer;
1396 bufferView->byteOffset = static_cast<size_t>(byteOffset);
1397 bufferView->byteLength = static_cast<size_t>(byteLength);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001398
1399 return true;
1400}
1401
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001402static bool ParseAccessor(Accessor *accessor, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001403 const picojson::object &o) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001404 std::string bufferView;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001405 if (!ParseStringProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001406 return false;
1407 }
1408
1409 double byteOffset;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001410 if (!ParseNumberProperty(&byteOffset, err, o, "byteOffset", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001411 return false;
1412 }
1413
1414 double componentType;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001415 if (!ParseNumberProperty(&componentType, err, o, "componentType", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001416 return false;
1417 }
1418
1419 double count = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001420 if (!ParseNumberProperty(&count, err, o, "count", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001421 return false;
1422 }
1423
1424 std::string type;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001425 if (!ParseStringProperty(&type, err, o, "type", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001426 return false;
1427 }
1428
1429 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001430 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001431 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001432 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001433 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001434 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001435 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001436 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001437 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001438 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001439 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001440 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001441 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001442 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001443 } else {
1444 std::stringstream ss;
1445 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001446 if (err) {
1447 (*err) += ss.str();
1448 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001449 return false;
1450 }
1451
1452 double byteStride = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001453 ParseNumberProperty(&byteStride, err, o, "byteStride", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001454
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001455 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001456
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001457 accessor->minValues.clear();
1458 accessor->maxValues.clear();
1459 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false);
1460 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001461
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001462 accessor->count = static_cast<size_t>(count);
1463 accessor->bufferView = bufferView;
1464 accessor->byteOffset = static_cast<size_t>(byteOffset);
1465 accessor->byteStride = static_cast<size_t>(byteStride);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001466
1467 {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001468 int comp = static_cast<int>(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001469 if (comp >= TINYGLTF_COMPONENT_TYPE_BYTE &&
1470 comp <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
1471 // OK
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001472 accessor->componentType = comp;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001473 } else {
1474 std::stringstream ss;
1475 ss << "Invalid `componentType` in accessor. Got " << comp << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001476 if (err) {
1477 (*err) += ss.str();
1478 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001479 return false;
1480 }
1481 }
1482
1483 return true;
1484}
1485
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001486static bool ParsePrimitive(Primitive *primitive, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001487 const picojson::object &o) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001488 if (!ParseStringProperty(&primitive->material, err, o, "material", true,
1489 "mesh.primitive")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001490 return false;
1491 }
1492
1493 double mode = static_cast<double>(TINYGLTF_MODE_TRIANGLES);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001494 ParseNumberProperty(&mode, err, o, "mode", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001495
1496 int primMode = static_cast<int>(mode);
1497 if (primMode != TINYGLTF_MODE_TRIANGLES) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001498 if (err) {
1499 (*err) +=
1500 "Currently TinyGLTFLoader doesn not support primitive mode other \n"
1501 "than TRIANGLES.\n";
1502 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001503 return false;
1504 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001505 primitive->mode = primMode;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001506
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001507 primitive->indices = "";
1508 ParseStringProperty(&primitive->indices, err, o, "indices", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001509
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001510 ParseStringMapProperty(&primitive->attributes, err, o, "attributes", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001511
1512 return true;
1513}
1514
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001515static bool ParseMesh(Mesh *mesh, std::string *err, const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001516 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001517
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001518 mesh->primitives.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001519 picojson::object::const_iterator primObject = o.find("primitives");
1520 if ((primObject != o.end()) && (primObject->second).is<picojson::array>()) {
1521 const picojson::array &primArray =
1522 (primObject->second).get<picojson::array>();
1523 for (size_t i = 0; i < primArray.size(); i++) {
1524 Primitive primitive;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001525 if (ParsePrimitive(&primitive, err,
1526 primArray[i].get<picojson::object>())) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04001527 // Only add the primitive if the parsing succeeds.
1528 mesh->primitives.push_back(primitive);
1529 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001530 }
1531 }
1532
1533 return true;
1534}
1535
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001536static bool ParseNode(Node *node, std::string *err, const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001537 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001538
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001539 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
1540 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
1541 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
1542 ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false);
1543 ParseStringArrayProperty(&node->meshes, err, o, "meshes", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001544
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001545 node->children.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001546 picojson::object::const_iterator childrenObject = o.find("children");
1547 if ((childrenObject != o.end()) &&
1548 (childrenObject->second).is<picojson::array>()) {
1549 const picojson::array &childrenArray =
1550 (childrenObject->second).get<picojson::array>();
1551 for (size_t i = 0; i < childrenArray.size(); i++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001552 if (!childrenArray[i].is<std::string>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001553 if (err) {
1554 (*err) += "Invalid `children` array.\n";
1555 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001556 return false;
1557 }
1558 const std::string &childrenNode = childrenArray[i].get<std::string>();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001559 node->children.push_back(childrenNode);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001560 }
1561 }
1562
1563 return true;
1564}
1565
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001566static bool ParseParameterProperty(Parameter *param, std::string *err,
1567 const picojson::object &o,
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001568 const std::string &prop, bool required) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001569 double num_val;
1570
1571 // A parameter value can either be a string or an array of either a boolean or
1572 // a number. Booleans of any kind aren't supported here. Granted, it
1573 // complicates the Parameter structure and breaks it semantically in the sense
1574 // that the client probably works off the assumption that if the string is
1575 // empty the vector is used, etc. Would a tagged union work?
1576 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
1577 // Found string property.
1578 return true;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001579 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
1580 false)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001581 // Found a number array.
1582 return true;
1583 } else if (ParseNumberProperty(&num_val, err, o, prop, false)) {
1584 param->number_array.push_back(num_val);
1585 return true;
1586 } else {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001587 if (required) {
1588 if (err) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001589 (*err) += "parameter must be a string or number / number array.\n";
1590 }
1591 }
1592 return false;
1593 }
1594}
1595
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001596static bool ParseMaterial(Material *material, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001597 const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001598 ParseStringProperty(&material->name, err, o, "name", false);
1599 ParseStringProperty(&material->technique, err, o, "technique", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001600
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001601 material->values.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001602 picojson::object::const_iterator valuesIt = o.find("values");
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001603
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001604 if ((valuesIt != o.end()) && (valuesIt->second).is<picojson::object>()) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001605 const picojson::object &values_object =
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001606 (valuesIt->second).get<picojson::object>();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001607
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001608 picojson::object::const_iterator it(values_object.begin());
1609 picojson::object::const_iterator itEnd(values_object.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001610
1611 for (; it != itEnd; it++) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001612 Parameter param;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001613 if (ParseParameterProperty(&param, err, values_object, it->first,
1614 false)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001615 material->values[it->first] = param;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001616 }
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001617 }
1618 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001619
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001620 return true;
1621}
1622
1623static bool ParseShader(Shader *shader, std::string *err,
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001624 const picojson::object &o, const std::string &basedir,
1625 bool is_binary = false,
1626 const unsigned char *bin_data = NULL,
1627 size_t bin_size = 0) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001628 std::string uri;
1629 if (!ParseStringProperty(&uri, err, o, "uri", true)) {
1630 return false;
1631 }
1632
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001633 if (is_binary) {
1634 // Still binary glTF accepts external dataURI. First try external resources.
1635 bool loaded = false;
1636 if (IsDataURI(uri)) {
1637 loaded = DecodeDataURI(&shader->source, uri, 0, false);
1638 } else {
1639 // Assume external .bin file.
1640 loaded = LoadExternalFile(&shader->source, err, uri, basedir, 0, false);
1641 }
1642
1643 if (!loaded) {
1644 // load data from (embedded) binary data
1645
1646 if ((bin_size == 0) || (bin_data == NULL)) {
1647 if (err) {
1648 (*err) += "Invalid binary data.\n";
1649 }
1650 return false;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001651 }
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001652
1653 // There should be "extensions" property.
1654 // "extensions":{"KHR_binary_glTF":{"bufferView": "id", ...
1655
1656 std::string buffer_view;
1657 std::string mime_type;
1658 int image_width;
1659 int image_height;
1660 bool ret = ParseKHRBinaryExtension(o, err, &buffer_view, &mime_type,
1661 &image_width, &image_height);
1662 if (!ret) {
1663 return false;
1664 }
1665
1666 if (uri.compare("data:,") == 0) {
1667 // ok
1668 } else {
1669 if (err) {
1670 (*err) += "Invalid URI for binary data.\n";
1671 }
1672 return false;
1673 }
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001674 }
1675 } else {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001676 // Load shader source from data uri
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001677 // TODO(syoyo): Support ascii or utf-8 data uris.
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001678 if (IsDataURI(uri)) {
1679 if (!DecodeDataURI(&shader->source, uri, 0, false)) {
1680 if (err) {
1681 (*err) += "Failed to decode 'uri' for shader parameter.\n";
1682 }
1683 return false;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001684 }
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001685 } else {
1686 // Assume external file
1687 if (!LoadExternalFile(&shader->source, err, uri, basedir, 0, false)) {
1688 if (err) {
1689 (*err) += "Failed to load external 'uri' for shader parameter.\n";
1690 }
1691 return false;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001692 }
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001693 if (shader->source.empty()) {
1694 if (err) {
1695 (*err) += "shader is empty.\n"; // This may be OK?
1696 }
1697 return false;
1698 }
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001699 }
1700 }
1701
1702 double type;
1703 if (!ParseNumberProperty(&type, err, o, "type", true)) {
1704 return false;
1705 }
1706
1707 shader->type = static_cast<int>(type);
1708
1709 return true;
1710}
1711
1712static bool ParseProgram(Program *program, std::string *err,
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001713 const picojson::object &o) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001714 ParseStringProperty(&program->name, err, o, "name", false);
1715
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001716 if (!ParseStringProperty(&program->vertexShader, err, o, "vertexShader",
1717 true)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001718 return false;
1719 }
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001720 if (!ParseStringProperty(&program->fragmentShader, err, o, "fragmentShader",
1721 true)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001722 return false;
1723 }
1724
1725 // I suppose the list of attributes isn't needed, but a technique doesn't
1726 // really make sense without it.
1727 ParseStringArrayProperty(&program->attributes, err, o, "attributes", false);
1728
1729 return true;
1730}
1731
1732static bool ParseTechniqueParameter(TechniqueParameter *param, std::string *err,
1733 const picojson::object &o) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001734 double count = 1;
1735 ParseNumberProperty(&count, err, o, "count", false);
1736
1737 double type;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001738 if (!ParseNumberProperty(&type, err, o, "type", true)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001739 return false;
1740 }
1741
1742 ParseStringProperty(&param->node, err, o, "node", false);
1743 ParseStringProperty(&param->semantic, err, o, "semantic", false);
1744
1745 ParseParameterProperty(&param->value, err, o, "value", false);
1746
1747 param->count = static_cast<int>(count);
1748 param->type = static_cast<int>(type);
1749
1750 return true;
1751}
1752
1753static bool ParseTechnique(Technique *technique, std::string *err,
1754 const picojson::object &o) {
1755 ParseStringProperty(&technique->name, err, o, "name", false);
1756
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001757 if (!ParseStringProperty(&technique->program, err, o, "program", true)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001758 return false;
1759 }
1760
1761 ParseStringMapProperty(&technique->attributes, err, o, "attributes", false);
1762 ParseStringMapProperty(&technique->uniforms, err, o, "uniforms", false);
1763
1764 technique->parameters.clear();
1765 picojson::object::const_iterator paramsIt = o.find("parameters");
1766
1767 // Verify parameters is an object
1768 if ((paramsIt != o.end()) && (paramsIt->second).is<picojson::object>()) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001769 // For each parameter in params_object.
1770 const picojson::object &params_object =
1771 (paramsIt->second).get<picojson::object>();
1772
1773 picojson::object::const_iterator it(params_object.begin());
1774 picojson::object::const_iterator itEnd(params_object.end());
1775
1776 for (; it != itEnd; it++) {
1777 TechniqueParameter param;
1778
1779 // Skip non-objects
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001780 if (!it->second.is<picojson::object>()) continue;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001781
1782 // Parse the technique parameter
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09001783 const picojson::object &param_obj = it->second.get<picojson::object>();
1784 if (ParseTechniqueParameter(&param, err, param_obj)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04001785 // Add if successful
1786 technique->parameters[it->first] = param;
1787 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001788 }
1789 }
1790
1791 return true;
1792}
Syoyo Fujita7c877972016-03-08 01:31:49 +09001793
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09001794static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err,
1795 const picojson::object &o) {
1796 if (!ParseStringProperty(&channel->sampler, err, o, "sampler", true)) {
1797 if (err) {
1798 (*err) += "`sampler` field is missing in animation channels\n";
1799 }
1800 return false;
1801 }
1802
1803 picojson::object::const_iterator targetIt = o.find("target");
1804 if ((targetIt != o.end()) && (targetIt->second).is<picojson::object>()) {
1805 const picojson::object &target_object =
1806 (targetIt->second).get<picojson::object>();
1807
1808 if (!ParseStringProperty(&channel->target_id, err, target_object, "id",
1809 true)) {
1810 if (err) {
1811 (*err) += "`id` field is missing in animation.channels.target\n";
1812 }
1813 return false;
1814 }
1815
1816 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
1817 true)) {
1818 if (err) {
1819 (*err) += "`path` field is missing in animation.channels.target\n";
1820 }
1821 return false;
1822 }
1823 }
1824
1825 return true;
1826}
1827
1828static bool ParseAnimation(Animation *animation, std::string *err,
1829 const picojson::object &o) {
1830 {
1831 picojson::object::const_iterator channelsIt = o.find("channels");
1832 if ((channelsIt != o.end()) && (channelsIt->second).is<picojson::array>()) {
1833 const picojson::array &channelArray =
1834 (channelsIt->second).get<picojson::array>();
1835 for (size_t i = 0; i < channelArray.size(); i++) {
1836 AnimationChannel channel;
1837 if (ParseAnimationChannel(&channel, err,
1838 channelArray[i].get<picojson::object>())) {
1839 // Only add the channel if the parsing succeeds.
1840 animation->channels.push_back(channel);
1841 }
1842 }
1843 }
1844 }
1845
1846 {
1847 picojson::object::const_iterator samplerIt = o.find("samplers");
1848 if ((samplerIt != o.end()) && (samplerIt->second).is<picojson::object>()) {
1849 const picojson::object &sampler_object =
1850 (samplerIt->second).get<picojson::object>();
1851
1852 picojson::object::const_iterator it = sampler_object.begin();
1853 picojson::object::const_iterator itEnd = sampler_object.end();
1854
1855 for (; it != itEnd; it++) {
1856 // Skip non-objects
1857 if (!it->second.is<picojson::object>()) continue;
1858
1859 const picojson::object &s = it->second.get<picojson::object>();
1860
1861 AnimationSampler sampler;
1862 if (!ParseStringProperty(&sampler.input, err, s, "input", true)) {
1863 if (err) {
1864 (*err) += "`input` field is missing in animation.sampler\n";
1865 }
1866 return false;
1867 }
1868 if (!ParseStringProperty(&sampler.interpolation, err, s,
1869 "interpolation", true)) {
1870 if (err) {
1871 (*err) += "`interpolation` field is missing in animation.sampler\n";
1872 }
1873 return false;
1874 }
1875 if (!ParseStringProperty(&sampler.output, err, s, "output", true)) {
1876 if (err) {
1877 (*err) += "`output` field is missing in animation.sampler\n";
1878 }
1879 return false;
1880 }
1881
1882 animation->samplers[it->first] = sampler;
1883 }
1884 }
1885 }
1886
1887 picojson::object::const_iterator parametersIt = o.find("parameters");
1888 if ((parametersIt != o.end()) &&
1889 (parametersIt->second).is<picojson::object>()) {
1890 const picojson::object &parameters_object =
1891 (parametersIt->second).get<picojson::object>();
1892
1893 picojson::object::const_iterator it(parameters_object.begin());
1894 picojson::object::const_iterator itEnd(parameters_object.end());
1895
1896 for (; it != itEnd; it++) {
1897 Parameter param;
1898 if (ParseParameterProperty(&param, err, parameters_object, it->first,
1899 false)) {
1900 animation->parameters[it->first] = param;
1901 }
1902 }
1903 }
1904 ParseStringProperty(&animation->name, err, o, "name", false);
1905
1906 return true;
1907}
1908
Syoyo Fujitac2615632016-06-19 21:56:06 +09001909static bool ParseSampler(Sampler *sampler, std::string *err,
1910 const picojson::object &o) {
1911 ParseStringProperty(&sampler->name, err, o, "name", false);
1912
1913 double minFilter =
1914 static_cast<double>(TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR);
1915 double magFilter = static_cast<double>(TINYGLTF_TEXTURE_FILTER_LINEAR);
1916 double wrapS = static_cast<double>(TINYGLTF_TEXTURE_WRAP_RPEAT);
1917 double wrapT = static_cast<double>(TINYGLTF_TEXTURE_WRAP_RPEAT);
1918 ParseNumberProperty(&minFilter, err, o, "minFilter", false);
1919 ParseNumberProperty(&magFilter, err, o, "magFilter", false);
1920 ParseNumberProperty(&wrapS, err, o, "wrapS", false);
1921 ParseNumberProperty(&wrapT, err, o, "wrapT", false);
1922
1923 sampler->minFilter = static_cast<int>(minFilter);
1924 sampler->magFilter = static_cast<int>(magFilter);
1925 sampler->wrapS = static_cast<int>(wrapS);
1926 sampler->wrapT = static_cast<int>(wrapT);
1927
1928 return true;
1929}
1930
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001931bool TinyGLTFLoader::LoadFromString(Scene *scene, std::string *err,
Syoyo Fujitabde70212016-02-07 17:38:17 +09001932 const char *str, unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001933 const std::string &base_dir,
1934 unsigned int check_sections) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001935 picojson::value v;
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001936 std::string perr = picojson::parse(v, str, str + length);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001937
1938 if (!perr.empty()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001939 if (err) {
1940 (*err) = perr;
1941 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001942 return false;
1943 }
1944
1945 if (v.contains("scene") && v.get("scene").is<std::string>()) {
1946 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04001947 } else if (check_sections & REQUIRE_SCENE) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001948 if (err) {
1949 (*err) += "\"scene\" object not found in .gltf\n";
1950 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001951 return false;
1952 }
1953
1954 if (v.contains("scenes") && v.get("scenes").is<picojson::object>()) {
1955 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04001956 } else if (check_sections & REQUIRE_SCENES) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001957 if (err) {
1958 (*err) += "\"scenes\" object not found in .gltf\n";
1959 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001960 return false;
1961 }
1962
1963 if (v.contains("nodes") && v.get("nodes").is<picojson::object>()) {
1964 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04001965 } else if (check_sections & REQUIRE_NODES) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001966 if (err) {
1967 (*err) += "\"nodes\" object not found in .gltf\n";
1968 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001969 return false;
1970 }
1971
1972 if (v.contains("accessors") && v.get("accessors").is<picojson::object>()) {
1973 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04001974 } else if (check_sections & REQUIRE_ACCESSORS) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001975 if (err) {
1976 (*err) += "\"accessors\" object not found in .gltf\n";
1977 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001978 return false;
1979 }
1980
1981 if (v.contains("buffers") && v.get("buffers").is<picojson::object>()) {
1982 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04001983 } else if (check_sections & REQUIRE_BUFFERS) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001984 if (err) {
1985 (*err) += "\"buffers\" object not found in .gltf\n";
1986 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001987 return false;
1988 }
1989
1990 if (v.contains("bufferViews") &&
1991 v.get("bufferViews").is<picojson::object>()) {
1992 // OK
Luke San Antonio9e36b612016-06-23 14:10:51 -04001993 } else if (check_sections & REQUIRE_BUFFER_VIEWS) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001994 if (err) {
1995 (*err) += "\"bufferViews\" object not found in .gltf\n";
1996 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001997 return false;
1998 }
1999
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002000 scene->buffers.clear();
2001 scene->bufferViews.clear();
2002 scene->accessors.clear();
2003 scene->meshes.clear();
2004 scene->nodes.clear();
2005 scene->defaultScene = "";
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002006
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002007 // 0. Parse Asset
2008 if (v.contains("asset") && v.get("asset").is<picojson::object>()) {
2009 const picojson::object &root = v.get("asset").get<picojson::object>();
2010
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002011 ParseAsset(&scene->asset, err, root);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002012 }
2013
2014 // 1. Parse Buffer
2015 if (v.contains("buffers") && v.get("buffers").is<picojson::object>()) {
2016 const picojson::object &root = v.get("buffers").get<picojson::object>();
2017
2018 picojson::object::const_iterator it(root.begin());
2019 picojson::object::const_iterator itEnd(root.end());
2020 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002021 Buffer buffer;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002022 if (!ParseBuffer(&buffer, err, (it->second).get<picojson::object>(),
Syoyo Fujitabeded612016-05-01 20:03:43 +09002023 base_dir, is_binary_, bin_data_, bin_size_)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002024 return false;
2025 }
2026
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002027 scene->buffers[it->first] = buffer;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002028 }
2029 }
2030
2031 // 2. Parse BufferView
2032 if (v.contains("bufferViews") &&
2033 v.get("bufferViews").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002034 const picojson::object &root = v.get("bufferViews").get<picojson::object>();
2035
2036 picojson::object::const_iterator it(root.begin());
2037 picojson::object::const_iterator itEnd(root.end());
2038 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002039 BufferView bufferView;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002040 if (!ParseBufferView(&bufferView, err,
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002041 (it->second).get<picojson::object>())) {
2042 return false;
2043 }
2044
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002045 scene->bufferViews[it->first] = bufferView;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002046 }
2047 }
2048
2049 // 3. Parse Accessor
2050 if (v.contains("accessors") && v.get("accessors").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002051 const picojson::object &root = v.get("accessors").get<picojson::object>();
2052
2053 picojson::object::const_iterator it(root.begin());
2054 picojson::object::const_iterator itEnd(root.end());
2055 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002056 Accessor accessor;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002057 if (!ParseAccessor(&accessor, err,
2058 (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002059 return false;
2060 }
2061
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002062 scene->accessors[it->first] = accessor;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002063 }
2064 }
2065
2066 // 4. Parse Mesh
2067 if (v.contains("meshes") && v.get("meshes").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002068 const picojson::object &root = v.get("meshes").get<picojson::object>();
2069
2070 picojson::object::const_iterator it(root.begin());
2071 picojson::object::const_iterator itEnd(root.end());
2072 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002073 Mesh mesh;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002074 if (!ParseMesh(&mesh, err, (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002075 return false;
2076 }
2077
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002078 scene->meshes[it->first] = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002079 }
2080 }
2081
2082 // 5. Parse Node
2083 if (v.contains("nodes") && v.get("nodes").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002084 const picojson::object &root = v.get("nodes").get<picojson::object>();
2085
2086 picojson::object::const_iterator it(root.begin());
2087 picojson::object::const_iterator itEnd(root.end());
2088 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002089 Node node;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002090 if (!ParseNode(&node, err, (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002091 return false;
2092 }
2093
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002094 scene->nodes[it->first] = node;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002095 }
2096 }
2097
2098 // 6. Parse scenes.
2099 if (v.contains("scenes") && v.get("scenes").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002100 const picojson::object &root = v.get("scenes").get<picojson::object>();
2101
2102 picojson::object::const_iterator it(root.begin());
2103 picojson::object::const_iterator itEnd(root.end());
2104 for (; it != itEnd; it++) {
Syoyo Fujitae04b1b52016-06-05 22:31:20 +09002105 if (!((it->second).is<picojson::object>())) {
2106 if (err) {
2107 (*err) += "`scenes' does not contain an object.";
2108 }
2109 return false;
2110 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002111 const picojson::object &o = (it->second).get<picojson::object>();
2112 std::vector<std::string> nodes;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002113 if (!ParseStringArrayProperty(&nodes, err, o, "nodes", false)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002114 return false;
2115 }
2116
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002117 scene->scenes[it->first] = nodes;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002118 }
2119 }
2120
2121 // 7. Parse default scenes.
2122 if (v.contains("scene") && v.get("scene").is<std::string>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002123 const std::string defaultScene = v.get("scene").get<std::string>();
2124
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002125 scene->defaultScene = defaultScene;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002126 }
2127
2128 // 8. Parse Material
2129 if (v.contains("materials") && v.get("materials").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002130 const picojson::object &root = v.get("materials").get<picojson::object>();
2131
2132 picojson::object::const_iterator it(root.begin());
2133 picojson::object::const_iterator itEnd(root.end());
2134 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002135 Material material;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002136 if (!ParseMaterial(&material, err,
2137 (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002138 return false;
2139 }
2140
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002141 scene->materials[it->first] = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002142 }
2143 }
2144
2145 // 9. Parse Image
2146 if (v.contains("images") && v.get("images").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002147 const picojson::object &root = v.get("images").get<picojson::object>();
2148
2149 picojson::object::const_iterator it(root.begin());
2150 picojson::object::const_iterator itEnd(root.end());
2151 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002152 Image image;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002153 if (!ParseImage(&image, err, (it->second).get<picojson::object>(),
Syoyo Fujitabeded612016-05-01 20:03:43 +09002154 base_dir, is_binary_, bin_data_, bin_size_)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002155 return false;
2156 }
2157
Syoyo Fujitabeded612016-05-01 20:03:43 +09002158 if (!image.bufferView.empty()) {
2159 // Load image from the buffer view.
2160 if (scene->bufferViews.find(image.bufferView) ==
2161 scene->bufferViews.end()) {
2162 if (err) {
2163 std::stringstream ss;
2164 ss << "bufferView \"" << image.bufferView
2165 << "\" not found in the scene." << std::endl;
2166 (*err) += ss.str();
2167 }
2168 return false;
2169 }
2170
2171 const BufferView &bufferView = scene->bufferViews[image.bufferView];
2172 const Buffer &buffer = scene->buffers[bufferView.buffer];
2173
2174 bool ret = LoadImageData(&image, err, image.width, image.height,
2175 &buffer.data[bufferView.byteOffset],
2176 static_cast<int>(bufferView.byteLength));
2177 if (!ret) {
2178 return false;
2179 }
2180 }
2181
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002182 scene->images[it->first] = image;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002183 }
2184 }
2185
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002186 // 10. Parse Texture
Syoyo Fujitabde70212016-02-07 17:38:17 +09002187 if (v.contains("textures") && v.get("textures").is<picojson::object>()) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09002188 const picojson::object &root = v.get("textures").get<picojson::object>();
2189
2190 picojson::object::const_iterator it(root.begin());
2191 picojson::object::const_iterator itEnd(root.end());
2192 for (; it != itEnd; it++) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09002193 Texture texture;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002194 if (!ParseTexture(&texture, err, (it->second).get<picojson::object>(),
Syoyo Fujitabeded612016-05-01 20:03:43 +09002195 base_dir)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09002196 return false;
2197 }
2198
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002199 scene->textures[it->first] = texture;
Syoyo Fujitabde70212016-02-07 17:38:17 +09002200 }
2201 }
2202
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002203 // 11. Parse Shader
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002204 if (v.contains("shaders") && v.get("shaders").is<picojson::object>()) {
2205 const picojson::object &root = v.get("shaders").get<picojson::object>();
2206
2207 picojson::object::const_iterator it(root.begin());
2208 picojson::object::const_iterator itEnd(root.end());
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002209 for (; it != itEnd; ++it) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002210 Shader shader;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002211 if (!ParseShader(&shader, err, (it->second).get<picojson::object>(),
2212 base_dir, is_binary_, bin_data_, bin_size_)) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002213 return false;
2214 }
2215
2216 scene->shaders[it->first] = shader;
2217 }
2218 }
2219
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002220 // 12. Parse Program
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002221 if (v.contains("programs") && v.get("programs").is<picojson::object>()) {
2222 const picojson::object &root = v.get("programs").get<picojson::object>();
2223
2224 picojson::object::const_iterator it(root.begin());
2225 picojson::object::const_iterator itEnd(root.end());
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002226 for (; it != itEnd; ++it) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002227 Program program;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002228 if (!ParseProgram(&program, err, (it->second).get<picojson::object>())) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002229 return false;
2230 }
2231
2232 scene->programs[it->first] = program;
2233 }
2234 }
2235
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002236 // 13. Parse Technique
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002237 if (v.contains("techniques") && v.get("techniques").is<picojson::object>()) {
2238 const picojson::object &root = v.get("techniques").get<picojson::object>();
2239
2240 picojson::object::const_iterator it(root.begin());
2241 picojson::object::const_iterator itEnd(root.end());
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002242 for (; it != itEnd; ++it) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002243 Technique technique;
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09002244 if (!ParseTechnique(&technique, err,
2245 (it->second).get<picojson::object>())) {
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04002246 return false;
2247 }
2248
2249 scene->techniques[it->first] = technique;
2250 }
2251 }
2252
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09002253 // 14. Parse Animation
2254 if (v.contains("animations") && v.get("animations").is<picojson::object>()) {
2255 const picojson::object &root = v.get("animations").get<picojson::object>();
2256
2257 picojson::object::const_iterator it(root.begin());
2258 picojson::object::const_iterator itEnd(root.end());
2259 for (; it != itEnd; ++it) {
2260 Animation animation;
2261 if (!ParseAnimation(&animation, err,
2262 (it->second).get<picojson::object>())) {
2263 return false;
2264 }
2265
2266 scene->animations[it->first] = animation;
2267 }
2268 }
2269
Syoyo Fujitac2615632016-06-19 21:56:06 +09002270 // 15. Parse Sampler
2271 if (v.contains("samplers") && v.get("samplers").is<picojson::object>()) {
2272 const picojson::object &root = v.get("samplers").get<picojson::object>();
2273
2274 picojson::object::const_iterator it(root.begin());
2275 picojson::object::const_iterator itEnd(root.end());
2276 for (; it != itEnd; ++it) {
2277 Sampler sampler;
2278 if (!ParseSampler(&sampler, err, (it->second).get<picojson::object>())) {
2279 return false;
2280 }
2281
2282 scene->samplers[it->first] = sampler;
2283 }
2284 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002285 return true;
2286}
2287
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002288bool TinyGLTFLoader::LoadASCIIFromString(Scene *scene, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09002289 const char *str, unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04002290 const std::string &base_dir,
2291 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002292 is_binary_ = false;
2293 bin_data_ = NULL;
2294 bin_size_ = 0;
2295
Luke San Antonio9e36b612016-06-23 14:10:51 -04002296 return LoadFromString(scene, err, str, length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002297}
2298
2299bool TinyGLTFLoader::LoadASCIIFromFile(Scene *scene, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -04002300 const std::string &filename,
2301 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002302 std::stringstream ss;
2303
2304 std::ifstream f(filename.c_str());
2305 if (!f) {
2306 ss << "Failed to open file: " << filename << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002307 if (err) {
2308 (*err) = ss.str();
2309 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002310 return false;
2311 }
2312
2313 f.seekg(0, f.end);
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +09002314 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002315 std::vector<char> buf(sz);
2316
Luke San Antoniob310b4a2016-06-23 14:17:37 -04002317 if (sz == 0) {
2318 if (err) {
2319 (*err) = "Empty file.";
2320 }
2321 return false;
2322 }
2323
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002324 f.seekg(0, f.beg);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002325 f.read(&buf.at(0), static_cast<std::streamsize>(sz));
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002326 f.close();
2327
2328 std::string basedir = GetBaseDir(filename);
2329
Luke San Antonio9e36b612016-06-23 14:10:51 -04002330 bool ret = LoadASCIIFromString(scene, err, &buf.at(0),
2331 static_cast<unsigned int>(buf.size()), basedir,
2332 check_sections);
2333
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002334 return ret;
2335}
2336
2337bool TinyGLTFLoader::LoadBinaryFromMemory(Scene *scene, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09002338 const unsigned char *bytes,
2339 unsigned int size,
Luke San Antonio9e36b612016-06-23 14:10:51 -04002340 const std::string &base_dir,
2341 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002342 if (size < 20) {
2343 if (err) {
2344 (*err) = "Too short data size for glTF Binary.";
2345 }
2346 return false;
2347 }
2348
Syoyo Fujitabeded612016-05-01 20:03:43 +09002349 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
2350 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002351 // ok
2352 } else {
2353 if (err) {
2354 (*err) = "Invalid magic.";
2355 }
2356 return false;
2357 }
2358
Syoyo Fujitabeded612016-05-01 20:03:43 +09002359 unsigned int version; // 4 bytes
2360 unsigned int length; // 4 bytes
2361 unsigned int scene_length; // 4 bytes
2362 unsigned int scene_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002363
2364 // @todo { Endian swap for big endian machine. }
Syoyo Fujitabeded612016-05-01 20:03:43 +09002365 memcpy(&version, bytes + 4, 4);
2366 swap4(&version);
2367 memcpy(&length, bytes + 8, 4);
2368 swap4(&length);
2369 memcpy(&scene_length, bytes + 12, 4);
2370 swap4(&scene_length);
2371 memcpy(&scene_format, bytes + 16, 4);
2372 swap4(&scene_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002373
Syoyo Fujitabeded612016-05-01 20:03:43 +09002374 if ((20 + scene_length >= size) || (scene_length < 1) ||
2375 (scene_format != 0)) { // 0 = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002376 if (err) {
2377 (*err) = "Invalid glTF binary.";
2378 }
2379 return false;
2380 }
2381
2382 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09002383 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
2384 scene_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002385
2386 is_binary_ = true;
2387 bin_data_ = bytes + 20 + scene_length;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002388 bin_size_ =
2389 length - (20 + scene_length); // extract header + JSON scene data.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002390
Syoyo Fujitabeded612016-05-01 20:03:43 +09002391 bool ret =
2392 LoadFromString(scene, err, reinterpret_cast<const char *>(&bytes[20]),
Luke San Antonio9e36b612016-06-23 14:10:51 -04002393 scene_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002394 if (!ret) {
2395 return ret;
2396 }
2397
2398 return true;
2399}
2400
2401bool TinyGLTFLoader::LoadBinaryFromFile(Scene *scene, std::string *err,
Luke San Antonio9e36b612016-06-23 14:10:51 -04002402 const std::string &filename,
2403 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002404 std::stringstream ss;
2405
2406 std::ifstream f(filename.c_str());
2407 if (!f) {
2408 ss << "Failed to open file: " << filename << std::endl;
2409 if (err) {
2410 (*err) = ss.str();
2411 }
2412 return false;
2413 }
2414
2415 f.seekg(0, f.end);
2416 size_t sz = static_cast<size_t>(f.tellg());
2417 std::vector<char> buf(sz);
2418
2419 f.seekg(0, f.beg);
2420 f.read(&buf.at(0), static_cast<std::streamsize>(sz));
2421 f.close();
2422
Syoyo Fujitabeded612016-05-01 20:03:43 +09002423 std::string basedir = GetBaseDir(filename);
2424
2425 bool ret = LoadBinaryFromMemory(
2426 scene, err, reinterpret_cast<unsigned char *>(&buf.at(0)),
Luke San Antonio9e36b612016-06-23 14:10:51 -04002427 static_cast<unsigned int>(buf.size()), basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09002428
2429 return ret;
2430}
2431
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09002432} // namespace tinygltf
2433
Syoyo Fujita7c877972016-03-08 01:31:49 +09002434#endif // TINYGLTF_LOADER_IMPLEMENTATION
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002435
Syoyo Fujita7c877972016-03-08 01:31:49 +09002436#endif // TINY_GLTF_LOADER_H_