|
|
|
|
@@ -26,10 +26,14 @@
|
|
|
|
|
// THE SOFTWARE.
|
|
|
|
|
|
|
|
|
|
// Version:
|
|
|
|
|
// - v2.7.0 Change WriteImageDataFunction user callback function signature. PR#393.
|
|
|
|
|
// - v2.6.3 Fix GLB file with empty BIN chunk was not handled. PR#382 and PR#383.
|
|
|
|
|
// - v2.6.2 Fix out-of-bounds access of accessors. PR#379.
|
|
|
|
|
// - v2.6.1 Better GLB validation check when loading.
|
|
|
|
|
// - v2.6.0 Support serializing sparse accessor(Thanks to @fynv).
|
|
|
|
|
// Disable expanding file path for security(no use of awkward `wordexp` anymore).
|
|
|
|
|
// - v2.5.0 Add SetPreserveImageChannels() option to load image data as is.
|
|
|
|
|
// - v2.4.3 Fix null object output when when material has all default
|
|
|
|
|
// - v2.4.3 Fix null object output when material has all default
|
|
|
|
|
// parameters.
|
|
|
|
|
// - v2.4.2 Decode percent-encoded URI.
|
|
|
|
|
// - v2.4.1 Fix some glTF object class does not have `extensions` and/or
|
|
|
|
|
@@ -43,7 +47,7 @@
|
|
|
|
|
// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks
|
|
|
|
|
// to @Ybalrid)
|
|
|
|
|
// - v2.1.0 Add draco compression.
|
|
|
|
|
// - v2.0.1 Add comparsion feature(Thanks to @Selmar).
|
|
|
|
|
// - v2.0.1 Add comparison feature(Thanks to @Selmar).
|
|
|
|
|
// - v2.0.0 glTF 2.0!.
|
|
|
|
|
//
|
|
|
|
|
// Tiny glTF loader is using following third party libraries:
|
|
|
|
|
@@ -66,6 +70,11 @@
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
//Auto-detect C++14 standard version
|
|
|
|
|
#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && (__cplusplus >= 201402L)
|
|
|
|
|
#define TINYGLTF_USE_CPP14
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef TINYGLTF_USE_CPP14
|
|
|
|
|
#include <functional>
|
|
|
|
|
#endif
|
|
|
|
|
@@ -192,7 +201,11 @@ namespace tinygltf {
|
|
|
|
|
|
|
|
|
|
#ifdef __ANDROID__
|
|
|
|
|
#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
|
|
|
|
|
#ifdef TINYGLTF_IMPLEMENTATION
|
|
|
|
|
AAssetManager *asset_manager = nullptr;
|
|
|
|
|
#else
|
|
|
|
|
extern AAssetManager *asset_manager;
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
@@ -225,7 +238,7 @@ static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
|
|
|
|
|
} else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
|
|
|
|
|
return 8;
|
|
|
|
|
} else {
|
|
|
|
|
// Unknown componenty type
|
|
|
|
|
// Unknown component type
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -246,7 +259,7 @@ static inline int32_t GetNumComponentsInType(uint32_t ty) {
|
|
|
|
|
} else if (ty == TINYGLTF_TYPE_MAT4) {
|
|
|
|
|
return 16;
|
|
|
|
|
} else {
|
|
|
|
|
// Unknown componenty type
|
|
|
|
|
// Unknown component type
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -433,7 +446,7 @@ TINYGLTF_VALUE_GET(Value::Object, object_value_)
|
|
|
|
|
#pragma clang diagnostic ignored "-Wpadded"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/// Agregate object for representing a color
|
|
|
|
|
/// Aggregate object for representing a color
|
|
|
|
|
using ColorValue = std::array<double, 4>;
|
|
|
|
|
|
|
|
|
|
// === legacy interface ====
|
|
|
|
|
@@ -470,7 +483,7 @@ struct Parameter {
|
|
|
|
|
if (it != std::end(json_double_value)) {
|
|
|
|
|
return int(it->second);
|
|
|
|
|
}
|
|
|
|
|
// As per the spec, if texCoord is ommited, this parameter is 0
|
|
|
|
|
// As per the spec, if texCoord is omitted, this parameter is 0
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -482,7 +495,7 @@ struct Parameter {
|
|
|
|
|
if (it != std::end(json_double_value)) {
|
|
|
|
|
return it->second;
|
|
|
|
|
}
|
|
|
|
|
// As per the spec, if scale is ommited, this paramter is 1
|
|
|
|
|
// As per the spec, if scale is omitted, this parameter is 1
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -494,7 +507,7 @@ struct Parameter {
|
|
|
|
|
if (it != std::end(json_double_value)) {
|
|
|
|
|
return it->second;
|
|
|
|
|
}
|
|
|
|
|
// As per the spec, if strenghth is ommited, this parameter is 1
|
|
|
|
|
// As per the spec, if strength is omitted, this parameter is 1
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -508,7 +521,7 @@ struct Parameter {
|
|
|
|
|
/// material
|
|
|
|
|
ColorValue ColorFactor() const {
|
|
|
|
|
return {
|
|
|
|
|
{// this agregate intialize the std::array object, and uses C++11 RVO.
|
|
|
|
|
{// this aggregate initialize the std::array object, and uses C++11 RVO.
|
|
|
|
|
number_array[0], number_array[1], number_array[2],
|
|
|
|
|
(number_array.size() > 3 ? number_array[3] : 1.0)}};
|
|
|
|
|
}
|
|
|
|
|
@@ -819,7 +832,7 @@ struct BufferView {
|
|
|
|
|
size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
|
|
|
|
|
// understood to be tightly packed
|
|
|
|
|
int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
|
|
|
|
|
// or atttribs. Could be 0 for other data
|
|
|
|
|
// or attribs. Could be 0 for other data
|
|
|
|
|
Value extras;
|
|
|
|
|
ExtensionMap extensions;
|
|
|
|
|
|
|
|
|
|
@@ -895,7 +908,7 @@ struct Accessor {
|
|
|
|
|
|
|
|
|
|
return componentSizeInBytes * numComponents;
|
|
|
|
|
} else {
|
|
|
|
|
// Check if byteStride is a mulple of the size of the accessor's component
|
|
|
|
|
// Check if byteStride is a multiple of the size of the accessor's component
|
|
|
|
|
// type.
|
|
|
|
|
int componentSizeInBytes =
|
|
|
|
|
GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
|
|
|
|
|
@@ -934,7 +947,7 @@ struct PerspectiveCamera {
|
|
|
|
|
PerspectiveCamera()
|
|
|
|
|
: aspectRatio(0.0),
|
|
|
|
|
yfov(0.0),
|
|
|
|
|
zfar(0.0) // 0 = use infinite projecton matrix
|
|
|
|
|
zfar(0.0) // 0 = use infinite projection matrix
|
|
|
|
|
,
|
|
|
|
|
znear(0.0) {}
|
|
|
|
|
DEFAULT_METHODS(PerspectiveCamera)
|
|
|
|
|
@@ -995,7 +1008,7 @@ struct Primitive {
|
|
|
|
|
int indices; // The index of the accessor that contains the indices.
|
|
|
|
|
int mode; // one of TINYGLTF_MODE_***
|
|
|
|
|
std::vector<std::map<std::string, int> > targets; // array of morph targets,
|
|
|
|
|
// where each target is a dict with attribues in ["POSITION, "NORMAL",
|
|
|
|
|
// where each target is a dict with attributes in ["POSITION, "NORMAL",
|
|
|
|
|
// "TANGENT"] pointing
|
|
|
|
|
// to their corresponding accessors
|
|
|
|
|
ExtensionMap extensions;
|
|
|
|
|
@@ -1130,7 +1143,7 @@ struct Light {
|
|
|
|
|
std::vector<double> color;
|
|
|
|
|
double intensity{1.0};
|
|
|
|
|
std::string type;
|
|
|
|
|
double range{0.0}; // 0.0 = inifinite
|
|
|
|
|
double range{0.0}; // 0.0 = infinite
|
|
|
|
|
SpotLight spot;
|
|
|
|
|
|
|
|
|
|
Light() : intensity(1.0), range(0.0) {}
|
|
|
|
|
@@ -1204,9 +1217,13 @@ typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
|
/// WriteImageDataFunction type. Signature for custom image writing callbacks.
|
|
|
|
|
/// The out_uri parameter becomes the URI written to the gltf and may reference
|
|
|
|
|
/// a file or contain a data URI.
|
|
|
|
|
///
|
|
|
|
|
typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
|
|
|
|
|
Image *, bool, void *);
|
|
|
|
|
typedef bool (*WriteImageDataFunction)(const std::string *basepath,
|
|
|
|
|
const std::string *filename,
|
|
|
|
|
const Image *image, bool embedImages,
|
|
|
|
|
std::string *out_uri, void *user_pointer);
|
|
|
|
|
|
|
|
|
|
#ifndef TINYGLTF_NO_STB_IMAGE
|
|
|
|
|
// Declaration of default image loader callback
|
|
|
|
|
@@ -1218,7 +1235,8 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err,
|
|
|
|
|
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
|
|
|
|
|
// Declaration of default image writer callback
|
|
|
|
|
bool WriteImageData(const std::string *basepath, const std::string *filename,
|
|
|
|
|
Image *image, bool embedImages, void *);
|
|
|
|
|
const Image *image, bool embedImages, std::string *out_uri,
|
|
|
|
|
void *);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
|
@@ -1280,7 +1298,7 @@ bool WriteWholeFile(std::string *err, const std::string &filepath,
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
|
/// glTF Parser/Serialier context.
|
|
|
|
|
/// glTF Parser/Serializer context.
|
|
|
|
|
///
|
|
|
|
|
class TinyGLTF {
|
|
|
|
|
public:
|
|
|
|
|
@@ -1343,15 +1361,15 @@ class TinyGLTF {
|
|
|
|
|
unsigned int check_sections = REQUIRE_VERSION);
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
|
/// Write glTF to stream, buffers and images will be embeded
|
|
|
|
|
/// Write glTF to stream, buffers and images will be embedded
|
|
|
|
|
///
|
|
|
|
|
bool WriteGltfSceneToStream(Model *model, std::ostream &stream,
|
|
|
|
|
bool WriteGltfSceneToStream(const Model *model, std::ostream &stream,
|
|
|
|
|
bool prettyPrint, bool writeBinary);
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
|
/// Write glTF to file.
|
|
|
|
|
///
|
|
|
|
|
bool WriteGltfSceneToFile(Model *model, const std::string &filename,
|
|
|
|
|
bool WriteGltfSceneToFile(const Model *model, const std::string &filename,
|
|
|
|
|
bool embedImages, bool embedBuffers,
|
|
|
|
|
bool prettyPrint, bool writeBinary);
|
|
|
|
|
|
|
|
|
|
@@ -1378,7 +1396,7 @@ class TinyGLTF {
|
|
|
|
|
///
|
|
|
|
|
/// Set serializing default values(default = false).
|
|
|
|
|
/// When true, default values are force serialized to .glTF.
|
|
|
|
|
/// This may be helpfull if you want to serialize a full description of glTF
|
|
|
|
|
/// This may be helpful if you want to serialize a full description of glTF
|
|
|
|
|
/// data.
|
|
|
|
|
///
|
|
|
|
|
/// TODO(LTE): Supply parsing option as function arguments to
|
|
|
|
|
@@ -1404,8 +1422,8 @@ class TinyGLTF {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
|
/// Specify whether preserve image channales when loading images or not.
|
|
|
|
|
/// (Not effective when the user suppy their own LoadImageData callbacks)
|
|
|
|
|
/// Specify whether preserve image channels when loading images or not.
|
|
|
|
|
/// (Not effective when the user supplies their own LoadImageData callbacks)
|
|
|
|
|
///
|
|
|
|
|
void SetPreserveImageChannels(bool onoff) {
|
|
|
|
|
preserve_image_channels_ = onoff;
|
|
|
|
|
@@ -1542,7 +1560,7 @@ class TinyGLTF {
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Disable GCC warnigs
|
|
|
|
|
// Disable GCC warnings
|
|
|
|
|
#ifdef __GNUC__
|
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
|
#pragma GCC diagnostic ignored "-Wtype-limits"
|
|
|
|
|
@@ -1591,7 +1609,7 @@ class TinyGLTF {
|
|
|
|
|
|
|
|
|
|
// issue 143.
|
|
|
|
|
// Define NOMINMAX to avoid min/max defines,
|
|
|
|
|
// but undef it after included windows.h
|
|
|
|
|
// but undef it after included Windows.h
|
|
|
|
|
#ifndef NOMINMAX
|
|
|
|
|
#define TINYGLTF_INTERNAL_NOMINMAX
|
|
|
|
|
#define NOMINMAX
|
|
|
|
|
@@ -1601,7 +1619,11 @@ class TinyGLTF {
|
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
|
#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
|
|
|
|
|
#endif
|
|
|
|
|
#include <windows.h> // include API for expanding a file path
|
|
|
|
|
#ifndef __MINGW32__
|
|
|
|
|
#include <Windows.h> // include API for expanding a file path
|
|
|
|
|
#else
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
|
|
|
|
|
#undef WIN32_LEAN_AND_MEAN
|
|
|
|
|
@@ -1732,7 +1754,7 @@ namespace tinygltf {
|
|
|
|
|
struct LoadImageDataOption {
|
|
|
|
|
// true: preserve image channels(e.g. load as RGB image if the image has RGB
|
|
|
|
|
// channels) default `false`(channels are expanded to RGBA for backward
|
|
|
|
|
// compatiblity).
|
|
|
|
|
// compatibility).
|
|
|
|
|
bool preserve_channels{false};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -2379,7 +2401,7 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err,
|
|
|
|
|
|
|
|
|
|
// It is possible that the image we want to load is a 16bit per channel image
|
|
|
|
|
// We are going to attempt to load it as 16bit per channel, and if it worked,
|
|
|
|
|
// set the image data accodingly. We are casting the returned pointer into
|
|
|
|
|
// set the image data accordingly. We are casting the returned pointer into
|
|
|
|
|
// unsigned char, because we are representing "bytes". But we are updating
|
|
|
|
|
// the Image metadata to signal that this image uses 2 bytes (16bits) per
|
|
|
|
|
// channel:
|
|
|
|
|
@@ -2394,7 +2416,7 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err,
|
|
|
|
|
|
|
|
|
|
// at this point, if data is still NULL, it means that the image wasn't
|
|
|
|
|
// 16bit per channel, we are going to load it as a normal 8bit per channel
|
|
|
|
|
// mage as we used to do:
|
|
|
|
|
// image as we used to do:
|
|
|
|
|
// if image cannot be decoded, ignore parsing and keep it by its path
|
|
|
|
|
// don't break in this case
|
|
|
|
|
// FIXME we should only enter this function if the image is embedded. If
|
|
|
|
|
@@ -2479,7 +2501,8 @@ static void WriteToMemory_stbi(void *context, void *data, int size) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool WriteImageData(const std::string *basepath, const std::string *filename,
|
|
|
|
|
Image *image, bool embedImages, void *fsPtr) {
|
|
|
|
|
const Image *image, bool embedImages, std::string *out_uri,
|
|
|
|
|
void *fsPtr) {
|
|
|
|
|
const std::string ext = GetFilePathExtension(*filename);
|
|
|
|
|
|
|
|
|
|
// Write image to temporary buffer
|
|
|
|
|
@@ -2521,9 +2544,8 @@ bool WriteImageData(const std::string *basepath, const std::string *filename,
|
|
|
|
|
if (embedImages) {
|
|
|
|
|
// Embed base64-encoded image into URI
|
|
|
|
|
if (data.size()) {
|
|
|
|
|
image->uri =
|
|
|
|
|
header +
|
|
|
|
|
base64_encode(&data[0], static_cast<unsigned int>(data.size()));
|
|
|
|
|
*out_uri = header +
|
|
|
|
|
base64_encode(&data[0], static_cast<unsigned int>(data.size()));
|
|
|
|
|
} else {
|
|
|
|
|
// Throw error?
|
|
|
|
|
}
|
|
|
|
|
@@ -2541,7 +2563,7 @@ bool WriteImageData(const std::string *basepath, const std::string *filename,
|
|
|
|
|
} else {
|
|
|
|
|
// Throw error?
|
|
|
|
|
}
|
|
|
|
|
image->uri = *filename;
|
|
|
|
|
*out_uri = *filename;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
@@ -2554,7 +2576,7 @@ void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
|
|
|
|
|
static inline std::wstring UTF8ToWchar(const std::string &str) {
|
|
|
|
|
int wstr_size =
|
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
|
|
|
|
|
std::wstring wstr(wstr_size, 0);
|
|
|
|
|
std::wstring wstr((size_t)wstr_size, 0);
|
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
|
|
|
|
|
(int)wstr.size());
|
|
|
|
|
return wstr;
|
|
|
|
|
@@ -2562,10 +2584,10 @@ static inline std::wstring UTF8ToWchar(const std::string &str) {
|
|
|
|
|
|
|
|
|
|
static inline std::string WcharToUTF8(const std::wstring &wstr) {
|
|
|
|
|
int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
|
|
|
|
|
nullptr, 0, NULL, NULL);
|
|
|
|
|
std::string str(str_size, 0);
|
|
|
|
|
nullptr, 0, nullptr, nullptr);
|
|
|
|
|
std::string str((size_t)str_size, 0);
|
|
|
|
|
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
|
|
|
|
|
(int)str.size(), NULL, NULL);
|
|
|
|
|
(int)str.size(), nullptr, nullptr);
|
|
|
|
|
return str;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
@@ -2589,7 +2611,7 @@ bool FileExists(const std::string &abs_filename, void *) {
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
#if defined(_MSC_VER) || defined(__GLIBCXX__)
|
|
|
|
|
#if defined(_MSC_VER) || defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
|
|
|
|
|
FILE *fp = nullptr;
|
|
|
|
|
errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
|
|
|
|
|
if (err != 0) {
|
|
|
|
|
@@ -2812,13 +2834,13 @@ static std::string MimeToExt(const std::string &mimeType) {
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UpdateImageObject(Image &image, std::string &baseDir, int index,
|
|
|
|
|
bool embedImages,
|
|
|
|
|
WriteImageDataFunction *WriteImageData = nullptr,
|
|
|
|
|
void *user_data = nullptr) {
|
|
|
|
|
static void UpdateImageObject(const Image &image, std::string &baseDir,
|
|
|
|
|
int index, bool embedImages,
|
|
|
|
|
WriteImageDataFunction *WriteImageData,
|
|
|
|
|
std::string *out_uri, void *user_data) {
|
|
|
|
|
std::string filename;
|
|
|
|
|
std::string ext;
|
|
|
|
|
// If image has uri, use it it as a filename
|
|
|
|
|
// If image has uri, use it as a filename
|
|
|
|
|
if (image.uri.size()) {
|
|
|
|
|
filename = GetBaseFilename(image.uri);
|
|
|
|
|
ext = GetFilePathExtension(filename);
|
|
|
|
|
@@ -2836,9 +2858,15 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If callback is set, modify image data object
|
|
|
|
|
bool imageWritten = false;
|
|
|
|
|
if (*WriteImageData != nullptr && !filename.empty()) {
|
|
|
|
|
std::string uri;
|
|
|
|
|
(*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
|
|
|
|
|
imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages,
|
|
|
|
|
out_uri, user_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use the original uri if the image was not written.
|
|
|
|
|
if (!imageWritten) {
|
|
|
|
|
*out_uri = image.uri;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -3229,9 +3257,11 @@ static bool ParseJsonAsValue(Value *ret, const json &o) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
const bool isNotNull = val.Type() != NULL_TYPE;
|
|
|
|
|
|
|
|
|
|
if (ret) *ret = std::move(val);
|
|
|
|
|
|
|
|
|
|
return val.Type() != NULL_TYPE;
|
|
|
|
|
return isNotNull;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ParseExtrasProperty(Value *ret, const json &o) {
|
|
|
|
|
@@ -3671,7 +3701,8 @@ static bool ParseParameterProperty(Parameter *param, std::string *err,
|
|
|
|
|
// Found a number array.
|
|
|
|
|
return true;
|
|
|
|
|
} else if (ParseNumberProperty(¶m->number_value, err, o, prop, false)) {
|
|
|
|
|
return param->has_number_value = true;
|
|
|
|
|
param->has_number_value = true;
|
|
|
|
|
return true;
|
|
|
|
|
} else if (ParseJSONProperty(¶m->json_double_value, err, o, prop,
|
|
|
|
|
false)) {
|
|
|
|
|
return true;
|
|
|
|
|
@@ -3864,7 +3895,7 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
|
|
|
|
|
image->uri = uri;
|
|
|
|
|
#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
|
|
|
|
|
return true;
|
|
|
|
|
#endif
|
|
|
|
|
#else
|
|
|
|
|
std::string decoded_uri = dlib::urldecode(uri);
|
|
|
|
|
if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
|
|
|
|
|
/* required */ false, /* required bytes */ 0,
|
|
|
|
|
@@ -3886,6 +3917,7 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*LoadImageData == nullptr) {
|
|
|
|
|
@@ -4102,7 +4134,7 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
|
|
|
|
|
|
|
|
|
|
if ((bin_size == 0) || (bin_data == nullptr)) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) += "Invalid binary data in `Buffer'.\n";
|
|
|
|
|
(*err) += "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@@ -4260,7 +4292,7 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!FindMember(o, "values", values_iterator)) {
|
|
|
|
|
(*err) = "the sparse object ob ths accessor doesn't have values";
|
|
|
|
|
(*err) = "the sparse object of this accessor doesn't have values";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -4606,7 +4638,7 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
|
|
|
|
|
|
|
|
|
|
int mode = TINYGLTF_MODE_TRIANGLES;
|
|
|
|
|
ParseIntegerProperty(&mode, err, o, "mode", false);
|
|
|
|
|
primitive->mode = mode; // Why only triangled were supported ?
|
|
|
|
|
primitive->mode = mode; // Why only triangles were supported ?
|
|
|
|
|
|
|
|
|
|
int indices = -1;
|
|
|
|
|
ParseIntegerProperty(&indices, err, o, "indices", false);
|
|
|
|
|
@@ -4889,7 +4921,7 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o,
|
|
|
|
|
|
|
|
|
|
// Old code path. For backward compatibility, we still store material values
|
|
|
|
|
// as Parameter. This will create duplicated information for
|
|
|
|
|
// example(pbrMetallicRoughness), but should be neglible in terms of memory
|
|
|
|
|
// example(pbrMetallicRoughness), but should be negligible in terms of memory
|
|
|
|
|
// consumption.
|
|
|
|
|
// TODO(syoyo): Remove in the next major release.
|
|
|
|
|
material->values.clear();
|
|
|
|
|
@@ -4922,7 +4954,7 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o,
|
|
|
|
|
Parameter param;
|
|
|
|
|
if (ParseParameterProperty(¶m, err, o, key, false)) {
|
|
|
|
|
// names of materials have already been parsed. Putting it in this map
|
|
|
|
|
// doesn't correctly reflext the glTF specification
|
|
|
|
|
// doesn't correctly reflect the glTF specification
|
|
|
|
|
if (key != "name")
|
|
|
|
|
material->additionalValues.emplace(std::move(key), std::move(param));
|
|
|
|
|
}
|
|
|
|
|
@@ -5128,7 +5160,7 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
|
|
|
|
|
// ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf
|
|
|
|
|
// extension
|
|
|
|
|
|
|
|
|
|
// TODO(syoyo): Check the value is alloed one.
|
|
|
|
|
// TODO(syoyo): Check the value is allowed one.
|
|
|
|
|
// (e.g. we allow 9728(NEAREST), but don't allow 9727)
|
|
|
|
|
|
|
|
|
|
sampler->minFilter = minFilter;
|
|
|
|
|
@@ -5337,7 +5369,7 @@ static bool ParseCamera(Camera *camera, std::string *err, const json &o,
|
|
|
|
|
if (!FindMember(o, "orthographic", orthoIt)) {
|
|
|
|
|
if (err) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "Orhographic camera description not found." << std::endl;
|
|
|
|
|
ss << "Orthographic camera description not found." << std::endl;
|
|
|
|
|
(*err) += ss.str();
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
@@ -5789,25 +5821,32 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
|
|
|
|
|
|
|
|
|
model->bufferViews[size_t(bufferView)].target =
|
|
|
|
|
TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
|
|
|
|
|
// we could optionally check if acessors' bufferView type is Scalar, as
|
|
|
|
|
// we could optionally check if accessors' bufferView type is Scalar, as
|
|
|
|
|
// it should be
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto &attribute : primitive.attributes) {
|
|
|
|
|
model
|
|
|
|
|
->bufferViews[size_t(
|
|
|
|
|
model->accessors[size_t(attribute.second)].bufferView)]
|
|
|
|
|
.target = TINYGLTF_TARGET_ARRAY_BUFFER;
|
|
|
|
|
const auto accessorsIndex = size_t(attribute.second);
|
|
|
|
|
if (accessorsIndex < model->accessors.size()) {
|
|
|
|
|
const auto bufferView = model->accessors[accessorsIndex].bufferView;
|
|
|
|
|
// bufferView could be null(-1) for sparse morph target
|
|
|
|
|
if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
|
|
|
|
|
model->bufferViews[size_t(bufferView)].target =
|
|
|
|
|
TINYGLTF_TARGET_ARRAY_BUFFER;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto &target : primitive.targets) {
|
|
|
|
|
for (auto &attribute : target) {
|
|
|
|
|
auto bufferView =
|
|
|
|
|
model->accessors[size_t(attribute.second)].bufferView;
|
|
|
|
|
// bufferView could be null(-1) for sparse morph target
|
|
|
|
|
if (bufferView >= 0) {
|
|
|
|
|
model->bufferViews[size_t(bufferView)].target =
|
|
|
|
|
TINYGLTF_TARGET_ARRAY_BUFFER;
|
|
|
|
|
const auto accessorsIndex = size_t(attribute.second);
|
|
|
|
|
if (accessorsIndex < model->accessors.size()) {
|
|
|
|
|
const auto bufferView = model->accessors[accessorsIndex].bufferView;
|
|
|
|
|
// bufferView could be null(-1) for sparse morph target
|
|
|
|
|
if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
|
|
|
|
|
model->bufferViews[size_t(bufferView)].target =
|
|
|
|
|
TINYGLTF_TARGET_ARRAY_BUFFER;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -6268,15 +6307,15 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
|
|
|
|
|
//
|
|
|
|
|
// https://github.com/syoyo/tinygltf/issues/372
|
|
|
|
|
// Use 64bit uint to avoid integer overflow.
|
|
|
|
|
uint64_t json_size = 20ull + uint64_t(chunk0_length);
|
|
|
|
|
uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
|
|
|
|
|
|
|
|
|
|
if (json_size > std::numeric_limits<uint32_t>::max()) {
|
|
|
|
|
if (header_and_json_size > std::numeric_limits<uint32_t>::max()) {
|
|
|
|
|
// Do not allow 4GB or more GLB data.
|
|
|
|
|
(*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) ||
|
|
|
|
|
(json_size > uint64_t(length)) ||
|
|
|
|
|
if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) ||
|
|
|
|
|
(header_and_json_size > uint64_t(length)) ||
|
|
|
|
|
(chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) = "Invalid glTF binary.";
|
|
|
|
|
@@ -6287,61 +6326,77 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
|
|
|
|
|
// Padding check
|
|
|
|
|
// The start and the end of each chunk must be aligned to a 4-byte boundary.
|
|
|
|
|
// No padding check for chunk0 start since its 4byte-boundary is ensured.
|
|
|
|
|
if ((json_size % 4) != 0) {
|
|
|
|
|
if ((header_and_json_size % 4) != 0) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read Chunk1 info(BIN data)
|
|
|
|
|
if ((json_size + 8ull) > uint64_t(length)) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) = "Insufficient storage space for Chunk1(BIN data).";
|
|
|
|
|
//std::cout << "header_and_json_size = " << header_and_json_size << "\n";
|
|
|
|
|
//std::cout << "length = " << length << "\n";
|
|
|
|
|
|
|
|
|
|
// Chunk1(BIN) data
|
|
|
|
|
// The spec says: When the binary buffer is empty or when it is stored by other means, this chunk SHOULD be omitted.
|
|
|
|
|
// So when header + JSON data == binary size, Chunk1 is omitted.
|
|
|
|
|
if (header_and_json_size == uint64_t(length)) {
|
|
|
|
|
|
|
|
|
|
bin_data_ = nullptr;
|
|
|
|
|
bin_size_ = 0;
|
|
|
|
|
} else {
|
|
|
|
|
// Read Chunk1 info(BIN data)
|
|
|
|
|
// At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin payload could be 1~3 bytes, but need to be aligned to 4 bytes)
|
|
|
|
|
if ((header_and_json_size + 12ull) > uint64_t(length)) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) = "Insufficient storage space for Chunk1(BIN data). At least Chunk1 Must have 4 bytes or more bytes, but got " + std::to_string((header_and_json_size + 12ull) - uint64_t(length)) + ".\n";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int chunk1_length; // 4 bytes
|
|
|
|
|
unsigned int chunk1_format; // 4 bytes;
|
|
|
|
|
memcpy(&chunk1_length, bytes + json_size, 4); // JSON data length
|
|
|
|
|
swap4(&chunk1_length);
|
|
|
|
|
memcpy(&chunk1_format, bytes + json_size + 4, 4);
|
|
|
|
|
swap4(&chunk1_format);
|
|
|
|
|
unsigned int chunk1_length; // 4 bytes
|
|
|
|
|
unsigned int chunk1_format; // 4 bytes;
|
|
|
|
|
memcpy(&chunk1_length, bytes + header_and_json_size, 4); // JSON data length
|
|
|
|
|
swap4(&chunk1_length);
|
|
|
|
|
memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
|
|
|
|
|
swap4(&chunk1_format);
|
|
|
|
|
|
|
|
|
|
if (chunk1_length < 4) {
|
|
|
|
|
// TODO: Do we allow 0byte BIN data?
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) = "Insufficient Chunk1(BIN) data size.";
|
|
|
|
|
//std::cout << "chunk1_length = " << chunk1_length << "\n";
|
|
|
|
|
|
|
|
|
|
if (chunk1_length < 4) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) = "Insufficient Chunk1(BIN) data size.";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((chunk1_length % 4) != 0) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) = "BIN Chunk end does not aligned to a 4-byte boundary.";
|
|
|
|
|
if ((chunk1_length % 4) != 0) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) = "BIN Chunk end does not aligned to a 4-byte boundary.";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (uint64_t(chunk1_length) + json_size > uint64_t(length)) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) = "BIN Chunk data length exceeds the GLB size.";
|
|
|
|
|
if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) = "BIN Chunk data length exceeds the GLB size.";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (chunk1_format != 0x004e4942) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) = "Invlid type for chunk1 data.";
|
|
|
|
|
if (chunk1_format != 0x004e4942) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) = "Invalid type for chunk1 data.";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//std::cout << "chunk1_length = " << chunk1_length << "\n";
|
|
|
|
|
|
|
|
|
|
bin_data_ = bytes + header_and_json_size +
|
|
|
|
|
8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
|
|
|
|
|
|
|
|
|
|
bin_size_ = size_t(chunk1_length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bin_data_ = bytes + json_size +
|
|
|
|
|
8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
|
|
|
|
|
|
|
|
|
|
bin_size_ = size_t(chunk1_length);
|
|
|
|
|
|
|
|
|
|
// Extract JSON string.
|
|
|
|
|
std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
|
|
|
|
|
chunk0_length);
|
|
|
|
|
@@ -6618,7 +6673,7 @@ static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
|
|
|
|
|
SerializeStringProperty("uri", header + encodedData, o);
|
|
|
|
|
} else {
|
|
|
|
|
// Issue #229
|
|
|
|
|
// size 0 is allowd. Just emit mime header.
|
|
|
|
|
// size 0 is allowed. Just emit mime header.
|
|
|
|
|
SerializeStringProperty("uri", header, o);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -6712,7 +6767,7 @@ static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
|
|
|
|
|
JsonAddMember(o, "extensions", std::move(extMap));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfAccessor(Accessor &accessor, json &o) {
|
|
|
|
|
static void SerializeGltfAccessor(const Accessor &accessor, json &o) {
|
|
|
|
|
if (accessor.bufferView >= 0)
|
|
|
|
|
SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
|
|
|
|
|
|
|
|
|
|
@@ -6804,7 +6859,8 @@ static void SerializeGltfAccessor(Accessor &accessor, json &o) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
|
|
|
|
|
static void SerializeGltfAnimationChannel(const AnimationChannel &channel,
|
|
|
|
|
json &o) {
|
|
|
|
|
SerializeNumberProperty("sampler", channel.sampler, o);
|
|
|
|
|
{
|
|
|
|
|
json target;
|
|
|
|
|
@@ -6823,7 +6879,8 @@ static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
|
|
|
|
|
SerializeExtensionMap(channel.extensions, o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
|
|
|
|
|
static void SerializeGltfAnimationSampler(const AnimationSampler &sampler,
|
|
|
|
|
json &o) {
|
|
|
|
|
SerializeNumberProperty("input", sampler.input, o);
|
|
|
|
|
SerializeNumberProperty("output", sampler.output, o);
|
|
|
|
|
SerializeStringProperty("interpolation", sampler.interpolation, o);
|
|
|
|
|
@@ -6833,7 +6890,7 @@ static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfAnimation(Animation &animation, json &o) {
|
|
|
|
|
static void SerializeGltfAnimation(const Animation &animation, json &o) {
|
|
|
|
|
if (!animation.name.empty())
|
|
|
|
|
SerializeStringProperty("name", animation.name, o);
|
|
|
|
|
|
|
|
|
|
@@ -6869,7 +6926,7 @@ static void SerializeGltfAnimation(Animation &animation, json &o) {
|
|
|
|
|
SerializeExtensionMap(animation.extensions, o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfAsset(Asset &asset, json &o) {
|
|
|
|
|
static void SerializeGltfAsset(const Asset &asset, json &o) {
|
|
|
|
|
if (!asset.generator.empty()) {
|
|
|
|
|
SerializeStringProperty("generator", asset.generator, o);
|
|
|
|
|
}
|
|
|
|
|
@@ -6878,14 +6935,15 @@ static void SerializeGltfAsset(Asset &asset, json &o) {
|
|
|
|
|
SerializeStringProperty("copyright", asset.copyright, o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (asset.version.empty()) {
|
|
|
|
|
auto version = asset.version;
|
|
|
|
|
if (version.empty()) {
|
|
|
|
|
// Just in case
|
|
|
|
|
// `version` must be defined
|
|
|
|
|
asset.version = "2.0";
|
|
|
|
|
version = "2.0";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
|
|
|
|
|
SerializeStringProperty("version", asset.version, o);
|
|
|
|
|
SerializeStringProperty("version", version, o);
|
|
|
|
|
|
|
|
|
|
if (asset.extras.Keys().size()) {
|
|
|
|
|
SerializeValue("extras", asset.extras, o);
|
|
|
|
|
@@ -6894,7 +6952,7 @@ static void SerializeGltfAsset(Asset &asset, json &o) {
|
|
|
|
|
SerializeExtensionMap(asset.extensions, o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfBufferBin(Buffer &buffer, json &o,
|
|
|
|
|
static void SerializeGltfBufferBin(const Buffer &buffer, json &o,
|
|
|
|
|
std::vector<unsigned char> &binBuffer) {
|
|
|
|
|
SerializeNumberProperty("byteLength", buffer.data.size(), o);
|
|
|
|
|
binBuffer = buffer.data;
|
|
|
|
|
@@ -6906,7 +6964,7 @@ static void SerializeGltfBufferBin(Buffer &buffer, json &o,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfBuffer(Buffer &buffer, json &o) {
|
|
|
|
|
static void SerializeGltfBuffer(const Buffer &buffer, json &o) {
|
|
|
|
|
SerializeNumberProperty("byteLength", buffer.data.size(), o);
|
|
|
|
|
SerializeGltfBufferData(buffer.data, o);
|
|
|
|
|
|
|
|
|
|
@@ -6917,7 +6975,7 @@ static void SerializeGltfBuffer(Buffer &buffer, json &o) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool SerializeGltfBuffer(Buffer &buffer, json &o,
|
|
|
|
|
static bool SerializeGltfBuffer(const Buffer &buffer, json &o,
|
|
|
|
|
const std::string &binFilename,
|
|
|
|
|
const std::string &binBaseFilename) {
|
|
|
|
|
if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
|
|
|
|
|
@@ -6932,7 +6990,7 @@ static bool SerializeGltfBuffer(Buffer &buffer, json &o,
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
|
|
|
|
|
static void SerializeGltfBufferView(const BufferView &bufferView, json &o) {
|
|
|
|
|
SerializeNumberProperty("buffer", bufferView.buffer, o);
|
|
|
|
|
SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
|
|
|
|
|
|
|
|
|
|
@@ -6958,14 +7016,16 @@ static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfImage(Image &image, json &o) {
|
|
|
|
|
// if uri empty, the mimeType and bufferview should be set
|
|
|
|
|
if (image.uri.empty()) {
|
|
|
|
|
static void SerializeGltfImage(const Image &image, const std::string uri,
|
|
|
|
|
json &o) {
|
|
|
|
|
// From 2.7.0, we look for `uri` parameter, not `Image.uri`
|
|
|
|
|
// if uri is empty, the mimeType and bufferview should be set
|
|
|
|
|
if (uri.empty()) {
|
|
|
|
|
SerializeStringProperty("mimeType", image.mimeType, o);
|
|
|
|
|
SerializeNumberProperty<int>("bufferView", image.bufferView, o);
|
|
|
|
|
} else {
|
|
|
|
|
// TODO(syoyo): dlib::urilencode?
|
|
|
|
|
SerializeStringProperty("uri", image.uri, o);
|
|
|
|
|
SerializeStringProperty("uri", uri, o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (image.name.size()) {
|
|
|
|
|
@@ -6979,7 +7039,7 @@ static void SerializeGltfImage(Image &image, json &o) {
|
|
|
|
|
SerializeExtensionMap(image.extensions, o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) {
|
|
|
|
|
static void SerializeGltfTextureInfo(const TextureInfo &texinfo, json &o) {
|
|
|
|
|
SerializeNumberProperty("index", texinfo.index, o);
|
|
|
|
|
|
|
|
|
|
if (texinfo.texCoord != 0) {
|
|
|
|
|
@@ -6993,7 +7053,7 @@ static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) {
|
|
|
|
|
SerializeExtensionMap(texinfo.extensions, o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo,
|
|
|
|
|
static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo,
|
|
|
|
|
json &o) {
|
|
|
|
|
SerializeNumberProperty("index", texinfo.index, o);
|
|
|
|
|
|
|
|
|
|
@@ -7012,8 +7072,8 @@ static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo,
|
|
|
|
|
SerializeExtensionMap(texinfo.extensions, o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo,
|
|
|
|
|
json &o) {
|
|
|
|
|
static void SerializeGltfOcclusionTextureInfo(
|
|
|
|
|
const OcclusionTextureInfo &texinfo, json &o) {
|
|
|
|
|
SerializeNumberProperty("index", texinfo.index, o);
|
|
|
|
|
|
|
|
|
|
if (texinfo.texCoord != 0) {
|
|
|
|
|
@@ -7031,7 +7091,7 @@ static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo,
|
|
|
|
|
SerializeExtensionMap(texinfo.extensions, o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr,
|
|
|
|
|
static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr,
|
|
|
|
|
json &o) {
|
|
|
|
|
std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
|
|
|
|
|
if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
|
|
|
|
|
@@ -7066,7 +7126,7 @@ static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfMaterial(Material &material, json &o) {
|
|
|
|
|
static void SerializeGltfMaterial(const Material &material, json &o) {
|
|
|
|
|
if (material.name.size()) {
|
|
|
|
|
SerializeStringProperty("name", material.name, o);
|
|
|
|
|
}
|
|
|
|
|
@@ -7116,7 +7176,7 @@ static void SerializeGltfMaterial(Material &material, json &o) {
|
|
|
|
|
// Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
|
|
|
|
|
// default values(json is null). Otherwise it will serialize to
|
|
|
|
|
// `pbrMetallicRoughness : null`, which cannot be read by other glTF
|
|
|
|
|
// importers(and validators).
|
|
|
|
|
// importers (and validators).
|
|
|
|
|
//
|
|
|
|
|
if (!JsonIsNull(pbrMetallicRoughness)) {
|
|
|
|
|
JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
|
|
|
|
|
@@ -7142,7 +7202,7 @@ static void SerializeGltfMaterial(Material &material, json &o) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfMesh(Mesh &mesh, json &o) {
|
|
|
|
|
static void SerializeGltfMesh(const Mesh &mesh, json &o) {
|
|
|
|
|
json primitives;
|
|
|
|
|
JsonReserveArray(primitives, mesh.primitives.size());
|
|
|
|
|
for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
|
|
|
|
|
@@ -7158,7 +7218,7 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) {
|
|
|
|
|
JsonAddMember(primitive, "attributes", std::move(attributes));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Indicies is optional
|
|
|
|
|
// Indices is optional
|
|
|
|
|
if (gltfPrimitive.indices > -1) {
|
|
|
|
|
SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
|
|
|
|
|
}
|
|
|
|
|
@@ -7211,7 +7271,7 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeSpotLight(SpotLight &spot, json &o) {
|
|
|
|
|
static void SerializeSpotLight(const SpotLight &spot, json &o) {
|
|
|
|
|
SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
|
|
|
|
|
SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
|
|
|
|
|
SerializeExtensionMap(spot.extensions, o);
|
|
|
|
|
@@ -7220,7 +7280,7 @@ static void SerializeSpotLight(SpotLight &spot, json &o) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfLight(Light &light, json &o) {
|
|
|
|
|
static void SerializeGltfLight(const Light &light, json &o) {
|
|
|
|
|
if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
|
|
|
|
|
SerializeNumberProperty("intensity", light.intensity, o);
|
|
|
|
|
if (light.range > 0.0) {
|
|
|
|
|
@@ -7239,7 +7299,7 @@ static void SerializeGltfLight(Light &light, json &o) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfNode(Node &node, json &o) {
|
|
|
|
|
static void SerializeGltfNode(const Node &node, json &o) {
|
|
|
|
|
if (node.translation.size() > 0) {
|
|
|
|
|
SerializeNumberArrayProperty<double>("translation", node.translation, o);
|
|
|
|
|
}
|
|
|
|
|
@@ -7277,7 +7337,7 @@ static void SerializeGltfNode(Node &node, json &o) {
|
|
|
|
|
SerializeNumberArrayProperty<int>("children", node.children, o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfSampler(Sampler &sampler, json &o) {
|
|
|
|
|
static void SerializeGltfSampler(const Sampler &sampler, json &o) {
|
|
|
|
|
if (sampler.magFilter != -1) {
|
|
|
|
|
SerializeNumberProperty("magFilter", sampler.magFilter, o);
|
|
|
|
|
}
|
|
|
|
|
@@ -7346,7 +7406,7 @@ static void SerializeGltfCamera(const Camera &camera, json &o) {
|
|
|
|
|
SerializeExtensionMap(camera.extensions, o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfScene(Scene &scene, json &o) {
|
|
|
|
|
static void SerializeGltfScene(const Scene &scene, json &o) {
|
|
|
|
|
SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
|
|
|
|
|
|
|
|
|
|
if (scene.name.size()) {
|
|
|
|
|
@@ -7358,7 +7418,7 @@ static void SerializeGltfScene(Scene &scene, json &o) {
|
|
|
|
|
SerializeExtensionMap(scene.extensions, o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfSkin(Skin &skin, json &o) {
|
|
|
|
|
static void SerializeGltfSkin(const Skin &skin, json &o) {
|
|
|
|
|
// required
|
|
|
|
|
SerializeNumberArrayProperty<int>("joints", skin.joints, o);
|
|
|
|
|
|
|
|
|
|
@@ -7375,7 +7435,7 @@ static void SerializeGltfSkin(Skin &skin, json &o) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SerializeGltfTexture(Texture &texture, json &o) {
|
|
|
|
|
static void SerializeGltfTexture(const Texture &texture, json &o) {
|
|
|
|
|
if (texture.sampler > -1) {
|
|
|
|
|
SerializeNumberProperty("sampler", texture.sampler, o);
|
|
|
|
|
}
|
|
|
|
|
@@ -7394,7 +7454,7 @@ static void SerializeGltfTexture(Texture &texture, json &o) {
|
|
|
|
|
///
|
|
|
|
|
/// Serialize all properties except buffers and images.
|
|
|
|
|
///
|
|
|
|
|
static void SerializeGltfModel(Model *model, json &o) {
|
|
|
|
|
static void SerializeGltfModel(const Model *model, json &o) {
|
|
|
|
|
// ACCESSORS
|
|
|
|
|
if (model->accessors.size()) {
|
|
|
|
|
json accessors;
|
|
|
|
|
@@ -7720,7 +7780,7 @@ static bool WriteBinaryGltfFile(const std::string &output,
|
|
|
|
|
return WriteBinaryGltfStream(gltfFile, content, binBuffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
|
|
|
|
|
bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream,
|
|
|
|
|
bool prettyPrint = true,
|
|
|
|
|
bool writeBinary = false) {
|
|
|
|
|
JsonDocument output;
|
|
|
|
|
@@ -7756,9 +7816,11 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
|
|
|
|
|
// UpdateImageObject need baseDir but only uses it if embeddedImages is
|
|
|
|
|
// enabled, since we won't write separate images when writing to a stream
|
|
|
|
|
// we
|
|
|
|
|
std::string uri;
|
|
|
|
|
UpdateImageObject(model->images[i], dummystring, int(i), true,
|
|
|
|
|
&this->WriteImageData, this->write_image_user_data_);
|
|
|
|
|
SerializeGltfImage(model->images[i], image);
|
|
|
|
|
&this->WriteImageData, &uri,
|
|
|
|
|
this->write_image_user_data_);
|
|
|
|
|
SerializeGltfImage(model->images[i], uri, image);
|
|
|
|
|
JsonPushBack(images, std::move(image));
|
|
|
|
|
}
|
|
|
|
|
JsonAddMember(output, "images", std::move(images));
|
|
|
|
|
@@ -7769,10 +7831,10 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
|
|
|
|
|
} else {
|
|
|
|
|
return WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
|
|
|
|
|
bool TinyGLTF::WriteGltfSceneToFile(const Model *model,
|
|
|
|
|
const std::string &filename,
|
|
|
|
|
bool embedImages = false,
|
|
|
|
|
bool embedBuffers = false,
|
|
|
|
|
bool prettyPrint = true,
|
|
|
|
|
@@ -7845,9 +7907,11 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
|
|
|
|
|
for (unsigned int i = 0; i < model->images.size(); ++i) {
|
|
|
|
|
json image;
|
|
|
|
|
|
|
|
|
|
std::string uri;
|
|
|
|
|
UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
|
|
|
|
|
&this->WriteImageData, this->write_image_user_data_);
|
|
|
|
|
SerializeGltfImage(model->images[i], image);
|
|
|
|
|
&this->WriteImageData, &uri,
|
|
|
|
|
this->write_image_user_data_);
|
|
|
|
|
SerializeGltfImage(model->images[i], uri, image);
|
|
|
|
|
JsonPushBack(images, std::move(image));
|
|
|
|
|
}
|
|
|
|
|
JsonAddMember(output, "images", std::move(images));
|
|
|
|
|
@@ -7858,7 +7922,6 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
|
|
|
|
|
} else {
|
|
|
|
|
return WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace tinygltf
|
|
|
|
|
|