tiny_gltf 1.0.1 → 2.5.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +11 -9
- data/ext/tiny_gltf/json.hpp +19421 -7390
- data/ext/tiny_gltf/rb_tiny_gltf.h +1 -2
- data/ext/tiny_gltf/rb_tiny_gltf_mesh.cpp +1 -1
- data/ext/tiny_gltf/rb_tiny_gltf_node.cpp +14 -14
- data/ext/tiny_gltf/rb_tiny_gltf_sampler.cpp +3 -1
- data/ext/tiny_gltf/tiny_gltf.h +573 -244
- data/lib/tiny_gltf/version.rb +1 -1
- data/tiny_gltf.gemspec +1 -1
- metadata +9 -9
data/ext/tiny_gltf/tiny_gltf.h
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
//
|
5
5
|
// The MIT License (MIT)
|
6
6
|
//
|
7
|
-
// Copyright (c) 2015 -
|
7
|
+
// Copyright (c) 2015 - Present Syoyo Fujita, Aurélien Chatelain and many
|
8
8
|
// contributors.
|
9
9
|
//
|
10
10
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
@@ -26,6 +26,10 @@
|
|
26
26
|
// THE SOFTWARE.
|
27
27
|
|
28
28
|
// Version:
|
29
|
+
// - v2.5.0 Add SetPreserveImageChannels() option to load image data as is.
|
30
|
+
// - v2.4.3 Fix null object output when when material has all default
|
31
|
+
// parameters.
|
32
|
+
// - v2.4.2 Decode percent-encoded URI.
|
29
33
|
// - v2.4.1 Fix some glTF object class does not have `extensions` and/or
|
30
34
|
// `extras` property.
|
31
35
|
// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone).
|
@@ -51,6 +55,7 @@
|
|
51
55
|
|
52
56
|
#include <array>
|
53
57
|
#include <cassert>
|
58
|
+
#include <cmath> // std::fabs
|
54
59
|
#include <cstdint>
|
55
60
|
#include <cstdlib>
|
56
61
|
#include <cstring>
|
@@ -103,7 +108,7 @@ namespace tinygltf {
|
|
103
108
|
#define TINYGLTF_COMPONENT_TYPE_INT (5124)
|
104
109
|
#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
|
105
110
|
#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
|
106
|
-
#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130)
|
111
|
+
#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not support double type even the schema seems allow any value of integer: https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22
|
107
112
|
|
108
113
|
#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
|
109
114
|
#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
|
@@ -186,14 +191,14 @@ AAssetManager *asset_manager = nullptr;
|
|
186
191
|
#endif
|
187
192
|
|
188
193
|
typedef enum {
|
189
|
-
NULL_TYPE
|
190
|
-
REAL_TYPE
|
191
|
-
INT_TYPE
|
192
|
-
BOOL_TYPE
|
193
|
-
STRING_TYPE
|
194
|
-
ARRAY_TYPE
|
195
|
-
BINARY_TYPE
|
196
|
-
OBJECT_TYPE
|
194
|
+
NULL_TYPE,
|
195
|
+
REAL_TYPE,
|
196
|
+
INT_TYPE,
|
197
|
+
BOOL_TYPE,
|
198
|
+
STRING_TYPE,
|
199
|
+
ARRAY_TYPE,
|
200
|
+
BINARY_TYPE,
|
201
|
+
OBJECT_TYPE
|
197
202
|
} Type;
|
198
203
|
|
199
204
|
static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
|
@@ -248,7 +253,6 @@ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
|
|
248
253
|
#ifdef __clang__
|
249
254
|
#pragma clang diagnostic push
|
250
255
|
// Suppress warning for : static Value null_value
|
251
|
-
// https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang
|
252
256
|
#pragma clang diagnostic ignored "-Wexit-time-destructors"
|
253
257
|
#pragma clang diagnostic ignored "-Wpadded"
|
254
258
|
#endif
|
@@ -293,7 +297,7 @@ class Value {
|
|
293
297
|
|
294
298
|
DEFAULT_METHODS(Value)
|
295
299
|
|
296
|
-
char Type() const { return static_cast<
|
300
|
+
char Type() const { return static_cast<char>(type_); }
|
297
301
|
|
298
302
|
bool IsBool() const { return (type_ == BOOL_TYPE); }
|
299
303
|
|
@@ -321,7 +325,8 @@ class Value {
|
|
321
325
|
}
|
322
326
|
|
323
327
|
// Use this function if you want to have number value as int.
|
324
|
-
|
328
|
+
// TODO(syoyo): Support int value larger than 32 bits
|
329
|
+
int GetNumberAsInt() const {
|
325
330
|
if (type_ == REAL_TYPE) {
|
326
331
|
return int(real_value_);
|
327
332
|
} else {
|
@@ -526,10 +531,12 @@ struct AnimationChannel {
|
|
526
531
|
// "weights"]
|
527
532
|
Value extras;
|
528
533
|
ExtensionMap extensions;
|
534
|
+
ExtensionMap target_extensions;
|
529
535
|
|
530
536
|
// Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
|
531
537
|
std::string extras_json_string;
|
532
538
|
std::string extensions_json_string;
|
539
|
+
std::string target_extensions_json_string;
|
533
540
|
|
534
541
|
AnimationChannel() : sampler(-1), target_node(-1) {}
|
535
542
|
DEFAULT_METHODS(AnimationChannel)
|
@@ -596,7 +603,7 @@ struct Sampler {
|
|
596
603
|
// `magFilter`. Set -1 in TinyGLTF(issue #186)
|
597
604
|
int minFilter =
|
598
605
|
-1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
|
599
|
-
// "
|
606
|
+
// "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
|
600
607
|
// "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
|
601
608
|
int magFilter =
|
602
609
|
-1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
|
@@ -606,7 +613,7 @@ struct Sampler {
|
|
606
613
|
int wrapT =
|
607
614
|
TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
|
608
615
|
// "REPEAT"], default "REPEAT"
|
609
|
-
int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension
|
616
|
+
//int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently not used.
|
610
617
|
|
611
618
|
Value extras;
|
612
619
|
ExtensionMap extensions;
|
@@ -619,8 +626,7 @@ struct Sampler {
|
|
619
626
|
: minFilter(-1),
|
620
627
|
magFilter(-1),
|
621
628
|
wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
|
622
|
-
wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT)
|
623
|
-
wrapR(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
|
629
|
+
wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
|
624
630
|
DEFAULT_METHODS(Sampler)
|
625
631
|
bool operator==(const Sampler &) const;
|
626
632
|
};
|
@@ -637,7 +643,8 @@ struct Image {
|
|
637
643
|
int bufferView; // (required if no uri)
|
638
644
|
std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
|
639
645
|
// "image/bmp", "image/gif"]
|
640
|
-
std::string uri; // (required if no mimeType)
|
646
|
+
std::string uri; // (required if no mimeType) uri is not decoded(e.g.
|
647
|
+
// whitespace may be represented as %20)
|
641
648
|
Value extras;
|
642
649
|
ExtensionMap extensions;
|
643
650
|
|
@@ -658,6 +665,8 @@ struct Image {
|
|
658
665
|
width = -1;
|
659
666
|
height = -1;
|
660
667
|
component = -1;
|
668
|
+
bits = -1;
|
669
|
+
pixel_type = -1;
|
661
670
|
}
|
662
671
|
DEFAULT_METHODS(Image)
|
663
672
|
|
@@ -797,12 +806,13 @@ struct Material {
|
|
797
806
|
|
798
807
|
struct BufferView {
|
799
808
|
std::string name;
|
800
|
-
int buffer;
|
801
|
-
size_t byteOffset; // minimum 0, default 0
|
802
|
-
size_t byteLength; // required, minimum 1
|
803
|
-
size_t byteStride; // minimum 4, maximum 252 (multiple of 4), default 0 =
|
804
|
-
|
805
|
-
int target;
|
809
|
+
int buffer{-1}; // Required
|
810
|
+
size_t byteOffset{0}; // minimum 0, default 0
|
811
|
+
size_t byteLength{0}; // required, minimum 1. 0 = invalid
|
812
|
+
size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
|
813
|
+
// understood to be tightly packed
|
814
|
+
int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
|
815
|
+
// or atttribs. Could be 0 for other data
|
806
816
|
Value extras;
|
807
817
|
ExtensionMap extensions;
|
808
818
|
|
@@ -810,9 +820,15 @@ struct BufferView {
|
|
810
820
|
std::string extras_json_string;
|
811
821
|
std::string extensions_json_string;
|
812
822
|
|
813
|
-
bool dracoDecoded; // Flag indicating this has been draco decoded
|
823
|
+
bool dracoDecoded{false}; // Flag indicating this has been draco decoded
|
814
824
|
|
815
|
-
BufferView()
|
825
|
+
BufferView()
|
826
|
+
: buffer(-1),
|
827
|
+
byteOffset(0),
|
828
|
+
byteLength(0),
|
829
|
+
byteStride(0),
|
830
|
+
target(0),
|
831
|
+
dracoDecoded(false) {}
|
816
832
|
DEFAULT_METHODS(BufferView)
|
817
833
|
bool operator==(const BufferView &) const;
|
818
834
|
};
|
@@ -833,8 +849,10 @@ struct Accessor {
|
|
833
849
|
std::string extras_json_string;
|
834
850
|
std::string extensions_json_string;
|
835
851
|
|
836
|
-
std::vector<double>
|
837
|
-
|
852
|
+
std::vector<double>
|
853
|
+
minValues; // optional. integer value is promoted to double
|
854
|
+
std::vector<double>
|
855
|
+
maxValues; // optional. integer value is promoted to double
|
838
856
|
|
839
857
|
struct {
|
840
858
|
int count;
|
@@ -887,13 +905,13 @@ struct Accessor {
|
|
887
905
|
// unreachable return 0;
|
888
906
|
}
|
889
907
|
|
890
|
-
Accessor()
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
908
|
+
Accessor()
|
909
|
+
: bufferView(-1),
|
910
|
+
byteOffset(0),
|
911
|
+
normalized(false),
|
912
|
+
componentType(-1),
|
913
|
+
count(0),
|
914
|
+
type(-1) {
|
897
915
|
sparse.isSparse = false;
|
898
916
|
}
|
899
917
|
DEFAULT_METHODS(Accessor)
|
@@ -983,6 +1001,7 @@ struct Primitive {
|
|
983
1001
|
Primitive() {
|
984
1002
|
material = -1;
|
985
1003
|
indices = -1;
|
1004
|
+
mode = -1;
|
986
1005
|
}
|
987
1006
|
DEFAULT_METHODS(Primitive)
|
988
1007
|
bool operator==(const Primitive &) const;
|
@@ -1037,6 +1056,7 @@ struct Buffer {
|
|
1037
1056
|
std::vector<unsigned char> data;
|
1038
1057
|
std::string
|
1039
1058
|
uri; // considered as required here but not in the spec (need to clarify)
|
1059
|
+
// uri is not decoded(e.g. whitespace may be represented as %20)
|
1040
1060
|
Value extras;
|
1041
1061
|
ExtensionMap extensions;
|
1042
1062
|
|
@@ -1050,7 +1070,7 @@ struct Buffer {
|
|
1050
1070
|
};
|
1051
1071
|
|
1052
1072
|
struct Asset {
|
1053
|
-
std::string version; // required
|
1073
|
+
std::string version = "2.0"; // required
|
1054
1074
|
std::string generator;
|
1055
1075
|
std::string minVersion;
|
1056
1076
|
std::string copyright;
|
@@ -1101,9 +1121,9 @@ struct SpotLight {
|
|
1101
1121
|
struct Light {
|
1102
1122
|
std::string name;
|
1103
1123
|
std::vector<double> color;
|
1104
|
-
double intensity;
|
1124
|
+
double intensity{1.0};
|
1105
1125
|
std::string type;
|
1106
|
-
double range;
|
1126
|
+
double range{0.0}; // 0.0 = inifinite
|
1107
1127
|
SpotLight spot;
|
1108
1128
|
|
1109
1129
|
Light() : intensity(1.0), range(0.0) {}
|
@@ -1141,7 +1161,7 @@ class Model {
|
|
1141
1161
|
std::vector<Scene> scenes;
|
1142
1162
|
std::vector<Light> lights;
|
1143
1163
|
|
1144
|
-
int defaultScene;
|
1164
|
+
int defaultScene = -1;
|
1145
1165
|
std::vector<std::string> extensionsUsed;
|
1146
1166
|
std::vector<std::string> extensionsRequired;
|
1147
1167
|
|
@@ -1172,7 +1192,8 @@ enum SectionCheck {
|
|
1172
1192
|
///
|
1173
1193
|
typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
|
1174
1194
|
std::string *, int, int,
|
1175
|
-
const unsigned char *, int,
|
1195
|
+
const unsigned char *, int,
|
1196
|
+
void *user_pointer);
|
1176
1197
|
|
1177
1198
|
///
|
1178
1199
|
/// WriteImageDataFunction type. Signature for custom image writing callbacks.
|
@@ -1235,7 +1256,14 @@ struct FsCallbacks {
|
|
1235
1256
|
|
1236
1257
|
bool FileExists(const std::string &abs_filename, void *);
|
1237
1258
|
|
1238
|
-
|
1259
|
+
///
|
1260
|
+
/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
|
1261
|
+
/// `C:\\Users\\tinygltf\\AppData`)
|
1262
|
+
///
|
1263
|
+
/// @param[in] filepath File path string. Assume UTF-8
|
1264
|
+
/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
|
1265
|
+
///
|
1266
|
+
std::string ExpandFilePath(const std::string &filepath, void *userdata);
|
1239
1267
|
|
1240
1268
|
bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
|
1241
1269
|
const std::string &filepath, void *);
|
@@ -1321,6 +1349,11 @@ class TinyGLTF {
|
|
1321
1349
|
///
|
1322
1350
|
void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
|
1323
1351
|
|
1352
|
+
///
|
1353
|
+
/// Unset(remove) callback of loading image data
|
1354
|
+
///
|
1355
|
+
void RemoveImageLoader();
|
1356
|
+
|
1324
1357
|
///
|
1325
1358
|
/// Set callback to use for writing image data
|
1326
1359
|
///
|
@@ -1359,6 +1392,16 @@ class TinyGLTF {
|
|
1359
1392
|
return store_original_json_for_extras_and_extensions_;
|
1360
1393
|
}
|
1361
1394
|
|
1395
|
+
///
|
1396
|
+
/// Specify whether preserve image channales when loading images or not.
|
1397
|
+
/// (Not effective when the user suppy their own LoadImageData callbacks)
|
1398
|
+
///
|
1399
|
+
void SetPreserveImageChannels(bool onoff) {
|
1400
|
+
preserve_image_channels_ = onoff;
|
1401
|
+
}
|
1402
|
+
|
1403
|
+
bool GetPreserveImageChannels() const { return preserve_image_channels_; }
|
1404
|
+
|
1362
1405
|
private:
|
1363
1406
|
///
|
1364
1407
|
/// Loads glTF asset from string(memory).
|
@@ -1378,6 +1421,9 @@ class TinyGLTF {
|
|
1378
1421
|
|
1379
1422
|
bool store_original_json_for_extras_and_extensions_ = false;
|
1380
1423
|
|
1424
|
+
bool preserve_image_channels_ = false; /// Default false(expand channels to
|
1425
|
+
/// RGBA) for backward compatibility.
|
1426
|
+
|
1381
1427
|
FsCallbacks fs = {
|
1382
1428
|
#ifndef TINYGLTF_NO_FS
|
1383
1429
|
&tinygltf::FileExists, &tinygltf::ExpandFilePath,
|
@@ -1397,7 +1443,8 @@ class TinyGLTF {
|
|
1397
1443
|
#else
|
1398
1444
|
nullptr;
|
1399
1445
|
#endif
|
1400
|
-
void *load_image_user_data_
|
1446
|
+
void *load_image_user_data_{nullptr};
|
1447
|
+
bool user_image_loader_{false};
|
1401
1448
|
|
1402
1449
|
WriteImageDataFunction WriteImageData =
|
1403
1450
|
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
|
@@ -1405,7 +1452,7 @@ class TinyGLTF {
|
|
1405
1452
|
#else
|
1406
1453
|
nullptr;
|
1407
1454
|
#endif
|
1408
|
-
void *write_image_user_data_
|
1455
|
+
void *write_image_user_data_{nullptr};
|
1409
1456
|
};
|
1410
1457
|
|
1411
1458
|
#ifdef __clang__
|
@@ -1490,6 +1537,7 @@ class TinyGLTF {
|
|
1490
1537
|
#ifndef TINYGLTF_USE_RAPIDJSON
|
1491
1538
|
#include "json.hpp"
|
1492
1539
|
#else
|
1540
|
+
#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
|
1493
1541
|
#include "document.h"
|
1494
1542
|
#include "prettywriter.h"
|
1495
1543
|
#include "rapidjson.h"
|
@@ -1497,6 +1545,7 @@ class TinyGLTF {
|
|
1497
1545
|
#include "writer.h"
|
1498
1546
|
#endif
|
1499
1547
|
#endif
|
1548
|
+
#endif
|
1500
1549
|
|
1501
1550
|
#ifdef TINYGLTF_ENABLE_DRACO
|
1502
1551
|
#include "draco/compression/decode.h"
|
@@ -1547,14 +1596,15 @@ class TinyGLTF {
|
|
1547
1596
|
#undef NOMINMAX
|
1548
1597
|
#endif
|
1549
1598
|
|
1550
|
-
#if defined(__GLIBCXX__)
|
1599
|
+
#if defined(__GLIBCXX__) // mingw
|
1551
1600
|
|
1552
|
-
#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
|
1553
1601
|
#include <fcntl.h> // _O_RDONLY
|
1554
1602
|
|
1603
|
+
#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
|
1604
|
+
|
1555
1605
|
#endif
|
1556
1606
|
|
1557
|
-
#elif !defined(__ANDROID__)
|
1607
|
+
#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
|
1558
1608
|
#include <wordexp.h>
|
1559
1609
|
#endif
|
1560
1610
|
|
@@ -1658,6 +1708,19 @@ void JsonParse(JsonDocument &doc, const char *str, size_t length,
|
|
1658
1708
|
|
1659
1709
|
namespace tinygltf {
|
1660
1710
|
|
1711
|
+
///
|
1712
|
+
/// Internal LoadImageDataOption struct.
|
1713
|
+
/// This struct is passed through `user_pointer` in LoadImageData.
|
1714
|
+
/// The struct is not passed when the user supply their own LoadImageData
|
1715
|
+
/// callbacks.
|
1716
|
+
///
|
1717
|
+
struct LoadImageDataOption {
|
1718
|
+
// true: preserve image channels(e.g. load as RGB image if the image has RGB
|
1719
|
+
// channels) default `false`(channels are expanded to RGBA for backward
|
1720
|
+
// compatiblity).
|
1721
|
+
bool preserve_channels{false};
|
1722
|
+
};
|
1723
|
+
|
1661
1724
|
// Equals function for Value, for recursivity
|
1662
1725
|
static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
|
1663
1726
|
if (one.Type() != other.Type()) return false;
|
@@ -1870,8 +1933,10 @@ bool Sampler::operator==(const Sampler &other) const {
|
|
1870
1933
|
return this->extensions == other.extensions && this->extras == other.extras &&
|
1871
1934
|
this->magFilter == other.magFilter &&
|
1872
1935
|
this->minFilter == other.minFilter && this->name == other.name &&
|
1873
|
-
this->
|
1936
|
+
this->wrapS == other.wrapS &&
|
1874
1937
|
this->wrapT == other.wrapT;
|
1938
|
+
|
1939
|
+
//this->wrapR == other.wrapR
|
1875
1940
|
}
|
1876
1941
|
bool Scene::operator==(const Scene &other) const {
|
1877
1942
|
return this->extensions == other.extensions && this->extras == other.extras &&
|
@@ -1975,9 +2040,11 @@ static std::string GetBaseDir(const std::string &filepath) {
|
|
1975
2040
|
return "";
|
1976
2041
|
}
|
1977
2042
|
|
1978
|
-
// https://stackoverflow.com/questions/8520560/get-a-file-name-from-a-path
|
1979
2043
|
static std::string GetBaseFilename(const std::string &filepath) {
|
1980
|
-
|
2044
|
+
auto idx = filepath.find_last_of("/\\");
|
2045
|
+
if (idx != std::string::npos)
|
2046
|
+
return filepath.substr(idx + 1);
|
2047
|
+
return filepath;
|
1981
2048
|
}
|
1982
2049
|
|
1983
2050
|
std::string base64_encode(unsigned char const *, unsigned int len);
|
@@ -2119,6 +2186,76 @@ std::string base64_decode(std::string const &encoded_string) {
|
|
2119
2186
|
#pragma clang diagnostic pop
|
2120
2187
|
#endif
|
2121
2188
|
|
2189
|
+
// https://github.com/syoyo/tinygltf/issues/228
|
2190
|
+
// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
|
2191
|
+
// decoding?
|
2192
|
+
//
|
2193
|
+
// Uri Decoding from DLIB
|
2194
|
+
// http://dlib.net/dlib/server/server_http.cpp.html
|
2195
|
+
// --- dlib begin ------------------------------------------------------------
|
2196
|
+
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
|
2197
|
+
// License: Boost Software License
|
2198
|
+
// Boost Software License - Version 1.0 - August 17th, 2003
|
2199
|
+
|
2200
|
+
// Permission is hereby granted, free of charge, to any person or organization
|
2201
|
+
// obtaining a copy of the software and accompanying documentation covered by
|
2202
|
+
// this license (the "Software") to use, reproduce, display, distribute,
|
2203
|
+
// execute, and transmit the Software, and to prepare derivative works of the
|
2204
|
+
// Software, and to permit third-parties to whom the Software is furnished to
|
2205
|
+
// do so, all subject to the following:
|
2206
|
+
// The copyright notices in the Software and this entire statement, including
|
2207
|
+
// the above license grant, this restriction and the following disclaimer,
|
2208
|
+
// must be included in all copies of the Software, in whole or in part, and
|
2209
|
+
// all derivative works of the Software, unless such copies or derivative
|
2210
|
+
// works are solely in the form of machine-executable object code generated by
|
2211
|
+
// a source language processor.
|
2212
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
2213
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
2214
|
+
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
2215
|
+
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
2216
|
+
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
2217
|
+
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
2218
|
+
// DEALINGS IN THE SOFTWARE.
|
2219
|
+
//
|
2220
|
+
namespace dlib {
|
2221
|
+
|
2222
|
+
inline unsigned char from_hex(unsigned char ch) {
|
2223
|
+
if (ch <= '9' && ch >= '0')
|
2224
|
+
ch -= '0';
|
2225
|
+
else if (ch <= 'f' && ch >= 'a')
|
2226
|
+
ch -= 'a' - 10;
|
2227
|
+
else if (ch <= 'F' && ch >= 'A')
|
2228
|
+
ch -= 'A' - 10;
|
2229
|
+
else
|
2230
|
+
ch = 0;
|
2231
|
+
return ch;
|
2232
|
+
}
|
2233
|
+
|
2234
|
+
static const std::string urldecode(const std::string &str) {
|
2235
|
+
using namespace std;
|
2236
|
+
string result;
|
2237
|
+
string::size_type i;
|
2238
|
+
for (i = 0; i < str.size(); ++i) {
|
2239
|
+
if (str[i] == '+') {
|
2240
|
+
result += ' ';
|
2241
|
+
} else if (str[i] == '%' && str.size() > i + 2) {
|
2242
|
+
const unsigned char ch1 =
|
2243
|
+
from_hex(static_cast<unsigned char>(str[i + 1]));
|
2244
|
+
const unsigned char ch2 =
|
2245
|
+
from_hex(static_cast<unsigned char>(str[i + 2]));
|
2246
|
+
const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
|
2247
|
+
result += static_cast<char>(ch);
|
2248
|
+
i += 2;
|
2249
|
+
} else {
|
2250
|
+
result += str[i];
|
2251
|
+
}
|
2252
|
+
}
|
2253
|
+
return result;
|
2254
|
+
}
|
2255
|
+
|
2256
|
+
} // namespace dlib
|
2257
|
+
// --- dlib end --------------------------------------------------------------
|
2258
|
+
|
2122
2259
|
static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
|
2123
2260
|
std::string *warn, const std::string &filename,
|
2124
2261
|
const std::string &basedir, bool required,
|
@@ -2190,22 +2327,40 @@ static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
|
|
2190
2327
|
void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
|
2191
2328
|
LoadImageData = func;
|
2192
2329
|
load_image_user_data_ = user_data;
|
2330
|
+
user_image_loader_ = true;
|
2331
|
+
}
|
2332
|
+
|
2333
|
+
void TinyGLTF::RemoveImageLoader() {
|
2334
|
+
LoadImageData =
|
2335
|
+
#ifndef TINYGLTF_NO_STB_IMAGE
|
2336
|
+
&tinygltf::LoadImageData;
|
2337
|
+
#else
|
2338
|
+
nullptr;
|
2339
|
+
#endif
|
2340
|
+
|
2341
|
+
load_image_user_data_ = nullptr;
|
2342
|
+
user_image_loader_ = false;
|
2193
2343
|
}
|
2194
2344
|
|
2195
2345
|
#ifndef TINYGLTF_NO_STB_IMAGE
|
2196
2346
|
bool LoadImageData(Image *image, const int image_idx, std::string *err,
|
2197
2347
|
std::string *warn, int req_width, int req_height,
|
2198
2348
|
const unsigned char *bytes, int size, void *user_data) {
|
2199
|
-
(void)user_data;
|
2200
2349
|
(void)warn;
|
2201
2350
|
|
2351
|
+
LoadImageDataOption option;
|
2352
|
+
if (user_data) {
|
2353
|
+
option = *reinterpret_cast<LoadImageDataOption *>(user_data);
|
2354
|
+
}
|
2355
|
+
|
2202
2356
|
int w = 0, h = 0, comp = 0, req_comp = 0;
|
2203
2357
|
|
2204
2358
|
unsigned char *data = nullptr;
|
2205
2359
|
|
2206
|
-
//
|
2207
|
-
//
|
2208
|
-
|
2360
|
+
// preserve_channels true: Use channels stored in the image file.
|
2361
|
+
// false: force 32-bit textures for common Vulkan compatibility. It appears
|
2362
|
+
// that some GPU drivers do not support 24-bit images for Vulkan
|
2363
|
+
req_comp = option.preserve_channels ? 0 : 4;
|
2209
2364
|
int bits = 8;
|
2210
2365
|
int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
|
2211
2366
|
|
@@ -2277,13 +2432,18 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err,
|
|
2277
2432
|
}
|
2278
2433
|
}
|
2279
2434
|
|
2435
|
+
if (req_comp != 0) {
|
2436
|
+
// loaded data has `req_comp` channels(components)
|
2437
|
+
comp = req_comp;
|
2438
|
+
}
|
2439
|
+
|
2280
2440
|
image->width = w;
|
2281
2441
|
image->height = h;
|
2282
|
-
image->component =
|
2442
|
+
image->component = comp;
|
2283
2443
|
image->bits = bits;
|
2284
2444
|
image->pixel_type = pixel_type;
|
2285
|
-
image->image.resize(static_cast<size_t>(w * h *
|
2286
|
-
std::copy(data, data + w * h *
|
2445
|
+
image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
|
2446
|
+
std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
|
2287
2447
|
stbi_image_free(data);
|
2288
2448
|
|
2289
2449
|
return true;
|
@@ -2379,11 +2539,22 @@ void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
|
|
2379
2539
|
|
2380
2540
|
#ifdef _WIN32
|
2381
2541
|
static inline std::wstring UTF8ToWchar(const std::string &str) {
|
2382
|
-
int wstr_size =
|
2542
|
+
int wstr_size =
|
2543
|
+
MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
|
2383
2544
|
std::wstring wstr(wstr_size, 0);
|
2384
|
-
MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
|
2545
|
+
MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
|
2546
|
+
(int)wstr.size());
|
2385
2547
|
return wstr;
|
2386
2548
|
}
|
2549
|
+
|
2550
|
+
static inline std::string WcharToUTF8(const std::wstring &wstr) {
|
2551
|
+
int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
|
2552
|
+
nullptr, 0, NULL, NULL);
|
2553
|
+
std::string str(str_size, 0);
|
2554
|
+
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
|
2555
|
+
(int)str.size(), NULL, NULL);
|
2556
|
+
return str;
|
2557
|
+
}
|
2387
2558
|
#endif
|
2388
2559
|
|
2389
2560
|
#ifndef TINYGLTF_NO_FS
|
@@ -2435,19 +2606,20 @@ bool FileExists(const std::string &abs_filename, void *) {
|
|
2435
2606
|
|
2436
2607
|
std::string ExpandFilePath(const std::string &filepath, void *) {
|
2437
2608
|
#ifdef _WIN32
|
2438
|
-
|
2439
|
-
|
2440
|
-
|
2609
|
+
// Assume input `filepath` is encoded in UTF-8
|
2610
|
+
std::wstring wfilepath = UTF8ToWchar(filepath);
|
2611
|
+
DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
|
2612
|
+
wchar_t *wstr = new wchar_t[wlen];
|
2613
|
+
ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
|
2441
2614
|
|
2442
|
-
std::
|
2615
|
+
std::wstring ws(wstr);
|
2616
|
+
delete[] wstr;
|
2617
|
+
return WcharToUTF8(ws);
|
2443
2618
|
|
2444
|
-
delete[] str;
|
2445
|
-
|
2446
|
-
return s;
|
2447
2619
|
#else
|
2448
2620
|
|
2449
2621
|
#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
|
2450
|
-
defined(__ANDROID__) || defined(__EMSCRIPTEN__)
|
2622
|
+
defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
|
2451
2623
|
// no expansion
|
2452
2624
|
std::string s = filepath;
|
2453
2625
|
#else
|
@@ -2458,8 +2630,10 @@ std::string ExpandFilePath(const std::string &filepath, void *) {
|
|
2458
2630
|
return "";
|
2459
2631
|
}
|
2460
2632
|
|
2633
|
+
// Quote the string to keep any spaces in filepath intact.
|
2634
|
+
std::string quoted_path = "\"" + filepath + "\"";
|
2461
2635
|
// char** w;
|
2462
|
-
int ret = wordexp(
|
2636
|
+
int ret = wordexp(quoted_path.c_str(), &p, 0);
|
2463
2637
|
if (ret) {
|
2464
2638
|
// err
|
2465
2639
|
s = filepath;
|
@@ -2493,11 +2667,12 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
|
|
2493
2667
|
return false;
|
2494
2668
|
}
|
2495
2669
|
size_t size = AAsset_getLength(asset);
|
2496
|
-
if (size
|
2670
|
+
if (size == 0) {
|
2497
2671
|
if (err) {
|
2498
2672
|
(*err) += "Invalid file size : " + filepath +
|
2499
2673
|
" (does the path point to a directory?)";
|
2500
2674
|
}
|
2675
|
+
return false;
|
2501
2676
|
}
|
2502
2677
|
out->resize(size);
|
2503
2678
|
AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
|
@@ -2511,13 +2686,17 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
|
|
2511
2686
|
}
|
2512
2687
|
#else
|
2513
2688
|
#ifdef _WIN32
|
2514
|
-
#if defined(__GLIBCXX__)
|
2515
|
-
int file_descriptor =
|
2689
|
+
#if defined(__GLIBCXX__) // mingw
|
2690
|
+
int file_descriptor =
|
2691
|
+
_wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
|
2516
2692
|
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
|
2517
2693
|
std::istream f(&wfile_buf);
|
2518
|
-
#elif defined(_MSC_VER)
|
2694
|
+
#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
|
2695
|
+
// For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
|
2696
|
+
// `wchar_t *`
|
2519
2697
|
std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
|
2520
|
-
#else
|
2698
|
+
#else
|
2699
|
+
// Unknown compiler/runtime
|
2521
2700
|
std::ifstream f(filepath.c_str(), std::ifstream::binary);
|
2522
2701
|
#endif
|
2523
2702
|
#else
|
@@ -2558,13 +2737,15 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
|
|
2558
2737
|
bool WriteWholeFile(std::string *err, const std::string &filepath,
|
2559
2738
|
const std::vector<unsigned char> &contents, void *) {
|
2560
2739
|
#ifdef _WIN32
|
2561
|
-
#if defined(__GLIBCXX__)
|
2562
|
-
int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
|
2563
|
-
|
2740
|
+
#if defined(__GLIBCXX__) // mingw
|
2741
|
+
int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
|
2742
|
+
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
|
2743
|
+
__gnu_cxx::stdio_filebuf<char> wfile_buf(
|
2744
|
+
file_descriptor, std::ios_base::out | std::ios_base::binary);
|
2564
2745
|
std::ostream f(&wfile_buf);
|
2565
2746
|
#elif defined(_MSC_VER)
|
2566
2747
|
std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
|
2567
|
-
#else
|
2748
|
+
#else // clang?
|
2568
2749
|
std::ofstream f(filepath.c_str(), std::ofstream::binary);
|
2569
2750
|
#endif
|
2570
2751
|
#else
|
@@ -2611,12 +2792,13 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index,
|
|
2611
2792
|
void *user_data = nullptr) {
|
2612
2793
|
std::string filename;
|
2613
2794
|
std::string ext;
|
2614
|
-
|
2615
|
-
// If image have uri. Use it it as a filename
|
2795
|
+
// If image has uri, use it it as a filename
|
2616
2796
|
if (image.uri.size()) {
|
2617
2797
|
filename = GetBaseFilename(image.uri);
|
2618
2798
|
ext = GetFilePathExtension(filename);
|
2619
|
-
|
2799
|
+
} else if (image.bufferView != -1) {
|
2800
|
+
// If there's no URI and the data exists in a buffer,
|
2801
|
+
// don't change properties or write images
|
2620
2802
|
} else if (image.name.size()) {
|
2621
2803
|
ext = MimeToExt(image.mimeType);
|
2622
2804
|
// Otherwise use name as filename
|
@@ -2628,7 +2810,7 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index,
|
|
2628
2810
|
}
|
2629
2811
|
|
2630
2812
|
// If callback is set, modify image data object
|
2631
|
-
if (*WriteImageData != nullptr) {
|
2813
|
+
if (*WriteImageData != nullptr && !filename.empty()) {
|
2632
2814
|
std::string uri;
|
2633
2815
|
(*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
|
2634
2816
|
}
|
@@ -2728,6 +2910,7 @@ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
|
|
2728
2910
|
}
|
2729
2911
|
}
|
2730
2912
|
|
2913
|
+
// TODO(syoyo): Allow empty buffer? #229
|
2731
2914
|
if (data.empty()) {
|
2732
2915
|
return false;
|
2733
2916
|
}
|
@@ -2872,7 +3055,9 @@ json_const_iterator ObjectEnd(const json &o) {
|
|
2872
3055
|
#endif
|
2873
3056
|
}
|
2874
3057
|
|
2875
|
-
const char
|
3058
|
+
// Making this a const char* results in a pointer to a temporary when
|
3059
|
+
// TINYGLTF_USE_RAPIDJSON is off.
|
3060
|
+
std::string GetKey(json_const_iterator &it) {
|
2876
3061
|
#ifdef TINYGLTF_USE_RAPIDJSON
|
2877
3062
|
return it->name.GetString();
|
2878
3063
|
#else
|
@@ -3007,6 +3192,7 @@ static bool ParseJsonAsValue(Value *ret, const json &o) {
|
|
3007
3192
|
break;
|
3008
3193
|
case json::value_t::null:
|
3009
3194
|
case json::value_t::discarded:
|
3195
|
+
case json::value_t::binary:
|
3010
3196
|
// default:
|
3011
3197
|
break;
|
3012
3198
|
}
|
@@ -3508,6 +3694,7 @@ static bool ParseAsset(Asset *asset, std::string *err, const json &o,
|
|
3508
3694
|
ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
|
3509
3695
|
ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
|
3510
3696
|
ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
|
3697
|
+
ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
|
3511
3698
|
|
3512
3699
|
ParseExtensionsProperty(&asset->extensions, err, o);
|
3513
3700
|
|
@@ -3646,7 +3833,10 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
|
|
3646
3833
|
#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
|
3647
3834
|
return true;
|
3648
3835
|
#endif
|
3649
|
-
|
3836
|
+
std::string decoded_uri = dlib::urldecode(uri);
|
3837
|
+
if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
|
3838
|
+
/* required */ false, /* required bytes */ 0,
|
3839
|
+
/* checksize */ false, fs)) {
|
3650
3840
|
if (warn) {
|
3651
3841
|
(*warn) += "Failed to load external 'uri' for image[" +
|
3652
3842
|
std::to_string(image_idx) + "] name = [" + image->name +
|
@@ -3868,9 +4058,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
|
|
3868
4058
|
}
|
3869
4059
|
} else {
|
3870
4060
|
// External .bin file.
|
4061
|
+
std::string decoded_uri = dlib::urldecode(buffer->uri);
|
3871
4062
|
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
|
3872
|
-
|
3873
|
-
fs)) {
|
4063
|
+
decoded_uri, basedir, /* required */ true,
|
4064
|
+
byteLength, /* checkSize */ true, fs)) {
|
3874
4065
|
return false;
|
3875
4066
|
}
|
3876
4067
|
}
|
@@ -3912,8 +4103,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
|
|
3912
4103
|
}
|
3913
4104
|
} else {
|
3914
4105
|
// Assume external .bin file.
|
3915
|
-
|
3916
|
-
|
4106
|
+
std::string decoded_uri = dlib::urldecode(buffer->uri);
|
4107
|
+
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
|
4108
|
+
basedir, /* required */ true, byteLength,
|
4109
|
+
/* checkSize */ true, fs)) {
|
3917
4110
|
return false;
|
3918
4111
|
}
|
3919
4112
|
}
|
@@ -4023,7 +4216,9 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
|
|
4023
4216
|
accessor->sparse.isSparse = true;
|
4024
4217
|
|
4025
4218
|
int count = 0;
|
4026
|
-
ParseIntegerProperty(&count, err, o, "count", true)
|
4219
|
+
if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
|
4220
|
+
return false;
|
4221
|
+
}
|
4027
4222
|
|
4028
4223
|
json_const_iterator indices_iterator;
|
4029
4224
|
json_const_iterator values_iterator;
|
@@ -4041,18 +4236,24 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
|
|
4041
4236
|
const json &values_obj = GetValue(values_iterator);
|
4042
4237
|
|
4043
4238
|
int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
|
4044
|
-
ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView",
|
4045
|
-
true)
|
4239
|
+
if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView",
|
4240
|
+
true, "SparseAccessor")) {
|
4241
|
+
return false;
|
4242
|
+
}
|
4046
4243
|
ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
|
4047
|
-
|
4048
|
-
ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
|
4049
|
-
true)
|
4244
|
+
false);
|
4245
|
+
if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
|
4246
|
+
true, "SparseAccessor")) {
|
4247
|
+
return false;
|
4248
|
+
}
|
4050
4249
|
|
4051
4250
|
int values_buffer_view = 0, values_byte_offset = 0;
|
4052
|
-
ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
|
4053
|
-
true)
|
4251
|
+
if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
|
4252
|
+
true, "SparseAccessor")) {
|
4253
|
+
return false;
|
4254
|
+
}
|
4054
4255
|
ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
|
4055
|
-
|
4256
|
+
false);
|
4056
4257
|
|
4057
4258
|
accessor->sparse.count = count;
|
4058
4259
|
accessor->sparse.indices.bufferView = indices_buffer_view;
|
@@ -4061,8 +4262,6 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
|
|
4061
4262
|
accessor->sparse.values.bufferView = values_buffer_view;
|
4062
4263
|
accessor->sparse.values.byteOffset = values_byte_offset;
|
4063
4264
|
|
4064
|
-
// todo check theses values
|
4065
|
-
|
4066
4265
|
return true;
|
4067
4266
|
}
|
4068
4267
|
|
@@ -4270,6 +4469,7 @@ static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
|
|
4270
4469
|
static bool ParseDracoExtension(Primitive *primitive, Model *model,
|
4271
4470
|
std::string *err,
|
4272
4471
|
const Value &dracoExtensionValue) {
|
4472
|
+
(void)err;
|
4273
4473
|
auto bufferViewValue = dracoExtensionValue.Get("bufferView");
|
4274
4474
|
if (!bufferViewValue.IsInt()) return false;
|
4275
4475
|
auto attributesValue = dracoExtensionValue.Get("attributes");
|
@@ -4330,7 +4530,6 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model,
|
|
4330
4530
|
|
4331
4531
|
int dracoAttributeIndex = attribute.second.Get<int>();
|
4332
4532
|
const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
|
4333
|
-
const auto pBuffer = pAttribute->buffer();
|
4334
4533
|
const auto componentType =
|
4335
4534
|
model->accessors[primitiveAttribute->second].componentType;
|
4336
4535
|
|
@@ -4751,6 +4950,13 @@ static bool ParseAnimationChannel(
|
|
4751
4950
|
}
|
4752
4951
|
return false;
|
4753
4952
|
}
|
4953
|
+
ParseExtensionsProperty(&channel->target_extensions, err, target_object);
|
4954
|
+
if (store_original_json_for_extras_and_extensions) {
|
4955
|
+
json_const_iterator it;
|
4956
|
+
if (FindMember(target_object, "extensions", it)) {
|
4957
|
+
channel->target_extensions_json_string = JsonToString(GetValue(it));
|
4958
|
+
}
|
4959
|
+
}
|
4754
4960
|
}
|
4755
4961
|
|
4756
4962
|
channel->sampler = samplerIndex;
|
@@ -4882,12 +5088,12 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
|
|
4882
5088
|
int magFilter = -1;
|
4883
5089
|
int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
|
4884
5090
|
int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
|
4885
|
-
int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
|
5091
|
+
//int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
|
4886
5092
|
ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
|
4887
5093
|
ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
|
4888
5094
|
ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
|
4889
5095
|
ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
|
4890
|
-
ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf extension
|
5096
|
+
//ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf extension
|
4891
5097
|
|
4892
5098
|
// TODO(syoyo): Check the value is alloed one.
|
4893
5099
|
// (e.g. we allow 9728(NEAREST), but don't allow 9727)
|
@@ -4896,7 +5102,7 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
|
|
4896
5102
|
sampler->magFilter = magFilter;
|
4897
5103
|
sampler->wrapS = wrapS;
|
4898
5104
|
sampler->wrapT = wrapT;
|
4899
|
-
sampler->wrapR = wrapR;
|
5105
|
+
//sampler->wrapR = wrapR;
|
4900
5106
|
|
4901
5107
|
ParseExtensionsProperty(&(sampler->extensions), err, o);
|
4902
5108
|
ParseExtrasProperty(&(sampler->extras), o);
|
@@ -5252,7 +5458,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
|
5252
5458
|
|
5253
5459
|
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
|
5254
5460
|
defined(_CPPUNWIND)) && \
|
5255
|
-
|
5461
|
+
!defined(TINYGLTF_NOEXCEPTION)
|
5256
5462
|
try {
|
5257
5463
|
JsonParse(v, json_str, json_str_length, true);
|
5258
5464
|
|
@@ -5525,7 +5731,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
|
5525
5731
|
|
5526
5732
|
// Assign missing bufferView target types
|
5527
5733
|
// - Look for missing Mesh indices
|
5528
|
-
// - Look for missing
|
5734
|
+
// - Look for missing Mesh attributes
|
5529
5735
|
for (auto &mesh : model->meshes) {
|
5530
5736
|
for (auto &primitive : mesh.primitives) {
|
5531
5737
|
if (primitive.indices >
|
@@ -5553,14 +5759,25 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
|
5553
5759
|
// we could optionally check if acessors' bufferView type is Scalar, as
|
5554
5760
|
// it should be
|
5555
5761
|
}
|
5556
|
-
|
5557
|
-
|
5558
|
-
|
5559
|
-
|
5560
|
-
|
5561
|
-
|
5562
|
-
|
5563
|
-
|
5762
|
+
|
5763
|
+
for (auto &attribute : primitive.attributes) {
|
5764
|
+
model
|
5765
|
+
->bufferViews[size_t(
|
5766
|
+
model->accessors[size_t(attribute.second)].bufferView)]
|
5767
|
+
.target = TINYGLTF_TARGET_ARRAY_BUFFER;
|
5768
|
+
}
|
5769
|
+
|
5770
|
+
for (auto &target : primitive.targets) {
|
5771
|
+
for (auto &attribute : target) {
|
5772
|
+
auto bufferView =
|
5773
|
+
model->accessors[size_t(attribute.second)].bufferView;
|
5774
|
+
// bufferView could be null(-1) for sparse morph target
|
5775
|
+
if (bufferView >= 0) {
|
5776
|
+
model->bufferViews[size_t(bufferView)].target =
|
5777
|
+
TINYGLTF_TARGET_ARRAY_BUFFER;
|
5778
|
+
}
|
5779
|
+
}
|
5780
|
+
}
|
5564
5781
|
}
|
5565
5782
|
}
|
5566
5783
|
|
@@ -5612,13 +5829,13 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
|
5612
5829
|
{
|
5613
5830
|
json_const_iterator it;
|
5614
5831
|
if (FindMember(o, "extensions", it)) {
|
5615
|
-
|
5832
|
+
scene.extensions_json_string = JsonToString(GetValue(it));
|
5616
5833
|
}
|
5617
5834
|
}
|
5618
5835
|
{
|
5619
5836
|
json_const_iterator it;
|
5620
5837
|
if (FindMember(o, "extras", it)) {
|
5621
|
-
|
5838
|
+
scene.extras_json_string = JsonToString(GetValue(it));
|
5622
5839
|
}
|
5623
5840
|
}
|
5624
5841
|
}
|
@@ -5668,6 +5885,18 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
|
5668
5885
|
}
|
5669
5886
|
|
5670
5887
|
// 11. Parse Image
|
5888
|
+
void *load_image_user_data{nullptr};
|
5889
|
+
|
5890
|
+
LoadImageDataOption load_image_option;
|
5891
|
+
|
5892
|
+
if (user_image_loader_) {
|
5893
|
+
// Use user supplied pointer
|
5894
|
+
load_image_user_data = load_image_user_data_;
|
5895
|
+
} else {
|
5896
|
+
load_image_option.preserve_channels = preserve_image_channels_;
|
5897
|
+
load_image_user_data = reinterpret_cast<void *>(&load_image_option);
|
5898
|
+
}
|
5899
|
+
|
5671
5900
|
{
|
5672
5901
|
int idx = 0;
|
5673
5902
|
bool success = ForEachInArray(v, "images", [&](const json &o) {
|
@@ -5680,7 +5909,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
|
5680
5909
|
Image image;
|
5681
5910
|
if (!ParseImage(&image, idx, err, warn, o,
|
5682
5911
|
store_original_json_for_extras_and_extensions_, base_dir,
|
5683
|
-
&fs, &this->LoadImageData,
|
5912
|
+
&fs, &this->LoadImageData, load_image_user_data)) {
|
5684
5913
|
return false;
|
5685
5914
|
}
|
5686
5915
|
|
@@ -5718,7 +5947,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
|
5718
5947
|
bool ret = LoadImageData(
|
5719
5948
|
&image, idx, err, warn, image.width, image.height,
|
5720
5949
|
&buffer.data[bufferView.byteOffset],
|
5721
|
-
static_cast<int>(bufferView.byteLength),
|
5950
|
+
static_cast<int>(bufferView.byteLength), load_image_user_data);
|
5722
5951
|
if (!ret) {
|
5723
5952
|
return false;
|
5724
5953
|
}
|
@@ -6143,6 +6372,13 @@ static void SerializeNumberProperty(const std::string &key, T number,
|
|
6143
6372
|
JsonAddMember(obj, key.c_str(), json(number));
|
6144
6373
|
}
|
6145
6374
|
|
6375
|
+
#ifdef TINYGLTF_USE_RAPIDJSON
|
6376
|
+
template <>
|
6377
|
+
void SerializeNumberProperty(const std::string &key, size_t number, json &obj) {
|
6378
|
+
JsonAddMember(obj, key.c_str(), json(static_cast<uint64_t>(number)));
|
6379
|
+
}
|
6380
|
+
#endif
|
6381
|
+
|
6146
6382
|
template <typename T>
|
6147
6383
|
static void SerializeNumberArrayProperty(const std::string &key,
|
6148
6384
|
const std::vector<T> &value,
|
@@ -6278,17 +6514,25 @@ static void SerializeValue(const std::string &key, const Value &value,
|
|
6278
6514
|
static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
|
6279
6515
|
json &o) {
|
6280
6516
|
std::string header = "data:application/octet-stream;base64,";
|
6281
|
-
|
6282
|
-
|
6283
|
-
|
6517
|
+
if (data.size() > 0) {
|
6518
|
+
std::string encodedData =
|
6519
|
+
base64_encode(&data[0], static_cast<unsigned int>(data.size()));
|
6520
|
+
SerializeStringProperty("uri", header + encodedData, o);
|
6521
|
+
} else {
|
6522
|
+
// Issue #229
|
6523
|
+
// size 0 is allowd. Just emit mime header.
|
6524
|
+
SerializeStringProperty("uri", header, o);
|
6525
|
+
}
|
6284
6526
|
}
|
6285
6527
|
|
6286
6528
|
static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
|
6287
6529
|
const std::string &binFilename) {
|
6288
6530
|
#ifdef _WIN32
|
6289
|
-
#if defined(__GLIBCXX__)
|
6290
|
-
|
6291
|
-
|
6531
|
+
#if defined(__GLIBCXX__) // mingw
|
6532
|
+
int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
|
6533
|
+
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
|
6534
|
+
__gnu_cxx::stdio_filebuf<char> wfile_buf(
|
6535
|
+
file_descriptor, std::ios_base::out | std::ios_base::binary);
|
6292
6536
|
std::ostream output(&wfile_buf);
|
6293
6537
|
if (!wfile_buf.is_open()) return false;
|
6294
6538
|
#elif defined(_MSC_VER)
|
@@ -6302,8 +6546,14 @@ static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
|
|
6302
6546
|
std::ofstream output(binFilename.c_str(), std::ofstream::binary);
|
6303
6547
|
if (!output.is_open()) return false;
|
6304
6548
|
#endif
|
6305
|
-
|
6306
|
-
|
6549
|
+
if (data.size() > 0) {
|
6550
|
+
output.write(reinterpret_cast<const char *>(&data[0]),
|
6551
|
+
std::streamsize(data.size()));
|
6552
|
+
} else {
|
6553
|
+
// Issue #229
|
6554
|
+
// size 0 will be still valid buffer data.
|
6555
|
+
// write empty file.
|
6556
|
+
}
|
6307
6557
|
return true;
|
6308
6558
|
}
|
6309
6559
|
|
@@ -6365,15 +6615,41 @@ static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
|
|
6365
6615
|
}
|
6366
6616
|
|
6367
6617
|
static void SerializeGltfAccessor(Accessor &accessor, json &o) {
|
6368
|
-
|
6618
|
+
if (accessor.bufferView >= 0)
|
6619
|
+
SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
|
6369
6620
|
|
6370
|
-
if (accessor.byteOffset != 0
|
6621
|
+
if (accessor.byteOffset != 0)
|
6371
6622
|
SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
|
6372
6623
|
|
6373
6624
|
SerializeNumberProperty<int>("componentType", accessor.componentType, o);
|
6374
6625
|
SerializeNumberProperty<size_t>("count", accessor.count, o);
|
6375
|
-
|
6376
|
-
|
6626
|
+
|
6627
|
+
if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
|
6628
|
+
(accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
|
6629
|
+
SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
|
6630
|
+
SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
|
6631
|
+
} else {
|
6632
|
+
// Issue #301. Serialize as integer.
|
6633
|
+
// Assume int value is within [-2**31-1, 2**31-1]
|
6634
|
+
{
|
6635
|
+
std::vector<int> values;
|
6636
|
+
std::transform(accessor.minValues.begin(), accessor.minValues.end(),
|
6637
|
+
std::back_inserter(values),
|
6638
|
+
[](double v) { return static_cast<int>(v); });
|
6639
|
+
|
6640
|
+
SerializeNumberArrayProperty<int>("min", values, o);
|
6641
|
+
}
|
6642
|
+
|
6643
|
+
{
|
6644
|
+
std::vector<int> values;
|
6645
|
+
std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
|
6646
|
+
std::back_inserter(values),
|
6647
|
+
[](double v) { return static_cast<int>(v); });
|
6648
|
+
|
6649
|
+
SerializeNumberArrayProperty<int>("max", values, o);
|
6650
|
+
}
|
6651
|
+
}
|
6652
|
+
|
6377
6653
|
if (accessor.normalized)
|
6378
6654
|
SerializeValue("normalized", Value(accessor.normalized), o);
|
6379
6655
|
std::string type;
|
@@ -6416,6 +6692,8 @@ static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
|
|
6416
6692
|
SerializeNumberProperty("node", channel.target_node, target);
|
6417
6693
|
SerializeStringProperty("path", channel.target_path, target);
|
6418
6694
|
|
6695
|
+
SerializeExtensionMap(channel.target_extensions, target);
|
6696
|
+
|
6419
6697
|
JsonAddMember(o, "target", std::move(target));
|
6420
6698
|
}
|
6421
6699
|
|
@@ -6455,6 +6733,7 @@ static void SerializeGltfAnimation(Animation &animation, json &o) {
|
|
6455
6733
|
|
6456
6734
|
{
|
6457
6735
|
json samplers;
|
6736
|
+
JsonReserveArray(samplers, animation.samplers.size());
|
6458
6737
|
for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
|
6459
6738
|
json sampler;
|
6460
6739
|
AnimationSampler gltfSampler = animation.samplers[i];
|
@@ -6480,10 +6759,15 @@ static void SerializeGltfAsset(Asset &asset, json &o) {
|
|
6480
6759
|
SerializeStringProperty("copyright", asset.copyright, o);
|
6481
6760
|
}
|
6482
6761
|
|
6483
|
-
if (
|
6484
|
-
|
6762
|
+
if (asset.version.empty()) {
|
6763
|
+
// Just in case
|
6764
|
+
// `version` must be defined
|
6765
|
+
asset.version = "2.0";
|
6485
6766
|
}
|
6486
6767
|
|
6768
|
+
// TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
|
6769
|
+
SerializeStringProperty("version", asset.version, o);
|
6770
|
+
|
6487
6771
|
if (asset.extras.Keys().size()) {
|
6488
6772
|
SerializeValue("extras", asset.extras, o);
|
6489
6773
|
}
|
@@ -6491,10 +6775,10 @@ static void SerializeGltfAsset(Asset &asset, json &o) {
|
|
6491
6775
|
SerializeExtensionMap(asset.extensions, o);
|
6492
6776
|
}
|
6493
6777
|
|
6494
|
-
|
6495
|
-
|
6778
|
+
static void SerializeGltfBufferBin(Buffer &buffer, json &o,
|
6779
|
+
std::vector<unsigned char> &binBuffer) {
|
6496
6780
|
SerializeNumberProperty("byteLength", buffer.data.size(), o);
|
6497
|
-
binBuffer=buffer.data;
|
6781
|
+
binBuffer = buffer.data;
|
6498
6782
|
|
6499
6783
|
if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
|
6500
6784
|
|
@@ -6561,6 +6845,7 @@ static void SerializeGltfImage(Image &image, json &o) {
|
|
6561
6845
|
SerializeStringProperty("mimeType", image.mimeType, o);
|
6562
6846
|
SerializeNumberProperty<int>("bufferView", image.bufferView, o);
|
6563
6847
|
} else {
|
6848
|
+
// TODO(syoyo): dlib::urilencode?
|
6564
6849
|
SerializeStringProperty("uri", image.uri, o);
|
6565
6850
|
}
|
6566
6851
|
|
@@ -6677,8 +6962,8 @@ static void SerializeGltfMaterial(Material &material, json &o) {
|
|
6677
6962
|
SerializeStringProperty("alphaMode", material.alphaMode, o);
|
6678
6963
|
}
|
6679
6964
|
|
6680
|
-
if(material.doubleSided != false)
|
6681
|
-
|
6965
|
+
if (material.doubleSided != false)
|
6966
|
+
JsonAddMember(o, "doubleSided", json(material.doubleSided));
|
6682
6967
|
|
6683
6968
|
if (material.normalTexture.index > -1) {
|
6684
6969
|
json texinfo;
|
@@ -6782,7 +7067,7 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) {
|
|
6782
7067
|
JsonAddMember(primitive, "targets", std::move(targets));
|
6783
7068
|
}
|
6784
7069
|
|
6785
|
-
SerializeExtensionMap(gltfPrimitive.extensions,
|
7070
|
+
SerializeExtensionMap(gltfPrimitive.extensions, primitive);
|
6786
7071
|
|
6787
7072
|
if (gltfPrimitive.extras.Type() != NULL_TYPE) {
|
6788
7073
|
SerializeValue("extras", gltfPrimitive.extras, primitive);
|
@@ -6819,7 +7104,9 @@ static void SerializeSpotLight(SpotLight &spot, json &o) {
|
|
6819
7104
|
static void SerializeGltfLight(Light &light, json &o) {
|
6820
7105
|
if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
|
6821
7106
|
SerializeNumberProperty("intensity", light.intensity, o);
|
6822
|
-
|
7107
|
+
if (light.range > 0.0) {
|
7108
|
+
SerializeNumberProperty("range", light.range, o);
|
7109
|
+
}
|
6823
7110
|
SerializeNumberArrayProperty("color", light.color, o);
|
6824
7111
|
SerializeStringProperty("type", light.type, o);
|
6825
7112
|
if (light.type == "spot") {
|
@@ -6878,7 +7165,7 @@ static void SerializeGltfSampler(Sampler &sampler, json &o) {
|
|
6878
7165
|
if (sampler.minFilter != -1) {
|
6879
7166
|
SerializeNumberProperty("minFilter", sampler.minFilter, o);
|
6880
7167
|
}
|
6881
|
-
SerializeNumberProperty("wrapR", sampler.wrapR, o);
|
7168
|
+
//SerializeNumberProperty("wrapR", sampler.wrapR, o);
|
6882
7169
|
SerializeNumberProperty("wrapS", sampler.wrapS, o);
|
6883
7170
|
SerializeNumberProperty("wrapT", sampler.wrapT, o);
|
6884
7171
|
|
@@ -6933,6 +7220,11 @@ static void SerializeGltfCamera(const Camera &camera, json &o) {
|
|
6933
7220
|
} else {
|
6934
7221
|
// ???
|
6935
7222
|
}
|
7223
|
+
|
7224
|
+
if (camera.extras.Type() != NULL_TYPE) {
|
7225
|
+
SerializeValue("extras", camera.extras, o);
|
7226
|
+
}
|
7227
|
+
SerializeExtensionMap(camera.extensions, o);
|
6936
7228
|
}
|
6937
7229
|
|
6938
7230
|
static void SerializeGltfScene(Scene &scene, json &o) {
|
@@ -6948,11 +7240,17 @@ static void SerializeGltfScene(Scene &scene, json &o) {
|
|
6948
7240
|
}
|
6949
7241
|
|
6950
7242
|
static void SerializeGltfSkin(Skin &skin, json &o) {
|
6951
|
-
|
7243
|
+
// required
|
7244
|
+
SerializeNumberArrayProperty<int>("joints", skin.joints, o);
|
7245
|
+
|
7246
|
+
if (skin.inverseBindMatrices >= 0) {
|
6952
7247
|
SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
|
7248
|
+
}
|
7249
|
+
|
7250
|
+
if (skin.skeleton >= 0) {
|
7251
|
+
SerializeNumberProperty("skeleton", skin.skeleton, o);
|
7252
|
+
}
|
6953
7253
|
|
6954
|
-
SerializeNumberArrayProperty<int>("joints", skin.joints, o);
|
6955
|
-
SerializeNumberProperty("skeleton", skin.skeleton, o);
|
6956
7254
|
if (skin.name.size()) {
|
6957
7255
|
SerializeStringProperty("name", skin.name, o);
|
6958
7256
|
}
|
@@ -6979,14 +7277,16 @@ static void SerializeGltfTexture(Texture &texture, json &o) {
|
|
6979
7277
|
///
|
6980
7278
|
static void SerializeGltfModel(Model *model, json &o) {
|
6981
7279
|
// ACCESSORS
|
6982
|
-
|
6983
|
-
|
6984
|
-
|
6985
|
-
|
6986
|
-
|
6987
|
-
|
7280
|
+
if (model->accessors.size()) {
|
7281
|
+
json accessors;
|
7282
|
+
JsonReserveArray(accessors, model->accessors.size());
|
7283
|
+
for (unsigned int i = 0; i < model->accessors.size(); ++i) {
|
7284
|
+
json accessor;
|
7285
|
+
SerializeGltfAccessor(model->accessors[i], accessor);
|
7286
|
+
JsonPushBack(accessors, std::move(accessor));
|
7287
|
+
}
|
7288
|
+
JsonAddMember(o, "accessors", std::move(accessors));
|
6988
7289
|
}
|
6989
|
-
JsonAddMember(o, "accessors", std::move(accessors));
|
6990
7290
|
|
6991
7291
|
// ANIMATIONS
|
6992
7292
|
if (model->animations.size()) {
|
@@ -7009,18 +7309,15 @@ static void SerializeGltfModel(Model *model, json &o) {
|
|
7009
7309
|
JsonAddMember(o, "asset", std::move(asset));
|
7010
7310
|
|
7011
7311
|
// BUFFERVIEWS
|
7012
|
-
|
7013
|
-
|
7014
|
-
|
7015
|
-
|
7016
|
-
|
7017
|
-
|
7018
|
-
|
7019
|
-
|
7020
|
-
|
7021
|
-
// Extensions used
|
7022
|
-
if (model->extensionsUsed.size()) {
|
7023
|
-
SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed, o);
|
7312
|
+
if (model->bufferViews.size()) {
|
7313
|
+
json bufferViews;
|
7314
|
+
JsonReserveArray(bufferViews, model->bufferViews.size());
|
7315
|
+
for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
|
7316
|
+
json bufferView;
|
7317
|
+
SerializeGltfBufferView(model->bufferViews[i], bufferView);
|
7318
|
+
JsonPushBack(bufferViews, std::move(bufferView));
|
7319
|
+
}
|
7320
|
+
JsonAddMember(o, "bufferViews", std::move(bufferViews));
|
7024
7321
|
}
|
7025
7322
|
|
7026
7323
|
// Extensions required
|
@@ -7036,6 +7333,16 @@ static void SerializeGltfModel(Model *model, json &o) {
|
|
7036
7333
|
for (unsigned int i = 0; i < model->materials.size(); ++i) {
|
7037
7334
|
json material;
|
7038
7335
|
SerializeGltfMaterial(model->materials[i], material);
|
7336
|
+
|
7337
|
+
if (JsonIsNull(material)) {
|
7338
|
+
// Issue 294.
|
7339
|
+
// `material` does not have any required parameters
|
7340
|
+
// so the result may be null(unmodified) when all material parameters
|
7341
|
+
// have default value.
|
7342
|
+
//
|
7343
|
+
// null is not allowed thus we create an empty JSON object.
|
7344
|
+
JsonSetObject(material);
|
7345
|
+
}
|
7039
7346
|
JsonPushBack(materials, std::move(material));
|
7040
7347
|
}
|
7041
7348
|
JsonAddMember(o, "materials", std::move(materials));
|
@@ -7133,7 +7440,9 @@ static void SerializeGltfModel(Model *model, json &o) {
|
|
7133
7440
|
// EXTENSIONS
|
7134
7441
|
SerializeExtensionMap(model->extensions, o);
|
7135
7442
|
|
7136
|
-
|
7443
|
+
auto extensionsUsed = model->extensionsUsed;
|
7444
|
+
|
7445
|
+
// LIGHTS as KHR_lights_punctual
|
7137
7446
|
if (model->lights.size()) {
|
7138
7447
|
json lights;
|
7139
7448
|
JsonReserveArray(lights, model->lights.size());
|
@@ -7148,7 +7457,7 @@ static void SerializeGltfModel(Model *model, json &o) {
|
|
7148
7457
|
|
7149
7458
|
{
|
7150
7459
|
json_const_iterator it;
|
7151
|
-
if (
|
7460
|
+
if (FindMember(o, "extensions", it)) {
|
7152
7461
|
JsonAssign(ext_j, GetValue(it));
|
7153
7462
|
}
|
7154
7463
|
}
|
@@ -7156,6 +7465,24 @@ static void SerializeGltfModel(Model *model, json &o) {
|
|
7156
7465
|
JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
|
7157
7466
|
|
7158
7467
|
JsonAddMember(o, "extensions", std::move(ext_j));
|
7468
|
+
|
7469
|
+
// Also add "KHR_lights_punctual" to `extensionsUsed`
|
7470
|
+
{
|
7471
|
+
auto has_khr_lights_punctual =
|
7472
|
+
std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
|
7473
|
+
[](const std::string &s) {
|
7474
|
+
return (s.compare("KHR_lights_punctual") == 0);
|
7475
|
+
});
|
7476
|
+
|
7477
|
+
if (has_khr_lights_punctual == extensionsUsed.end()) {
|
7478
|
+
extensionsUsed.push_back("KHR_lights_punctual");
|
7479
|
+
}
|
7480
|
+
}
|
7481
|
+
}
|
7482
|
+
|
7483
|
+
// Extensions used
|
7484
|
+
if (extensionsUsed.size()) {
|
7485
|
+
SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
|
7159
7486
|
}
|
7160
7487
|
|
7161
7488
|
// EXTRAS
|
@@ -7175,8 +7502,10 @@ static bool WriteGltfFile(const std::string &output,
|
|
7175
7502
|
#if defined(_MSC_VER)
|
7176
7503
|
std::ofstream gltfFile(UTF8ToWchar(output).c_str());
|
7177
7504
|
#elif defined(__GLIBCXX__)
|
7178
|
-
int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
|
7179
|
-
|
7505
|
+
int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
|
7506
|
+
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
|
7507
|
+
__gnu_cxx::stdio_filebuf<char> wfile_buf(
|
7508
|
+
file_descriptor, std::ios_base::out | std::ios_base::binary);
|
7180
7509
|
std::ostream gltfFile(&wfile_buf);
|
7181
7510
|
if (!wfile_buf.is_open()) return false;
|
7182
7511
|
#else
|
@@ -7196,33 +7525,25 @@ static void WriteBinaryGltfStream(std::ostream &stream,
|
|
7196
7525
|
const std::string header = "glTF";
|
7197
7526
|
const int version = 2;
|
7198
7527
|
|
7199
|
-
|
7200
|
-
|
7201
|
-
|
7202
|
-
|
7203
|
-
|
7204
|
-
|
7205
|
-
uint32_t remainder = numToRound % multiple;
|
7206
|
-
if (remainder == 0)
|
7207
|
-
return numToRound;
|
7208
|
-
|
7209
|
-
return numToRound + multiple - remainder;
|
7210
|
-
};
|
7211
|
-
|
7212
|
-
const uint32_t padding_size = roundUp(content.size(), 4) - content.size();
|
7528
|
+
const uint32_t content_size = uint32_t(content.size());
|
7529
|
+
const uint32_t binBuffer_size = uint32_t(binBuffer.size());
|
7530
|
+
// determine number of padding bytes required to ensure 4 byte alignment
|
7531
|
+
const uint32_t content_padding_size = content_size % 4 == 0 ? 0 : 4 - content_size % 4;
|
7532
|
+
const uint32_t bin_padding_size = binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
|
7213
7533
|
|
7214
7534
|
// 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
|
7215
|
-
// Chunk data must be located at 4-byte boundary
|
7216
|
-
const
|
7217
|
-
|
7535
|
+
// Chunk data must be located at 4-byte boundary, which may require padding
|
7536
|
+
const uint32_t length =
|
7537
|
+
12 + 8 + content_size + content_padding_size +
|
7538
|
+
(binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
|
7218
7539
|
|
7219
7540
|
stream.write(header.c_str(), std::streamsize(header.size()));
|
7220
7541
|
stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
|
7221
7542
|
stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
|
7222
7543
|
|
7223
7544
|
// JSON chunk info, then JSON data
|
7224
|
-
const
|
7225
|
-
const
|
7545
|
+
const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
|
7546
|
+
const uint32_t model_format = 0x4E4F534A;
|
7226
7547
|
stream.write(reinterpret_cast<const char *>(&model_length),
|
7227
7548
|
sizeof(model_length));
|
7228
7549
|
stream.write(reinterpret_cast<const char *>(&model_format),
|
@@ -7230,24 +7551,26 @@ static void WriteBinaryGltfStream(std::ostream &stream,
|
|
7230
7551
|
stream.write(content.c_str(), std::streamsize(content.size()));
|
7231
7552
|
|
7232
7553
|
// Chunk must be multiplies of 4, so pad with spaces
|
7233
|
-
if (
|
7234
|
-
const std::string padding = std::string(size_t(
|
7554
|
+
if (content_padding_size > 0) {
|
7555
|
+
const std::string padding = std::string(size_t(content_padding_size), ' ');
|
7235
7556
|
stream.write(padding.c_str(), std::streamsize(padding.size()));
|
7236
7557
|
}
|
7237
|
-
if (binBuffer.size() > 0){
|
7238
|
-
const uint32_t bin_padding_size = roundUp(binBuffer.size(), 4) - binBuffer.size();
|
7558
|
+
if (binBuffer.size() > 0) {
|
7239
7559
|
// BIN chunk info, then BIN data
|
7240
|
-
const
|
7241
|
-
const
|
7560
|
+
const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
|
7561
|
+
const uint32_t bin_format = 0x004e4942;
|
7242
7562
|
stream.write(reinterpret_cast<const char *>(&bin_length),
|
7243
|
-
|
7563
|
+
sizeof(bin_length));
|
7244
7564
|
stream.write(reinterpret_cast<const char *>(&bin_format),
|
7245
|
-
|
7246
|
-
stream.write(
|
7565
|
+
sizeof(bin_format));
|
7566
|
+
stream.write(reinterpret_cast<const char *>(binBuffer.data()),
|
7567
|
+
std::streamsize(binBuffer.size()));
|
7247
7568
|
// Chunksize must be multiplies of 4, so pad with zeroes
|
7248
7569
|
if (bin_padding_size > 0) {
|
7249
|
-
const std::vector<unsigned char> padding =
|
7250
|
-
|
7570
|
+
const std::vector<unsigned char> padding =
|
7571
|
+
std::vector<unsigned char>(size_t(bin_padding_size), 0);
|
7572
|
+
stream.write(reinterpret_cast<const char *>(padding.data()),
|
7573
|
+
std::streamsize(padding.size()));
|
7251
7574
|
}
|
7252
7575
|
}
|
7253
7576
|
}
|
@@ -7259,8 +7582,10 @@ static void WriteBinaryGltfFile(const std::string &output,
|
|
7259
7582
|
#if defined(_MSC_VER)
|
7260
7583
|
std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
|
7261
7584
|
#elif defined(__GLIBCXX__)
|
7262
|
-
int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
|
7263
|
-
|
7585
|
+
int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
|
7586
|
+
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
|
7587
|
+
__gnu_cxx::stdio_filebuf<char> wfile_buf(
|
7588
|
+
file_descriptor, std::ios_base::out | std::ios_base::binary);
|
7264
7589
|
std::ostream gltfFile(&wfile_buf);
|
7265
7590
|
#else
|
7266
7591
|
std::ofstream gltfFile(output.c_str(), std::ios::binary);
|
@@ -7268,7 +7593,7 @@ static void WriteBinaryGltfFile(const std::string &output,
|
|
7268
7593
|
#else
|
7269
7594
|
std::ofstream gltfFile(output.c_str(), std::ios::binary);
|
7270
7595
|
#endif
|
7271
|
-
WriteBinaryGltfStream(gltfFile, content,binBuffer);
|
7596
|
+
WriteBinaryGltfStream(gltfFile, content, binBuffer);
|
7272
7597
|
}
|
7273
7598
|
|
7274
7599
|
bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
|
@@ -7280,20 +7605,21 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
|
|
7280
7605
|
SerializeGltfModel(model, output);
|
7281
7606
|
|
7282
7607
|
// BUFFERS
|
7283
|
-
std::vector<std::string> usedUris;
|
7284
7608
|
std::vector<unsigned char> binBuffer;
|
7285
|
-
|
7286
|
-
|
7287
|
-
|
7288
|
-
|
7289
|
-
|
7290
|
-
|
7291
|
-
|
7292
|
-
|
7609
|
+
if (model->buffers.size()) {
|
7610
|
+
json buffers;
|
7611
|
+
JsonReserveArray(buffers, model->buffers.size());
|
7612
|
+
for (unsigned int i = 0; i < model->buffers.size(); ++i) {
|
7613
|
+
json buffer;
|
7614
|
+
if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
|
7615
|
+
SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
|
7616
|
+
} else {
|
7617
|
+
SerializeGltfBuffer(model->buffers[i], buffer);
|
7618
|
+
}
|
7619
|
+
JsonPushBack(buffers, std::move(buffer));
|
7293
7620
|
}
|
7294
|
-
|
7621
|
+
JsonAddMember(output, "buffers", std::move(buffers));
|
7295
7622
|
}
|
7296
|
-
JsonAddMember(output, "buffers", std::move(buffers));
|
7297
7623
|
|
7298
7624
|
// IMAGES
|
7299
7625
|
if (model->images.size()) {
|
@@ -7303,9 +7629,9 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
|
|
7303
7629
|
json image;
|
7304
7630
|
|
7305
7631
|
std::string dummystring = "";
|
7306
|
-
// UpdateImageObject need baseDir but only uses it if
|
7307
|
-
//
|
7308
|
-
//
|
7632
|
+
// UpdateImageObject need baseDir but only uses it if embeddedImages is
|
7633
|
+
// enabled, since we won't write separate images when writing to a stream
|
7634
|
+
// we
|
7309
7635
|
UpdateImageObject(model->images[i], dummystring, int(i), false,
|
7310
7636
|
&this->WriteImageData, this->write_image_user_data_);
|
7311
7637
|
SerializeGltfImage(model->images[i], image);
|
@@ -7315,7 +7641,7 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
|
|
7315
7641
|
}
|
7316
7642
|
|
7317
7643
|
if (writeBinary) {
|
7318
|
-
WriteBinaryGltfStream(stream, JsonToString(output),binBuffer);
|
7644
|
+
WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
|
7319
7645
|
} else {
|
7320
7646
|
WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
|
7321
7647
|
}
|
@@ -7347,44 +7673,47 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
|
|
7347
7673
|
// BUFFERS
|
7348
7674
|
std::vector<std::string> usedUris;
|
7349
7675
|
std::vector<unsigned char> binBuffer;
|
7350
|
-
|
7351
|
-
|
7352
|
-
|
7353
|
-
|
7354
|
-
|
7355
|
-
|
7356
|
-
|
7357
|
-
|
7358
|
-
|
7359
|
-
std::string binSavePath;
|
7360
|
-
std::string binUri;
|
7361
|
-
if (!model->buffers[i].uri.empty() && !IsDataURI(model->buffers[i].uri)) {
|
7362
|
-
binUri = model->buffers[i].uri;
|
7676
|
+
if (model->buffers.size()) {
|
7677
|
+
json buffers;
|
7678
|
+
JsonReserveArray(buffers, model->buffers.size());
|
7679
|
+
for (unsigned int i = 0; i < model->buffers.size(); ++i) {
|
7680
|
+
json buffer;
|
7681
|
+
if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
|
7682
|
+
SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
|
7683
|
+
} else if (embedBuffers) {
|
7684
|
+
SerializeGltfBuffer(model->buffers[i], buffer);
|
7363
7685
|
} else {
|
7364
|
-
|
7365
|
-
|
7366
|
-
|
7367
|
-
|
7368
|
-
|
7369
|
-
|
7370
|
-
|
7371
|
-
|
7372
|
-
|
7373
|
-
|
7374
|
-
|
7686
|
+
std::string binSavePath;
|
7687
|
+
std::string binUri;
|
7688
|
+
if (!model->buffers[i].uri.empty() &&
|
7689
|
+
!IsDataURI(model->buffers[i].uri)) {
|
7690
|
+
binUri = model->buffers[i].uri;
|
7691
|
+
} else {
|
7692
|
+
binUri = defaultBinFilename + defaultBinFileExt;
|
7693
|
+
bool inUse = true;
|
7694
|
+
int numUsed = 0;
|
7695
|
+
while (inUse) {
|
7696
|
+
inUse = false;
|
7697
|
+
for (const std::string &usedName : usedUris) {
|
7698
|
+
if (binUri.compare(usedName) != 0) continue;
|
7699
|
+
inUse = true;
|
7700
|
+
binUri = defaultBinFilename + std::to_string(numUsed++) +
|
7701
|
+
defaultBinFileExt;
|
7702
|
+
break;
|
7703
|
+
}
|
7375
7704
|
}
|
7376
7705
|
}
|
7706
|
+
usedUris.push_back(binUri);
|
7707
|
+
binSavePath = JoinPath(baseDir, binUri);
|
7708
|
+
if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
|
7709
|
+
binUri)) {
|
7710
|
+
return false;
|
7711
|
+
}
|
7377
7712
|
}
|
7378
|
-
|
7379
|
-
binSavePath = JoinPath(baseDir, binUri);
|
7380
|
-
if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
|
7381
|
-
binUri)) {
|
7382
|
-
return false;
|
7383
|
-
}
|
7713
|
+
JsonPushBack(buffers, std::move(buffer));
|
7384
7714
|
}
|
7385
|
-
|
7715
|
+
JsonAddMember(output, "buffers", std::move(buffers));
|
7386
7716
|
}
|
7387
|
-
JsonAddMember(output, "buffers", std::move(buffers));
|
7388
7717
|
|
7389
7718
|
// IMAGES
|
7390
7719
|
if (model->images.size()) {
|
@@ -7402,7 +7731,7 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
|
|
7402
7731
|
}
|
7403
7732
|
|
7404
7733
|
if (writeBinary) {
|
7405
|
-
WriteBinaryGltfFile(filename, JsonToString(output),binBuffer);
|
7734
|
+
WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
|
7406
7735
|
} else {
|
7407
7736
|
WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
|
7408
7737
|
}
|