blob: 90babeb5e0f5333dac5141a1ba3ee40bfe0cfcfd [file] [log] [blame]
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001//
2// Tiny glTF loader.
3//
4// Copyright (c) 2015, Syoyo Fujita.
5// 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 Fujitabde70212016-02-07 17:38:17 +090034// - v0.9.2 Support parsing `texture`
35// - v0.9.1 Support loading glTF asset from memory
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090036// - v0.9.0 Initial
37//
38// Tiny glTF loader is using following libraries:
39//
40// - picojson: C++ JSON library.
41// - base64: base64 decode/encode library.
42// - stb_image: Image loading library.
43//
Syoyo Fujita7c877972016-03-08 01:31:49 +090044#ifndef TINY_GLTF_LOADER_H_
45#define TINY_GLTF_LOADER_H_
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090046
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090047#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090048#include <string>
49#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090050
51namespace tinygltf {
52
53#define TINYGLTF_MODE_POINTS (0)
54#define TINYGLTF_MODE_LINE (1)
55#define TINYGLTF_MODE_LINE_LOOP (2)
56#define TINYGLTF_MODE_TRIANGLES (4)
57#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
58#define TINYGLTF_MODE_TRIANGLE_FAN (6)
59
60#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
61#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
62#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
63#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
64#define TINYGLTF_COMPONENT_TYPE_INT (5124)
65#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
66#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
67#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5127)
68
69#define TINYGLTF_TYPE_VEC2 (2)
70#define TINYGLTF_TYPE_VEC3 (3)
71#define TINYGLTF_TYPE_VEC4 (4)
72#define TINYGLTF_TYPE_MAT2 (32 + 2)
73#define TINYGLTF_TYPE_MAT3 (32 + 3)
74#define TINYGLTF_TYPE_MAT4 (32 + 4)
75#define TINYGLTF_TYPE_SCALAR (64 + 1)
76#define TINYGLTF_TYPE_VECTOR (64 + 4)
77#define TINYGLTF_TYPE_MATRIX (64 + 16)
78
79#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
80#define TINYGLTF_IMAGE_FORMAT_PNG (1)
81#define TINYGLTF_IMAGE_FORMAT_BMP (2)
82#define TINYGLTF_IMAGE_FORMAT_GIF (3)
83
Syoyo Fujitabde70212016-02-07 17:38:17 +090084#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
85#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
86#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
87
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090088#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
89#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
90
Syoyo Fujitabde70212016-02-07 17:38:17 +090091typedef struct {
Syoyo Fujitaec39a522016-05-01 18:33:31 +090092 std::string string_value;
93 std::vector<double> number_array;
Syoyo Fujitabde70212016-02-07 17:38:17 +090094} Parameter;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090095
Syoyo Fujitabde70212016-02-07 17:38:17 +090096typedef std::map<std::string, Parameter> ParameterMap;
97
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090098typedef struct {
99 std::string name;
100 int width;
101 int height;
102 int component;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900103 int pad0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900104 std::vector<unsigned char> image;
Syoyo Fujitabeded612016-05-01 20:03:43 +0900105
106 std::string bufferView; // KHR_binary_glTF extenstion.
107 std::string mimeType; // KHR_binary_glTF extenstion.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900108} Image;
109
110typedef struct {
Syoyo Fujitabde70212016-02-07 17:38:17 +0900111 int format;
112 int internalFormat;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900113 std::string sampler; // Required
114 std::string source; // Required
Syoyo Fujitabde70212016-02-07 17:38:17 +0900115 int target;
116 int type;
117 std::string name;
118} Texture;
119
120typedef struct {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900121 std::string name;
122 std::string technique;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900123 ParameterMap values;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900124} Material;
125
126typedef struct {
127 std::string name;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900128 std::string buffer; // Required
129 size_t byteOffset; // Required
130 size_t byteLength; // default: 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900131 int target;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900132 int pad0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900133} BufferView;
134
135typedef struct {
136 std::string bufferView;
137 std::string name;
138 size_t byteOffset;
139 size_t byteStride;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900140 int componentType; // One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900141 int pad0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900142 size_t count;
Syoyo Fujitabeded612016-05-01 20:03:43 +0900143 int type; // One of TINYGLTF_TYPE_***
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900144 int pad1;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900145 std::vector<double> minValues; // Optional
146 std::vector<double> maxValues; // Optional
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900147} Accessor;
148
149class Camera {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900150 public:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900151 Camera() {}
152 ~Camera() {}
153
154 std::string name;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900155 bool isOrthographic; // false = perspective.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900156
157 // Some common properties.
158 float aspectRatio;
159 float yFov;
160 float zFar;
161 float zNear;
162};
163
164typedef struct {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900165 std::map<std::string, std::string> attributes; // A dictionary object of
166 // strings, where each string
167 // is the ID of the accessor
168 // containing an attribute.
169 std::string material; // The ID of the material to apply to this primitive
170 // when rendering.
171 std::string indices; // The ID of the accessor that contains the indices.
172 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900173 int pad0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900174} Primitive;
175
176typedef struct {
177 std::string name;
178 std::vector<Primitive> primitives;
179} Mesh;
180
181class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900182 public:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900183 Node() {}
184 ~Node() {}
185
Syoyo Fujita7c877972016-03-08 01:31:49 +0900186 std::string camera; // camera object referenced by this node.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900187
188 std::string name;
189 std::vector<std::string> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900190 std::vector<double> rotation; // length must be 0 or 4
191 std::vector<double> scale; // length must be 0 or 3
192 std::vector<double> translation; // length must be 0 or 3
193 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900194 std::vector<std::string> meshes;
195};
196
197typedef struct {
198 std::string name;
199 std::vector<unsigned char> data;
200} Buffer;
201
202typedef struct {
203 std::string generator;
204 std::string version;
205 std::string profile_api;
206 std::string profile_version;
207 bool premultipliedAlpha;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900208 char pad[7];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900209} Asset;
210
211class Scene {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900212 public:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900213 Scene() {}
214 ~Scene() {}
215
216 std::map<std::string, Accessor> accessors;
217 std::map<std::string, Buffer> buffers;
218 std::map<std::string, BufferView> bufferViews;
219 std::map<std::string, Material> materials;
220 std::map<std::string, Mesh> meshes;
221 std::map<std::string, Node> nodes;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900222 std::map<std::string, Texture> textures;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900223 std::map<std::string, Image> images;
Syoyo Fujita7c877972016-03-08 01:31:49 +0900224 std::map<std::string, std::vector<std::string> > scenes; // list of nodes
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900225
226 std::string defaultScene;
227
228 Asset asset;
229};
230
231class TinyGLTFLoader {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900232 public:
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900233 TinyGLTFLoader() : bin_data_(NULL), bin_size_(0), is_binary_(false) {
234 pad[0] = pad[1] = pad[2] = pad[3] = pad[4] = pad[5] = pad[6] = 0;
235 }
Syoyo Fujita7c877972016-03-08 01:31:49 +0900236 ~TinyGLTFLoader() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900237
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900238 /// Loads glTF ASCII asset from a file.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900239 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900240 bool LoadASCIIFromFile(Scene *scene, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900241 const std::string &filename);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900242
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900243 /// Loads glTF ASCII asset from string(memory).
244 /// `length` = strlen(str);
245 /// Returns false and set error string to `err` if there's an error.
246 bool LoadASCIIFromString(Scene *scene, std::string *err, const char *str,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900247 const unsigned int length,
248 const std::string &base_dir);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900249
250 /// Loads glTF binary asset from a file.
251 /// Returns false and set error string to `err` if there's an error.
252 bool LoadBinaryFromFile(Scene *scene, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900253 const std::string &filename);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900254
255 /// Loads glTF binary asset from memory.
256 /// `length` = strlen(str);
257 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujitabeded612016-05-01 20:03:43 +0900258 bool LoadBinaryFromMemory(Scene *scene, std::string *err,
259 const unsigned char *bytes,
260 const unsigned int length,
261 const std::string &base_dir = "");
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900262
Syoyo Fujitabeded612016-05-01 20:03:43 +0900263 private:
Syoyo Fujita61fb52b2016-02-06 17:26:44 +0900264 /// Loads glTF asset from string(memory).
265 /// `length` = strlen(str);
266 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900267 bool LoadFromString(Scene *scene, std::string *err, const char *str,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900268 const unsigned int length, const std::string &base_dir);
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900269
Syoyo Fujitabeded612016-05-01 20:03:43 +0900270 const unsigned char *bin_data_;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900271 size_t bin_size_;
272 bool is_binary_;
273 char pad[7];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900274};
275
Syoyo Fujita7c877972016-03-08 01:31:49 +0900276} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900277
278#ifdef TINYGLTF_LOADER_IMPLEMENTATION
Syoyo Fujita7c877972016-03-08 01:31:49 +0900279#include <algorithm>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900280#include <cassert>
281#include <fstream>
282#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +0900283
Syoyo Fujita643ce102016-05-01 17:19:37 +0900284// Disable some warnings for external files.
285#pragma clang diagnostic push
286#pragma clang diagnostic ignored "-Wfloat-equal"
287#pragma clang diagnostic ignored "-Wexit-time-destructors"
288#pragma clang diagnostic ignored "-Wconversion"
289#pragma clang diagnostic ignored "-Wold-style-cast"
290#pragma clang diagnostic ignored "-Wdouble-promotion"
291#pragma clang diagnostic ignored "-Wglobal-constructors"
292#pragma clang diagnostic ignored "-Wreserved-id-macro"
293#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
294#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900295
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +0900296#include "./picojson.h"
297#include "./stb_image.h"
Syoyo Fujita643ce102016-05-01 17:19:37 +0900298#pragma clang diagnostic pop
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900299
300#ifdef _WIN32
301#include <Windows.h>
302#else
303#include <wordexp.h>
304#endif
305
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900306#if defined(__sparcv9)
307// Big endian
308#else
309#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
310#define TINYGLTF_LITTLE_ENDIAN 1
311#endif
312#endif
313
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +0900314namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900315
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900316static void swap4(unsigned int *val) {
317#ifdef TINYGLTF_LITTLE_ENDIAN
318 (void)val;
319#else
320 unsigned int tmp = *val;
321 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
322 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
323
324 dst[0] = src[3];
325 dst[1] = src[2];
326 dst[2] = src[1];
327 dst[3] = src[0];
328#endif
329}
330
Syoyo Fujita643ce102016-05-01 17:19:37 +0900331static bool FileExists(const std::string &abs_filename) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900332 bool ret;
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900333#ifdef _WIN32
334 FILE *fp;
335 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
336 if (err != 0) {
Syoyo Fujitabeded612016-05-01 20:03:43 +0900337 return false;
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900338 }
339#else
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900340 FILE *fp = fopen(abs_filename.c_str(), "rb");
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900341#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900342 if (fp) {
343 ret = true;
344 fclose(fp);
345 } else {
346 ret = false;
347 }
348
349 return ret;
350}
351
Syoyo Fujita643ce102016-05-01 17:19:37 +0900352static std::string ExpandFilePath(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900353#ifdef _WIN32
354 DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
355 char *str = new char[len];
356 ExpandEnvironmentStringsA(filepath.c_str(), str, len);
357
358 std::string s(str);
359
360 delete[] str;
361
362 return s;
363#else
364
Syoyo Fujita643ce102016-05-01 17:19:37 +0900365#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900366 // no expansion
367 std::string s = filepath;
368#else
369 std::string s;
370 wordexp_t p;
371
372 if (filepath.empty()) {
373 return "";
374 }
375
376 // char** w;
377 int ret = wordexp(filepath.c_str(), &p, 0);
378 if (ret) {
379 // err
380 s = filepath;
381 return s;
382 }
383
384 // Use first element only.
385 if (p.we_wordv) {
386 s = std::string(p.we_wordv[0]);
387 wordfree(&p);
388 } else {
389 s = filepath;
390 }
391
392#endif
393
394 return s;
395#endif
396}
397
Syoyo Fujitabeded612016-05-01 20:03:43 +0900398static std::string JoinPath(const std::string &path0,
399 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900400 if (path0.empty()) {
401 return path1;
402 } else {
403 // check '/'
404 char lastChar = *path0.rbegin();
405 if (lastChar != '/') {
406 return path0 + std::string("/") + path1;
407 } else {
408 return path0 + path1;
409 }
410 }
411}
412
Syoyo Fujita643ce102016-05-01 17:19:37 +0900413static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900414 const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900415 for (size_t i = 0; i < paths.size(); i++) {
416 std::string absPath = ExpandFilePath(JoinPath(paths[i], filepath));
417 if (FileExists(absPath)) {
418 return absPath;
419 }
420 }
421
422 return std::string();
423}
424
425// std::string GetFilePathExtension(const std::string& FileName)
426//{
427// if(FileName.find_last_of(".") != std::string::npos)
428// return FileName.substr(FileName.find_last_of(".")+1);
429// return "";
430//}
431
Syoyo Fujita643ce102016-05-01 17:19:37 +0900432static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900433 if (filepath.find_last_of("/\\") != std::string::npos)
434 return filepath.substr(0, filepath.find_last_of("/\\"));
435 return "";
436}
437
438// std::string base64_encode(unsigned char const* , unsigned int len);
439std::string base64_decode(std::string const &s);
440
441/*
442 base64.cpp and base64.h
443
444 Copyright (C) 2004-2008 René Nyffenegger
445
446 This source code is provided 'as-is', without any express or implied
447 warranty. In no event will the author be held liable for any damages
448 arising from the use of this software.
449
450 Permission is granted to anyone to use this software for any purpose,
451 including commercial applications, and to alter it and redistribute it
452 freely, subject to the following restrictions:
453
454 1. The origin of this source code must not be misrepresented; you must not
455 claim that you wrote the original source code. If you use this source code
456 in a product, an acknowledgment in the product documentation would be
457 appreciated but is not required.
458
459 2. Altered source versions must be plainly marked as such, and must not be
460 misrepresented as being the original source code.
461
462 3. This notice may not be removed or altered from any source distribution.
463
464 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
465
466*/
467
Syoyo Fujita643ce102016-05-01 17:19:37 +0900468#pragma clang diagnostic push
469#pragma clang diagnostic ignored "-Wexit-time-destructors"
470#pragma clang diagnostic ignored "-Wglobal-constructors"
471#pragma clang diagnostic ignored "-Wsign-conversion"
472#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita7c877972016-03-08 01:31:49 +0900473static const std::string base64_chars =
474 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
475 "abcdefghijklmnopqrstuvwxyz"
476 "0123456789+/";
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900477
478static inline bool is_base64(unsigned char c) {
479 return (isalnum(c) || (c == '+') || (c == '/'));
480}
481
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900482std::string base64_decode(std::string const &encoded_string) {
483 int in_len = encoded_string.size();
484 int i = 0;
485 int j = 0;
486 int in_ = 0;
487 unsigned char char_array_4[4], char_array_3[3];
488 std::string ret;
489
490 while (in_len-- && (encoded_string[in_] != '=') &&
491 is_base64(encoded_string[in_])) {
492 char_array_4[i++] = encoded_string[in_];
493 in_++;
494 if (i == 4) {
495 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +0900496 char_array_4[i] =
497 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900498
499 char_array_3[0] =
500 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
501 char_array_3[1] =
502 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
503 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
504
Syoyo Fujita7c877972016-03-08 01:31:49 +0900505 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900506 i = 0;
507 }
508 }
509
510 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900511 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900512
513 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +0900514 char_array_4[j] =
515 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900516
517 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
518 char_array_3[1] =
519 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
520 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
521
Syoyo Fujita7c877972016-03-08 01:31:49 +0900522 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900523 }
524
525 return ret;
526}
Syoyo Fujita643ce102016-05-01 17:19:37 +0900527#pragma clang diagnostic pop
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900528
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900529static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900530 const std::string &filename,
531 const std::string &basedir, size_t reqBytes,
532 bool checkSize) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900533 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900534
535 std::vector<std::string> paths;
536 paths.push_back(basedir);
537 paths.push_back(".");
538
539 std::string filepath = FindFile(paths, filename);
540 if (filepath.empty()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900541 if (err) {
542 (*err) += "File not found : " + filename;
543 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900544 return false;
545 }
546
547 std::ifstream f(filepath.c_str(), std::ifstream::binary);
548 if (!f) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900549 if (err) {
550 (*err) += "File open error : " + filepath;
551 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900552 return false;
553 }
554
555 f.seekg(0, f.end);
Syoyo Fujita643ce102016-05-01 17:19:37 +0900556 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900557 std::vector<unsigned char> buf(sz);
558
559 f.seekg(0, f.beg);
Syoyo Fujitabeded612016-05-01 20:03:43 +0900560 f.read(reinterpret_cast<char *>(&buf.at(0)),
561 static_cast<std::streamsize>(sz));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900562 f.close();
563
564 if (checkSize) {
565 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900566 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900567 return true;
568 } else {
569 std::stringstream ss;
570 ss << "File size mismatch : " << filepath << ", requestedBytes "
571 << reqBytes << ", but got " << sz << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900572 if (err) {
573 (*err) += ss.str();
574 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900575 return false;
576 }
577 }
578
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900579 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900580 return true;
581}
582
Syoyo Fujitabeded612016-05-01 20:03:43 +0900583static bool LoadImageData(Image *image, std::string *err, int req_width,
584 int req_height, const unsigned char *bytes,
585 int size) {
586 int w, h, comp;
587 unsigned char *data = stbi_load_from_memory(bytes, size, &w, &h, &comp, 0);
588 if (!data) {
589 if (err) {
590 (*err) += "Unknown image format.\n";
591 }
592 return false;
593 }
594
595 if (w < 1 || h < 1) {
596 if (err) {
597 (*err) += "Unknown image format.\n";
598 }
599 return false;
600 }
601
602 if (req_width > 0) {
603 if (req_width != w) {
604 if (err) {
605 (*err) += "Image width mismatch.\n";
606 }
607 return false;
608 }
609 }
610
611 if (req_height > 0) {
612 if (req_height != h) {
613 if (err) {
614 (*err) += "Image height mismatch.\n";
615 }
616 return false;
617 }
618 }
619
620 image->width = w;
621 image->height = h;
622 image->component = comp;
623 image->image.resize(static_cast<size_t>(w * h * comp));
624 std::copy(data, data + w * h * comp, image->image.begin());
625
626 return true;
627}
628
Syoyo Fujita643ce102016-05-01 17:19:37 +0900629static bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900630 std::string header = "data:application/octet-stream;base64,";
631 if (in.find(header) == 0) {
632 return true;
633 }
Syoyo Fujita620eed12016-01-02 23:37:12 +0900634
635 header = "data:image/png;base64,";
636 if (in.find(header) == 0) {
637 return true;
638 }
639
640 header = "data:image/jpeg;base64,";
641 if (in.find(header) == 0) {
642 return true;
643 }
644
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900645 return false;
646}
647
Syoyo Fujitabeded612016-05-01 20:03:43 +0900648static bool DecodeDataURI(std::vector<unsigned char> *out,
649 const std::string &in, size_t reqBytes,
650 bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900651 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +0900652 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900653 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900654 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900655 }
Syoyo Fujita620eed12016-01-02 23:37:12 +0900656
657 if (data.empty()) {
658 header = "data:image/jpeg;base64,";
659 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900660 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +0900661 }
662 }
663
664 if (data.empty()) {
665 header = "data:image/png;base64,";
666 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900667 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +0900668 }
669 }
670
671 if (data.empty()) {
672 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900673 }
Syoyo Fujita620eed12016-01-02 23:37:12 +0900674
675 if (checkSize) {
676 if (data.size() != reqBytes) {
677 return false;
678 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900679 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +0900680 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900681 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +0900682 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900683 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +0900684 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900685}
686
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900687static bool ParseBooleanProperty(bool *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900688 const picojson::object &o,
689 const std::string &property, bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900690 picojson::object::const_iterator it = o.find(property);
691 if (it == o.end()) {
692 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900693 if (err) {
694 (*err) += "'" + property + "' property is missing.\n";
695 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900696 }
697 return false;
698 }
699
700 if (!it->second.is<bool>()) {
701 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900702 if (err) {
703 (*err) += "'" + property + "' property is not a bool type.\n";
704 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900705 }
706 return false;
707 }
708
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900709 if (ret) {
710 (*ret) = it->second.get<bool>();
711 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900712
713 return true;
714}
715
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900716static bool ParseNumberProperty(double *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900717 const picojson::object &o,
718 const std::string &property, bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900719 picojson::object::const_iterator it = o.find(property);
720 if (it == o.end()) {
721 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900722 if (err) {
723 (*err) += "'" + property + "' property is missing.\n";
724 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900725 }
726 return false;
727 }
728
729 if (!it->second.is<double>()) {
730 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900731 if (err) {
732 (*err) += "'" + property + "' property is not a number type.\n";
733 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900734 }
735 return false;
736 }
737
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900738 if (ret) {
739 (*ret) = it->second.get<double>();
740 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900741
742 return true;
743}
744
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900745static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900746 const picojson::object &o,
747 const std::string &property,
748 bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900749 picojson::object::const_iterator it = o.find(property);
750 if (it == o.end()) {
751 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900752 if (err) {
753 (*err) += "'" + property + "' property is missing.\n";
754 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900755 }
756 return false;
757 }
758
759 if (!it->second.is<picojson::array>()) {
760 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900761 if (err) {
762 (*err) += "'" + property + "' property is not an array.\n";
763 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900764 }
765 return false;
766 }
767
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900768 ret->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900769 const picojson::array &arr = it->second.get<picojson::array>();
770 for (size_t i = 0; i < arr.size(); i++) {
771 if (!arr[i].is<double>()) {
772 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900773 if (err) {
774 (*err) += "'" + property + "' property is not a number.\n";
775 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900776 }
777 return false;
778 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900779 ret->push_back(arr[i].get<double>());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900780 }
781
782 return true;
783}
784
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900785static bool ParseStringProperty(std::string *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900786 const picojson::object &o,
787 const std::string &property, bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900788 picojson::object::const_iterator it = o.find(property);
789 if (it == o.end()) {
790 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900791 if (err) {
792 (*err) += "'" + property + "' property is missing.\n";
793 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900794 }
795 return false;
796 }
797
798 if (!it->second.is<std::string>()) {
799 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900800 if (err) {
801 (*err) += "'" + property + "' property is not a string type.\n";
802 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900803 }
804 return false;
805 }
806
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900807 if (ret) {
808 (*ret) = it->second.get<std::string>();
809 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900810
811 return true;
812}
813
Syoyo Fujitabeded612016-05-01 20:03:43 +0900814static bool ParseStringArrayProperty(std::vector<std::string> *ret,
815 std::string *err,
816 const picojson::object &o,
817 const std::string &property,
818 bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900819 picojson::object::const_iterator it = o.find(property);
820 if (it == o.end()) {
821 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900822 if (err) {
823 (*err) += "'" + property + "' property is missing.\n";
824 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900825 }
826 return false;
827 }
828
829 if (!it->second.is<picojson::array>()) {
830 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900831 if (err) {
832 (*err) += "'" + property + "' property is not an array.\n";
833 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900834 }
835 return false;
836 }
837
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900838 ret->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900839 const picojson::array &arr = it->second.get<picojson::array>();
840 for (size_t i = 0; i < arr.size(); i++) {
841 if (!arr[i].is<std::string>()) {
842 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900843 if (err) {
844 (*err) += "'" + property + "' property is not a string.\n";
845 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900846 }
847 return false;
848 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900849 ret->push_back(arr[i].get<std::string>());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900850 }
851
852 return true;
853}
854
Syoyo Fujitabeded612016-05-01 20:03:43 +0900855static bool ParseKHRBinaryExtension(const picojson::object &o, std::string *err,
856 std::string *buffer_view,
857 std::string *mime_type, int *image_width,
858 int *image_height) {
859 picojson::object j = o;
860
861 if (j.find("extensions") == j.end()) {
862 if (err) {
863 (*err) += "`extensions' property is missing.\n";
864 }
865 return false;
866 }
867
868 if (!(j["extensions"].is<picojson::object>())) {
869 if (err) {
870 (*err) += "Invalid `extensions' property.\n";
871 }
872 return false;
873 }
874
875 picojson::object ext = j["extensions"].get<picojson::object>();
876
877 if (ext.find("KHR_binary_glTF") == ext.end()) {
878 if (err) {
879 (*err) +=
880 "`KHR_binary_glTF' property is missing in extension property.\n";
881 }
882 return false;
883 }
884
885 if (!(ext["KHR_binary_glTF"].is<picojson::object>())) {
886 if (err) {
887 (*err) += "Invalid `KHR_binary_glTF' property.\n";
888 }
889 return false;
890 }
891
892 picojson::object k = ext["KHR_binary_glTF"].get<picojson::object>();
893
894 if (!ParseStringProperty(buffer_view, err, k, "bufferView", true)) {
895 return false;
896 }
897
898 if (mime_type) {
899 ParseStringProperty(mime_type, err, k, "mimeType", false);
900 }
901
902 if (image_width) {
903 double width = 0.0;
904 if (ParseNumberProperty(&width, err, k, "width", false)) {
905 (*image_width) = static_cast<int>(width);
906 }
907 }
908
909 if (image_height) {
910 double height = 0.0;
911 if (ParseNumberProperty(&height, err, k, "height", false)) {
912 (*image_height) = static_cast<int>(height);
913 }
914 }
915
916 return true;
917}
918
919static bool ParseAsset(Asset *asset, std::string *err,
920 const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900921 ParseStringProperty(&asset->generator, err, o, "generator", false);
922 ParseBooleanProperty(&asset->premultipliedAlpha, err, o, "premultipliedAlpha",
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900923 false);
924
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900925 ParseStringProperty(&asset->version, err, o, "version", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900926
927 picojson::object::const_iterator profile = o.find("profile");
928 if (profile != o.end()) {
929 const picojson::value &v = profile->second;
930 if (v.contains("api") & v.get("api").is<std::string>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900931 asset->profile_api = v.get("api").get<std::string>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900932 }
933 if (v.contains("version") & v.get("version").is<std::string>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900934 asset->profile_version = v.get("version").get<std::string>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900935 }
936 }
937
938 return true;
939}
940
Syoyo Fujitabeded612016-05-01 20:03:43 +0900941static bool ParseImage(Image *image, std::string *err,
942 const picojson::object &o, const std::string &basedir,
943 bool is_binary, const unsigned char *bin_data,
944 size_t bin_size) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900945 std::string uri;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900946 if (!ParseStringProperty(&uri, err, o, "uri", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900947 return false;
948 }
949
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900950 ParseStringProperty(&image->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900951
952 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +0900953
954 if (is_binary) {
955 // Still binary glTF accepts external dataURI. First try external resources.
956 bool loaded = false;
957 if (IsDataURI(uri)) {
958 loaded = DecodeDataURI(&img, uri, 0, false);
959 } else {
960 // Assume external .bin file.
961 loaded = LoadExternalFile(&img, err, uri, basedir, 0, false);
962 }
963
964 if (!loaded) {
965 // load data from (embedded) binary data
966
967 if ((bin_size == 0) || (bin_data == NULL)) {
968 if (err) {
969 (*err) += "Invalid binary data.\n";
970 }
971 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900972 }
Syoyo Fujitabeded612016-05-01 20:03:43 +0900973
974 // There should be "extensions" property.
975 // "extensions":{"KHR_binary_glTF":{"bufferView": "id", ...
976
977 std::string buffer_view;
978 std::string mime_type;
979 int image_width;
980 int image_height;
981 bool ret = ParseKHRBinaryExtension(o, err, &buffer_view, &mime_type,
982 &image_width, &image_height);
983 if (!ret) {
984 return false;
985 }
986
987 if (uri.compare("data:,") == 0) {
988 // ok
989 } else {
990 if (err) {
991 (*err) += "Invalid URI for binary data.\n";
992 }
993 return false;
994 }
995
996 // Just only save some information here. Loading actual image data from
997 // bufferView is done in other place.
998 image->bufferView = buffer_view;
999 image->mimeType = mime_type;
1000 image->width = image_width;
1001 image->height = image_height;
1002
1003 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001004 }
1005 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001006 if (IsDataURI(uri)) {
1007 if (!DecodeDataURI(&img, uri, 0, false)) {
1008 if (err) {
1009 (*err) += "Failed to decode 'uri'.\n";
1010 }
1011 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001012 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001013 } else {
1014 // Assume external file
1015 if (!LoadExternalFile(&img, err, uri, basedir, 0, false)) {
1016 if (err) {
1017 (*err) += "Failed to load external 'uri'.\n";
1018 }
1019 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001020 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001021 if (img.empty()) {
1022 if (err) {
1023 (*err) += "File is empty.\n";
1024 }
1025 return false;
1026 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001027 }
1028 }
1029
Syoyo Fujitabeded612016-05-01 20:03:43 +09001030 return LoadImageData(image, err, 0, 0, &img.at(0),
1031 static_cast<int>(img.size()));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001032}
1033
Syoyo Fujitabeded612016-05-01 20:03:43 +09001034static bool ParseTexture(Texture *texture, std::string *err,
1035 const picojson::object &o,
1036 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001037 (void)basedir;
1038
1039 if (!ParseStringProperty(&texture->sampler, err, o, "sampler", true)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001040 return false;
1041 }
1042
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001043 if (!ParseStringProperty(&texture->source, err, o, "source", true)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001044 return false;
1045 }
1046
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001047 ParseStringProperty(&texture->name, err, o, "name", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001048
1049 double format = TINYGLTF_TEXTURE_FORMAT_RGBA;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001050 ParseNumberProperty(&format, err, o, "format", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001051
1052 double internalFormat = TINYGLTF_TEXTURE_FORMAT_RGBA;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001053 ParseNumberProperty(&internalFormat, err, o, "internalFormat", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001054
1055 double target = TINYGLTF_TEXTURE_TARGET_TEXTURE2D;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001056 ParseNumberProperty(&target, err, o, "target", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001057
1058 double type = TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001059 ParseNumberProperty(&type, err, o, "type", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001060
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001061 texture->format = static_cast<int>(format);
1062 texture->internalFormat = static_cast<int>(internalFormat);
1063 texture->target = static_cast<int>(target);
1064 texture->type = static_cast<int>(type);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001065
1066 return true;
1067}
1068
Syoyo Fujitabeded612016-05-01 20:03:43 +09001069static bool ParseBuffer(Buffer *buffer, std::string *err,
1070 const picojson::object &o, const std::string &basedir,
1071 bool is_binary = false,
1072 const unsigned char *bin_data = NULL,
1073 size_t bin_size = 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001074 double byteLength;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001075 if (!ParseNumberProperty(&byteLength, err, o, "byteLength", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001076 return false;
1077 }
1078
1079 std::string uri;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001080 if (!ParseStringProperty(&uri, err, o, "uri", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001081 return false;
1082 }
1083
1084 picojson::object::const_iterator type = o.find("type");
1085 if (type != o.end()) {
1086 if (type->second.is<std::string>()) {
1087 const std::string &ty = (type->second).get<std::string>();
1088 if (ty.compare("arraybuffer") == 0) {
1089 // buffer.type = "arraybuffer";
1090 }
1091 }
1092 }
1093
1094 size_t bytes = static_cast<size_t>(byteLength);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001095 if (is_binary) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001096 // Still binary glTF accepts external dataURI. First try external resources.
1097 bool loaded = false;
1098 if (IsDataURI(uri)) {
1099 loaded = DecodeDataURI(&buffer->data, uri, bytes, true);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001100 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001101 // Assume external .bin file.
1102 loaded = LoadExternalFile(&buffer->data, err, uri, basedir, bytes, true);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001103 }
1104
Syoyo Fujitabeded612016-05-01 20:03:43 +09001105 if (!loaded) {
1106 // load data from (embedded) binary data
1107
1108 if ((bin_size == 0) || (bin_data == NULL)) {
1109 if (err) {
1110 (*err) += "Invalid binary data.\n";
1111 }
1112 return false;
1113 }
1114
1115 if (byteLength > bin_size) {
1116 if (err) {
1117 std::stringstream ss;
1118 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
1119 "`byteLength' = " << byteLength
1120 << ", binary size = " << bin_size << std::endl;
1121 (*err) += ss.str();
1122 }
1123 return false;
1124 }
1125
1126 if (uri.compare("data:,") == 0) {
1127 // @todo { check uri }
1128 buffer->data.resize(static_cast<size_t>(byteLength));
1129 memcpy(&(buffer->data.at(0)), bin_data,
1130 static_cast<size_t>(byteLength));
1131
1132 } else {
1133 if (err) {
1134 (*err) += "Invalid URI for binary data.\n";
1135 }
1136 return false;
1137 }
1138 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001139
1140 } else {
1141 if (IsDataURI(uri)) {
1142 if (!DecodeDataURI(&buffer->data, uri, bytes, true)) {
1143 if (err) {
1144 (*err) += "Failed to decode 'uri'.\n";
1145 }
1146 return false;
1147 }
1148 } else {
1149 // Assume external .bin file.
1150 if (!LoadExternalFile(&buffer->data, err, uri, basedir, bytes, true)) {
1151 return false;
1152 }
1153 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001154 }
1155
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001156 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001157
1158 return true;
1159}
1160
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001161static bool ParseBufferView(BufferView *bufferView, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001162 const picojson::object &o) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001163 std::string buffer;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001164 if (!ParseStringProperty(&buffer, err, o, "buffer", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001165 return false;
1166 }
1167
1168 double byteOffset;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001169 if (!ParseNumberProperty(&byteOffset, err, o, "byteOffset", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001170 return false;
1171 }
1172
1173 double byteLength = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001174 ParseNumberProperty(&byteLength, err, o, "byteLength", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001175
1176 double target = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001177 ParseNumberProperty(&target, err, o, "target", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001178 int targetValue = static_cast<int>(target);
1179 if ((targetValue == TINYGLTF_TARGET_ARRAY_BUFFER) ||
1180 (targetValue == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
1181 // OK
1182 } else {
1183 targetValue = 0;
1184 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001185 bufferView->target = targetValue;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001186
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001187 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001188
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001189 bufferView->buffer = buffer;
1190 bufferView->byteOffset = static_cast<size_t>(byteOffset);
1191 bufferView->byteLength = static_cast<size_t>(byteLength);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001192
1193 return true;
1194}
1195
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001196static bool ParseAccessor(Accessor *accessor, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001197 const picojson::object &o) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001198 std::string bufferView;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001199 if (!ParseStringProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001200 return false;
1201 }
1202
1203 double byteOffset;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001204 if (!ParseNumberProperty(&byteOffset, err, o, "byteOffset", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001205 return false;
1206 }
1207
1208 double componentType;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001209 if (!ParseNumberProperty(&componentType, err, o, "componentType", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001210 return false;
1211 }
1212
1213 double count = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001214 if (!ParseNumberProperty(&count, err, o, "count", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001215 return false;
1216 }
1217
1218 std::string type;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001219 if (!ParseStringProperty(&type, err, o, "type", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001220 return false;
1221 }
1222
1223 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001224 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001225 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001226 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001227 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001228 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001229 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001230 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001231 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001232 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001233 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001234 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001235 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001236 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001237 } else {
1238 std::stringstream ss;
1239 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001240 if (err) {
1241 (*err) += ss.str();
1242 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001243 return false;
1244 }
1245
1246 double byteStride = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001247 ParseNumberProperty(&byteStride, err, o, "byteStride", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001248
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001249 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001250
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001251 accessor->minValues.clear();
1252 accessor->maxValues.clear();
1253 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false);
1254 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001255
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001256 accessor->count = static_cast<size_t>(count);
1257 accessor->bufferView = bufferView;
1258 accessor->byteOffset = static_cast<size_t>(byteOffset);
1259 accessor->byteStride = static_cast<size_t>(byteStride);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001260
1261 {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001262 int comp = static_cast<int>(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001263 if (comp >= TINYGLTF_COMPONENT_TYPE_BYTE &&
1264 comp <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
1265 // OK
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001266 accessor->componentType = comp;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001267 } else {
1268 std::stringstream ss;
1269 ss << "Invalid `componentType` in accessor. Got " << comp << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001270 if (err) {
1271 (*err) += ss.str();
1272 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001273 return false;
1274 }
1275 }
1276
1277 return true;
1278}
1279
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001280static bool ParsePrimitive(Primitive *primitive, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001281 const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001282 if (!ParseStringProperty(&primitive->material, err, o, "material", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001283 return false;
1284 }
1285
1286 double mode = static_cast<double>(TINYGLTF_MODE_TRIANGLES);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001287 ParseNumberProperty(&mode, err, o, "mode", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001288
1289 int primMode = static_cast<int>(mode);
1290 if (primMode != TINYGLTF_MODE_TRIANGLES) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001291 if (err) {
1292 (*err) +=
1293 "Currently TinyGLTFLoader doesn not support primitive mode other \n"
1294 "than TRIANGLES.\n";
1295 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001296 return false;
1297 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001298 primitive->mode = primMode;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001299
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001300 primitive->indices = "";
1301 ParseStringProperty(&primitive->indices, err, o, "indices", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001302
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001303 primitive->attributes.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001304 picojson::object::const_iterator attribsObject = o.find("attributes");
1305 if ((attribsObject != o.end()) &&
1306 (attribsObject->second).is<picojson::object>()) {
1307 const picojson::object &attribs =
1308 (attribsObject->second).get<picojson::object>();
1309 picojson::object::const_iterator it(attribs.begin());
1310 picojson::object::const_iterator itEnd(attribs.end());
1311 for (; it != itEnd; it++) {
1312 const std::string &name = it->first;
1313 if (!(it->second).is<std::string>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001314 if (err) {
1315 (*err) += "attribute expects string value.\n";
1316 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001317 return false;
1318 }
1319 const std::string &value = (it->second).get<std::string>();
1320
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001321 primitive->attributes[name] = value;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001322 }
1323 }
1324
1325 return true;
1326}
1327
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001328static bool ParseMesh(Mesh *mesh, std::string *err, const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001329 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001330
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001331 mesh->primitives.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001332 picojson::object::const_iterator primObject = o.find("primitives");
1333 if ((primObject != o.end()) && (primObject->second).is<picojson::array>()) {
1334 const picojson::array &primArray =
1335 (primObject->second).get<picojson::array>();
1336 for (size_t i = 0; i < primArray.size(); i++) {
1337 Primitive primitive;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001338 ParsePrimitive(&primitive, err, primArray[i].get<picojson::object>());
1339 mesh->primitives.push_back(primitive);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001340 }
1341 }
1342
1343 return true;
1344}
1345
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001346static bool ParseNode(Node *node, std::string *err, const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001347 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001348
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001349 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
1350 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
1351 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
1352 ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false);
1353 ParseStringArrayProperty(&node->meshes, err, o, "meshes", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001354
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001355 node->children.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001356 picojson::object::const_iterator childrenObject = o.find("children");
1357 if ((childrenObject != o.end()) &&
1358 (childrenObject->second).is<picojson::array>()) {
1359 const picojson::array &childrenArray =
1360 (childrenObject->second).get<picojson::array>();
1361 for (size_t i = 0; i < childrenArray.size(); i++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001362 if (!childrenArray[i].is<std::string>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001363 if (err) {
1364 (*err) += "Invalid `children` array.\n";
1365 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001366 return false;
1367 }
1368 const std::string &childrenNode = childrenArray[i].get<std::string>();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001369 node->children.push_back(childrenNode);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001370 }
1371 }
1372
1373 return true;
1374}
1375
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001376static bool ParseMaterial(Material *material, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001377 const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001378 ParseStringProperty(&material->name, err, o, "name", false);
1379 ParseStringProperty(&material->technique, err, o, "technique", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001380
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001381 material->values.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001382 picojson::object::const_iterator valuesIt = o.find("values");
1383 if ((valuesIt != o.end()) && (valuesIt->second).is<picojson::object>()) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001384 const picojson::object &values_object =
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001385 (valuesIt->second).get<picojson::object>();
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001386 picojson::object::const_iterator it(values_object.begin());
1387 picojson::object::const_iterator itEnd(values_object.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001388
1389 for (; it != itEnd; it++) {
1390 // Assume number values.
Syoyo Fujitabde70212016-02-07 17:38:17 +09001391 Parameter param;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001392 if (ParseStringProperty(&param.string_value, err, values_object,
1393 it->first, false)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001394 // Found string property.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001395 } else if (!ParseNumberArrayProperty(&param.number_array, err,
1396 values_object, it->first, false)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001397 // Fallback to numer property.
1398 double value;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001399 if (ParseNumberProperty(&value, err, values_object, it->first, false)) {
1400 param.number_array.push_back(value);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001401 }
1402 }
1403
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001404 material->values[it->first] = param;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001405 }
1406 }
1407
1408 return true;
1409}
Syoyo Fujita7c877972016-03-08 01:31:49 +09001410
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001411bool TinyGLTFLoader::LoadFromString(Scene *scene, std::string *err,
Syoyo Fujitabde70212016-02-07 17:38:17 +09001412 const char *str, unsigned int length,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001413 const std::string &base_dir) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001414 picojson::value v;
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001415 std::string perr = picojson::parse(v, str, str + length);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001416
1417 if (!perr.empty()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001418 if (err) {
1419 (*err) = perr;
1420 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001421 return false;
1422 }
1423
1424 if (v.contains("scene") && v.get("scene").is<std::string>()) {
1425 // OK
1426 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001427 if (err) {
1428 (*err) += "\"scene\" object not found in .gltf\n";
1429 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001430 return false;
1431 }
1432
1433 if (v.contains("scenes") && v.get("scenes").is<picojson::object>()) {
1434 // OK
1435 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001436 if (err) {
1437 (*err) += "\"scenes\" object not found in .gltf\n";
1438 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001439 return false;
1440 }
1441
1442 if (v.contains("nodes") && v.get("nodes").is<picojson::object>()) {
1443 // OK
1444 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001445 if (err) {
1446 (*err) += "\"nodes\" object not found in .gltf\n";
1447 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001448 return false;
1449 }
1450
1451 if (v.contains("accessors") && v.get("accessors").is<picojson::object>()) {
1452 // OK
1453 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001454 if (err) {
1455 (*err) += "\"accessors\" object not found in .gltf\n";
1456 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001457 return false;
1458 }
1459
1460 if (v.contains("buffers") && v.get("buffers").is<picojson::object>()) {
1461 // OK
1462 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001463 if (err) {
1464 (*err) += "\"buffers\" object not found in .gltf\n";
1465 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001466 return false;
1467 }
1468
1469 if (v.contains("bufferViews") &&
1470 v.get("bufferViews").is<picojson::object>()) {
1471 // OK
1472 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001473 if (err) {
1474 (*err) += "\"bufferViews\" object not found in .gltf\n";
1475 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001476 return false;
1477 }
1478
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001479 scene->buffers.clear();
1480 scene->bufferViews.clear();
1481 scene->accessors.clear();
1482 scene->meshes.clear();
1483 scene->nodes.clear();
1484 scene->defaultScene = "";
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001485
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001486 // 0. Parse Asset
1487 if (v.contains("asset") && v.get("asset").is<picojson::object>()) {
1488 const picojson::object &root = v.get("asset").get<picojson::object>();
1489
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001490 ParseAsset(&scene->asset, err, root);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001491 }
1492
1493 // 1. Parse Buffer
1494 if (v.contains("buffers") && v.get("buffers").is<picojson::object>()) {
1495 const picojson::object &root = v.get("buffers").get<picojson::object>();
1496
1497 picojson::object::const_iterator it(root.begin());
1498 picojson::object::const_iterator itEnd(root.end());
1499 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001500 Buffer buffer;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001501 if (!ParseBuffer(&buffer, err, (it->second).get<picojson::object>(),
Syoyo Fujitabeded612016-05-01 20:03:43 +09001502 base_dir, is_binary_, bin_data_, bin_size_)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001503 return false;
1504 }
1505
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001506 scene->buffers[it->first] = buffer;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001507 }
1508 }
1509
1510 // 2. Parse BufferView
1511 if (v.contains("bufferViews") &&
1512 v.get("bufferViews").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001513 const picojson::object &root = v.get("bufferViews").get<picojson::object>();
1514
1515 picojson::object::const_iterator it(root.begin());
1516 picojson::object::const_iterator itEnd(root.end());
1517 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001518 BufferView bufferView;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001519 if (!ParseBufferView(&bufferView, err,
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001520 (it->second).get<picojson::object>())) {
1521 return false;
1522 }
1523
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001524 scene->bufferViews[it->first] = bufferView;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001525 }
1526 }
1527
1528 // 3. Parse Accessor
1529 if (v.contains("accessors") && v.get("accessors").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001530 const picojson::object &root = v.get("accessors").get<picojson::object>();
1531
1532 picojson::object::const_iterator it(root.begin());
1533 picojson::object::const_iterator itEnd(root.end());
1534 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001535 Accessor accessor;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001536 if (!ParseAccessor(&accessor, err,
1537 (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001538 return false;
1539 }
1540
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001541 scene->accessors[it->first] = accessor;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001542 }
1543 }
1544
1545 // 4. Parse Mesh
1546 if (v.contains("meshes") && v.get("meshes").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001547 const picojson::object &root = v.get("meshes").get<picojson::object>();
1548
1549 picojson::object::const_iterator it(root.begin());
1550 picojson::object::const_iterator itEnd(root.end());
1551 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001552 Mesh mesh;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001553 if (!ParseMesh(&mesh, err, (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001554 return false;
1555 }
1556
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001557 scene->meshes[it->first] = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001558 }
1559 }
1560
1561 // 5. Parse Node
1562 if (v.contains("nodes") && v.get("nodes").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001563 const picojson::object &root = v.get("nodes").get<picojson::object>();
1564
1565 picojson::object::const_iterator it(root.begin());
1566 picojson::object::const_iterator itEnd(root.end());
1567 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001568 Node node;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001569 if (!ParseNode(&node, err, (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001570 return false;
1571 }
1572
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001573 scene->nodes[it->first] = node;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001574 }
1575 }
1576
1577 // 6. Parse scenes.
1578 if (v.contains("scenes") && v.get("scenes").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001579 const picojson::object &root = v.get("scenes").get<picojson::object>();
1580
1581 picojson::object::const_iterator it(root.begin());
1582 picojson::object::const_iterator itEnd(root.end());
1583 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001584 const picojson::object &o = (it->second).get<picojson::object>();
1585 std::vector<std::string> nodes;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001586 if (!ParseStringArrayProperty(&nodes, err, o, "nodes", false)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001587 return false;
1588 }
1589
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001590 scene->scenes[it->first] = nodes;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001591 }
1592 }
1593
1594 // 7. Parse default scenes.
1595 if (v.contains("scene") && v.get("scene").is<std::string>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001596 const std::string defaultScene = v.get("scene").get<std::string>();
1597
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001598 scene->defaultScene = defaultScene;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001599 }
1600
1601 // 8. Parse Material
1602 if (v.contains("materials") && v.get("materials").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001603 const picojson::object &root = v.get("materials").get<picojson::object>();
1604
1605 picojson::object::const_iterator it(root.begin());
1606 picojson::object::const_iterator itEnd(root.end());
1607 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001608 Material material;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001609 if (!ParseMaterial(&material, err,
1610 (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001611 return false;
1612 }
1613
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001614 scene->materials[it->first] = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001615 }
1616 }
1617
1618 // 9. Parse Image
1619 if (v.contains("images") && v.get("images").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001620 const picojson::object &root = v.get("images").get<picojson::object>();
1621
1622 picojson::object::const_iterator it(root.begin());
1623 picojson::object::const_iterator itEnd(root.end());
1624 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001625 Image image;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001626 if (!ParseImage(&image, err, (it->second).get<picojson::object>(),
Syoyo Fujitabeded612016-05-01 20:03:43 +09001627 base_dir, is_binary_, bin_data_, bin_size_)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001628 return false;
1629 }
1630
Syoyo Fujitabeded612016-05-01 20:03:43 +09001631 if (!image.bufferView.empty()) {
1632 // Load image from the buffer view.
1633 if (scene->bufferViews.find(image.bufferView) ==
1634 scene->bufferViews.end()) {
1635 if (err) {
1636 std::stringstream ss;
1637 ss << "bufferView \"" << image.bufferView
1638 << "\" not found in the scene." << std::endl;
1639 (*err) += ss.str();
1640 }
1641 return false;
1642 }
1643
1644 const BufferView &bufferView = scene->bufferViews[image.bufferView];
1645 const Buffer &buffer = scene->buffers[bufferView.buffer];
1646
1647 bool ret = LoadImageData(&image, err, image.width, image.height,
1648 &buffer.data[bufferView.byteOffset],
1649 static_cast<int>(bufferView.byteLength));
1650 if (!ret) {
1651 return false;
1652 }
1653 }
1654
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001655 scene->images[it->first] = image;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001656 }
1657 }
1658
Syoyo Fujitabde70212016-02-07 17:38:17 +09001659 // 9. Parse Texture
1660 if (v.contains("textures") && v.get("textures").is<picojson::object>()) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001661 const picojson::object &root = v.get("textures").get<picojson::object>();
1662
1663 picojson::object::const_iterator it(root.begin());
1664 picojson::object::const_iterator itEnd(root.end());
1665 for (; it != itEnd; it++) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001666 Texture texture;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001667 if (!ParseTexture(&texture, err, (it->second).get<picojson::object>(),
Syoyo Fujitabeded612016-05-01 20:03:43 +09001668 base_dir)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001669 return false;
1670 }
1671
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001672 scene->textures[it->first] = texture;
Syoyo Fujitabde70212016-02-07 17:38:17 +09001673 }
1674 }
1675
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001676 return true;
1677}
1678
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001679bool TinyGLTFLoader::LoadASCIIFromString(Scene *scene, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001680 const char *str, unsigned int length,
1681 const std::string &base_dir) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001682 is_binary_ = false;
1683 bin_data_ = NULL;
1684 bin_size_ = 0;
1685
Syoyo Fujitabeded612016-05-01 20:03:43 +09001686 return LoadFromString(scene, err, str, length, base_dir);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001687}
1688
1689bool TinyGLTFLoader::LoadASCIIFromFile(Scene *scene, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001690 const std::string &filename) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001691 std::stringstream ss;
1692
1693 std::ifstream f(filename.c_str());
1694 if (!f) {
1695 ss << "Failed to open file: " << filename << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001696 if (err) {
1697 (*err) = ss.str();
1698 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001699 return false;
1700 }
1701
1702 f.seekg(0, f.end);
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +09001703 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001704 std::vector<char> buf(sz);
1705
1706 f.seekg(0, f.beg);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001707 f.read(&buf.at(0), static_cast<std::streamsize>(sz));
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001708 f.close();
1709
1710 std::string basedir = GetBaseDir(filename);
1711
Syoyo Fujitabeded612016-05-01 20:03:43 +09001712 bool ret = LoadASCIIFromString(
1713 scene, err, &buf.at(0), static_cast<unsigned int>(buf.size()), basedir);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001714
1715 return ret;
1716}
1717
1718bool TinyGLTFLoader::LoadBinaryFromMemory(Scene *scene, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001719 const unsigned char *bytes,
1720 unsigned int size,
1721 const std::string &base_dir) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001722 if (size < 20) {
1723 if (err) {
1724 (*err) = "Too short data size for glTF Binary.";
1725 }
1726 return false;
1727 }
1728
Syoyo Fujitabeded612016-05-01 20:03:43 +09001729 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
1730 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001731 // ok
1732 } else {
1733 if (err) {
1734 (*err) = "Invalid magic.";
1735 }
1736 return false;
1737 }
1738
Syoyo Fujitabeded612016-05-01 20:03:43 +09001739 unsigned int version; // 4 bytes
1740 unsigned int length; // 4 bytes
1741 unsigned int scene_length; // 4 bytes
1742 unsigned int scene_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001743
1744 // @todo { Endian swap for big endian machine. }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001745 memcpy(&version, bytes + 4, 4);
1746 swap4(&version);
1747 memcpy(&length, bytes + 8, 4);
1748 swap4(&length);
1749 memcpy(&scene_length, bytes + 12, 4);
1750 swap4(&scene_length);
1751 memcpy(&scene_format, bytes + 16, 4);
1752 swap4(&scene_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001753
Syoyo Fujitabeded612016-05-01 20:03:43 +09001754 if ((20 + scene_length >= size) || (scene_length < 1) ||
1755 (scene_format != 0)) { // 0 = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001756 if (err) {
1757 (*err) = "Invalid glTF binary.";
1758 }
1759 return false;
1760 }
1761
1762 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09001763 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
1764 scene_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001765
1766 is_binary_ = true;
1767 bin_data_ = bytes + 20 + scene_length;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001768 bin_size_ =
1769 length - (20 + scene_length); // extract header + JSON scene data.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001770
Syoyo Fujitabeded612016-05-01 20:03:43 +09001771 bool ret =
1772 LoadFromString(scene, err, reinterpret_cast<const char *>(&bytes[20]),
1773 scene_length, base_dir);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001774 if (!ret) {
1775 return ret;
1776 }
1777
1778 return true;
1779}
1780
1781bool TinyGLTFLoader::LoadBinaryFromFile(Scene *scene, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001782 const std::string &filename) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001783 std::stringstream ss;
1784
1785 std::ifstream f(filename.c_str());
1786 if (!f) {
1787 ss << "Failed to open file: " << filename << std::endl;
1788 if (err) {
1789 (*err) = ss.str();
1790 }
1791 return false;
1792 }
1793
1794 f.seekg(0, f.end);
1795 size_t sz = static_cast<size_t>(f.tellg());
1796 std::vector<char> buf(sz);
1797
1798 f.seekg(0, f.beg);
1799 f.read(&buf.at(0), static_cast<std::streamsize>(sz));
1800 f.close();
1801
Syoyo Fujitabeded612016-05-01 20:03:43 +09001802 std::string basedir = GetBaseDir(filename);
1803
1804 bool ret = LoadBinaryFromMemory(
1805 scene, err, reinterpret_cast<unsigned char *>(&buf.at(0)),
1806 static_cast<unsigned int>(buf.size()), basedir);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001807
1808 return ret;
1809}
1810
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001811} // namespace tinygltf
1812
Syoyo Fujita7c877972016-03-08 01:31:49 +09001813#endif // TINYGLTF_LOADER_IMPLEMENTATION
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001814
Syoyo Fujita7c877972016-03-08 01:31:49 +09001815#endif // TINY_GLTF_LOADER_H_