blob: c1e63a64e973b45f96fe0a502db40b82159ff983 [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 Fujita6fd91622016-05-01 20:27:30 +0900284#ifdef TINYGLTF_APPLY_CLANG_WEVERYTHING
Syoyo Fujita643ce102016-05-01 17:19:37 +0900285// Disable some warnings for external files.
286#pragma clang diagnostic push
287#pragma clang diagnostic ignored "-Wfloat-equal"
288#pragma clang diagnostic ignored "-Wexit-time-destructors"
289#pragma clang diagnostic ignored "-Wconversion"
290#pragma clang diagnostic ignored "-Wold-style-cast"
291#pragma clang diagnostic ignored "-Wdouble-promotion"
292#pragma clang diagnostic ignored "-Wglobal-constructors"
293#pragma clang diagnostic ignored "-Wreserved-id-macro"
294#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
295#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900296#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900297
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +0900298#include "./picojson.h"
299#include "./stb_image.h"
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900300#ifdef TINYGLTF_APPLY_CLANG_WEVERYTHING
Syoyo Fujita643ce102016-05-01 17:19:37 +0900301#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900302#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900303
304#ifdef _WIN32
305#include <Windows.h>
306#else
307#include <wordexp.h>
308#endif
309
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900310#if defined(__sparcv9)
311// Big endian
312#else
313#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
314#define TINYGLTF_LITTLE_ENDIAN 1
315#endif
316#endif
317
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +0900318namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900319
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900320static void swap4(unsigned int *val) {
321#ifdef TINYGLTF_LITTLE_ENDIAN
322 (void)val;
323#else
324 unsigned int tmp = *val;
325 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
326 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
327
328 dst[0] = src[3];
329 dst[1] = src[2];
330 dst[2] = src[1];
331 dst[3] = src[0];
332#endif
333}
334
Syoyo Fujita643ce102016-05-01 17:19:37 +0900335static bool FileExists(const std::string &abs_filename) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900336 bool ret;
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900337#ifdef _WIN32
338 FILE *fp;
339 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
340 if (err != 0) {
Syoyo Fujitabeded612016-05-01 20:03:43 +0900341 return false;
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900342 }
343#else
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900344 FILE *fp = fopen(abs_filename.c_str(), "rb");
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +0900345#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900346 if (fp) {
347 ret = true;
348 fclose(fp);
349 } else {
350 ret = false;
351 }
352
353 return ret;
354}
355
Syoyo Fujita643ce102016-05-01 17:19:37 +0900356static std::string ExpandFilePath(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900357#ifdef _WIN32
358 DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
359 char *str = new char[len];
360 ExpandEnvironmentStringsA(filepath.c_str(), str, len);
361
362 std::string s(str);
363
364 delete[] str;
365
366 return s;
367#else
368
Syoyo Fujita643ce102016-05-01 17:19:37 +0900369#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900370 // no expansion
371 std::string s = filepath;
372#else
373 std::string s;
374 wordexp_t p;
375
376 if (filepath.empty()) {
377 return "";
378 }
379
380 // char** w;
381 int ret = wordexp(filepath.c_str(), &p, 0);
382 if (ret) {
383 // err
384 s = filepath;
385 return s;
386 }
387
388 // Use first element only.
389 if (p.we_wordv) {
390 s = std::string(p.we_wordv[0]);
391 wordfree(&p);
392 } else {
393 s = filepath;
394 }
395
396#endif
397
398 return s;
399#endif
400}
401
Syoyo Fujitabeded612016-05-01 20:03:43 +0900402static std::string JoinPath(const std::string &path0,
403 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900404 if (path0.empty()) {
405 return path1;
406 } else {
407 // check '/'
408 char lastChar = *path0.rbegin();
409 if (lastChar != '/') {
410 return path0 + std::string("/") + path1;
411 } else {
412 return path0 + path1;
413 }
414 }
415}
416
Syoyo Fujita643ce102016-05-01 17:19:37 +0900417static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900418 const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900419 for (size_t i = 0; i < paths.size(); i++) {
420 std::string absPath = ExpandFilePath(JoinPath(paths[i], filepath));
421 if (FileExists(absPath)) {
422 return absPath;
423 }
424 }
425
426 return std::string();
427}
428
429// std::string GetFilePathExtension(const std::string& FileName)
430//{
431// if(FileName.find_last_of(".") != std::string::npos)
432// return FileName.substr(FileName.find_last_of(".")+1);
433// return "";
434//}
435
Syoyo Fujita643ce102016-05-01 17:19:37 +0900436static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900437 if (filepath.find_last_of("/\\") != std::string::npos)
438 return filepath.substr(0, filepath.find_last_of("/\\"));
439 return "";
440}
441
442// std::string base64_encode(unsigned char const* , unsigned int len);
443std::string base64_decode(std::string const &s);
444
445/*
446 base64.cpp and base64.h
447
448 Copyright (C) 2004-2008 René Nyffenegger
449
450 This source code is provided 'as-is', without any express or implied
451 warranty. In no event will the author be held liable for any damages
452 arising from the use of this software.
453
454 Permission is granted to anyone to use this software for any purpose,
455 including commercial applications, and to alter it and redistribute it
456 freely, subject to the following restrictions:
457
458 1. The origin of this source code must not be misrepresented; you must not
459 claim that you wrote the original source code. If you use this source code
460 in a product, an acknowledgment in the product documentation would be
461 appreciated but is not required.
462
463 2. Altered source versions must be plainly marked as such, and must not be
464 misrepresented as being the original source code.
465
466 3. This notice may not be removed or altered from any source distribution.
467
468 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
469
470*/
471
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900472#ifdef TINYGLTF_APPLY_CLANG_WEVERYTHING
Syoyo Fujita643ce102016-05-01 17:19:37 +0900473#pragma clang diagnostic push
474#pragma clang diagnostic ignored "-Wexit-time-destructors"
475#pragma clang diagnostic ignored "-Wglobal-constructors"
476#pragma clang diagnostic ignored "-Wsign-conversion"
477#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900478#endif
Syoyo Fujita7c877972016-03-08 01:31:49 +0900479static const std::string base64_chars =
480 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
481 "abcdefghijklmnopqrstuvwxyz"
482 "0123456789+/";
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900483
484static inline bool is_base64(unsigned char c) {
485 return (isalnum(c) || (c == '+') || (c == '/'));
486}
487
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900488std::string base64_decode(std::string const &encoded_string) {
489 int in_len = encoded_string.size();
490 int i = 0;
491 int j = 0;
492 int in_ = 0;
493 unsigned char char_array_4[4], char_array_3[3];
494 std::string ret;
495
496 while (in_len-- && (encoded_string[in_] != '=') &&
497 is_base64(encoded_string[in_])) {
498 char_array_4[i++] = encoded_string[in_];
499 in_++;
500 if (i == 4) {
501 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +0900502 char_array_4[i] =
503 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900504
505 char_array_3[0] =
506 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
507 char_array_3[1] =
508 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
509 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
510
Syoyo Fujita7c877972016-03-08 01:31:49 +0900511 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900512 i = 0;
513 }
514 }
515
516 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900517 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900518
519 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +0900520 char_array_4[j] =
521 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900522
523 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
524 char_array_3[1] =
525 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
526 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
527
Syoyo Fujita7c877972016-03-08 01:31:49 +0900528 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900529 }
530
531 return ret;
532}
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900533#ifdef TINYGLTF_APPLY_CLANG_WEVERYTHING
Syoyo Fujita643ce102016-05-01 17:19:37 +0900534#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +0900535#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900536
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900537static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900538 const std::string &filename,
539 const std::string &basedir, size_t reqBytes,
540 bool checkSize) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900541 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900542
543 std::vector<std::string> paths;
544 paths.push_back(basedir);
545 paths.push_back(".");
546
547 std::string filepath = FindFile(paths, filename);
548 if (filepath.empty()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900549 if (err) {
550 (*err) += "File not found : " + filename;
551 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900552 return false;
553 }
554
555 std::ifstream f(filepath.c_str(), std::ifstream::binary);
556 if (!f) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900557 if (err) {
558 (*err) += "File open error : " + filepath;
559 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900560 return false;
561 }
562
563 f.seekg(0, f.end);
Syoyo Fujita643ce102016-05-01 17:19:37 +0900564 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900565 std::vector<unsigned char> buf(sz);
566
567 f.seekg(0, f.beg);
Syoyo Fujitabeded612016-05-01 20:03:43 +0900568 f.read(reinterpret_cast<char *>(&buf.at(0)),
569 static_cast<std::streamsize>(sz));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900570 f.close();
571
572 if (checkSize) {
573 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900574 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900575 return true;
576 } else {
577 std::stringstream ss;
578 ss << "File size mismatch : " << filepath << ", requestedBytes "
579 << reqBytes << ", but got " << sz << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900580 if (err) {
581 (*err) += ss.str();
582 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900583 return false;
584 }
585 }
586
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900587 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900588 return true;
589}
590
Syoyo Fujitabeded612016-05-01 20:03:43 +0900591static bool LoadImageData(Image *image, std::string *err, int req_width,
592 int req_height, const unsigned char *bytes,
593 int size) {
594 int w, h, comp;
595 unsigned char *data = stbi_load_from_memory(bytes, size, &w, &h, &comp, 0);
596 if (!data) {
597 if (err) {
598 (*err) += "Unknown image format.\n";
599 }
600 return false;
601 }
602
603 if (w < 1 || h < 1) {
604 if (err) {
605 (*err) += "Unknown image format.\n";
606 }
607 return false;
608 }
609
610 if (req_width > 0) {
611 if (req_width != w) {
612 if (err) {
613 (*err) += "Image width mismatch.\n";
614 }
615 return false;
616 }
617 }
618
619 if (req_height > 0) {
620 if (req_height != h) {
621 if (err) {
622 (*err) += "Image height mismatch.\n";
623 }
624 return false;
625 }
626 }
627
628 image->width = w;
629 image->height = h;
630 image->component = comp;
631 image->image.resize(static_cast<size_t>(w * h * comp));
632 std::copy(data, data + w * h * comp, image->image.begin());
633
634 return true;
635}
636
Syoyo Fujita643ce102016-05-01 17:19:37 +0900637static bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900638 std::string header = "data:application/octet-stream;base64,";
639 if (in.find(header) == 0) {
640 return true;
641 }
Syoyo Fujita620eed12016-01-02 23:37:12 +0900642
643 header = "data:image/png;base64,";
644 if (in.find(header) == 0) {
645 return true;
646 }
647
648 header = "data:image/jpeg;base64,";
649 if (in.find(header) == 0) {
650 return true;
651 }
652
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900653 return false;
654}
655
Syoyo Fujitabeded612016-05-01 20:03:43 +0900656static bool DecodeDataURI(std::vector<unsigned char> *out,
657 const std::string &in, size_t reqBytes,
658 bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900659 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +0900660 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900661 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900662 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900663 }
Syoyo Fujita620eed12016-01-02 23:37:12 +0900664
665 if (data.empty()) {
666 header = "data:image/jpeg;base64,";
667 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900668 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +0900669 }
670 }
671
672 if (data.empty()) {
673 header = "data:image/png;base64,";
674 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +0900675 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +0900676 }
677 }
678
679 if (data.empty()) {
680 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900681 }
Syoyo Fujita620eed12016-01-02 23:37:12 +0900682
683 if (checkSize) {
684 if (data.size() != reqBytes) {
685 return false;
686 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900687 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +0900688 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900689 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +0900690 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900691 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +0900692 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900693}
694
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900695static bool ParseBooleanProperty(bool *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900696 const picojson::object &o,
697 const std::string &property, bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900698 picojson::object::const_iterator it = o.find(property);
699 if (it == o.end()) {
700 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900701 if (err) {
702 (*err) += "'" + property + "' property is missing.\n";
703 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900704 }
705 return false;
706 }
707
708 if (!it->second.is<bool>()) {
709 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900710 if (err) {
711 (*err) += "'" + property + "' property is not a bool type.\n";
712 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900713 }
714 return false;
715 }
716
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900717 if (ret) {
718 (*ret) = it->second.get<bool>();
719 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900720
721 return true;
722}
723
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900724static bool ParseNumberProperty(double *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900725 const picojson::object &o,
726 const std::string &property, bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900727 picojson::object::const_iterator it = o.find(property);
728 if (it == o.end()) {
729 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900730 if (err) {
731 (*err) += "'" + property + "' property is missing.\n";
732 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900733 }
734 return false;
735 }
736
737 if (!it->second.is<double>()) {
738 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900739 if (err) {
740 (*err) += "'" + property + "' property is not a number type.\n";
741 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900742 }
743 return false;
744 }
745
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900746 if (ret) {
747 (*ret) = it->second.get<double>();
748 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900749
750 return true;
751}
752
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900753static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900754 const picojson::object &o,
755 const std::string &property,
756 bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900757 picojson::object::const_iterator it = o.find(property);
758 if (it == o.end()) {
759 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900760 if (err) {
761 (*err) += "'" + property + "' property is missing.\n";
762 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900763 }
764 return false;
765 }
766
767 if (!it->second.is<picojson::array>()) {
768 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900769 if (err) {
770 (*err) += "'" + property + "' property is not an array.\n";
771 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900772 }
773 return false;
774 }
775
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900776 ret->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900777 const picojson::array &arr = it->second.get<picojson::array>();
778 for (size_t i = 0; i < arr.size(); i++) {
779 if (!arr[i].is<double>()) {
780 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900781 if (err) {
782 (*err) += "'" + property + "' property is not a number.\n";
783 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900784 }
785 return false;
786 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900787 ret->push_back(arr[i].get<double>());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900788 }
789
790 return true;
791}
792
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900793static bool ParseStringProperty(std::string *ret, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +0900794 const picojson::object &o,
795 const std::string &property, bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900796 picojson::object::const_iterator it = o.find(property);
797 if (it == o.end()) {
798 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900799 if (err) {
800 (*err) += "'" + property + "' property is missing.\n";
801 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900802 }
803 return false;
804 }
805
806 if (!it->second.is<std::string>()) {
807 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900808 if (err) {
809 (*err) += "'" + property + "' property is not a string type.\n";
810 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900811 }
812 return false;
813 }
814
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900815 if (ret) {
816 (*ret) = it->second.get<std::string>();
817 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900818
819 return true;
820}
821
Syoyo Fujitabeded612016-05-01 20:03:43 +0900822static bool ParseStringArrayProperty(std::vector<std::string> *ret,
823 std::string *err,
824 const picojson::object &o,
825 const std::string &property,
826 bool required) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900827 picojson::object::const_iterator it = o.find(property);
828 if (it == o.end()) {
829 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900830 if (err) {
831 (*err) += "'" + property + "' property is missing.\n";
832 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900833 }
834 return false;
835 }
836
837 if (!it->second.is<picojson::array>()) {
838 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900839 if (err) {
840 (*err) += "'" + property + "' property is not an array.\n";
841 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900842 }
843 return false;
844 }
845
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900846 ret->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900847 const picojson::array &arr = it->second.get<picojson::array>();
848 for (size_t i = 0; i < arr.size(); i++) {
849 if (!arr[i].is<std::string>()) {
850 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900851 if (err) {
852 (*err) += "'" + property + "' property is not a string.\n";
853 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900854 }
855 return false;
856 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900857 ret->push_back(arr[i].get<std::string>());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900858 }
859
860 return true;
861}
862
Syoyo Fujitabeded612016-05-01 20:03:43 +0900863static bool ParseKHRBinaryExtension(const picojson::object &o, std::string *err,
864 std::string *buffer_view,
865 std::string *mime_type, int *image_width,
866 int *image_height) {
867 picojson::object j = o;
868
869 if (j.find("extensions") == j.end()) {
870 if (err) {
871 (*err) += "`extensions' property is missing.\n";
872 }
873 return false;
874 }
875
876 if (!(j["extensions"].is<picojson::object>())) {
877 if (err) {
878 (*err) += "Invalid `extensions' property.\n";
879 }
880 return false;
881 }
882
883 picojson::object ext = j["extensions"].get<picojson::object>();
884
885 if (ext.find("KHR_binary_glTF") == ext.end()) {
886 if (err) {
887 (*err) +=
888 "`KHR_binary_glTF' property is missing in extension property.\n";
889 }
890 return false;
891 }
892
893 if (!(ext["KHR_binary_glTF"].is<picojson::object>())) {
894 if (err) {
895 (*err) += "Invalid `KHR_binary_glTF' property.\n";
896 }
897 return false;
898 }
899
900 picojson::object k = ext["KHR_binary_glTF"].get<picojson::object>();
901
902 if (!ParseStringProperty(buffer_view, err, k, "bufferView", true)) {
903 return false;
904 }
905
906 if (mime_type) {
907 ParseStringProperty(mime_type, err, k, "mimeType", false);
908 }
909
910 if (image_width) {
911 double width = 0.0;
912 if (ParseNumberProperty(&width, err, k, "width", false)) {
913 (*image_width) = static_cast<int>(width);
914 }
915 }
916
917 if (image_height) {
918 double height = 0.0;
919 if (ParseNumberProperty(&height, err, k, "height", false)) {
920 (*image_height) = static_cast<int>(height);
921 }
922 }
923
924 return true;
925}
926
927static bool ParseAsset(Asset *asset, std::string *err,
928 const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900929 ParseStringProperty(&asset->generator, err, o, "generator", false);
930 ParseBooleanProperty(&asset->premultipliedAlpha, err, o, "premultipliedAlpha",
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900931 false);
932
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900933 ParseStringProperty(&asset->version, err, o, "version", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900934
935 picojson::object::const_iterator profile = o.find("profile");
936 if (profile != o.end()) {
937 const picojson::value &v = profile->second;
938 if (v.contains("api") & v.get("api").is<std::string>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900939 asset->profile_api = v.get("api").get<std::string>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900940 }
941 if (v.contains("version") & v.get("version").is<std::string>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900942 asset->profile_version = v.get("version").get<std::string>();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900943 }
944 }
945
946 return true;
947}
948
Syoyo Fujitabeded612016-05-01 20:03:43 +0900949static bool ParseImage(Image *image, std::string *err,
950 const picojson::object &o, const std::string &basedir,
951 bool is_binary, const unsigned char *bin_data,
952 size_t bin_size) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900953 std::string uri;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900954 if (!ParseStringProperty(&uri, err, o, "uri", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900955 return false;
956 }
957
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900958 ParseStringProperty(&image->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900959
960 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +0900961
962 if (is_binary) {
963 // Still binary glTF accepts external dataURI. First try external resources.
964 bool loaded = false;
965 if (IsDataURI(uri)) {
966 loaded = DecodeDataURI(&img, uri, 0, false);
967 } else {
968 // Assume external .bin file.
969 loaded = LoadExternalFile(&img, err, uri, basedir, 0, false);
970 }
971
972 if (!loaded) {
973 // load data from (embedded) binary data
974
975 if ((bin_size == 0) || (bin_data == NULL)) {
976 if (err) {
977 (*err) += "Invalid binary data.\n";
978 }
979 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +0900980 }
Syoyo Fujitabeded612016-05-01 20:03:43 +0900981
982 // There should be "extensions" property.
983 // "extensions":{"KHR_binary_glTF":{"bufferView": "id", ...
984
985 std::string buffer_view;
986 std::string mime_type;
987 int image_width;
988 int image_height;
989 bool ret = ParseKHRBinaryExtension(o, err, &buffer_view, &mime_type,
990 &image_width, &image_height);
991 if (!ret) {
992 return false;
993 }
994
995 if (uri.compare("data:,") == 0) {
996 // ok
997 } else {
998 if (err) {
999 (*err) += "Invalid URI for binary data.\n";
1000 }
1001 return false;
1002 }
1003
1004 // Just only save some information here. Loading actual image data from
1005 // bufferView is done in other place.
1006 image->bufferView = buffer_view;
1007 image->mimeType = mime_type;
1008 image->width = image_width;
1009 image->height = image_height;
1010
1011 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001012 }
1013 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001014 if (IsDataURI(uri)) {
1015 if (!DecodeDataURI(&img, uri, 0, false)) {
1016 if (err) {
1017 (*err) += "Failed to decode 'uri'.\n";
1018 }
1019 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001020 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001021 } else {
1022 // Assume external file
1023 if (!LoadExternalFile(&img, err, uri, basedir, 0, false)) {
1024 if (err) {
1025 (*err) += "Failed to load external 'uri'.\n";
1026 }
1027 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001028 }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001029 if (img.empty()) {
1030 if (err) {
1031 (*err) += "File is empty.\n";
1032 }
1033 return false;
1034 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001035 }
1036 }
1037
Syoyo Fujitabeded612016-05-01 20:03:43 +09001038 return LoadImageData(image, err, 0, 0, &img.at(0),
1039 static_cast<int>(img.size()));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001040}
1041
Syoyo Fujitabeded612016-05-01 20:03:43 +09001042static bool ParseTexture(Texture *texture, std::string *err,
1043 const picojson::object &o,
1044 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001045 (void)basedir;
1046
1047 if (!ParseStringProperty(&texture->sampler, err, o, "sampler", true)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001048 return false;
1049 }
1050
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001051 if (!ParseStringProperty(&texture->source, err, o, "source", true)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001052 return false;
1053 }
1054
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001055 ParseStringProperty(&texture->name, err, o, "name", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001056
1057 double format = TINYGLTF_TEXTURE_FORMAT_RGBA;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001058 ParseNumberProperty(&format, err, o, "format", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001059
1060 double internalFormat = TINYGLTF_TEXTURE_FORMAT_RGBA;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001061 ParseNumberProperty(&internalFormat, err, o, "internalFormat", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001062
1063 double target = TINYGLTF_TEXTURE_TARGET_TEXTURE2D;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001064 ParseNumberProperty(&target, err, o, "target", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001065
1066 double type = TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001067 ParseNumberProperty(&type, err, o, "type", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001068
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001069 texture->format = static_cast<int>(format);
1070 texture->internalFormat = static_cast<int>(internalFormat);
1071 texture->target = static_cast<int>(target);
1072 texture->type = static_cast<int>(type);
Syoyo Fujitabde70212016-02-07 17:38:17 +09001073
1074 return true;
1075}
1076
Syoyo Fujitabeded612016-05-01 20:03:43 +09001077static bool ParseBuffer(Buffer *buffer, std::string *err,
1078 const picojson::object &o, const std::string &basedir,
1079 bool is_binary = false,
1080 const unsigned char *bin_data = NULL,
1081 size_t bin_size = 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001082 double byteLength;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001083 if (!ParseNumberProperty(&byteLength, err, o, "byteLength", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001084 return false;
1085 }
1086
1087 std::string uri;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001088 if (!ParseStringProperty(&uri, err, o, "uri", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001089 return false;
1090 }
1091
1092 picojson::object::const_iterator type = o.find("type");
1093 if (type != o.end()) {
1094 if (type->second.is<std::string>()) {
1095 const std::string &ty = (type->second).get<std::string>();
1096 if (ty.compare("arraybuffer") == 0) {
1097 // buffer.type = "arraybuffer";
1098 }
1099 }
1100 }
1101
1102 size_t bytes = static_cast<size_t>(byteLength);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001103 if (is_binary) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001104 // Still binary glTF accepts external dataURI. First try external resources.
1105 bool loaded = false;
1106 if (IsDataURI(uri)) {
1107 loaded = DecodeDataURI(&buffer->data, uri, bytes, true);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001108 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09001109 // Assume external .bin file.
1110 loaded = LoadExternalFile(&buffer->data, err, uri, basedir, bytes, true);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001111 }
1112
Syoyo Fujitabeded612016-05-01 20:03:43 +09001113 if (!loaded) {
1114 // load data from (embedded) binary data
1115
1116 if ((bin_size == 0) || (bin_data == NULL)) {
1117 if (err) {
1118 (*err) += "Invalid binary data.\n";
1119 }
1120 return false;
1121 }
1122
1123 if (byteLength > bin_size) {
1124 if (err) {
1125 std::stringstream ss;
1126 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
1127 "`byteLength' = " << byteLength
1128 << ", binary size = " << bin_size << std::endl;
1129 (*err) += ss.str();
1130 }
1131 return false;
1132 }
1133
1134 if (uri.compare("data:,") == 0) {
1135 // @todo { check uri }
1136 buffer->data.resize(static_cast<size_t>(byteLength));
1137 memcpy(&(buffer->data.at(0)), bin_data,
1138 static_cast<size_t>(byteLength));
1139
1140 } else {
1141 if (err) {
1142 (*err) += "Invalid URI for binary data.\n";
1143 }
1144 return false;
1145 }
1146 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001147
1148 } else {
1149 if (IsDataURI(uri)) {
1150 if (!DecodeDataURI(&buffer->data, uri, bytes, true)) {
1151 if (err) {
1152 (*err) += "Failed to decode 'uri'.\n";
1153 }
1154 return false;
1155 }
1156 } else {
1157 // Assume external .bin file.
1158 if (!LoadExternalFile(&buffer->data, err, uri, basedir, bytes, true)) {
1159 return false;
1160 }
1161 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001162 }
1163
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001164 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001165
1166 return true;
1167}
1168
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001169static bool ParseBufferView(BufferView *bufferView, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001170 const picojson::object &o) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001171 std::string buffer;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001172 if (!ParseStringProperty(&buffer, err, o, "buffer", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001173 return false;
1174 }
1175
1176 double byteOffset;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001177 if (!ParseNumberProperty(&byteOffset, err, o, "byteOffset", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001178 return false;
1179 }
1180
1181 double byteLength = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001182 ParseNumberProperty(&byteLength, err, o, "byteLength", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001183
1184 double target = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001185 ParseNumberProperty(&target, err, o, "target", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001186 int targetValue = static_cast<int>(target);
1187 if ((targetValue == TINYGLTF_TARGET_ARRAY_BUFFER) ||
1188 (targetValue == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
1189 // OK
1190 } else {
1191 targetValue = 0;
1192 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001193 bufferView->target = targetValue;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001194
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001195 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001196
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001197 bufferView->buffer = buffer;
1198 bufferView->byteOffset = static_cast<size_t>(byteOffset);
1199 bufferView->byteLength = static_cast<size_t>(byteLength);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001200
1201 return true;
1202}
1203
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001204static bool ParseAccessor(Accessor *accessor, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001205 const picojson::object &o) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001206 std::string bufferView;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001207 if (!ParseStringProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001208 return false;
1209 }
1210
1211 double byteOffset;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001212 if (!ParseNumberProperty(&byteOffset, err, o, "byteOffset", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001213 return false;
1214 }
1215
1216 double componentType;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001217 if (!ParseNumberProperty(&componentType, err, o, "componentType", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001218 return false;
1219 }
1220
1221 double count = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001222 if (!ParseNumberProperty(&count, err, o, "count", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001223 return false;
1224 }
1225
1226 std::string type;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001227 if (!ParseStringProperty(&type, err, o, "type", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001228 return false;
1229 }
1230
1231 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001232 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001233 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001234 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001235 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001236 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001237 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001238 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001239 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001240 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001241 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001242 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001243 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001244 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001245 } else {
1246 std::stringstream ss;
1247 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001248 if (err) {
1249 (*err) += ss.str();
1250 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001251 return false;
1252 }
1253
1254 double byteStride = 0.0;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001255 ParseNumberProperty(&byteStride, err, o, "byteStride", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001256
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001257 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001258
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001259 accessor->minValues.clear();
1260 accessor->maxValues.clear();
1261 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false);
1262 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001263
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001264 accessor->count = static_cast<size_t>(count);
1265 accessor->bufferView = bufferView;
1266 accessor->byteOffset = static_cast<size_t>(byteOffset);
1267 accessor->byteStride = static_cast<size_t>(byteStride);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001268
1269 {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001270 int comp = static_cast<int>(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001271 if (comp >= TINYGLTF_COMPONENT_TYPE_BYTE &&
1272 comp <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
1273 // OK
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001274 accessor->componentType = comp;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001275 } else {
1276 std::stringstream ss;
1277 ss << "Invalid `componentType` in accessor. Got " << comp << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001278 if (err) {
1279 (*err) += ss.str();
1280 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001281 return false;
1282 }
1283 }
1284
1285 return true;
1286}
1287
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001288static bool ParsePrimitive(Primitive *primitive, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001289 const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001290 if (!ParseStringProperty(&primitive->material, err, o, "material", true)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001291 return false;
1292 }
1293
1294 double mode = static_cast<double>(TINYGLTF_MODE_TRIANGLES);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001295 ParseNumberProperty(&mode, err, o, "mode", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001296
1297 int primMode = static_cast<int>(mode);
1298 if (primMode != TINYGLTF_MODE_TRIANGLES) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001299 if (err) {
1300 (*err) +=
1301 "Currently TinyGLTFLoader doesn not support primitive mode other \n"
1302 "than TRIANGLES.\n";
1303 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001304 return false;
1305 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001306 primitive->mode = primMode;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001307
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001308 primitive->indices = "";
1309 ParseStringProperty(&primitive->indices, err, o, "indices", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001310
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001311 primitive->attributes.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001312 picojson::object::const_iterator attribsObject = o.find("attributes");
1313 if ((attribsObject != o.end()) &&
1314 (attribsObject->second).is<picojson::object>()) {
1315 const picojson::object &attribs =
1316 (attribsObject->second).get<picojson::object>();
1317 picojson::object::const_iterator it(attribs.begin());
1318 picojson::object::const_iterator itEnd(attribs.end());
1319 for (; it != itEnd; it++) {
1320 const std::string &name = it->first;
1321 if (!(it->second).is<std::string>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001322 if (err) {
1323 (*err) += "attribute expects string value.\n";
1324 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001325 return false;
1326 }
1327 const std::string &value = (it->second).get<std::string>();
1328
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001329 primitive->attributes[name] = value;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001330 }
1331 }
1332
1333 return true;
1334}
1335
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001336static bool ParseMesh(Mesh *mesh, std::string *err, const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001337 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001338
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001339 mesh->primitives.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001340 picojson::object::const_iterator primObject = o.find("primitives");
1341 if ((primObject != o.end()) && (primObject->second).is<picojson::array>()) {
1342 const picojson::array &primArray =
1343 (primObject->second).get<picojson::array>();
1344 for (size_t i = 0; i < primArray.size(); i++) {
1345 Primitive primitive;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001346 ParsePrimitive(&primitive, err, primArray[i].get<picojson::object>());
1347 mesh->primitives.push_back(primitive);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001348 }
1349 }
1350
1351 return true;
1352}
1353
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001354static bool ParseNode(Node *node, std::string *err, const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001355 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001356
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001357 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
1358 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
1359 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
1360 ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false);
1361 ParseStringArrayProperty(&node->meshes, err, o, "meshes", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001362
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001363 node->children.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001364 picojson::object::const_iterator childrenObject = o.find("children");
1365 if ((childrenObject != o.end()) &&
1366 (childrenObject->second).is<picojson::array>()) {
1367 const picojson::array &childrenArray =
1368 (childrenObject->second).get<picojson::array>();
1369 for (size_t i = 0; i < childrenArray.size(); i++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001370 if (!childrenArray[i].is<std::string>()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001371 if (err) {
1372 (*err) += "Invalid `children` array.\n";
1373 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001374 return false;
1375 }
1376 const std::string &childrenNode = childrenArray[i].get<std::string>();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001377 node->children.push_back(childrenNode);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001378 }
1379 }
1380
1381 return true;
1382}
1383
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001384static bool ParseMaterial(Material *material, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001385 const picojson::object &o) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001386 ParseStringProperty(&material->name, err, o, "name", false);
1387 ParseStringProperty(&material->technique, err, o, "technique", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001388
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001389 material->values.clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001390 picojson::object::const_iterator valuesIt = o.find("values");
1391 if ((valuesIt != o.end()) && (valuesIt->second).is<picojson::object>()) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001392 const picojson::object &values_object =
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001393 (valuesIt->second).get<picojson::object>();
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001394 picojson::object::const_iterator it(values_object.begin());
1395 picojson::object::const_iterator itEnd(values_object.end());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001396
1397 for (; it != itEnd; it++) {
1398 // Assume number values.
Syoyo Fujitabde70212016-02-07 17:38:17 +09001399 Parameter param;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001400 if (ParseStringProperty(&param.string_value, err, values_object,
1401 it->first, false)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001402 // Found string property.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001403 } else if (!ParseNumberArrayProperty(&param.number_array, err,
1404 values_object, it->first, false)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001405 // Fallback to numer property.
1406 double value;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001407 if (ParseNumberProperty(&value, err, values_object, it->first, false)) {
1408 param.number_array.push_back(value);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001409 }
1410 }
1411
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001412 material->values[it->first] = param;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001413 }
1414 }
1415
1416 return true;
1417}
Syoyo Fujita7c877972016-03-08 01:31:49 +09001418
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001419bool TinyGLTFLoader::LoadFromString(Scene *scene, std::string *err,
Syoyo Fujitabde70212016-02-07 17:38:17 +09001420 const char *str, unsigned int length,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001421 const std::string &base_dir) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001422 picojson::value v;
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001423 std::string perr = picojson::parse(v, str, str + length);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001424
1425 if (!perr.empty()) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001426 if (err) {
1427 (*err) = perr;
1428 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001429 return false;
1430 }
1431
1432 if (v.contains("scene") && v.get("scene").is<std::string>()) {
1433 // OK
1434 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001435 if (err) {
1436 (*err) += "\"scene\" object not found in .gltf\n";
1437 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001438 return false;
1439 }
1440
1441 if (v.contains("scenes") && v.get("scenes").is<picojson::object>()) {
1442 // OK
1443 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001444 if (err) {
1445 (*err) += "\"scenes\" object not found in .gltf\n";
1446 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001447 return false;
1448 }
1449
1450 if (v.contains("nodes") && v.get("nodes").is<picojson::object>()) {
1451 // OK
1452 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001453 if (err) {
1454 (*err) += "\"nodes\" object not found in .gltf\n";
1455 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001456 return false;
1457 }
1458
1459 if (v.contains("accessors") && v.get("accessors").is<picojson::object>()) {
1460 // OK
1461 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001462 if (err) {
1463 (*err) += "\"accessors\" object not found in .gltf\n";
1464 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001465 return false;
1466 }
1467
1468 if (v.contains("buffers") && v.get("buffers").is<picojson::object>()) {
1469 // OK
1470 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001471 if (err) {
1472 (*err) += "\"buffers\" object not found in .gltf\n";
1473 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001474 return false;
1475 }
1476
1477 if (v.contains("bufferViews") &&
1478 v.get("bufferViews").is<picojson::object>()) {
1479 // OK
1480 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001481 if (err) {
1482 (*err) += "\"bufferViews\" object not found in .gltf\n";
1483 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001484 return false;
1485 }
1486
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001487 scene->buffers.clear();
1488 scene->bufferViews.clear();
1489 scene->accessors.clear();
1490 scene->meshes.clear();
1491 scene->nodes.clear();
1492 scene->defaultScene = "";
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001493
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001494 // 0. Parse Asset
1495 if (v.contains("asset") && v.get("asset").is<picojson::object>()) {
1496 const picojson::object &root = v.get("asset").get<picojson::object>();
1497
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001498 ParseAsset(&scene->asset, err, root);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001499 }
1500
1501 // 1. Parse Buffer
1502 if (v.contains("buffers") && v.get("buffers").is<picojson::object>()) {
1503 const picojson::object &root = v.get("buffers").get<picojson::object>();
1504
1505 picojson::object::const_iterator it(root.begin());
1506 picojson::object::const_iterator itEnd(root.end());
1507 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001508 Buffer buffer;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001509 if (!ParseBuffer(&buffer, err, (it->second).get<picojson::object>(),
Syoyo Fujitabeded612016-05-01 20:03:43 +09001510 base_dir, is_binary_, bin_data_, bin_size_)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001511 return false;
1512 }
1513
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001514 scene->buffers[it->first] = buffer;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001515 }
1516 }
1517
1518 // 2. Parse BufferView
1519 if (v.contains("bufferViews") &&
1520 v.get("bufferViews").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001521 const picojson::object &root = v.get("bufferViews").get<picojson::object>();
1522
1523 picojson::object::const_iterator it(root.begin());
1524 picojson::object::const_iterator itEnd(root.end());
1525 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001526 BufferView bufferView;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001527 if (!ParseBufferView(&bufferView, err,
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001528 (it->second).get<picojson::object>())) {
1529 return false;
1530 }
1531
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001532 scene->bufferViews[it->first] = bufferView;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001533 }
1534 }
1535
1536 // 3. Parse Accessor
1537 if (v.contains("accessors") && v.get("accessors").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001538 const picojson::object &root = v.get("accessors").get<picojson::object>();
1539
1540 picojson::object::const_iterator it(root.begin());
1541 picojson::object::const_iterator itEnd(root.end());
1542 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001543 Accessor accessor;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001544 if (!ParseAccessor(&accessor, err,
1545 (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001546 return false;
1547 }
1548
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001549 scene->accessors[it->first] = accessor;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001550 }
1551 }
1552
1553 // 4. Parse Mesh
1554 if (v.contains("meshes") && v.get("meshes").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001555 const picojson::object &root = v.get("meshes").get<picojson::object>();
1556
1557 picojson::object::const_iterator it(root.begin());
1558 picojson::object::const_iterator itEnd(root.end());
1559 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001560 Mesh mesh;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001561 if (!ParseMesh(&mesh, err, (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001562 return false;
1563 }
1564
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001565 scene->meshes[it->first] = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001566 }
1567 }
1568
1569 // 5. Parse Node
1570 if (v.contains("nodes") && v.get("nodes").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001571 const picojson::object &root = v.get("nodes").get<picojson::object>();
1572
1573 picojson::object::const_iterator it(root.begin());
1574 picojson::object::const_iterator itEnd(root.end());
1575 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001576 Node node;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001577 if (!ParseNode(&node, err, (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001578 return false;
1579 }
1580
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001581 scene->nodes[it->first] = node;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001582 }
1583 }
1584
1585 // 6. Parse scenes.
1586 if (v.contains("scenes") && v.get("scenes").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001587 const picojson::object &root = v.get("scenes").get<picojson::object>();
1588
1589 picojson::object::const_iterator it(root.begin());
1590 picojson::object::const_iterator itEnd(root.end());
1591 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001592 const picojson::object &o = (it->second).get<picojson::object>();
1593 std::vector<std::string> nodes;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001594 if (!ParseStringArrayProperty(&nodes, err, o, "nodes", false)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001595 return false;
1596 }
1597
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001598 scene->scenes[it->first] = nodes;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001599 }
1600 }
1601
1602 // 7. Parse default scenes.
1603 if (v.contains("scene") && v.get("scene").is<std::string>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001604 const std::string defaultScene = v.get("scene").get<std::string>();
1605
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001606 scene->defaultScene = defaultScene;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001607 }
1608
1609 // 8. Parse Material
1610 if (v.contains("materials") && v.get("materials").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001611 const picojson::object &root = v.get("materials").get<picojson::object>();
1612
1613 picojson::object::const_iterator it(root.begin());
1614 picojson::object::const_iterator itEnd(root.end());
1615 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001616 Material material;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001617 if (!ParseMaterial(&material, err,
1618 (it->second).get<picojson::object>())) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001619 return false;
1620 }
1621
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001622 scene->materials[it->first] = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001623 }
1624 }
1625
1626 // 9. Parse Image
1627 if (v.contains("images") && v.get("images").is<picojson::object>()) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001628 const picojson::object &root = v.get("images").get<picojson::object>();
1629
1630 picojson::object::const_iterator it(root.begin());
1631 picojson::object::const_iterator itEnd(root.end());
1632 for (; it != itEnd; it++) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001633 Image image;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001634 if (!ParseImage(&image, err, (it->second).get<picojson::object>(),
Syoyo Fujitabeded612016-05-01 20:03:43 +09001635 base_dir, is_binary_, bin_data_, bin_size_)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001636 return false;
1637 }
1638
Syoyo Fujitabeded612016-05-01 20:03:43 +09001639 if (!image.bufferView.empty()) {
1640 // Load image from the buffer view.
1641 if (scene->bufferViews.find(image.bufferView) ==
1642 scene->bufferViews.end()) {
1643 if (err) {
1644 std::stringstream ss;
1645 ss << "bufferView \"" << image.bufferView
1646 << "\" not found in the scene." << std::endl;
1647 (*err) += ss.str();
1648 }
1649 return false;
1650 }
1651
1652 const BufferView &bufferView = scene->bufferViews[image.bufferView];
1653 const Buffer &buffer = scene->buffers[bufferView.buffer];
1654
1655 bool ret = LoadImageData(&image, err, image.width, image.height,
1656 &buffer.data[bufferView.byteOffset],
1657 static_cast<int>(bufferView.byteLength));
1658 if (!ret) {
1659 return false;
1660 }
1661 }
1662
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001663 scene->images[it->first] = image;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001664 }
1665 }
1666
Syoyo Fujitabde70212016-02-07 17:38:17 +09001667 // 9. Parse Texture
1668 if (v.contains("textures") && v.get("textures").is<picojson::object>()) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001669 const picojson::object &root = v.get("textures").get<picojson::object>();
1670
1671 picojson::object::const_iterator it(root.begin());
1672 picojson::object::const_iterator itEnd(root.end());
1673 for (; it != itEnd; it++) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001674 Texture texture;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001675 if (!ParseTexture(&texture, err, (it->second).get<picojson::object>(),
Syoyo Fujitabeded612016-05-01 20:03:43 +09001676 base_dir)) {
Syoyo Fujitabde70212016-02-07 17:38:17 +09001677 return false;
1678 }
1679
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001680 scene->textures[it->first] = texture;
Syoyo Fujitabde70212016-02-07 17:38:17 +09001681 }
1682 }
1683
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001684 return true;
1685}
1686
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001687bool TinyGLTFLoader::LoadASCIIFromString(Scene *scene, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001688 const char *str, unsigned int length,
1689 const std::string &base_dir) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001690 is_binary_ = false;
1691 bin_data_ = NULL;
1692 bin_size_ = 0;
1693
Syoyo Fujitabeded612016-05-01 20:03:43 +09001694 return LoadFromString(scene, err, str, length, base_dir);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001695}
1696
1697bool TinyGLTFLoader::LoadASCIIFromFile(Scene *scene, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001698 const std::string &filename) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001699 std::stringstream ss;
1700
1701 std::ifstream f(filename.c_str());
1702 if (!f) {
1703 ss << "Failed to open file: " << filename << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001704 if (err) {
1705 (*err) = ss.str();
1706 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001707 return false;
1708 }
1709
1710 f.seekg(0, f.end);
Syoyo Fujita5eebbdc2016-04-20 14:13:08 +09001711 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001712 std::vector<char> buf(sz);
1713
1714 f.seekg(0, f.beg);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001715 f.read(&buf.at(0), static_cast<std::streamsize>(sz));
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001716 f.close();
1717
1718 std::string basedir = GetBaseDir(filename);
1719
Syoyo Fujitabeded612016-05-01 20:03:43 +09001720 bool ret = LoadASCIIFromString(
1721 scene, err, &buf.at(0), static_cast<unsigned int>(buf.size()), basedir);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001722
1723 return ret;
1724}
1725
1726bool TinyGLTFLoader::LoadBinaryFromMemory(Scene *scene, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001727 const unsigned char *bytes,
1728 unsigned int size,
1729 const std::string &base_dir) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001730 if (size < 20) {
1731 if (err) {
1732 (*err) = "Too short data size for glTF Binary.";
1733 }
1734 return false;
1735 }
1736
Syoyo Fujitabeded612016-05-01 20:03:43 +09001737 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
1738 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001739 // ok
1740 } else {
1741 if (err) {
1742 (*err) = "Invalid magic.";
1743 }
1744 return false;
1745 }
1746
Syoyo Fujitabeded612016-05-01 20:03:43 +09001747 unsigned int version; // 4 bytes
1748 unsigned int length; // 4 bytes
1749 unsigned int scene_length; // 4 bytes
1750 unsigned int scene_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001751
1752 // @todo { Endian swap for big endian machine. }
Syoyo Fujitabeded612016-05-01 20:03:43 +09001753 memcpy(&version, bytes + 4, 4);
1754 swap4(&version);
1755 memcpy(&length, bytes + 8, 4);
1756 swap4(&length);
1757 memcpy(&scene_length, bytes + 12, 4);
1758 swap4(&scene_length);
1759 memcpy(&scene_format, bytes + 16, 4);
1760 swap4(&scene_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001761
Syoyo Fujitabeded612016-05-01 20:03:43 +09001762 if ((20 + scene_length >= size) || (scene_length < 1) ||
1763 (scene_format != 0)) { // 0 = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001764 if (err) {
1765 (*err) = "Invalid glTF binary.";
1766 }
1767 return false;
1768 }
1769
1770 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09001771 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
1772 scene_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001773
1774 is_binary_ = true;
1775 bin_data_ = bytes + 20 + scene_length;
Syoyo Fujitabeded612016-05-01 20:03:43 +09001776 bin_size_ =
1777 length - (20 + scene_length); // extract header + JSON scene data.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001778
Syoyo Fujitabeded612016-05-01 20:03:43 +09001779 bool ret =
1780 LoadFromString(scene, err, reinterpret_cast<const char *>(&bytes[20]),
1781 scene_length, base_dir);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001782 if (!ret) {
1783 return ret;
1784 }
1785
1786 return true;
1787}
1788
1789bool TinyGLTFLoader::LoadBinaryFromFile(Scene *scene, std::string *err,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001790 const std::string &filename) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001791 std::stringstream ss;
1792
1793 std::ifstream f(filename.c_str());
1794 if (!f) {
1795 ss << "Failed to open file: " << filename << std::endl;
1796 if (err) {
1797 (*err) = ss.str();
1798 }
1799 return false;
1800 }
1801
1802 f.seekg(0, f.end);
1803 size_t sz = static_cast<size_t>(f.tellg());
1804 std::vector<char> buf(sz);
1805
1806 f.seekg(0, f.beg);
1807 f.read(&buf.at(0), static_cast<std::streamsize>(sz));
1808 f.close();
1809
Syoyo Fujitabeded612016-05-01 20:03:43 +09001810 std::string basedir = GetBaseDir(filename);
1811
1812 bool ret = LoadBinaryFromMemory(
1813 scene, err, reinterpret_cast<unsigned char *>(&buf.at(0)),
1814 static_cast<unsigned int>(buf.size()), basedir);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001815
1816 return ret;
1817}
1818
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001819} // namespace tinygltf
1820
Syoyo Fujita7c877972016-03-08 01:31:49 +09001821#endif // TINYGLTF_LOADER_IMPLEMENTATION
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001822
Syoyo Fujita7c877972016-03-08 01:31:49 +09001823#endif // TINY_GLTF_LOADER_H_