tiny_gltf 1.0.1 → 2.5.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
}
|