Extern: Update TinyGLTF to include fix for CVE-2022-3008

The use of wordexp(3) permits arbitrary code execution from manually-crafted
glTF files. See https://github.com/syoyo/tinygltf/issues/368 for more details.
In practice this shouldn't be an issue for Blender since the GlTF data isn't
manually crafted but from the OpenXR runtime (a bit like a driver). But
updating the library to include the fix is not a big deal anyway.

Note that the warning that required the local modification is no longer present upstream since
  0bfcb4f49e

Pull Request: https://projects.blender.org/blender/blender/pulls/105536
This commit is contained in:
Julian Squires
2023-03-10 14:56:35 +01:00
committed by Julian Eisel
parent a60626ab0b
commit 466eb426ed
3 changed files with 1193 additions and 897 deletions

View File

@@ -1,6 +1,5 @@
Project: TinyGLTF
URL: https://github.com/syoyo/tinygltf
License: MIT
Upstream version: 2.5.0, 19a41d20ec0
Local modifications:
* Silence "enum value not handled in switch" warnings due to JSON dependency.
Upstream version: 2.8.3, 84a83d39f55d
Local modifications: None

Binary file not shown.

View File

@@ -26,8 +26,16 @@
// THE SOFTWARE.
// Version:
// - v2.8.1 Missed serialization texture sampler name fixed. PR#399.
// - v2.8.0 Add URICallbacks for custom URI handling in Buffer and Image. PR#397.
// - 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
@@ -41,7 +49,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:
@@ -64,6 +72,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
@@ -108,7 +121,11 @@ namespace tinygltf {
#define TINYGLTF_COMPONENT_TYPE_INT (5124)
#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130)
#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
#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
@@ -186,7 +203,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
@@ -219,7 +240,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;
}
}
@@ -240,7 +261,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;
}
}
@@ -253,7 +274,6 @@ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
#ifdef __clang__
#pragma clang diagnostic push
// Suppress warning for : static Value null_value
// https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang
#pragma clang diagnostic ignored "-Wexit-time-destructors"
#pragma clang diagnostic ignored "-Wpadded"
#endif
@@ -428,7 +448,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 ====
@@ -465,7 +485,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;
}
@@ -477,7 +497,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;
}
@@ -489,7 +509,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;
}
@@ -503,7 +523,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)}};
}
@@ -527,9 +547,10 @@ typedef std::map<std::string, Value> ExtensionMap;
struct AnimationChannel {
int sampler; // required
int target_node; // required (index of the node to target)
std::string target_path; // required in ["translation", "rotation", "scale",
// "weights"]
int target_node; // optional index of the node to target (alternative
// target should be provided by extension)
std::string target_path; // required with standard values of ["translation",
// "rotation", "scale", "weights"]
Value extras;
ExtensionMap extensions;
ExtensionMap target_extensions;
@@ -614,7 +635,8 @@ struct Sampler {
int wrapT =
TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
// "REPEAT"], default "REPEAT"
//int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently not used.
// int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently
// not used.
Value extras;
ExtensionMap extensions;
@@ -813,7 +835,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;
@@ -889,7 +911,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));
@@ -928,7 +950,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)
@@ -989,7 +1011,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;
@@ -1124,7 +1146,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) {}
@@ -1188,6 +1210,39 @@ enum SectionCheck {
REQUIRE_ALL = 0x7f
};
///
/// URIEncodeFunction type. Signature for custom URI encoding of external
/// resources such as .bin and image files. Used by tinygltf to re-encode the
/// final location of saved files. object_type may be used to encode buffer and
/// image URIs differently, for example. See
/// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#uris
///
typedef bool (*URIEncodeFunction)(const std::string &in_uri,
const std::string &object_type,
std::string *out_uri, void *user_data);
///
/// URIDecodeFunction type. Signature for custom URI decoding of external
/// resources such as .bin and image files. Used by tinygltf when computing
/// filenames to write resources.
///
typedef bool (*URIDecodeFunction)(const std::string &in_uri,
std::string *out_uri, void *user_data);
// Declaration of default uri decode function
bool URIDecode(const std::string &in_uri, std::string *out_uri,
void *user_data);
///
/// A structure containing URI callbacks and a pointer to their user data.
///
struct URICallbacks {
URIEncodeFunction encode; // Optional encode method
URIDecodeFunction decode; // Required decode method
void *user_data; // An argument that is passed to all uri callbacks
};
///
/// LoadImageDataFunction type. Signature for custom image loading callbacks.
///
@@ -1198,9 +1253,15 @@ 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,
const URICallbacks *uri_cb,
std::string *out_uri,
void *user_pointer);
#ifndef TINYGLTF_NO_STB_IMAGE
// Declaration of default image loader callback
@@ -1212,7 +1273,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,
const URICallbacks *uri_cb, std::string *out_uri, void *);
#endif
///
@@ -1274,7 +1336,7 @@ bool WriteWholeFile(std::string *err, const std::string &filepath,
#endif
///
/// glTF Parser/Serialier context.
/// glTF Parser/Serializer context.
///
class TinyGLTF {
public:
@@ -1303,8 +1365,10 @@ class TinyGLTF {
///
/// Loads glTF ASCII asset from string(memory).
/// `length` = strlen(str);
/// Set warning message to `warn` for example it fails to load asserts.
/// Returns false and set error string to `err` if there's an error.
/// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
/// expanded path (e.g. no tilde(`~`), no environment variables). Set warning
/// message to `warn` for example it fails to load asserts. Returns false and
/// set error string to `err` if there's an error.
///
bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
const char *str, const unsigned int length,
@@ -1323,6 +1387,8 @@ class TinyGLTF {
///
/// Loads glTF binary asset from memory.
/// `length` = strlen(str);
/// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
/// expanded path (e.g. no tilde(`~`), no environment variables).
/// Set warning message to `warn` for example it fails to load asserts.
/// Returns false and set error string to `err` if there's an error.
///
@@ -1333,15 +1399,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);
@@ -1360,6 +1426,11 @@ class TinyGLTF {
///
void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
///
/// Set callbacks to use for URI encoding and decoding and their user data
///
void SetURICallbacks(URICallbacks callbacks);
///
/// Set callbacks to use for filesystem (fs) access and their user data
///
@@ -1368,7 +1439,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
@@ -1394,8 +1465,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;
@@ -1425,6 +1496,10 @@ class TinyGLTF {
bool preserve_image_channels_ = false; /// Default false(expand channels to
/// RGBA) for backward compatibility.
// Warning & error messages
std::string warn_;
std::string err_;
FsCallbacks fs = {
#ifndef TINYGLTF_NO_FS
&tinygltf::FileExists, &tinygltf::ExpandFilePath,
@@ -1438,6 +1513,15 @@ class TinyGLTF {
#endif
};
URICallbacks uri_cb = {
// Use paths as-is by default. This will use JSON string escaping.
nullptr,
// Decode all URIs before using them as paths as the application may have
// percent encoded them.
&tinygltf::URIDecode,
// URI callback user data
nullptr};
LoadImageDataFunction LoadImageData =
#ifndef TINYGLTF_NO_STB_IMAGE
&tinygltf::LoadImageData;
@@ -1528,7 +1612,7 @@ class TinyGLTF {
#endif
#endif
// Disable GCC warnigs
// Disable GCC warnings
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits"
@@ -1577,7 +1661,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
@@ -1587,7 +1671,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
@@ -1606,10 +1694,10 @@ class TinyGLTF {
#endif
#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
#include <wordexp.h>
//#include <wordexp.h>
#endif
#if defined(__sparcv9)
#if defined(__sparcv9) || defined(__powerpc__)
// Big endian
#else
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
@@ -1617,7 +1705,8 @@ class TinyGLTF {
#endif
#endif
namespace {
namespace tinygltf {
namespace detail {
#ifdef TINYGLTF_USE_RAPIDJSON
#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
@@ -1693,10 +1782,11 @@ void JsonParse(JsonDocument &doc, const char *str, size_t length,
(void)throwExc;
doc.Parse(str, length);
#else
doc = json::parse(str, str + length, nullptr, throwExc);
doc = detail::json::parse(str, str + length, nullptr, throwExc);
#endif
}
} // namespace
}
#ifdef __APPLE__
#include "TargetConditionals.h"
@@ -1718,7 +1808,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};
};
@@ -1934,9 +2024,9 @@ bool Sampler::operator==(const Sampler &other) const {
return this->extensions == other.extensions && this->extras == other.extras &&
this->magFilter == other.magFilter &&
this->minFilter == other.minFilter && this->name == other.name &&
this->wrapT == other.wrapT;
this->wrapS == other.wrapS && this->wrapT == other.wrapT;
//this->wrapR == other.wrapR && this->wrapS == other.wrapS &&
// this->wrapR == other.wrapR
}
bool Scene::operator==(const Scene &other) const {
return this->extensions == other.extensions && this->extras == other.extras &&
@@ -2040,9 +2130,10 @@ static std::string GetBaseDir(const std::string &filepath) {
return "";
}
// https://stackoverflow.com/questions/8520560/get-a-file-name-from-a-path
static std::string GetBaseFilename(const std::string &filepath) {
return filepath.substr(filepath.find_last_of("/\\") + 1);
auto idx = filepath.find_last_of("/\\");
if (idx != std::string::npos) return filepath.substr(idx + 1);
return filepath;
}
std::string base64_encode(unsigned char const *, unsigned int len);
@@ -2188,47 +2279,35 @@ std::string base64_decode(std::string const &encoded_string) {
// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
// decoding?
//
// https://stackoverflow.com/questions/18307429/encode-decode-url-in-c
// Uri Decoding from DLIB
// http://dlib.net/dlib/server/server_http.cpp.html
// --- dlib beign ------------------------------------------------------------
// --- dlib begin ------------------------------------------------------------
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
// License: Boost Software License
// Boost Software License - Version 1.0 - August 17th, 2003
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
namespace dlib {
#if 0
inline unsigned char to_hex( unsigned char x )
{
return x + (x > 9 ? ('A'-10) : '0');
}
const std::string urlencode( const std::string& s )
{
std::ostringstream os;
for ( std::string::const_iterator ci = s.begin(); ci != s.end(); ++ci )
{
if ( (*ci >= 'a' && *ci <= 'z') ||
(*ci >= 'A' && *ci <= 'Z') ||
(*ci >= '0' && *ci <= '9') )
{ // allowed
os << *ci;
}
else if ( *ci == ' ')
{
os << '+';
}
else
{
os << '%' << to_hex(static_cast<unsigned char>(*ci >> 4)) << to_hex(static_cast<unsigned char>(*ci % 16));
}
}
return os.str();
}
#endif
inline unsigned char from_hex(unsigned char ch) {
if (ch <= '9' && ch >= '0')
ch -= '0';
@@ -2266,6 +2345,13 @@ static const std::string urldecode(const std::string &str) {
} // namespace dlib
// --- dlib end --------------------------------------------------------------
bool URIDecode(const std::string &in_uri, std::string *out_uri,
void *user_data) {
(void)user_data;
*out_uri = dlib::urldecode(in_uri);
return true;
}
static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
std::string *warn, const std::string &filename,
const std::string &basedir, bool required,
@@ -2376,7 +2462,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:
@@ -2391,7 +2477,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
@@ -2476,7 +2562,9 @@ 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,
const URICallbacks *uri_cb, std::string *out_uri,
void *fsPtr) {
const std::string ext = GetFilePathExtension(*filename);
// Write image to temporary buffer
@@ -2518,9 +2606,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?
}
@@ -2538,20 +2625,33 @@ bool WriteImageData(const std::string *basepath, const std::string *filename,
} else {
// Throw error?
}
image->uri = *filename;
if (uri_cb->encode) {
if (!uri_cb->encode(*filename, "image", out_uri, uri_cb->user_data)) {
return false;
}
} else {
*out_uri = *filename;
}
}
return true;
}
#endif
void TinyGLTF::SetURICallbacks(URICallbacks callbacks) {
assert(callbacks.decode);
if (callbacks.decode) {
uri_cb = callbacks;
}
}
void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
#ifdef _WIN32
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;
@@ -2559,10 +2659,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
@@ -2586,7 +2686,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) {
@@ -2615,6 +2715,18 @@ bool FileExists(const std::string &abs_filename, void *) {
}
std::string ExpandFilePath(const std::string &filepath, void *) {
// https://github.com/syoyo/tinygltf/issues/368
//
// No file path expansion in built-in FS function anymore, since glTF URI
// should not contain tilde('~') and environment variables, and for security
// reason(`wordexp`).
//
// Users need to supply `base_dir`(in `LoadASCIIFromString`,
// `LoadBinaryFromMemory`) in expanded absolute path.
return filepath;
#if 0
#ifdef _WIN32
// Assume input `filepath` is encoded in UTF-8
std::wstring wfilepath = UTF8ToWchar(filepath);
@@ -2662,6 +2774,7 @@ std::string ExpandFilePath(const std::string &filepath, void *) {
return s;
#endif
#endif
}
bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
@@ -2796,15 +2909,21 @@ 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 bool UpdateImageObject(const Image &image, std::string &baseDir,
int index, bool embedImages,
const URICallbacks *uri_cb,
WriteImageDataFunction *WriteImageData,
void *user_data, std::string *out_uri) {
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);
std::string decoded_uri;
if (!uri_cb->decode(image.uri, &decoded_uri, uri_cb->user_data)) {
// A decode failure results in a failure to write the gltf.
return false;
}
filename = GetBaseFilename(decoded_uri);
ext = GetFilePathExtension(filename);
} else if (image.bufferView != -1) {
// If there's no URI and the data exists in a buffer,
@@ -2819,11 +2938,24 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index,
filename = std::to_string(index) + "." + ext;
}
// If callback is set, modify image data object
if (*WriteImageData != nullptr && !filename.empty()) {
std::string uri;
(*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
// If callback is set and image data exists, modify image data object. If
// image data does not exist, this is not considered a failure and the
// original uri should be maintained.
bool imageWritten = false;
if (*WriteImageData != nullptr && !filename.empty() && !image.image.empty()) {
imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages,
uri_cb, out_uri, user_data);
if (!imageWritten) {
return false;
}
}
// Use the original uri if the image was not written.
if (!imageWritten) {
*out_uri = image.uri;
}
return true;
}
bool IsDataURI(const std::string &in) {
@@ -2937,8 +3069,8 @@ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
return true;
}
namespace {
bool GetInt(const json &o, int &val) {
namespace detail {
bool GetInt(const detail::json &o, int &val) {
#ifdef TINYGLTF_USE_RAPIDJSON
if (!o.IsDouble()) {
if (o.IsInt()) {
@@ -2960,8 +3092,8 @@ bool GetInt(const json &o, int &val) {
#else
auto type = o.type();
if ((type == json::value_t::number_integer) ||
(type == json::value_t::number_unsigned)) {
if ((type == detail::json::value_t::number_integer) ||
(type == detail::json::value_t::number_unsigned)) {
val = static_cast<int>(o.get<int64_t>());
return true;
}
@@ -2971,7 +3103,7 @@ bool GetInt(const json &o, int &val) {
}
#ifdef TINYGLTF_USE_RAPIDJSON
bool GetDouble(const json &o, double &val) {
bool GetDouble(const detail::json &o, double &val) {
if (o.IsDouble()) {
val = o.GetDouble();
return true;
@@ -2981,7 +3113,7 @@ bool GetDouble(const json &o, double &val) {
}
#endif
bool GetNumber(const json &o, double &val) {
bool GetNumber(const detail::json &o, double &val) {
#ifdef TINYGLTF_USE_RAPIDJSON
if (o.IsNumber()) {
val = o.GetDouble();
@@ -2999,7 +3131,7 @@ bool GetNumber(const json &o, double &val) {
#endif
}
bool GetString(const json &o, std::string &val) {
bool GetString(const detail::json &o, std::string &val) {
#ifdef TINYGLTF_USE_RAPIDJSON
if (o.IsString()) {
val = o.GetString();
@@ -3008,7 +3140,7 @@ bool GetString(const json &o, std::string &val) {
return false;
#else
if (o.type() == json::value_t::string) {
if (o.type() == detail::json::value_t::string) {
val = o.get<std::string>();
return true;
}
@@ -3017,7 +3149,7 @@ bool GetString(const json &o, std::string &val) {
#endif
}
bool IsArray(const json &o) {
bool IsArray(const detail::json &o) {
#ifdef TINYGLTF_USE_RAPIDJSON
return o.IsArray();
#else
@@ -3025,7 +3157,7 @@ bool IsArray(const json &o) {
#endif
}
json_const_array_iterator ArrayBegin(const json &o) {
detail::json_const_array_iterator ArrayBegin(const detail::json &o) {
#ifdef TINYGLTF_USE_RAPIDJSON
return o.Begin();
#else
@@ -3033,7 +3165,7 @@ json_const_array_iterator ArrayBegin(const json &o) {
#endif
}
json_const_array_iterator ArrayEnd(const json &o) {
detail::json_const_array_iterator ArrayEnd(const detail::json &o) {
#ifdef TINYGLTF_USE_RAPIDJSON
return o.End();
#else
@@ -3041,7 +3173,7 @@ json_const_array_iterator ArrayEnd(const json &o) {
#endif
}
bool IsObject(const json &o) {
bool IsObject(const detail::json &o) {
#ifdef TINYGLTF_USE_RAPIDJSON
return o.IsObject();
#else
@@ -3049,7 +3181,7 @@ bool IsObject(const json &o) {
#endif
}
json_const_iterator ObjectBegin(const json &o) {
detail::json_const_iterator ObjectBegin(const detail::json &o) {
#ifdef TINYGLTF_USE_RAPIDJSON
return o.MemberBegin();
#else
@@ -3057,7 +3189,7 @@ json_const_iterator ObjectBegin(const json &o) {
#endif
}
json_const_iterator ObjectEnd(const json &o) {
detail::json_const_iterator ObjectEnd(const detail::json &o) {
#ifdef TINYGLTF_USE_RAPIDJSON
return o.MemberEnd();
#else
@@ -3067,7 +3199,7 @@ json_const_iterator ObjectEnd(const json &o) {
// Making this a const char* results in a pointer to a temporary when
// TINYGLTF_USE_RAPIDJSON is off.
std::string GetKey(json_const_iterator &it) {
std::string GetKey(detail::json_const_iterator &it) {
#ifdef TINYGLTF_USE_RAPIDJSON
return it->name.GetString();
#else
@@ -3075,7 +3207,7 @@ std::string GetKey(json_const_iterator &it) {
#endif
}
bool FindMember(const json &o, const char *member, json_const_iterator &it) {
bool FindMember(const detail::json &o, const char *member, detail::json_const_iterator &it) {
#ifdef TINYGLTF_USE_RAPIDJSON
if (!o.IsObject()) {
return false;
@@ -3088,7 +3220,7 @@ bool FindMember(const json &o, const char *member, json_const_iterator &it) {
#endif
}
const json &GetValue(json_const_iterator &it) {
const detail::json &GetValue(detail::json_const_iterator &it) {
#ifdef TINYGLTF_USE_RAPIDJSON
return it->value;
#else
@@ -3096,17 +3228,23 @@ const json &GetValue(json_const_iterator &it) {
#endif
}
std::string JsonToString(const json &o, int spacing = -1) {
std::string JsonToString(const detail::json &o, int spacing = -1) {
#ifdef TINYGLTF_USE_RAPIDJSON
using namespace rapidjson;
StringBuffer buffer;
if (spacing == -1) {
Writer<StringBuffer> writer(buffer);
o.Accept(writer);
// TODO: Better error handling.
// https://github.com/syoyo/tinygltf/issues/332
if (!o.Accept(writer)) {
return "tiny_gltf::JsonToString() failed rapidjson conversion";
}
} else {
PrettyWriter<StringBuffer> writer(buffer);
writer.SetIndent(' ', uint32_t(spacing));
o.Accept(writer);
if (!o.Accept(writer)) {
return "tiny_gltf::JsonToString() failed rapidjson conversion";
}
}
return buffer.GetString();
#else
@@ -3116,7 +3254,7 @@ std::string JsonToString(const json &o, int spacing = -1) {
} // namespace
static bool ParseJsonAsValue(Value *ret, const json &o) {
static bool ParseJsonAsValue(Value *ret, const detail::json &o) {
Value val{};
#ifdef TINYGLTF_USE_RAPIDJSON
using rapidjson::Type;
@@ -3127,7 +3265,7 @@ static bool ParseJsonAsValue(Value *ret, const json &o) {
Value entry;
ParseJsonAsValue(&entry, it->value);
if (entry.Type() != NULL_TYPE)
value_object.emplace(GetKey(it), std::move(entry));
value_object.emplace(detail::GetKey(it), std::move(entry));
}
if (value_object.size() > 0) val = Value(std::move(value_object));
} break;
@@ -3152,11 +3290,11 @@ static bool ParseJsonAsValue(Value *ret, const json &o) {
case Type::kNumberType:
if (!o.IsDouble()) {
int i = 0;
GetInt(o, i);
detail::GetInt(o, i);
val = Value(i);
} else {
double d = 0.0;
GetDouble(o, d);
detail::GetDouble(o, d);
val = Value(d);
}
break;
@@ -3166,7 +3304,7 @@ static bool ParseJsonAsValue(Value *ret, const json &o) {
}
#else
switch (o.type()) {
case json::value_t::object: {
case detail::json::value_t::object: {
Value::Object value_object;
for (auto it = o.begin(); it != o.end(); it++) {
Value entry;
@@ -3176,7 +3314,7 @@ static bool ParseJsonAsValue(Value *ret, const json &o) {
}
if (value_object.size() > 0) val = Value(std::move(value_object));
} break;
case json::value_t::array: {
case detail::json::value_t::array: {
Value::Array value_array;
value_array.reserve(o.size());
for (auto it = o.begin(); it != o.end(); it++) {
@@ -3187,46 +3325,48 @@ static bool ParseJsonAsValue(Value *ret, const json &o) {
}
if (value_array.size() > 0) val = Value(std::move(value_array));
} break;
case json::value_t::string:
case detail::json::value_t::string:
val = Value(o.get<std::string>());
break;
case json::value_t::boolean:
case detail::json::value_t::boolean:
val = Value(o.get<bool>());
break;
case json::value_t::number_integer:
case json::value_t::number_unsigned:
case detail::json::value_t::number_integer:
case detail::json::value_t::number_unsigned:
val = Value(static_cast<int>(o.get<int64_t>()));
break;
case json::value_t::number_float:
case detail::json::value_t::number_float:
val = Value(o.get<double>());
break;
case json::value_t::null:
case json::value_t::binary:
case json::value_t::discarded:
case detail::json::value_t::null:
case detail::json::value_t::discarded:
case detail::json::value_t::binary:
// default:
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) {
json_const_iterator it;
if (!FindMember(o, "extras", it)) {
static bool ParseExtrasProperty(Value *ret, const detail::json &o) {
detail::json_const_iterator it;
if (!detail::FindMember(o, "extras", it)) {
return false;
}
return ParseJsonAsValue(ret, GetValue(it));
return ParseJsonAsValue(ret, detail::GetValue(it));
}
static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
static bool ParseBooleanProperty(bool *ret, std::string *err, const detail::json &o,
const std::string &property,
const bool required,
const std::string &parent_node = "") {
json_const_iterator it;
if (!FindMember(o, property.c_str(), it)) {
detail::json_const_iterator it;
if (!detail::FindMember(o, property.c_str(), it)) {
if (required) {
if (err) {
(*err) += "'" + property + "' property is missing";
@@ -3239,7 +3379,7 @@ static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
return false;
}
auto &value = GetValue(it);
auto &value = detail::GetValue(it);
bool isBoolean;
bool boolValue = false;
@@ -3270,12 +3410,12 @@ static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
return true;
}
static bool ParseIntegerProperty(int *ret, std::string *err, const json &o,
static bool ParseIntegerProperty(int *ret, std::string *err, const detail::json &o,
const std::string &property,
const bool required,
const std::string &parent_node = "") {
json_const_iterator it;
if (!FindMember(o, property.c_str(), it)) {
detail::json_const_iterator it;
if (!detail::FindMember(o, property.c_str(), it)) {
if (required) {
if (err) {
(*err) += "'" + property + "' property is missing";
@@ -3289,7 +3429,7 @@ static bool ParseIntegerProperty(int *ret, std::string *err, const json &o,
}
int intValue;
bool isInt = GetInt(GetValue(it), intValue);
bool isInt = detail::GetInt(detail::GetValue(it), intValue);
if (!isInt) {
if (required) {
if (err) {
@@ -3306,12 +3446,12 @@ static bool ParseIntegerProperty(int *ret, std::string *err, const json &o,
return true;
}
static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o,
static bool ParseUnsignedProperty(size_t *ret, std::string *err, const detail::json &o,
const std::string &property,
const bool required,
const std::string &parent_node = "") {
json_const_iterator it;
if (!FindMember(o, property.c_str(), it)) {
detail::json_const_iterator it;
if (!detail::FindMember(o, property.c_str(), it)) {
if (required) {
if (err) {
(*err) += "'" + property + "' property is missing";
@@ -3324,7 +3464,7 @@ static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o,
return false;
}
auto &value = GetValue(it);
auto &value = detail::GetValue(it);
size_t uValue = 0;
bool isUValue;
@@ -3359,13 +3499,13 @@ static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o,
return true;
}
static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
static bool ParseNumberProperty(double *ret, std::string *err, const detail::json &o,
const std::string &property,
const bool required,
const std::string &parent_node = "") {
json_const_iterator it;
detail::json_const_iterator it;
if (!FindMember(o, property.c_str(), it)) {
if (!detail::FindMember(o, property.c_str(), it)) {
if (required) {
if (err) {
(*err) += "'" + property + "' property is missing";
@@ -3379,7 +3519,7 @@ static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
}
double numberValue;
bool isNumber = GetNumber(GetValue(it), numberValue);
bool isNumber = detail::GetNumber(detail::GetValue(it), numberValue);
if (!isNumber) {
if (required) {
@@ -3398,11 +3538,11 @@ static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
}
static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
const json &o, const std::string &property,
const detail::json &o, const std::string &property,
bool required,
const std::string &parent_node = "") {
json_const_iterator it;
if (!FindMember(o, property.c_str(), it)) {
detail::json_const_iterator it;
if (!detail::FindMember(o, property.c_str(), it)) {
if (required) {
if (err) {
(*err) += "'" + property + "' property is missing";
@@ -3415,7 +3555,7 @@ static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
return false;
}
if (!IsArray(GetValue(it))) {
if (!detail::IsArray(detail::GetValue(it))) {
if (required) {
if (err) {
(*err) += "'" + property + "' property is not an array";
@@ -3429,10 +3569,10 @@ static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
}
ret->clear();
auto end = ArrayEnd(GetValue(it));
for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
auto end = detail::ArrayEnd(detail::GetValue(it));
for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
double numberValue;
const bool isNumber = GetNumber(*i, numberValue);
const bool isNumber = detail::GetNumber(*i, numberValue);
if (!isNumber) {
if (required) {
if (err) {
@@ -3452,12 +3592,12 @@ static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
}
static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
const json &o,
const detail::json &o,
const std::string &property,
bool required,
const std::string &parent_node = "") {
json_const_iterator it;
if (!FindMember(o, property.c_str(), it)) {
detail::json_const_iterator it;
if (!detail::FindMember(o, property.c_str(), it)) {
if (required) {
if (err) {
(*err) += "'" + property + "' property is missing";
@@ -3470,7 +3610,7 @@ static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
return false;
}
if (!IsArray(GetValue(it))) {
if (!detail::IsArray(detail::GetValue(it))) {
if (required) {
if (err) {
(*err) += "'" + property + "' property is not an array";
@@ -3484,10 +3624,10 @@ static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
}
ret->clear();
auto end = ArrayEnd(GetValue(it));
for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
auto end = detail::ArrayEnd(detail::GetValue(it));
for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
int numberValue;
bool isNumber = GetInt(*i, numberValue);
bool isNumber = detail::GetInt(*i, numberValue);
if (!isNumber) {
if (required) {
if (err) {
@@ -3507,11 +3647,11 @@ static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
}
static bool ParseStringProperty(
std::string *ret, std::string *err, const json &o,
std::string *ret, std::string *err, const detail::json &o,
const std::string &property, bool required,
const std::string &parent_node = std::string()) {
json_const_iterator it;
if (!FindMember(o, property.c_str(), it)) {
detail::json_const_iterator it;
if (!detail::FindMember(o, property.c_str(), it)) {
if (required) {
if (err) {
(*err) += "'" + property + "' property is missing";
@@ -3526,7 +3666,7 @@ static bool ParseStringProperty(
}
std::string strValue;
if (!GetString(GetValue(it), strValue)) {
if (!detail::GetString(detail::GetValue(it), strValue)) {
if (required) {
if (err) {
(*err) += "'" + property + "' property is not a string type.\n";
@@ -3543,12 +3683,12 @@ static bool ParseStringProperty(
}
static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
std::string *err, const json &o,
std::string *err, const detail::json &o,
const std::string &property,
bool required,
const std::string &parent = "") {
json_const_iterator it;
if (!FindMember(o, property.c_str(), it)) {
detail::json_const_iterator it;
if (!detail::FindMember(o, property.c_str(), it)) {
if (required) {
if (err) {
if (!parent.empty()) {
@@ -3562,10 +3702,10 @@ static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
return false;
}
const json &dict = GetValue(it);
const detail::json &dict = detail::GetValue(it);
// Make sure we are dealing with an object / dictionary.
if (!IsObject(dict)) {
if (!detail::IsObject(dict)) {
if (required) {
if (err) {
(*err) += "'" + property + "' property is not an object.\n";
@@ -3576,12 +3716,12 @@ static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
ret->clear();
json_const_iterator dictIt(ObjectBegin(dict));
json_const_iterator dictItEnd(ObjectEnd(dict));
detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
for (; dictIt != dictItEnd; ++dictIt) {
int intVal;
if (!GetInt(GetValue(dictIt), intVal)) {
if (!detail::GetInt(detail::GetValue(dictIt), intVal)) {
if (required) {
if (err) {
(*err) += "'" + property + "' value is not an integer type.\n";
@@ -3591,16 +3731,16 @@ static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
}
// Insert into the list.
(*ret)[GetKey(dictIt)] = intVal;
(*ret)[detail::GetKey(dictIt)] = intVal;
}
return true;
}
static bool ParseJSONProperty(std::map<std::string, double> *ret,
std::string *err, const json &o,
std::string *err, const detail::json &o,
const std::string &property, bool required) {
json_const_iterator it;
if (!FindMember(o, property.c_str(), it)) {
detail::json_const_iterator it;
if (!detail::FindMember(o, property.c_str(), it)) {
if (required) {
if (err) {
(*err) += "'" + property + "' property is missing. \n'";
@@ -3609,9 +3749,9 @@ static bool ParseJSONProperty(std::map<std::string, double> *ret,
return false;
}
const json &obj = GetValue(it);
const detail::json &obj = detail::GetValue(it);
if (!IsObject(obj)) {
if (!detail::IsObject(obj)) {
if (required) {
if (err) {
(*err) += "'" + property + "' property is not a JSON object.\n";
@@ -3622,19 +3762,19 @@ static bool ParseJSONProperty(std::map<std::string, double> *ret,
ret->clear();
json_const_iterator it2(ObjectBegin(obj));
json_const_iterator itEnd(ObjectEnd(obj));
detail::json_const_iterator it2(detail::ObjectBegin(obj));
detail::json_const_iterator itEnd(detail::ObjectEnd(obj));
for (; it2 != itEnd; ++it2) {
double numVal;
if (GetNumber(GetValue(it2), numVal))
ret->emplace(std::string(GetKey(it2)), numVal);
if (detail::GetNumber(detail::GetValue(it2), numVal))
ret->emplace(std::string(detail::GetKey(it2)), numVal);
}
return true;
}
static bool ParseParameterProperty(Parameter *param, std::string *err,
const json &o, const std::string &prop,
const detail::json &o, const std::string &prop,
bool required) {
// A parameter value can either be a string or an array of either a boolean or
// a number. Booleans of any kind aren't supported here. Granted, it
@@ -3649,7 +3789,8 @@ static bool ParseParameterProperty(Parameter *param, std::string *err,
// Found a number array.
return true;
} else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
return param->has_number_value = true;
param->has_number_value = true;
return true;
} else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
false)) {
return true;
@@ -3666,25 +3807,25 @@ static bool ParseParameterProperty(Parameter *param, std::string *err,
}
static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
const json &o) {
const detail::json &o) {
(void)err;
json_const_iterator it;
if (!FindMember(o, "extensions", it)) {
detail::json_const_iterator it;
if (!detail::FindMember(o, "extensions", it)) {
return false;
}
auto &obj = GetValue(it);
if (!IsObject(obj)) {
auto &obj = detail::GetValue(it);
if (!detail::IsObject(obj)) {
return false;
}
ExtensionMap extensions;
json_const_iterator extIt = ObjectBegin(obj); // it.value().begin();
json_const_iterator extEnd = ObjectEnd(obj);
detail::json_const_iterator extIt = detail::ObjectBegin(obj); // it.value().begin();
detail::json_const_iterator extEnd = detail::ObjectEnd(obj);
for (; extIt != extEnd; ++extIt) {
auto &itObj = GetValue(extIt);
if (!IsObject(itObj)) continue;
std::string key(GetKey(extIt));
auto &itObj = detail::GetValue(extIt);
if (!detail::IsObject(itObj)) continue;
std::string key(detail::GetKey(extIt));
if (!ParseJsonAsValue(&extensions[key], itObj)) {
if (!key.empty()) {
// create empty object so that an extension object is still of type
@@ -3699,7 +3840,7 @@ static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
return true;
}
static bool ParseAsset(Asset *asset, std::string *err, const json &o,
static bool ParseAsset(Asset *asset, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
@@ -3713,15 +3854,15 @@ static bool ParseAsset(Asset *asset, std::string *err, const json &o,
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
asset->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
asset->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
asset->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
asset->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -3730,18 +3871,19 @@ static bool ParseAsset(Asset *asset, std::string *err, const json &o,
}
static bool ParseImage(Image *image, const int image_idx, std::string *err,
std::string *warn, const json &o,
std::string *warn, const detail::json &o,
bool store_original_json_for_extras_and_extensions,
const std::string &basedir, FsCallbacks *fs,
const URICallbacks *uri_cb,
LoadImageDataFunction *LoadImageData = nullptr,
void *load_image_user_data = nullptr) {
// A glTF image must either reference a bufferView or an image uri
// schema says oneOf [`bufferView`, `uri`]
// TODO(syoyo): Check the type of each parameters.
json_const_iterator it;
bool hasBufferView = FindMember(o, "bufferView", it);
bool hasURI = FindMember(o, "uri", it);
detail::json_const_iterator it;
bool hasBufferView = detail::FindMember(o, "bufferView", it);
bool hasURI = detail::FindMember(o, "uri", it);
ParseStringProperty(&image->name, err, o, "name", false);
@@ -3770,15 +3912,15 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator eit;
if (FindMember(o, "extensions", eit)) {
image->extensions_json_string = JsonToString(GetValue(eit));
detail::json_const_iterator eit;
if (detail::FindMember(o, "extensions", eit)) {
image->extensions_json_string = detail::JsonToString(detail::GetValue(eit));
}
}
{
json_const_iterator eit;
if (FindMember(o, "extras", eit)) {
image->extras_json_string = JsonToString(GetValue(eit));
detail::json_const_iterator eit;
if (detail::FindMember(o, "extras", eit)) {
image->extras_json_string = detail::JsonToString(detail::GetValue(eit));
}
}
}
@@ -3842,8 +3984,19 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
image->uri = uri;
#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
return true;
#endif
std::string decoded_uri = dlib::urldecode(uri);
#else
std::string decoded_uri;
if (!uri_cb->decode(uri, &decoded_uri, uri_cb->user_data)) {
if (warn) {
(*warn) += "Failed to decode 'uri' for image[" +
std::to_string(image_idx) + "] name = [" + image->name +
"]\n";
}
// Image loading failure is not critical to overall gltf loading.
return true;
}
if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
/* required */ false, /* required bytes */ 0,
/* checksize */ false, fs)) {
@@ -3864,6 +4017,7 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
}
return false;
}
#endif
}
if (*LoadImageData == nullptr) {
@@ -3876,7 +4030,7 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
static_cast<int>(img.size()), load_image_user_data);
}
static bool ParseTexture(Texture *texture, std::string *err, const json &o,
static bool ParseTexture(Texture *texture, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions,
const std::string &basedir) {
(void)basedir;
@@ -3894,15 +4048,15 @@ static bool ParseTexture(Texture *texture, std::string *err, const json &o,
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
texture->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
texture->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
texture->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
texture->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -3913,7 +4067,7 @@ static bool ParseTexture(Texture *texture, std::string *err, const json &o,
}
static bool ParseTextureInfo(
TextureInfo *texinfo, std::string *err, const json &o,
TextureInfo *texinfo, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
if (texinfo == nullptr) {
return false;
@@ -3931,15 +4085,15 @@ static bool ParseTextureInfo(
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
texinfo->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
texinfo->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
texinfo->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
texinfo->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -3948,7 +4102,7 @@ static bool ParseTextureInfo(
}
static bool ParseNormalTextureInfo(
NormalTextureInfo *texinfo, std::string *err, const json &o,
NormalTextureInfo *texinfo, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
if (texinfo == nullptr) {
return false;
@@ -3967,15 +4121,15 @@ static bool ParseNormalTextureInfo(
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
texinfo->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
texinfo->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
texinfo->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
texinfo->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -3984,7 +4138,7 @@ static bool ParseNormalTextureInfo(
}
static bool ParseOcclusionTextureInfo(
OcclusionTextureInfo *texinfo, std::string *err, const json &o,
OcclusionTextureInfo *texinfo, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
if (texinfo == nullptr) {
return false;
@@ -4003,15 +4157,15 @@ static bool ParseOcclusionTextureInfo(
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
texinfo->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
texinfo->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
texinfo->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
texinfo->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -4019,10 +4173,10 @@ static bool ParseOcclusionTextureInfo(
return true;
}
static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions,
FsCallbacks *fs, const std::string &basedir,
bool is_binary = false,
FsCallbacks *fs, const URICallbacks *uri_cb,
const std::string &basedir, bool is_binary = false,
const unsigned char *bin_data = nullptr,
size_t bin_size = 0) {
size_t byteLength;
@@ -4042,10 +4196,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
}
}
json_const_iterator type;
if (FindMember(o, "type", type)) {
detail::json_const_iterator type;
if (detail::FindMember(o, "type", type)) {
std::string typeStr;
if (GetString(GetValue(type), typeStr)) {
if (detail::GetString(detail::GetValue(type), typeStr)) {
if (typeStr.compare("arraybuffer") == 0) {
// buffer.type = "arraybuffer";
}
@@ -4068,7 +4222,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
}
} else {
// External .bin file.
std::string decoded_uri = dlib::urldecode(buffer->uri);
std::string decoded_uri;
if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
return false;
}
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
decoded_uri, basedir, /* required */ true,
byteLength, /* checkSize */ true, fs)) {
@@ -4080,7 +4237,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;
}
@@ -4113,7 +4270,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
}
} else {
// Assume external .bin file.
std::string decoded_uri = dlib::urldecode(buffer->uri);
std::string decoded_uri;
if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
return false;
}
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
basedir, /* required */ true, byteLength,
/* checkSize */ true, fs)) {
@@ -4129,15 +4289,15 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
buffer->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
buffer->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
buffer->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
buffer->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -4146,7 +4306,7 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
}
static bool ParseBufferView(
BufferView *bufferView, std::string *err, const json &o,
BufferView *bufferView, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
int buffer = -1;
if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
@@ -4201,15 +4361,15 @@ static bool ParseBufferView(
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
bufferView->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
bufferView->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
bufferView->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
bufferView->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -4222,40 +4382,48 @@ static bool ParseBufferView(
}
static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
const json &o) {
const detail::json &o) {
accessor->sparse.isSparse = true;
int count = 0;
ParseIntegerProperty(&count, err, o, "count", true);
if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
return false;
}
json_const_iterator indices_iterator;
json_const_iterator values_iterator;
if (!FindMember(o, "indices", indices_iterator)) {
detail::json_const_iterator indices_iterator;
detail::json_const_iterator values_iterator;
if (!detail::FindMember(o, "indices", indices_iterator)) {
(*err) = "the sparse object of this accessor doesn't have indices";
return false;
}
if (!FindMember(o, "values", values_iterator)) {
(*err) = "the sparse object ob ths accessor doesn't have values";
if (!detail::FindMember(o, "values", values_iterator)) {
(*err) = "the sparse object of this accessor doesn't have values";
return false;
}
const json &indices_obj = GetValue(indices_iterator);
const json &values_obj = GetValue(values_iterator);
const detail::json &indices_obj = detail::GetValue(indices_iterator);
const detail::json &values_obj = detail::GetValue(values_iterator);
int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView",
true);
if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
"bufferView", true, "SparseAccessor")) {
return false;
}
ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
true);
ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
true);
false);
if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
true, "SparseAccessor")) {
return false;
}
int values_buffer_view = 0, values_byte_offset = 0;
ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
true);
if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
true, "SparseAccessor")) {
return false;
}
ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
true);
false);
accessor->sparse.count = count;
accessor->sparse.indices.bufferView = indices_buffer_view;
@@ -4264,12 +4432,10 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
accessor->sparse.values.bufferView = values_buffer_view;
accessor->sparse.values.byteOffset = values_byte_offset;
// todo check theses values
return true;
}
static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o,
static bool ParseAccessor(Accessor *accessor, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
int bufferView = -1;
ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
@@ -4354,24 +4520,24 @@ static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o,
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
accessor->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
accessor->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
accessor->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
accessor->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
// check if accessor has a "sparse" object:
json_const_iterator iterator;
if (FindMember(o, "sparse", iterator)) {
detail::json_const_iterator iterator;
if (detail::FindMember(o, "sparse", iterator)) {
// here this accessor has a "sparse" subobject
return ParseSparseAccessor(accessor, err, GetValue(iterator));
return ParseSparseAccessor(accessor, err, detail::GetValue(iterator));
}
return true;
@@ -4570,7 +4736,7 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model,
#endif
static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
const json &o,
const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
int material = -1;
ParseIntegerProperty(&material, err, o, "material", false);
@@ -4578,7 +4744,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);
@@ -4589,23 +4755,23 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
}
// Look for morph targets
json_const_iterator targetsObject;
if (FindMember(o, "targets", targetsObject) &&
IsArray(GetValue(targetsObject))) {
auto targetsObjectEnd = ArrayEnd(GetValue(targetsObject));
for (json_const_array_iterator i = ArrayBegin(GetValue(targetsObject));
detail::json_const_iterator targetsObject;
if (detail::FindMember(o, "targets", targetsObject) &&
detail::IsArray(detail::GetValue(targetsObject))) {
auto targetsObjectEnd = detail::ArrayEnd(detail::GetValue(targetsObject));
for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(targetsObject));
i != targetsObjectEnd; ++i) {
std::map<std::string, int> targetAttribues;
const json &dict = *i;
if (IsObject(dict)) {
json_const_iterator dictIt(ObjectBegin(dict));
json_const_iterator dictItEnd(ObjectEnd(dict));
const detail::json &dict = *i;
if (detail::IsObject(dict)) {
detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
for (; dictIt != dictItEnd; ++dictIt) {
int iVal;
if (GetInt(GetValue(dictIt), iVal))
targetAttribues[GetKey(dictIt)] = iVal;
if (detail::GetInt(detail::GetValue(dictIt), iVal))
targetAttribues[detail::GetKey(dictIt)] = iVal;
}
primitive->targets.emplace_back(std::move(targetAttribues));
}
@@ -4617,15 +4783,15 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
primitive->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
primitive->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
primitive->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
primitive->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -4643,16 +4809,16 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
return true;
}
static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o,
static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
ParseStringProperty(&mesh->name, err, o, "name", false);
mesh->primitives.clear();
json_const_iterator primObject;
if (FindMember(o, "primitives", primObject) &&
IsArray(GetValue(primObject))) {
json_const_array_iterator primEnd = ArrayEnd(GetValue(primObject));
for (json_const_array_iterator i = ArrayBegin(GetValue(primObject));
detail::json_const_iterator primObject;
if (detail::FindMember(o, "primitives", primObject) &&
detail::IsArray(detail::GetValue(primObject))) {
detail::json_const_array_iterator primEnd = detail::ArrayEnd(detail::GetValue(primObject));
for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(primObject));
i != primEnd; ++i) {
Primitive primitive;
if (ParsePrimitive(&primitive, model, err, *i,
@@ -4671,15 +4837,15 @@ static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o,
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
mesh->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
mesh->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
mesh->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
mesh->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -4687,7 +4853,7 @@ static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o,
return true;
}
static bool ParseNode(Node *node, std::string *err, const json &o,
static bool ParseNode(Node *node, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
ParseStringProperty(&node->name, err, o, "name", false);
@@ -4720,15 +4886,15 @@ static bool ParseNode(Node *node, std::string *err, const json &o,
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
node->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
node->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
node->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
node->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -4737,7 +4903,7 @@ static bool ParseNode(Node *node, std::string *err, const json &o,
}
static bool ParsePbrMetallicRoughness(
PbrMetallicRoughness *pbr, std::string *err, const json &o,
PbrMetallicRoughness *pbr, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
if (pbr == nullptr) {
return false;
@@ -4759,17 +4925,17 @@ static bool ParsePbrMetallicRoughness(
}
{
json_const_iterator it;
if (FindMember(o, "baseColorTexture", it)) {
ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it),
detail::json_const_iterator it;
if (detail::FindMember(o, "baseColorTexture", it)) {
ParseTextureInfo(&pbr->baseColorTexture, err, detail::GetValue(it),
store_original_json_for_extras_and_extensions);
}
}
{
json_const_iterator it;
if (FindMember(o, "metallicRoughnessTexture", it)) {
ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it),
detail::json_const_iterator it;
if (detail::FindMember(o, "metallicRoughnessTexture", it)) {
ParseTextureInfo(&pbr->metallicRoughnessTexture, err, detail::GetValue(it),
store_original_json_for_extras_and_extensions);
}
}
@@ -4782,15 +4948,15 @@ static bool ParsePbrMetallicRoughness(
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
pbr->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
pbr->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
pbr->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
pbr->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -4798,7 +4964,7 @@ static bool ParsePbrMetallicRoughness(
return true;
}
static bool ParseMaterial(Material *material, std::string *err, const json &o,
static bool ParseMaterial(Material *material, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
ParseStringProperty(&material->name, err, o, "name", /* required */ false);
@@ -4827,63 +4993,63 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o,
/* required */ false);
{
json_const_iterator it;
if (FindMember(o, "pbrMetallicRoughness", it)) {
detail::json_const_iterator it;
if (detail::FindMember(o, "pbrMetallicRoughness", it)) {
ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
GetValue(it),
detail::GetValue(it),
store_original_json_for_extras_and_extensions);
}
}
{
json_const_iterator it;
if (FindMember(o, "normalTexture", it)) {
ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it),
detail::json_const_iterator it;
if (detail::FindMember(o, "normalTexture", it)) {
ParseNormalTextureInfo(&material->normalTexture, err, detail::GetValue(it),
store_original_json_for_extras_and_extensions);
}
}
{
json_const_iterator it;
if (FindMember(o, "occlusionTexture", it)) {
ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it),
detail::json_const_iterator it;
if (detail::FindMember(o, "occlusionTexture", it)) {
ParseOcclusionTextureInfo(&material->occlusionTexture, err, detail::GetValue(it),
store_original_json_for_extras_and_extensions);
}
}
{
json_const_iterator it;
if (FindMember(o, "emissiveTexture", it)) {
ParseTextureInfo(&material->emissiveTexture, err, GetValue(it),
detail::json_const_iterator it;
if (detail::FindMember(o, "emissiveTexture", it)) {
ParseTextureInfo(&material->emissiveTexture, err, detail::GetValue(it),
store_original_json_for_extras_and_extensions);
}
}
// 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();
material->additionalValues.clear();
json_const_iterator it(ObjectBegin(o));
json_const_iterator itEnd(ObjectEnd(o));
detail::json_const_iterator it(detail::ObjectBegin(o));
detail::json_const_iterator itEnd(detail::ObjectEnd(o));
for (; it != itEnd; ++it) {
std::string key(GetKey(it));
std::string key(detail::GetKey(it));
if (key == "pbrMetallicRoughness") {
if (IsObject(GetValue(it))) {
const json &values_object = GetValue(it);
if (detail::IsObject(detail::GetValue(it))) {
const detail::json &values_object = detail::GetValue(it);
json_const_iterator itVal(ObjectBegin(values_object));
json_const_iterator itValEnd(ObjectEnd(values_object));
detail::json_const_iterator itVal(detail::ObjectBegin(values_object));
detail::json_const_iterator itValEnd(detail::ObjectEnd(values_object));
for (; itVal != itValEnd; ++itVal) {
Parameter param;
if (ParseParameterProperty(&param, err, values_object, GetKey(itVal),
if (ParseParameterProperty(&param, err, values_object, detail::GetKey(itVal),
false)) {
material->values.emplace(GetKey(itVal), std::move(param));
material->values.emplace(detail::GetKey(itVal), std::move(param));
}
}
}
@@ -4894,7 +5060,7 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o,
Parameter param;
if (ParseParameterProperty(&param, 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));
}
@@ -4907,15 +5073,15 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o,
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator eit;
if (FindMember(o, "extensions", eit)) {
material->extensions_json_string = JsonToString(GetValue(eit));
detail::json_const_iterator eit;
if (detail::FindMember(o, "extensions", eit)) {
material->extensions_json_string = detail::JsonToString(detail::GetValue(eit));
}
}
{
json_const_iterator eit;
if (FindMember(o, "extras", eit)) {
material->extras_json_string = JsonToString(GetValue(eit));
detail::json_const_iterator eit;
if (detail::FindMember(o, "extras", eit)) {
material->extras_json_string = detail::JsonToString(detail::GetValue(eit));
}
}
}
@@ -4924,7 +5090,7 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o,
}
static bool ParseAnimationChannel(
AnimationChannel *channel, std::string *err, const json &o,
AnimationChannel *channel, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
int samplerIndex = -1;
int targetIndex = -1;
@@ -4936,16 +5102,11 @@ static bool ParseAnimationChannel(
return false;
}
json_const_iterator targetIt;
if (FindMember(o, "target", targetIt) && IsObject(GetValue(targetIt))) {
const json &target_object = GetValue(targetIt);
detail::json_const_iterator targetIt;
if (detail::FindMember(o, "target", targetIt) && detail::IsObject(detail::GetValue(targetIt))) {
const detail::json &target_object = detail::GetValue(targetIt);
if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) {
if (err) {
(*err) += "`node` field is missing in animation.channels.target\n";
}
return false;
}
ParseIntegerProperty(&targetIndex, err, target_object, "node", false);
if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
true)) {
@@ -4956,9 +5117,9 @@ static bool ParseAnimationChannel(
}
ParseExtensionsProperty(&channel->target_extensions, err, target_object);
if (store_original_json_for_extras_and_extensions) {
json_const_iterator it;
if (FindMember(target_object, "extensions", it)) {
channel->target_extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(target_object, "extensions", it)) {
channel->target_extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -4971,15 +5132,15 @@ static bool ParseAnimationChannel(
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
channel->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
channel->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
channel->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
channel->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -4988,14 +5149,14 @@ static bool ParseAnimationChannel(
}
static bool ParseAnimation(Animation *animation, std::string *err,
const json &o,
const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
{
json_const_iterator channelsIt;
if (FindMember(o, "channels", channelsIt) &&
IsArray(GetValue(channelsIt))) {
json_const_array_iterator channelEnd = ArrayEnd(GetValue(channelsIt));
for (json_const_array_iterator i = ArrayBegin(GetValue(channelsIt));
detail::json_const_iterator channelsIt;
if (detail::FindMember(o, "channels", channelsIt) &&
detail::IsArray(detail::GetValue(channelsIt))) {
detail::json_const_array_iterator channelEnd = detail::ArrayEnd(detail::GetValue(channelsIt));
for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(channelsIt));
i != channelEnd; ++i) {
AnimationChannel channel;
if (ParseAnimationChannel(
@@ -5009,15 +5170,15 @@ static bool ParseAnimation(Animation *animation, std::string *err,
}
{
json_const_iterator samplerIt;
if (FindMember(o, "samplers", samplerIt) && IsArray(GetValue(samplerIt))) {
const json &sampler_array = GetValue(samplerIt);
detail::json_const_iterator samplerIt;
if (detail::FindMember(o, "samplers", samplerIt) && detail::IsArray(detail::GetValue(samplerIt))) {
const detail::json &sampler_array = detail::GetValue(samplerIt);
json_const_array_iterator it = ArrayBegin(sampler_array);
json_const_array_iterator itEnd = ArrayEnd(sampler_array);
detail::json_const_array_iterator it = detail::ArrayBegin(sampler_array);
detail::json_const_array_iterator itEnd = detail::ArrayEnd(sampler_array);
for (; it != itEnd; ++it) {
const json &s = *it;
const detail::json &s = *it;
AnimationSampler sampler;
int inputIndex = -1;
@@ -5043,15 +5204,15 @@ static bool ParseAnimation(Animation *animation, std::string *err,
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator eit;
if (FindMember(o, "extensions", eit)) {
sampler.extensions_json_string = JsonToString(GetValue(eit));
detail::json_const_iterator eit;
if (detail::FindMember(o, "extensions", eit)) {
sampler.extensions_json_string = detail::JsonToString(detail::GetValue(eit));
}
}
{
json_const_iterator eit;
if (FindMember(o, "extras", eit)) {
sampler.extras_json_string = JsonToString(GetValue(eit));
detail::json_const_iterator eit;
if (detail::FindMember(o, "extras", eit)) {
sampler.extras_json_string = detail::JsonToString(detail::GetValue(eit));
}
}
}
@@ -5068,15 +5229,15 @@ static bool ParseAnimation(Animation *animation, std::string *err,
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
animation->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
animation->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
animation->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
animation->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -5084,7 +5245,7 @@ static bool ParseAnimation(Animation *animation, std::string *err,
return true;
}
static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
static bool ParseSampler(Sampler *sampler, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
ParseStringProperty(&sampler->name, err, o, "name", false);
@@ -5092,36 +5253,37 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
int magFilter = -1;
int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
//int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
// int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
//ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf extension
// 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;
sampler->magFilter = magFilter;
sampler->wrapS = wrapS;
sampler->wrapT = wrapT;
//sampler->wrapR = wrapR;
// sampler->wrapR = wrapR;
ParseExtensionsProperty(&(sampler->extensions), err, o);
ParseExtrasProperty(&(sampler->extras), o);
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
sampler->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
sampler->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
sampler->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
sampler->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -5129,7 +5291,7 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
return true;
}
static bool ParseSkin(Skin *skin, std::string *err, const json &o,
static bool ParseSkin(Skin *skin, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
@@ -5152,15 +5314,15 @@ static bool ParseSkin(Skin *skin, std::string *err, const json &o,
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
skin->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
skin->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
skin->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
skin->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -5169,7 +5331,7 @@ static bool ParseSkin(Skin *skin, std::string *err, const json &o,
}
static bool ParsePerspectiveCamera(
PerspectiveCamera *camera, std::string *err, const json &o,
PerspectiveCamera *camera, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
double yfov = 0.0;
if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
@@ -5199,15 +5361,15 @@ static bool ParsePerspectiveCamera(
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
camera->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
camera->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
camera->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
camera->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -5217,7 +5379,7 @@ static bool ParsePerspectiveCamera(
return true;
}
static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o,
static bool ParseSpotLight(SpotLight *light, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
@@ -5227,15 +5389,15 @@ static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o,
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
light->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
light->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
light->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
light->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -5246,7 +5408,7 @@ static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o,
}
static bool ParseOrthographicCamera(
OrthographicCamera *camera, std::string *err, const json &o,
OrthographicCamera *camera, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
double xmag = 0.0;
if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
@@ -5274,15 +5436,15 @@ static bool ParseOrthographicCamera(
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
camera->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
camera->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
camera->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
camera->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -5297,25 +5459,25 @@ static bool ParseOrthographicCamera(
return true;
}
static bool ParseCamera(Camera *camera, std::string *err, const json &o,
static bool ParseCamera(Camera *camera, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
return false;
}
if (camera->type.compare("orthographic") == 0) {
json_const_iterator orthoIt;
if (!FindMember(o, "orthographic", orthoIt)) {
detail::json_const_iterator orthoIt;
if (!detail::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;
}
const json &v = GetValue(orthoIt);
if (!IsObject(v)) {
const detail::json &v = detail::GetValue(orthoIt);
if (!detail::IsObject(v)) {
if (err) {
std::stringstream ss;
ss << "\"orthographic\" is not a JSON object." << std::endl;
@@ -5330,8 +5492,8 @@ static bool ParseCamera(Camera *camera, std::string *err, const json &o,
return false;
}
} else if (camera->type.compare("perspective") == 0) {
json_const_iterator perspIt;
if (!FindMember(o, "perspective", perspIt)) {
detail::json_const_iterator perspIt;
if (!detail::FindMember(o, "perspective", perspIt)) {
if (err) {
std::stringstream ss;
ss << "Perspective camera description not found." << std::endl;
@@ -5340,8 +5502,8 @@ static bool ParseCamera(Camera *camera, std::string *err, const json &o,
return false;
}
const json &v = GetValue(perspIt);
if (!IsObject(v)) {
const detail::json &v = detail::GetValue(perspIt);
if (!detail::IsObject(v)) {
if (err) {
std::stringstream ss;
ss << "\"perspective\" is not a JSON object." << std::endl;
@@ -5372,15 +5534,15 @@ static bool ParseCamera(Camera *camera, std::string *err, const json &o,
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
camera->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
camera->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
camera->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
camera->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -5388,15 +5550,15 @@ static bool ParseCamera(Camera *camera, std::string *err, const json &o,
return true;
}
static bool ParseLight(Light *light, std::string *err, const json &o,
static bool ParseLight(Light *light, std::string *err, const detail::json &o,
bool store_original_json_for_extras_and_extensions) {
if (!ParseStringProperty(&light->type, err, o, "type", true)) {
return false;
}
if (light->type == "spot") {
json_const_iterator spotIt;
if (!FindMember(o, "spot", spotIt)) {
detail::json_const_iterator spotIt;
if (!detail::FindMember(o, "spot", spotIt)) {
if (err) {
std::stringstream ss;
ss << "Spot light description not found." << std::endl;
@@ -5405,8 +5567,8 @@ static bool ParseLight(Light *light, std::string *err, const json &o,
return false;
}
const json &v = GetValue(spotIt);
if (!IsObject(v)) {
const detail::json &v = detail::GetValue(spotIt);
if (!detail::IsObject(v)) {
if (err) {
std::stringstream ss;
ss << "\"spot\" is not a JSON object." << std::endl;
@@ -5430,15 +5592,15 @@ static bool ParseLight(Light *light, std::string *err, const json &o,
if (store_original_json_for_extras_and_extensions) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
light->extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
light->extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
light->extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
light->extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -5458,13 +5620,13 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
return false;
}
JsonDocument v;
detail::JsonDocument v;
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
defined(_CPPUNWIND)) && \
!defined(TINYGLTF_NOEXCEPTION)
try {
JsonParse(v, json_str, json_str_length, true);
detail::JsonParse(v, json_str, json_str_length, true);
} catch (const std::exception &e) {
if (err) {
@@ -5474,9 +5636,9 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
}
#else
{
JsonParse(v, json_str, json_str_length);
detail::JsonParse(v, json_str, json_str_length);
if (!IsObject(v)) {
if (!detail::IsObject(v)) {
// Assume parsing was failed.
if (err) {
(*err) = "Failed to parse JSON object\n";
@@ -5486,7 +5648,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
}
#endif
if (!IsObject(v)) {
if (!detail::IsObject(v)) {
// root is not an object.
if (err) {
(*err) = "Root element is not a JSON object\n";
@@ -5496,13 +5658,13 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
{
bool version_found = false;
json_const_iterator it;
if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
auto &itObj = GetValue(it);
json_const_iterator version_it;
detail::json_const_iterator it;
if (detail::FindMember(v, "asset", it) && detail::IsObject(detail::GetValue(it))) {
auto &itObj = detail::GetValue(it);
detail::json_const_iterator version_it;
std::string versionStr;
if (FindMember(itObj, "version", version_it) &&
GetString(GetValue(version_it), versionStr)) {
if (detail::FindMember(itObj, "version", version_it) &&
detail::GetString(detail::GetValue(version_it), versionStr)) {
version_found = true;
}
}
@@ -5519,9 +5681,9 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// scene is not mandatory.
// FIXME Maybe a better way to handle it than removing the code
auto IsArrayMemberPresent = [](const json &_v, const char *name) -> bool {
json_const_iterator it;
return FindMember(_v, name, it) && IsArray(GetValue(it));
auto IsArrayMemberPresent = [](const detail::json &_v, const char *name) -> bool {
detail::json_const_iterator it;
return detail::FindMember(_v, name, it) && detail::IsArray(detail::GetValue(it));
};
{
@@ -5586,9 +5748,9 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// 1. Parse Asset
{
json_const_iterator it;
if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
const json &root = GetValue(it);
detail::json_const_iterator it;
if (detail::FindMember(v, "asset", it) && detail::IsObject(detail::GetValue(it))) {
const detail::json &root = detail::GetValue(it);
ParseAsset(&model->asset, err, root,
store_original_json_for_extras_and_extensions_);
@@ -5596,21 +5758,21 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
}
#ifdef TINYGLTF_USE_CPP14
auto ForEachInArray = [](const json &_v, const char *member,
auto ForEachInArray = [](const detail::json &_v, const char *member,
const auto &cb) -> bool
#else
// The std::function<> implementation can be less efficient because it will
// allocate heap when the size of the captured lambda is above 16 bytes with
// clang and gcc, but it does not require C++14.
auto ForEachInArray = [](const json &_v, const char *member,
const std::function<bool(const json &)> &cb) -> bool
auto ForEachInArray = [](const detail::json &_v, const char *member,
const std::function<bool(const detail::json &)> &cb) -> bool
#endif
{
json_const_iterator itm;
if (FindMember(_v, member, itm) && IsArray(GetValue(itm))) {
const json &root = GetValue(itm);
auto it = ArrayBegin(root);
auto end = ArrayEnd(root);
detail::json_const_iterator itm;
if (detail::FindMember(_v, member, itm) && detail::IsArray(detail::GetValue(itm))) {
const detail::json &root = detail::GetValue(itm);
auto it = detail::ArrayBegin(root);
auto end = detail::ArrayEnd(root);
for (; it != end; ++it) {
if (!cb(*it)) return false;
}
@@ -5620,18 +5782,18 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// 2. Parse extensionUsed
{
ForEachInArray(v, "extensionsUsed", [&](const json &o) {
ForEachInArray(v, "extensionsUsed", [&](const detail::json &o) {
std::string str;
GetString(o, str);
detail::GetString(o, str);
model->extensionsUsed.emplace_back(std::move(str));
return true;
});
}
{
ForEachInArray(v, "extensionsRequired", [&](const json &o) {
ForEachInArray(v, "extensionsRequired", [&](const detail::json &o) {
std::string str;
GetString(o, str);
detail::GetString(o, str);
model->extensionsRequired.emplace_back(std::move(str));
return true;
});
@@ -5639,8 +5801,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// 3. Parse Buffer
{
bool success = ForEachInArray(v, "buffers", [&](const json &o) {
if (!IsObject(o)) {
bool success = ForEachInArray(v, "buffers", [&](const detail::json &o) {
if (!detail::IsObject(o)) {
if (err) {
(*err) += "`buffers' does not contain an JSON object.";
}
@@ -5649,7 +5811,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Buffer buffer;
if (!ParseBuffer(&buffer, err, o,
store_original_json_for_extras_and_extensions_, &fs,
base_dir, is_binary_, bin_data_, bin_size_)) {
&uri_cb, base_dir, is_binary_, bin_data_, bin_size_)) {
return false;
}
@@ -5663,8 +5825,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
}
// 4. Parse BufferView
{
bool success = ForEachInArray(v, "bufferViews", [&](const json &o) {
if (!IsObject(o)) {
bool success = ForEachInArray(v, "bufferViews", [&](const detail::json &o) {
if (!detail::IsObject(o)) {
if (err) {
(*err) += "`bufferViews' does not contain an JSON object.";
}
@@ -5687,8 +5849,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// 5. Parse Accessor
{
bool success = ForEachInArray(v, "accessors", [&](const json &o) {
if (!IsObject(o)) {
bool success = ForEachInArray(v, "accessors", [&](const detail::json &o) {
if (!detail::IsObject(o)) {
if (err) {
(*err) += "`accessors' does not contain an JSON object.";
}
@@ -5711,8 +5873,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// 6. Parse Mesh
{
bool success = ForEachInArray(v, "meshes", [&](const json &o) {
if (!IsObject(o)) {
bool success = ForEachInArray(v, "meshes", [&](const detail::json &o) {
if (!detail::IsObject(o)) {
if (err) {
(*err) += "`meshes' does not contain an JSON object.";
}
@@ -5760,25 +5922,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;
}
}
}
}
@@ -5787,8 +5956,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// 7. Parse Node
{
bool success = ForEachInArray(v, "nodes", [&](const json &o) {
if (!IsObject(o)) {
bool success = ForEachInArray(v, "nodes", [&](const detail::json &o) {
if (!detail::IsObject(o)) {
if (err) {
(*err) += "`nodes' does not contain an JSON object.";
}
@@ -5811,8 +5980,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// 8. Parse scenes.
{
bool success = ForEachInArray(v, "scenes", [&](const json &o) {
if (!IsObject(o)) {
bool success = ForEachInArray(v, "scenes", [&](const detail::json &o) {
if (!detail::IsObject(o)) {
if (err) {
(*err) += "`scenes' does not contain an JSON object.";
}
@@ -5831,15 +6000,15 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
if (store_original_json_for_extras_and_extensions_) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
scene.extensions_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
scene.extensions_json_string = detail::JsonToString(detail::GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
scene.extras_json_string = JsonToString(GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extras", it)) {
scene.extras_json_string = detail::JsonToString(detail::GetValue(it));
}
}
}
@@ -5855,17 +6024,17 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// 9. Parse default scenes.
{
json_const_iterator rootIt;
detail::json_const_iterator rootIt;
int iVal;
if (FindMember(v, "scene", rootIt) && GetInt(GetValue(rootIt), iVal)) {
if (detail::FindMember(v, "scene", rootIt) && detail::GetInt(detail::GetValue(rootIt), iVal)) {
model->defaultScene = iVal;
}
}
// 10. Parse Material
{
bool success = ForEachInArray(v, "materials", [&](const json &o) {
if (!IsObject(o)) {
bool success = ForEachInArray(v, "materials", [&](const detail::json &o) {
if (!detail::IsObject(o)) {
if (err) {
(*err) += "`materials' does not contain an JSON object.";
}
@@ -5903,8 +6072,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
{
int idx = 0;
bool success = ForEachInArray(v, "images", [&](const json &o) {
if (!IsObject(o)) {
bool success = ForEachInArray(v, "images", [&](const detail::json &o) {
if (!detail::IsObject(o)) {
if (err) {
(*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
}
@@ -5913,7 +6082,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Image image;
if (!ParseImage(&image, idx, err, warn, o,
store_original_json_for_extras_and_extensions_, base_dir,
&fs, &this->LoadImageData, load_image_user_data)) {
&fs, &uri_cb, &this->LoadImageData,
load_image_user_data)) {
return false;
}
@@ -5969,8 +6139,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// 12. Parse Texture
{
bool success = ForEachInArray(v, "textures", [&](const json &o) {
if (!IsObject(o)) {
bool success = ForEachInArray(v, "textures", [&](const detail::json &o) {
if (!detail::IsObject(o)) {
if (err) {
(*err) += "`textures' does not contain an JSON object.";
}
@@ -5994,8 +6164,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// 13. Parse Animation
{
bool success = ForEachInArray(v, "animations", [&](const json &o) {
if (!IsObject(o)) {
bool success = ForEachInArray(v, "animations", [&](const detail::json &o) {
if (!detail::IsObject(o)) {
if (err) {
(*err) += "`animations' does not contain an JSON object.";
}
@@ -6018,8 +6188,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// 14. Parse Skin
{
bool success = ForEachInArray(v, "skins", [&](const json &o) {
if (!IsObject(o)) {
bool success = ForEachInArray(v, "skins", [&](const detail::json &o) {
if (!detail::IsObject(o)) {
if (err) {
(*err) += "`skins' does not contain an JSON object.";
}
@@ -6042,8 +6212,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// 15. Parse Sampler
{
bool success = ForEachInArray(v, "samplers", [&](const json &o) {
if (!IsObject(o)) {
bool success = ForEachInArray(v, "samplers", [&](const detail::json &o) {
if (!detail::IsObject(o)) {
if (err) {
(*err) += "`samplers' does not contain an JSON object.";
}
@@ -6066,8 +6236,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// 16. Parse Camera
{
bool success = ForEachInArray(v, "cameras", [&](const json &o) {
if (!IsObject(o)) {
bool success = ForEachInArray(v, "cameras", [&](const detail::json &o) {
if (!detail::IsObject(o)) {
if (err) {
(*err) += "`cameras' does not contain an JSON object.";
}
@@ -6093,26 +6263,26 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// 18. Specific extension implementations
{
json_const_iterator rootIt;
if (FindMember(v, "extensions", rootIt) && IsObject(GetValue(rootIt))) {
const json &root = GetValue(rootIt);
detail::json_const_iterator rootIt;
if (detail::FindMember(v, "extensions", rootIt) && detail::IsObject(detail::GetValue(rootIt))) {
const detail::json &root = detail::GetValue(rootIt);
json_const_iterator it(ObjectBegin(root));
json_const_iterator itEnd(ObjectEnd(root));
detail::json_const_iterator it(detail::ObjectBegin(root));
detail::json_const_iterator itEnd(detail::ObjectEnd(root));
for (; it != itEnd; ++it) {
// parse KHR_lights_punctual extension
std::string key(GetKey(it));
if ((key == "KHR_lights_punctual") && IsObject(GetValue(it))) {
const json &object = GetValue(it);
json_const_iterator itLight;
if (FindMember(object, "lights", itLight)) {
const json &lights = GetValue(itLight);
if (!IsArray(lights)) {
std::string key(detail::GetKey(it));
if ((key == "KHR_lights_punctual") && detail::IsObject(detail::GetValue(it))) {
const detail::json &object = detail::GetValue(it);
detail::json_const_iterator itLight;
if (detail::FindMember(object, "lights", itLight)) {
const detail::json &lights = detail::GetValue(itLight);
if (!detail::IsArray(lights)) {
continue;
}
auto arrayIt(ArrayBegin(lights));
auto arrayItEnd(ArrayEnd(lights));
auto arrayIt(detail::ArrayBegin(lights));
auto arrayItEnd(detail::ArrayEnd(lights));
for (; arrayIt != arrayItEnd; ++arrayIt) {
Light light;
if (!ParseLight(&light, err, *arrayIt,
@@ -6131,8 +6301,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
ParseExtrasProperty(&model->extras, v);
if (store_original_json_for_extras_and_extensions_) {
model->extras_json_string = JsonToString(v["extras"]);
model->extensions_json_string = JsonToString(v["extensions"]);
model->extras_json_string = detail::JsonToString(v["extras"]);
model->extensions_json_string = detail::JsonToString(v["extensions"]);
}
return true;
@@ -6219,44 +6389,125 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
unsigned int version; // 4 bytes
unsigned int length; // 4 bytes
unsigned int model_length; // 4 bytes
unsigned int model_format; // 4 bytes;
unsigned int chunk0_length; // 4 bytes
unsigned int chunk0_format; // 4 bytes;
// @todo { Endian swap for big endian machine. }
memcpy(&version, bytes + 4, 4);
swap4(&version);
memcpy(&length, bytes + 8, 4);
swap4(&length);
memcpy(&model_length, bytes + 12, 4);
swap4(&model_length);
memcpy(&model_format, bytes + 16, 4);
swap4(&model_format);
memcpy(&chunk0_length, bytes + 12, 4); // JSON data length
swap4(&chunk0_length);
memcpy(&chunk0_format, bytes + 16, 4);
swap4(&chunk0_format);
// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout
//
// In case the Bin buffer is not present, the size is exactly 20 + size of
// JSON contents,
// so use "greater than" operator.
if ((20 + model_length > size) || (model_length < 1) || (length > size) ||
(20 + model_length > length) ||
(model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
//
// https://github.com/syoyo/tinygltf/issues/372
// Use 64bit uint to avoid integer overflow.
uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
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 ((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.";
}
return false;
}
// 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 ((header_and_json_size % 4) != 0) {
if (err) {
(*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
}
}
//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;
}
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);
//std::cout << "chunk1_length = " << chunk1_length << "\n";
if (chunk1_length < 4) {
if (err) {
(*err) = "Insufficient Chunk1(BIN) data size.";
}
return false;
}
if ((chunk1_length % 4) != 0) {
if (err) {
(*err) = "BIN Chunk end does not aligned to a 4-byte boundary.";
}
return false;
}
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;
}
if (chunk1_format != 0x004e4942) {
if (err) {
(*err) = "Invalid type for chunk1 data.";
}
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);
}
// Extract JSON string.
std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
model_length);
chunk0_length);
is_binary_ = true;
bin_data_ = bytes + 20 + model_length +
8; // 4 bytes (buffer_length) + 4 bytes(buffer_format)
bin_size_ =
length - (20 + model_length); // extract header + JSON scene data.
bool ret = LoadFromString(model, err, warn,
reinterpret_cast<const char *>(&bytes[20]),
model_length, base_dir, check_sections);
chunk0_length, base_dir, check_sections);
if (!ret) {
return ret;
}
@@ -6303,43 +6554,43 @@ bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
///////////////////////
// GLTF Serialization
///////////////////////
namespace {
json JsonFromString(const char *s) {
namespace detail {
detail::json JsonFromString(const char *s) {
#ifdef TINYGLTF_USE_RAPIDJSON
return json(s, GetAllocator());
return detail::json(s, detail::GetAllocator());
#else
return json(s);
return detail::json(s);
#endif
}
void JsonAssign(json &dest, const json &src) {
void JsonAssign(detail::json &dest, const detail::json &src) {
#ifdef TINYGLTF_USE_RAPIDJSON
dest.CopyFrom(src, GetAllocator());
dest.CopyFrom(src, detail::GetAllocator());
#else
dest = src;
#endif
}
void JsonAddMember(json &o, const char *key, json &&value) {
void JsonAddMember(detail::json &o, const char *key, detail::json &&value) {
#ifdef TINYGLTF_USE_RAPIDJSON
if (!o.IsObject()) {
o.SetObject();
}
o.AddMember(json(key, GetAllocator()), std::move(value), GetAllocator());
o.AddMember(detail::json(key, detail::GetAllocator()), std::move(value), detail::GetAllocator());
#else
o[key] = std::move(value);
#endif
}
void JsonPushBack(json &o, json &&value) {
void JsonPushBack(detail::json &o, detail::json &&value) {
#ifdef TINYGLTF_USE_RAPIDJSON
o.PushBack(std::move(value), GetAllocator());
o.PushBack(std::move(value), detail::GetAllocator());
#else
o.push_back(std::move(value));
#endif
}
bool JsonIsNull(const json &o) {
bool JsonIsNull(const detail::json &o) {
#ifdef TINYGLTF_USE_RAPIDJSON
return o.IsNull();
#else
@@ -6347,7 +6598,7 @@ bool JsonIsNull(const json &o) {
#endif
}
void JsonSetObject(json &o) {
void JsonSetObject(detail::json &o) {
#ifdef TINYGLTF_USE_RAPIDJSON
o.SetObject();
#else
@@ -6355,66 +6606,66 @@ void JsonSetObject(json &o) {
#endif
}
void JsonReserveArray(json &o, size_t s) {
void JsonReserveArray(detail::json &o, size_t s) {
#ifdef TINYGLTF_USE_RAPIDJSON
o.SetArray();
o.Reserve(static_cast<rapidjson::SizeType>(s), GetAllocator());
o.Reserve(static_cast<rapidjson::SizeType>(s), detail::GetAllocator());
#endif
(void)(o);
(void)(s);
}
} // namespace
// typedef std::pair<std::string, json> json_object_pair;
// typedef std::pair<std::string, detail::json> json_object_pair;
template <typename T>
static void SerializeNumberProperty(const std::string &key, T number,
json &obj) {
detail::json &obj) {
// obj.insert(
// json_object_pair(key, json(static_cast<double>(number))));
// json_object_pair(key, detail::json(static_cast<double>(number))));
// obj[key] = static_cast<double>(number);
JsonAddMember(obj, key.c_str(), json(number));
detail::JsonAddMember(obj, key.c_str(), detail::json(number));
}
#ifdef TINYGLTF_USE_RAPIDJSON
template <>
void SerializeNumberProperty(const std::string &key, size_t number, json &obj) {
JsonAddMember(obj, key.c_str(), json(static_cast<uint64_t>(number)));
void SerializeNumberProperty(const std::string &key, size_t number, detail::json &obj) {
detail::JsonAddMember(obj, key.c_str(), detail::json(static_cast<uint64_t>(number)));
}
#endif
template <typename T>
static void SerializeNumberArrayProperty(const std::string &key,
const std::vector<T> &value,
json &obj) {
detail::json &obj) {
if (value.empty()) return;
json ary;
JsonReserveArray(ary, value.size());
detail::json ary;
detail::JsonReserveArray(ary, value.size());
for (const auto &s : value) {
JsonPushBack(ary, json(s));
detail::JsonPushBack(ary, detail::json(s));
}
JsonAddMember(obj, key.c_str(), std::move(ary));
detail::JsonAddMember(obj, key.c_str(), std::move(ary));
}
static void SerializeStringProperty(const std::string &key,
const std::string &value, json &obj) {
JsonAddMember(obj, key.c_str(), JsonFromString(value.c_str()));
const std::string &value, detail::json &obj) {
detail::JsonAddMember(obj, key.c_str(), detail::JsonFromString(value.c_str()));
}
static void SerializeStringArrayProperty(const std::string &key,
const std::vector<std::string> &value,
json &obj) {
json ary;
JsonReserveArray(ary, value.size());
detail::json &obj) {
detail::json ary;
detail::JsonReserveArray(ary, value.size());
for (auto &s : value) {
JsonPushBack(ary, JsonFromString(s.c_str()));
detail::JsonPushBack(ary, detail::JsonFromString(s.c_str()));
}
JsonAddMember(obj, key.c_str(), std::move(ary));
detail::JsonAddMember(obj, key.c_str(), std::move(ary));
}
static bool ValueToJson(const Value &value, json *ret) {
json obj;
static bool ValueToJson(const Value &value, detail::json *ret) {
detail::json obj;
#ifdef TINYGLTF_USE_RAPIDJSON
switch (value.Type()) {
case REAL_TYPE:
@@ -6427,33 +6678,33 @@ static bool ValueToJson(const Value &value, json *ret) {
obj.SetBool(value.Get<bool>());
break;
case STRING_TYPE:
obj.SetString(value.Get<std::string>().c_str(), GetAllocator());
obj.SetString(value.Get<std::string>().c_str(), detail::GetAllocator());
break;
case ARRAY_TYPE: {
obj.SetArray();
obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
GetAllocator());
detail::GetAllocator());
for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
Value elementValue = value.Get(int(i));
json elementJson;
detail::json elementJson;
if (ValueToJson(value.Get(int(i)), &elementJson))
obj.PushBack(std::move(elementJson), GetAllocator());
obj.PushBack(std::move(elementJson), detail::GetAllocator());
}
break;
}
case BINARY_TYPE:
// TODO
// obj = json(value.Get<std::vector<unsigned char>>());
// obj = detail::json(value.Get<std::vector<unsigned char>>());
return false;
break;
case OBJECT_TYPE: {
obj.SetObject();
Value::Object objMap = value.Get<Value::Object>();
for (auto &it : objMap) {
json elementJson;
detail::json elementJson;
if (ValueToJson(it.second, &elementJson)) {
obj.AddMember(json(it.first.c_str(), GetAllocator()),
std::move(elementJson), GetAllocator());
obj.AddMember(detail::json(it.first.c_str(), detail::GetAllocator()),
std::move(elementJson), detail::GetAllocator());
}
}
break;
@@ -6465,21 +6716,21 @@ static bool ValueToJson(const Value &value, json *ret) {
#else
switch (value.Type()) {
case REAL_TYPE:
obj = json(value.Get<double>());
obj = detail::json(value.Get<double>());
break;
case INT_TYPE:
obj = json(value.Get<int>());
obj = detail::json(value.Get<int>());
break;
case BOOL_TYPE:
obj = json(value.Get<bool>());
obj = detail::json(value.Get<bool>());
break;
case STRING_TYPE:
obj = json(value.Get<std::string>());
obj = detail::json(value.Get<std::string>());
break;
case ARRAY_TYPE: {
for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
Value elementValue = value.Get(int(i));
json elementJson;
detail::json elementJson;
if (ValueToJson(value.Get(int(i)), &elementJson))
obj.push_back(elementJson);
}
@@ -6493,7 +6744,7 @@ static bool ValueToJson(const Value &value, json *ret) {
case OBJECT_TYPE: {
Value::Object objMap = value.Get<Value::Object>();
for (auto &it : objMap) {
json elementJson;
detail::json elementJson;
if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
}
break;
@@ -6508,15 +6759,15 @@ static bool ValueToJson(const Value &value, json *ret) {
}
static void SerializeValue(const std::string &key, const Value &value,
json &obj) {
json ret;
detail::json &obj) {
detail::json ret;
if (ValueToJson(value, &ret)) {
JsonAddMember(obj, key.c_str(), std::move(ret));
detail::JsonAddMember(obj, key.c_str(), std::move(ret));
}
}
static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
json &o) {
detail::json &o) {
std::string header = "data:application/octet-stream;base64,";
if (data.size() > 0) {
std::string encodedData =
@@ -6524,7 +6775,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);
}
}
@@ -6562,14 +6813,14 @@ static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
}
#if 0 // FIXME(syoyo): not used. will be removed in the future release.
static void SerializeParameterMap(ParameterMap &param, json &o) {
static void SerializeParameterMap(ParameterMap &param, detail::json &o) {
for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
++paramIt) {
if (paramIt->second.number_array.size()) {
SerializeNumberArrayProperty<double>(paramIt->first,
paramIt->second.number_array, o);
} else if (paramIt->second.json_double_value.size()) {
json json_double_value;
detail::json json_double_value;
for (std::map<std::string, double>::iterator it =
paramIt->second.json_double_value.begin();
it != paramIt->second.json_double_value.end(); ++it) {
@@ -6592,33 +6843,33 @@ static void SerializeParameterMap(ParameterMap &param, json &o) {
}
#endif
static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
static void SerializeExtensionMap(const ExtensionMap &extensions, detail::json &o) {
if (!extensions.size()) return;
json extMap;
detail::json extMap;
for (ExtensionMap::const_iterator extIt = extensions.begin();
extIt != extensions.end(); ++extIt) {
// Allow an empty object for extension(#97)
json ret;
detail::json ret;
bool isNull = true;
if (ValueToJson(extIt->second, &ret)) {
isNull = JsonIsNull(ret);
JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
isNull = detail::JsonIsNull(ret);
detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
}
if (isNull) {
if (!(extIt->first.empty())) { // name should not be empty, but for sure
// create empty object so that an extension name is still included in
// json.
json empty;
JsonSetObject(empty);
JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
detail::json empty;
detail::JsonSetObject(empty);
detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
}
}
}
JsonAddMember(o, "extensions", std::move(extMap));
detail::JsonAddMember(o, "extensions", std::move(extMap));
}
static void SerializeGltfAccessor(Accessor &accessor, json &o) {
static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) {
if (accessor.bufferView >= 0)
SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
@@ -6687,18 +6938,44 @@ static void SerializeGltfAccessor(Accessor &accessor, json &o) {
if (accessor.extras.Type() != NULL_TYPE) {
SerializeValue("extras", accessor.extras, o);
}
// sparse
if (accessor.sparse.isSparse)
{
detail::json sparse;
SerializeNumberProperty<int>("count", accessor.sparse.count, sparse);
{
detail::json indices;
SerializeNumberProperty<int>("bufferView", accessor.sparse.indices.bufferView, indices);
SerializeNumberProperty<int>("byteOffset", accessor.sparse.indices.byteOffset, indices);
SerializeNumberProperty<int>("componentType", accessor.sparse.indices.componentType, indices);
detail::JsonAddMember(sparse, "indices", std::move(indices));
}
{
detail::json values;
SerializeNumberProperty<int>("bufferView", accessor.sparse.values.bufferView, values);
SerializeNumberProperty<int>("byteOffset", accessor.sparse.values.byteOffset, values);
detail::JsonAddMember(sparse, "values", std::move(values));
}
detail::JsonAddMember(o, "sparse", std::move(sparse));
}
}
static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
static void SerializeGltfAnimationChannel(const AnimationChannel &channel,
detail::json &o) {
SerializeNumberProperty("sampler", channel.sampler, o);
{
json target;
SerializeNumberProperty("node", channel.target_node, target);
detail::json target;
if (channel.target_node > 0) {
SerializeNumberProperty("node", channel.target_node, target);
}
SerializeStringProperty("path", channel.target_path, target);
SerializeExtensionMap(channel.target_extensions, target);
JsonAddMember(o, "target", std::move(target));
detail::JsonAddMember(o, "target", std::move(target));
}
if (channel.extras.Type() != NULL_TYPE) {
@@ -6708,7 +6985,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,
detail::json &o) {
SerializeNumberProperty("input", sampler.input, o);
SerializeNumberProperty("output", sampler.output, o);
SerializeStringProperty("interpolation", sampler.interpolation, o);
@@ -6718,33 +6996,33 @@ static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
}
}
static void SerializeGltfAnimation(Animation &animation, json &o) {
static void SerializeGltfAnimation(const Animation &animation, detail::json &o) {
if (!animation.name.empty())
SerializeStringProperty("name", animation.name, o);
{
json channels;
JsonReserveArray(channels, animation.channels.size());
detail::json channels;
detail::JsonReserveArray(channels, animation.channels.size());
for (unsigned int i = 0; i < animation.channels.size(); ++i) {
json channel;
detail::json channel;
AnimationChannel gltfChannel = animation.channels[i];
SerializeGltfAnimationChannel(gltfChannel, channel);
JsonPushBack(channels, std::move(channel));
detail::JsonPushBack(channels, std::move(channel));
}
JsonAddMember(o, "channels", std::move(channels));
detail::JsonAddMember(o, "channels", std::move(channels));
}
{
json samplers;
JsonReserveArray(samplers, animation.samplers.size());
detail::json samplers;
detail::JsonReserveArray(samplers, animation.samplers.size());
for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
json sampler;
detail::json sampler;
AnimationSampler gltfSampler = animation.samplers[i];
SerializeGltfAnimationSampler(gltfSampler, sampler);
JsonPushBack(samplers, std::move(sampler));
detail::JsonPushBack(samplers, std::move(sampler));
}
JsonAddMember(o, "samplers", std::move(samplers));
detail::JsonAddMember(o, "samplers", std::move(samplers));
}
if (animation.extras.Type() != NULL_TYPE) {
@@ -6754,7 +7032,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, detail::json &o) {
if (!asset.generator.empty()) {
SerializeStringProperty("generator", asset.generator, o);
}
@@ -6763,14 +7041,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);
@@ -6779,7 +7058,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, detail::json &o,
std::vector<unsigned char> &binBuffer) {
SerializeNumberProperty("byteLength", buffer.data.size(), o);
binBuffer = buffer.data;
@@ -6791,7 +7070,7 @@ static void SerializeGltfBufferBin(Buffer &buffer, json &o,
}
}
static void SerializeGltfBuffer(Buffer &buffer, json &o) {
static void SerializeGltfBuffer(const Buffer &buffer, detail::json &o) {
SerializeNumberProperty("byteLength", buffer.data.size(), o);
SerializeGltfBufferData(buffer.data, o);
@@ -6802,12 +7081,12 @@ static void SerializeGltfBuffer(Buffer &buffer, json &o) {
}
}
static bool SerializeGltfBuffer(Buffer &buffer, json &o,
static bool SerializeGltfBuffer(const Buffer &buffer, detail::json &o,
const std::string &binFilename,
const std::string &binBaseFilename) {
const std::string &binUri) {
if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
SerializeNumberProperty("byteLength", buffer.data.size(), o);
SerializeStringProperty("uri", binBaseFilename, o);
SerializeStringProperty("uri", binUri, o);
if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
@@ -6817,7 +7096,7 @@ static bool SerializeGltfBuffer(Buffer &buffer, json &o,
return true;
}
static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
static void SerializeGltfBufferView(const BufferView &bufferView, detail::json &o) {
SerializeNumberProperty("buffer", bufferView.buffer, o);
SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
@@ -6843,14 +7122,15 @@ 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,
detail::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()) {
@@ -6864,7 +7144,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, detail::json &o) {
SerializeNumberProperty("index", texinfo.index, o);
if (texinfo.texCoord != 0) {
@@ -6878,8 +7158,8 @@ static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) {
SerializeExtensionMap(texinfo.extensions, o);
}
static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo,
json &o) {
static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo,
detail::json &o) {
SerializeNumberProperty("index", texinfo.index, o);
if (texinfo.texCoord != 0) {
@@ -6897,8 +7177,8 @@ static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo,
SerializeExtensionMap(texinfo.extensions, o);
}
static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo,
json &o) {
static void SerializeGltfOcclusionTextureInfo(
const OcclusionTextureInfo &texinfo, detail::json &o) {
SerializeNumberProperty("index", texinfo.index, o);
if (texinfo.texCoord != 0) {
@@ -6916,8 +7196,8 @@ static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo,
SerializeExtensionMap(texinfo.extensions, o);
}
static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr,
json &o) {
static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr,
detail::json &o) {
std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
@@ -6933,15 +7213,15 @@ static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr,
}
if (pbr.baseColorTexture.index > -1) {
json texinfo;
detail::json texinfo;
SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
JsonAddMember(o, "baseColorTexture", std::move(texinfo));
detail::JsonAddMember(o, "baseColorTexture", std::move(texinfo));
}
if (pbr.metallicRoughnessTexture.index > -1) {
json texinfo;
detail::json texinfo;
SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
detail::JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
}
SerializeExtensionMap(pbr.extensions, o);
@@ -6951,7 +7231,7 @@ static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr,
}
}
static void SerializeGltfMaterial(Material &material, json &o) {
static void SerializeGltfMaterial(const Material &material, detail::json &o) {
if (material.name.size()) {
SerializeStringProperty("name", material.name, o);
}
@@ -6967,24 +7247,24 @@ static void SerializeGltfMaterial(Material &material, json &o) {
}
if (material.doubleSided != false)
JsonAddMember(o, "doubleSided", json(material.doubleSided));
detail::JsonAddMember(o, "doubleSided", detail::json(material.doubleSided));
if (material.normalTexture.index > -1) {
json texinfo;
detail::json texinfo;
SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
JsonAddMember(o, "normalTexture", std::move(texinfo));
detail::JsonAddMember(o, "normalTexture", std::move(texinfo));
}
if (material.occlusionTexture.index > -1) {
json texinfo;
detail::json texinfo;
SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
JsonAddMember(o, "occlusionTexture", std::move(texinfo));
detail::JsonAddMember(o, "occlusionTexture", std::move(texinfo));
}
if (material.emissiveTexture.index > -1) {
json texinfo;
detail::json texinfo;
SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
JsonAddMember(o, "emissiveTexture", std::move(texinfo));
detail::JsonAddMember(o, "emissiveTexture", std::move(texinfo));
}
std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
@@ -6994,25 +7274,25 @@ static void SerializeGltfMaterial(Material &material, json &o) {
}
{
json pbrMetallicRoughness;
detail::json pbrMetallicRoughness;
SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
pbrMetallicRoughness);
// Issue 204
// 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));
if (!detail::JsonIsNull(pbrMetallicRoughness)) {
detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
}
}
#if 0 // legacy way. just for the record.
if (material.values.size()) {
json pbrMetallicRoughness;
detail::json pbrMetallicRoughness;
SerializeParameterMap(material.values, pbrMetallicRoughness);
JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
}
SerializeParameterMap(material.additionalValues, o);
@@ -7027,23 +7307,23 @@ static void SerializeGltfMaterial(Material &material, json &o) {
}
}
static void SerializeGltfMesh(Mesh &mesh, json &o) {
json primitives;
JsonReserveArray(primitives, mesh.primitives.size());
static void SerializeGltfMesh(const Mesh &mesh, detail::json &o) {
detail::json primitives;
detail::JsonReserveArray(primitives, mesh.primitives.size());
for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
json primitive;
detail::json primitive;
const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy
{
json attributes;
detail::json attributes;
for (auto attrIt = gltfPrimitive.attributes.begin();
attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
}
JsonAddMember(primitive, "attributes", std::move(attributes));
detail::JsonAddMember(primitive, "attributes", std::move(attributes));
}
// Indicies is optional
// Indices is optional
if (gltfPrimitive.indices > -1) {
SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
}
@@ -7056,19 +7336,19 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) {
// Morph targets
if (gltfPrimitive.targets.size()) {
json targets;
JsonReserveArray(targets, gltfPrimitive.targets.size());
detail::json targets;
detail::JsonReserveArray(targets, gltfPrimitive.targets.size());
for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
json targetAttributes;
detail::json targetAttributes;
std::map<std::string, int> targetData = gltfPrimitive.targets[k];
for (std::map<std::string, int>::iterator attrIt = targetData.begin();
attrIt != targetData.end(); ++attrIt) {
SerializeNumberProperty<int>(attrIt->first, attrIt->second,
targetAttributes);
}
JsonPushBack(targets, std::move(targetAttributes));
detail::JsonPushBack(targets, std::move(targetAttributes));
}
JsonAddMember(primitive, "targets", std::move(targets));
detail::JsonAddMember(primitive, "targets", std::move(targets));
}
SerializeExtensionMap(gltfPrimitive.extensions, primitive);
@@ -7077,10 +7357,10 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) {
SerializeValue("extras", gltfPrimitive.extras, primitive);
}
JsonPushBack(primitives, std::move(primitive));
detail::JsonPushBack(primitives, std::move(primitive));
}
JsonAddMember(o, "primitives", std::move(primitives));
detail::JsonAddMember(o, "primitives", std::move(primitives));
if (mesh.weights.size()) {
SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
@@ -7096,7 +7376,7 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) {
}
}
static void SerializeSpotLight(SpotLight &spot, json &o) {
static void SerializeSpotLight(const SpotLight &spot, detail::json &o) {
SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
SerializeExtensionMap(spot.extensions, o);
@@ -7105,7 +7385,7 @@ static void SerializeSpotLight(SpotLight &spot, json &o) {
}
}
static void SerializeGltfLight(Light &light, json &o) {
static void SerializeGltfLight(const Light &light, detail::json &o) {
if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
SerializeNumberProperty("intensity", light.intensity, o);
if (light.range > 0.0) {
@@ -7114,9 +7394,9 @@ static void SerializeGltfLight(Light &light, json &o) {
SerializeNumberArrayProperty("color", light.color, o);
SerializeStringProperty("type", light.type, o);
if (light.type == "spot") {
json spot;
detail::json spot;
SerializeSpotLight(light.spot, spot);
JsonAddMember(o, "spot", std::move(spot));
detail::JsonAddMember(o, "spot", std::move(spot));
}
SerializeExtensionMap(light.extensions, o);
if (light.extras.Type() != NULL_TYPE) {
@@ -7124,7 +7404,7 @@ static void SerializeGltfLight(Light &light, json &o) {
}
}
static void SerializeGltfNode(Node &node, json &o) {
static void SerializeGltfNode(const Node &node, detail::json &o) {
if (node.translation.size() > 0) {
SerializeNumberArrayProperty<double>("translation", node.translation, o);
}
@@ -7162,14 +7442,17 @@ 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, detail::json &o) {
if (!sampler.name.empty()) {
SerializeStringProperty("name", sampler.name, o);
}
if (sampler.magFilter != -1) {
SerializeNumberProperty("magFilter", sampler.magFilter, o);
}
if (sampler.minFilter != -1) {
SerializeNumberProperty("minFilter", sampler.minFilter, o);
}
//SerializeNumberProperty("wrapR", sampler.wrapR, o);
// SerializeNumberProperty("wrapR", sampler.wrapR, o);
SerializeNumberProperty("wrapS", sampler.wrapS, o);
SerializeNumberProperty("wrapT", sampler.wrapT, o);
@@ -7179,7 +7462,7 @@ static void SerializeGltfSampler(Sampler &sampler, json &o) {
}
static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
json &o) {
detail::json &o) {
SerializeNumberProperty("zfar", camera.zfar, o);
SerializeNumberProperty("znear", camera.znear, o);
SerializeNumberProperty("xmag", camera.xmag, o);
@@ -7191,7 +7474,7 @@ static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
}
static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
json &o) {
detail::json &o) {
SerializeNumberProperty("zfar", camera.zfar, o);
SerializeNumberProperty("znear", camera.znear, o);
if (camera.aspectRatio > 0) {
@@ -7207,20 +7490,20 @@ static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
}
}
static void SerializeGltfCamera(const Camera &camera, json &o) {
static void SerializeGltfCamera(const Camera &camera, detail::json &o) {
SerializeStringProperty("type", camera.type, o);
if (!camera.name.empty()) {
SerializeStringProperty("name", camera.name, o);
}
if (camera.type.compare("orthographic") == 0) {
json orthographic;
detail::json orthographic;
SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
JsonAddMember(o, "orthographic", std::move(orthographic));
detail::JsonAddMember(o, "orthographic", std::move(orthographic));
} else if (camera.type.compare("perspective") == 0) {
json perspective;
detail::json perspective;
SerializeGltfPerspectiveCamera(camera.perspective, perspective);
JsonAddMember(o, "perspective", std::move(perspective));
detail::JsonAddMember(o, "perspective", std::move(perspective));
} else {
// ???
}
@@ -7231,7 +7514,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, detail::json &o) {
SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
if (scene.name.size()) {
@@ -7243,7 +7526,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, detail::json &o) {
// required
SerializeNumberArrayProperty<int>("joints", skin.joints, o);
@@ -7260,7 +7543,7 @@ static void SerializeGltfSkin(Skin &skin, json &o) {
}
}
static void SerializeGltfTexture(Texture &texture, json &o) {
static void SerializeGltfTexture(const Texture &texture, detail::json &o) {
if (texture.sampler > -1) {
SerializeNumberProperty("sampler", texture.sampler, o);
}
@@ -7279,49 +7562,49 @@ 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, detail::json &o) {
// ACCESSORS
if (model->accessors.size()) {
json accessors;
JsonReserveArray(accessors, model->accessors.size());
detail::json accessors;
detail::JsonReserveArray(accessors, model->accessors.size());
for (unsigned int i = 0; i < model->accessors.size(); ++i) {
json accessor;
detail::json accessor;
SerializeGltfAccessor(model->accessors[i], accessor);
JsonPushBack(accessors, std::move(accessor));
detail::JsonPushBack(accessors, std::move(accessor));
}
JsonAddMember(o, "accessors", std::move(accessors));
detail::JsonAddMember(o, "accessors", std::move(accessors));
}
// ANIMATIONS
if (model->animations.size()) {
json animations;
JsonReserveArray(animations, model->animations.size());
detail::json animations;
detail::JsonReserveArray(animations, model->animations.size());
for (unsigned int i = 0; i < model->animations.size(); ++i) {
if (model->animations[i].channels.size()) {
json animation;
detail::json animation;
SerializeGltfAnimation(model->animations[i], animation);
JsonPushBack(animations, std::move(animation));
detail::JsonPushBack(animations, std::move(animation));
}
}
JsonAddMember(o, "animations", std::move(animations));
detail::JsonAddMember(o, "animations", std::move(animations));
}
// ASSET
json asset;
detail::json asset;
SerializeGltfAsset(model->asset, asset);
JsonAddMember(o, "asset", std::move(asset));
detail::JsonAddMember(o, "asset", std::move(asset));
// BUFFERVIEWS
if (model->bufferViews.size()) {
json bufferViews;
JsonReserveArray(bufferViews, model->bufferViews.size());
detail::json bufferViews;
detail::JsonReserveArray(bufferViews, model->bufferViews.size());
for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
json bufferView;
detail::json bufferView;
SerializeGltfBufferView(model->bufferViews[i], bufferView);
JsonPushBack(bufferViews, std::move(bufferView));
detail::JsonPushBack(bufferViews, std::move(bufferView));
}
JsonAddMember(o, "bufferViews", std::move(bufferViews));
detail::JsonAddMember(o, "bufferViews", std::move(bufferViews));
}
// Extensions required
@@ -7332,48 +7615,48 @@ static void SerializeGltfModel(Model *model, json &o) {
// MATERIALS
if (model->materials.size()) {
json materials;
JsonReserveArray(materials, model->materials.size());
detail::json materials;
detail::JsonReserveArray(materials, model->materials.size());
for (unsigned int i = 0; i < model->materials.size(); ++i) {
json material;
detail::json material;
SerializeGltfMaterial(model->materials[i], material);
if (JsonIsNull(material)) {
if (detail::JsonIsNull(material)) {
// Issue 294.
// `material` does not have any required parameters
// so the result may be null(unmodified) when all material parameters
// have default value.
//
// null is not allowed thus we create an empty JSON object.
JsonSetObject(material);
detail::JsonSetObject(material);
}
JsonPushBack(materials, std::move(material));
detail::JsonPushBack(materials, std::move(material));
}
JsonAddMember(o, "materials", std::move(materials));
detail::JsonAddMember(o, "materials", std::move(materials));
}
// MESHES
if (model->meshes.size()) {
json meshes;
JsonReserveArray(meshes, model->meshes.size());
detail::json meshes;
detail::JsonReserveArray(meshes, model->meshes.size());
for (unsigned int i = 0; i < model->meshes.size(); ++i) {
json mesh;
detail::json mesh;
SerializeGltfMesh(model->meshes[i], mesh);
JsonPushBack(meshes, std::move(mesh));
detail::JsonPushBack(meshes, std::move(mesh));
}
JsonAddMember(o, "meshes", std::move(meshes));
detail::JsonAddMember(o, "meshes", std::move(meshes));
}
// NODES
if (model->nodes.size()) {
json nodes;
JsonReserveArray(nodes, model->nodes.size());
detail::json nodes;
detail::JsonReserveArray(nodes, model->nodes.size());
for (unsigned int i = 0; i < model->nodes.size(); ++i) {
json node;
detail::json node;
SerializeGltfNode(model->nodes[i], node);
JsonPushBack(nodes, std::move(node));
detail::JsonPushBack(nodes, std::move(node));
}
JsonAddMember(o, "nodes", std::move(nodes));
detail::JsonAddMember(o, "nodes", std::move(nodes));
}
// SCENE
@@ -7383,62 +7666,62 @@ static void SerializeGltfModel(Model *model, json &o) {
// SCENES
if (model->scenes.size()) {
json scenes;
JsonReserveArray(scenes, model->scenes.size());
detail::json scenes;
detail::JsonReserveArray(scenes, model->scenes.size());
for (unsigned int i = 0; i < model->scenes.size(); ++i) {
json currentScene;
detail::json currentScene;
SerializeGltfScene(model->scenes[i], currentScene);
JsonPushBack(scenes, std::move(currentScene));
detail::JsonPushBack(scenes, std::move(currentScene));
}
JsonAddMember(o, "scenes", std::move(scenes));
detail::JsonAddMember(o, "scenes", std::move(scenes));
}
// SKINS
if (model->skins.size()) {
json skins;
JsonReserveArray(skins, model->skins.size());
detail::json skins;
detail::JsonReserveArray(skins, model->skins.size());
for (unsigned int i = 0; i < model->skins.size(); ++i) {
json skin;
detail::json skin;
SerializeGltfSkin(model->skins[i], skin);
JsonPushBack(skins, std::move(skin));
detail::JsonPushBack(skins, std::move(skin));
}
JsonAddMember(o, "skins", std::move(skins));
detail::JsonAddMember(o, "skins", std::move(skins));
}
// TEXTURES
if (model->textures.size()) {
json textures;
JsonReserveArray(textures, model->textures.size());
detail::json textures;
detail::JsonReserveArray(textures, model->textures.size());
for (unsigned int i = 0; i < model->textures.size(); ++i) {
json texture;
detail::json texture;
SerializeGltfTexture(model->textures[i], texture);
JsonPushBack(textures, std::move(texture));
detail::JsonPushBack(textures, std::move(texture));
}
JsonAddMember(o, "textures", std::move(textures));
detail::JsonAddMember(o, "textures", std::move(textures));
}
// SAMPLERS
if (model->samplers.size()) {
json samplers;
JsonReserveArray(samplers, model->samplers.size());
detail::json samplers;
detail::JsonReserveArray(samplers, model->samplers.size());
for (unsigned int i = 0; i < model->samplers.size(); ++i) {
json sampler;
detail::json sampler;
SerializeGltfSampler(model->samplers[i], sampler);
JsonPushBack(samplers, std::move(sampler));
detail::JsonPushBack(samplers, std::move(sampler));
}
JsonAddMember(o, "samplers", std::move(samplers));
detail::JsonAddMember(o, "samplers", std::move(samplers));
}
// CAMERAS
if (model->cameras.size()) {
json cameras;
JsonReserveArray(cameras, model->cameras.size());
detail::json cameras;
detail::JsonReserveArray(cameras, model->cameras.size());
for (unsigned int i = 0; i < model->cameras.size(); ++i) {
json camera;
detail::json camera;
SerializeGltfCamera(model->cameras[i], camera);
JsonPushBack(cameras, std::move(camera));
detail::JsonPushBack(cameras, std::move(camera));
}
JsonAddMember(o, "cameras", std::move(cameras));
detail::JsonAddMember(o, "cameras", std::move(cameras));
}
// EXTENSIONS
@@ -7448,27 +7731,27 @@ static void SerializeGltfModel(Model *model, json &o) {
// LIGHTS as KHR_lights_punctual
if (model->lights.size()) {
json lights;
JsonReserveArray(lights, model->lights.size());
detail::json lights;
detail::JsonReserveArray(lights, model->lights.size());
for (unsigned int i = 0; i < model->lights.size(); ++i) {
json light;
detail::json light;
SerializeGltfLight(model->lights[i], light);
JsonPushBack(lights, std::move(light));
detail::JsonPushBack(lights, std::move(light));
}
json khr_lights_cmn;
JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
json ext_j;
detail::json khr_lights_cmn;
detail::JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
detail::json ext_j;
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
JsonAssign(ext_j, GetValue(it));
detail::json_const_iterator it;
if (detail::FindMember(o, "extensions", it)) {
detail::JsonAssign(ext_j, detail::GetValue(it));
}
}
JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
detail::JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
JsonAddMember(o, "extensions", std::move(ext_j));
detail::JsonAddMember(o, "extensions", std::move(ext_j));
// Also add "KHR_lights_punctual" to `extensionsUsed`
{
@@ -7523,37 +7806,32 @@ static bool WriteGltfFile(const std::string &output,
return WriteGltfStream(gltfFile, content);
}
static void WriteBinaryGltfStream(std::ostream &stream,
static bool WriteBinaryGltfStream(std::ostream &stream,
const std::string &content,
const std::vector<unsigned char> &binBuffer) {
const std::string header = "glTF";
const int version = 2;
// https://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number
auto roundUp = [](uint32_t numToRound, uint32_t multiple) {
if (multiple == 0) return numToRound;
uint32_t remainder = numToRound % multiple;
if (remainder == 0) return numToRound;
return numToRound + multiple - remainder;
};
const uint32_t padding_size =
roundUp(uint32_t(content.size()), 4) - uint32_t(content.size());
const uint32_t content_size = uint32_t(content.size());
const uint32_t binBuffer_size = uint32_t(binBuffer.size());
// determine number of padding bytes required to ensure 4 byte alignment
const uint32_t content_padding_size =
content_size % 4 == 0 ? 0 : 4 - content_size % 4;
const uint32_t bin_padding_size =
binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
// 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
// Chunk data must be located at 4-byte boundary.
// Chunk data must be located at 4-byte boundary, which may require padding
const uint32_t length =
12 + 8 + roundUp(uint32_t(content.size()), 4) +
(binBuffer.size() ? (8 + roundUp(uint32_t(binBuffer.size()), 4)) : 0);
12 + 8 + content_size + content_padding_size +
(binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
stream.write(header.c_str(), std::streamsize(header.size()));
stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
// JSON chunk info, then JSON data
const uint32_t model_length = uint32_t(content.size()) + padding_size;
const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
const uint32_t model_format = 0x4E4F534A;
stream.write(reinterpret_cast<const char *>(&model_length),
sizeof(model_length));
@@ -7562,13 +7840,11 @@ static void WriteBinaryGltfStream(std::ostream &stream,
stream.write(content.c_str(), std::streamsize(content.size()));
// Chunk must be multiplies of 4, so pad with spaces
if (padding_size > 0) {
const std::string padding = std::string(size_t(padding_size), ' ');
if (content_padding_size > 0) {
const std::string padding = std::string(size_t(content_padding_size), ' ');
stream.write(padding.c_str(), std::streamsize(padding.size()));
}
if (binBuffer.size() > 0) {
const uint32_t bin_padding_size =
roundUp(uint32_t(binBuffer.size()), 4) - uint32_t(binBuffer.size());
// BIN chunk info, then BIN data
const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
const uint32_t bin_format = 0x004e4942;
@@ -7586,9 +7862,12 @@ static void WriteBinaryGltfStream(std::ostream &stream,
std::streamsize(padding.size()));
}
}
// TODO: Check error on stream.write
return true;
}
static void WriteBinaryGltfFile(const std::string &output,
static bool WriteBinaryGltfFile(const std::string &output,
const std::string &content,
const std::vector<unsigned char> &binBuffer) {
#ifdef _WIN32
@@ -7606,13 +7885,13 @@ static void WriteBinaryGltfFile(const std::string &output,
#else
std::ofstream gltfFile(output.c_str(), std::ios::binary);
#endif
WriteBinaryGltfStream(gltfFile, content, binBuffer);
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;
detail::JsonDocument output;
/// Serialize all properties except buffers and images.
SerializeGltfModel(model, output);
@@ -7620,54 +7899,57 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
// BUFFERS
std::vector<unsigned char> binBuffer;
if (model->buffers.size()) {
json buffers;
JsonReserveArray(buffers, model->buffers.size());
detail::json buffers;
detail::JsonReserveArray(buffers, model->buffers.size());
for (unsigned int i = 0; i < model->buffers.size(); ++i) {
json buffer;
detail::json buffer;
if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
} else {
SerializeGltfBuffer(model->buffers[i], buffer);
}
JsonPushBack(buffers, std::move(buffer));
detail::JsonPushBack(buffers, std::move(buffer));
}
JsonAddMember(output, "buffers", std::move(buffers));
detail::JsonAddMember(output, "buffers", std::move(buffers));
}
// IMAGES
if (model->images.size()) {
json images;
JsonReserveArray(images, model->images.size());
detail::json images;
detail::JsonReserveArray(images, model->images.size());
for (unsigned int i = 0; i < model->images.size(); ++i) {
json image;
detail::json image;
std::string dummystring = "";
// UpdateImageObject need baseDir but only uses it if embeddedImages is
// enabled, since we won't write separate images when writing to a stream
// we
UpdateImageObject(model->images[i], dummystring, int(i), false,
&this->WriteImageData, this->write_image_user_data_);
SerializeGltfImage(model->images[i], image);
JsonPushBack(images, std::move(image));
std::string uri;
if (!UpdateImageObject(model->images[i], dummystring, int(i), true,
&uri_cb, &this->WriteImageData,
this->write_image_user_data_, &uri)) {
return false;
}
SerializeGltfImage(model->images[i], uri, image);
detail::JsonPushBack(images, std::move(image));
}
JsonAddMember(output, "images", std::move(images));
detail::JsonAddMember(output, "images", std::move(images));
}
if (writeBinary) {
WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
return WriteBinaryGltfStream(stream, detail::JsonToString(output), binBuffer);
} else {
WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
return WriteGltfStream(stream, detail::JsonToString(output, prettyPrint ? 2 : -1));
}
return true;
}
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,
bool writeBinary = false) {
JsonDocument output;
detail::JsonDocument output;
std::string defaultBinFilename = GetBaseFilename(filename);
std::string defaultBinFileExt = ".bin";
std::string::size_type pos =
@@ -7684,72 +7966,87 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
SerializeGltfModel(model, output);
// BUFFERS
std::vector<std::string> usedUris;
std::vector<std::string> usedFilenames;
std::vector<unsigned char> binBuffer;
if (model->buffers.size()) {
json buffers;
JsonReserveArray(buffers, model->buffers.size());
detail::json buffers;
detail::JsonReserveArray(buffers, model->buffers.size());
for (unsigned int i = 0; i < model->buffers.size(); ++i) {
json buffer;
detail::json buffer;
if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
} else if (embedBuffers) {
SerializeGltfBuffer(model->buffers[i], buffer);
} else {
std::string binSavePath;
std::string binFilename;
std::string binUri;
if (!model->buffers[i].uri.empty() &&
!IsDataURI(model->buffers[i].uri)) {
binUri = model->buffers[i].uri;
if (!uri_cb.decode(binUri, &binFilename, uri_cb.user_data)) {
return false;
}
} else {
binUri = defaultBinFilename + defaultBinFileExt;
binFilename = defaultBinFilename + defaultBinFileExt;
bool inUse = true;
int numUsed = 0;
while (inUse) {
inUse = false;
for (const std::string &usedName : usedUris) {
if (binUri.compare(usedName) != 0) continue;
for (const std::string &usedName : usedFilenames) {
if (binFilename.compare(usedName) != 0) continue;
inUse = true;
binUri = defaultBinFilename + std::to_string(numUsed++) +
defaultBinFileExt;
binFilename = defaultBinFilename + std::to_string(numUsed++) +
defaultBinFileExt;
break;
}
}
if (uri_cb.encode) {
if (!uri_cb.encode(binFilename, "buffer", &binUri,
uri_cb.user_data)) {
return false;
}
} else {
binUri = binFilename;
}
}
usedUris.push_back(binUri);
binSavePath = JoinPath(baseDir, binUri);
usedFilenames.push_back(binFilename);
binSavePath = JoinPath(baseDir, binFilename);
if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
binUri)) {
return false;
}
}
JsonPushBack(buffers, std::move(buffer));
detail::JsonPushBack(buffers, std::move(buffer));
}
JsonAddMember(output, "buffers", std::move(buffers));
detail::JsonAddMember(output, "buffers", std::move(buffers));
}
// IMAGES
if (model->images.size()) {
json images;
JsonReserveArray(images, model->images.size());
detail::json images;
detail::JsonReserveArray(images, model->images.size());
for (unsigned int i = 0; i < model->images.size(); ++i) {
json image;
detail::json image;
UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
&this->WriteImageData, this->write_image_user_data_);
SerializeGltfImage(model->images[i], image);
JsonPushBack(images, std::move(image));
std::string uri;
if (!UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
&uri_cb, &this->WriteImageData,
this->write_image_user_data_, &uri)) {
return false;
}
SerializeGltfImage(model->images[i], uri, image);
detail::JsonPushBack(images, std::move(image));
}
JsonAddMember(output, "images", std::move(images));
detail::JsonAddMember(output, "images", std::move(images));
}
if (writeBinary) {
WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
return WriteBinaryGltfFile(filename, detail::JsonToString(output), binBuffer);
} else {
WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
return WriteGltfFile(filename, detail::JsonToString(output, (prettyPrint ? 2 : -1)));
}
return true;
}
} // namespace tinygltf