mirror of
https://github.com/syoyo/tinygltf.git
synced 2026-07-02 12:08:52 +00:00
Compare commits
35 Commits
store-json
...
uri-decode
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db2fbeec9d | ||
|
|
c4166e4c60 | ||
|
|
72f4a55edd | ||
|
|
4e47bc7799 | ||
|
|
9cc3e91968 | ||
|
|
d09788d10f | ||
|
|
a11f6e1939 | ||
|
|
8ced108571 | ||
|
|
3eb65e269a | ||
|
|
1322c7bf09 | ||
|
|
49651fe3d9 | ||
|
|
db93a87c4a | ||
|
|
7b0d81bf12 | ||
|
|
2e8a115d7e | ||
|
|
ae9364902a | ||
|
|
06d2fbdae6 | ||
|
|
d5ff5cbfb1 | ||
|
|
1d20520680 | ||
|
|
4ab0386d09 | ||
|
|
a471770f38 | ||
|
|
125d8e50a9 | ||
|
|
45cac78709 | ||
|
|
a9d86c1af4 | ||
|
|
2f26eddac4 | ||
|
|
a0939550ca | ||
|
|
58ab95be2f | ||
|
|
bf0e4f8e45 | ||
|
|
30f333c607 | ||
|
|
419162716e | ||
|
|
9533c352a1 | ||
|
|
9ff3d93084 | ||
|
|
aea514a975 | ||
|
|
05a4456948 | ||
|
|
fb256609f2 | ||
|
|
5cecef2b92 |
11
.travis.yml
11
.travis.yml
@@ -31,6 +31,17 @@ matrix:
|
||||
- addons: *1
|
||||
compiler: clang
|
||||
env: COMPILER_VERSION=3.9 BUILD_TYPE=Debug CFLAGS="-O0" CXXFLAGS="-O0"
|
||||
- addons: &3
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
compiler: gcc
|
||||
env: COMPILER_VERSION=4.8 BUILD_TYPE=Debug
|
||||
- addons: *3
|
||||
compiler: gcc
|
||||
env: COMPILER_VERSION=4.8 BUILD_TYPE=Release
|
||||
|
||||
before_install:
|
||||
- ./.travis-before-install.sh
|
||||
|
||||
@@ -4,14 +4,21 @@ PROJECT (tinygltf)
|
||||
|
||||
SET(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
ADD_EXECUTABLE ( loader_example
|
||||
loader_example.cc
|
||||
)
|
||||
option(TINYGLTF_BUILD_EXAMPLES "Build examples" ON)
|
||||
|
||||
ADD_SUBDIRECTORY ( examples/gltfutil )
|
||||
ADD_SUBDIRECTORY ( examples/glview )
|
||||
ADD_SUBDIRECTORY ( examples/validator )
|
||||
if (TINYGLTF_BUILD_EXAMPLES)
|
||||
ADD_EXECUTABLE ( loader_example
|
||||
loader_example.cc
|
||||
)
|
||||
|
||||
ADD_SUBDIRECTORY ( examples/gltfutil )
|
||||
ADD_SUBDIRECTORY ( examples/glview )
|
||||
ADD_SUBDIRECTORY ( examples/validator )
|
||||
endif (TINYGLTF_BUILD_EXAMPLES)
|
||||
|
||||
#
|
||||
# TinuGLTF is a header-only library, so no library build. just install header files.
|
||||
#
|
||||
INSTALL ( FILES
|
||||
json.hpp
|
||||
stb_image.h
|
||||
|
||||
17
README.md
17
README.md
@@ -34,8 +34,11 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch.
|
||||
* Moderate parsing time and memory consumption.
|
||||
* glTF specification v2.0.0
|
||||
* [x] ASCII glTF
|
||||
* [x] Load
|
||||
* [x] Save
|
||||
* [x] Binary glTF(GLB)
|
||||
* [x] PBR material description
|
||||
* [x] Load
|
||||
* [x] Save(.bin embedded .glb)
|
||||
* Buffers
|
||||
* [x] Parse BASE64 encoded embedded buffer data(DataURI).
|
||||
* [x] Load `.bin` file.
|
||||
@@ -55,6 +58,7 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch.
|
||||
* [x] Image save
|
||||
* Extensions
|
||||
* [x] Draco mesh decoding
|
||||
* [ ] Draco mesh encoding
|
||||
|
||||
## Note on extension property
|
||||
|
||||
@@ -75,9 +79,10 @@ In extension(`ExtensionMap`), JSON number value is parsed as int or float(number
|
||||
* GLTF loader plugin for OGRE 2.1. Support for PBR materials via HLMS/PBS https://github.com/Ybalrid/Ogre_glTF
|
||||
* [TinyGltfImporter](http://doc.magnum.graphics/magnum/classMagnum_1_1Trade_1_1TinyGltfImporter.html) plugin for [Magnum](https://github.com/mosra/magnum), a lightweight and modular C++11/C++14 graphics middleware for games and data visualization.
|
||||
* [Diligent Engine](https://github.com/DiligentGraphics/DiligentEngine) - A modern cross-platform low-level graphics library and rendering framework
|
||||
* Lighthouse 2: a rendering framework for real-time ray tracing / path tracing experiments. https://github.com/jbikker/lighthouse2
|
||||
* Lighthouse 2: a rendering framework for real-time ray tracing / path tracing experiments. https://github.com/jbikker/lighthouse2
|
||||
* [QuickLook GLTF](https://github.com/toshiks/glTF-quicklook) - quicklook plugin for macos. Also SceneKit wrapper for tinygltf.
|
||||
* [GlslViewer](https://github.com/patriciogonzalezvivo/glslViewer) - live GLSL coding for MacOS and Linux
|
||||
* [Vulkan-Samples](https://github.com/KhronosGroup/Vulkan-Samples) - The Vulkan Samples is collection of resources to help you develop optimized Vulkan applications.
|
||||
* Your projects here! (Please send PR)
|
||||
|
||||
## TODOs
|
||||
@@ -159,14 +164,17 @@ if (!ret) {
|
||||
|
||||
|
||||
### Saving gltTF 2.0 model
|
||||
* [ ] Buffers.
|
||||
|
||||
* Buffers.
|
||||
* [x] To file
|
||||
* [x] Embedded
|
||||
* [ ] Draco compressed?
|
||||
* [x] Images
|
||||
* [x] To file
|
||||
* [x] Embedded
|
||||
* [ ] Binary(.glb)
|
||||
* Binary(.glb)
|
||||
* [x] .bin embedded single .glb
|
||||
* [ ] External .bin
|
||||
|
||||
## Running tests.
|
||||
|
||||
@@ -200,3 +208,4 @@ $ ./tester_noexcept
|
||||
* stb_image : Public domain.
|
||||
* catch : Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. Distributed under the Boost Software License, Version 1.0.
|
||||
* RapidJSON : Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. http://rapidjson.org/
|
||||
* dlib(uridecode, uriencode) : Copyright (C) 2003 Davis E. King Boost Software License 1.0. http://dlib.net/dlib/server/server_http.cpp.html
|
||||
|
||||
372
tiny_gltf.h
372
tiny_gltf.h
@@ -26,6 +26,7 @@
|
||||
// THE SOFTWARE.
|
||||
|
||||
// Version:
|
||||
// - v2.4.2 Decode percent-encoded URI.
|
||||
// - v2.4.1 Fix some glTF object class does not have `extensions` and/or
|
||||
// `extras` property.
|
||||
// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone).
|
||||
@@ -637,7 +638,8 @@ struct Image {
|
||||
int bufferView; // (required if no uri)
|
||||
std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
|
||||
// "image/bmp", "image/gif"]
|
||||
std::string uri; // (required if no mimeType)
|
||||
std::string uri; // (required if no mimeType) uri is not decoded(e.g.
|
||||
// whitespace may be represented as %20)
|
||||
Value extras;
|
||||
ExtensionMap extensions;
|
||||
|
||||
@@ -797,12 +799,13 @@ struct Material {
|
||||
|
||||
struct BufferView {
|
||||
std::string name;
|
||||
int buffer; // Required
|
||||
size_t byteOffset; // minimum 0, default 0
|
||||
size_t byteLength; // required, minimum 1
|
||||
size_t byteStride; // minimum 4, maximum 252 (multiple of 4), default 0 =
|
||||
// understood to be tightly packed
|
||||
int target; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"]
|
||||
int buffer{-1}; // Required
|
||||
size_t byteOffset{0}; // minimum 0, default 0
|
||||
size_t byteLength{0}; // required, minimum 1. 0 = invalid
|
||||
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
|
||||
Value extras;
|
||||
ExtensionMap extensions;
|
||||
|
||||
@@ -810,9 +813,15 @@ struct BufferView {
|
||||
std::string extras_json_string;
|
||||
std::string extensions_json_string;
|
||||
|
||||
bool dracoDecoded; // Flag indicating this has been draco decoded
|
||||
bool dracoDecoded{false}; // Flag indicating this has been draco decoded
|
||||
|
||||
BufferView() : byteOffset(0), byteStride(0), dracoDecoded(false) {}
|
||||
BufferView()
|
||||
: buffer(-1),
|
||||
byteOffset(0),
|
||||
byteLength(0),
|
||||
byteStride(0),
|
||||
target(0),
|
||||
dracoDecoded(false) {}
|
||||
DEFAULT_METHODS(BufferView)
|
||||
bool operator==(const BufferView &) const;
|
||||
};
|
||||
@@ -887,8 +896,13 @@ struct Accessor {
|
||||
// unreachable return 0;
|
||||
}
|
||||
|
||||
Accessor() {
|
||||
bufferView = -1;
|
||||
Accessor()
|
||||
: bufferView(-1),
|
||||
byteOffset(0),
|
||||
normalized(false),
|
||||
componentType(-1),
|
||||
count(0),
|
||||
type(-1) {
|
||||
sparse.isSparse = false;
|
||||
}
|
||||
DEFAULT_METHODS(Accessor)
|
||||
@@ -1032,6 +1046,7 @@ struct Buffer {
|
||||
std::vector<unsigned char> data;
|
||||
std::string
|
||||
uri; // considered as required here but not in the spec (need to clarify)
|
||||
// uri is not decoded(e.g. whitespace may be represented as %20)
|
||||
Value extras;
|
||||
ExtensionMap extensions;
|
||||
|
||||
@@ -1415,6 +1430,7 @@ class TinyGLTF {
|
||||
#include <algorithm>
|
||||
//#include <cassert>
|
||||
#ifndef TINYGLTF_NO_FS
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#endif
|
||||
#include <sstream>
|
||||
@@ -1541,6 +1557,14 @@ class TinyGLTF {
|
||||
#undef NOMINMAX
|
||||
#endif
|
||||
|
||||
#if defined(__GLIBCXX__) // mingw
|
||||
|
||||
#include <fcntl.h> // _O_RDONLY
|
||||
|
||||
#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
|
||||
|
||||
#endif
|
||||
|
||||
#elif !defined(__ANDROID__)
|
||||
#include <wordexp.h>
|
||||
#endif
|
||||
@@ -2106,6 +2130,88 @@ std::string base64_decode(std::string const &encoded_string) {
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
// https://github.com/syoyo/tinygltf/issues/228
|
||||
// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
|
||||
// decoding?
|
||||
//
|
||||
// https://stackoverflow.com/questions/18307429/encode-decode-url-in-c
|
||||
// http://dlib.net/dlib/server/server_http.cpp.html
|
||||
|
||||
// --- dlib beign ------------------------------------------------------------
|
||||
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
|
||||
// License: Boost Software License See LICENSE.txt for the full license.
|
||||
|
||||
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';
|
||||
else if (ch <= 'f' && ch >= 'a')
|
||||
ch -= 'a' - 10;
|
||||
else if (ch <= 'F' && ch >= 'A')
|
||||
ch -= 'A' - 10;
|
||||
else
|
||||
ch = 0;
|
||||
return ch;
|
||||
}
|
||||
|
||||
static const std::string urldecode(const std::string &str) {
|
||||
using namespace std;
|
||||
string result;
|
||||
string::size_type i;
|
||||
for (i = 0; i < str.size(); ++i) {
|
||||
if (str[i] == '+') {
|
||||
result += ' ';
|
||||
} else if (str[i] == '%' && str.size() > i + 2) {
|
||||
const unsigned char ch1 =
|
||||
from_hex(static_cast<unsigned char>(str[i + 1]));
|
||||
const unsigned char ch2 =
|
||||
from_hex(static_cast<unsigned char>(str[i + 2]));
|
||||
const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
|
||||
result += static_cast<char>(ch);
|
||||
i += 2;
|
||||
} else {
|
||||
result += str[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace dlib
|
||||
// --- dlib end --------------------------------------------------------------
|
||||
|
||||
static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
|
||||
std::string *warn, const std::string &filename,
|
||||
const std::string &basedir, bool required,
|
||||
@@ -2364,6 +2470,17 @@ bool WriteImageData(const std::string *basepath, const std::string *filename,
|
||||
|
||||
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);
|
||||
MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
|
||||
(int)wstr.size());
|
||||
return wstr;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef TINYGLTF_NO_FS
|
||||
// Default implementations of filesystem functions
|
||||
|
||||
@@ -2383,11 +2500,20 @@ bool FileExists(const std::string &abs_filename, void *) {
|
||||
}
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
FILE *fp;
|
||||
#if defined(_MSC_VER) || defined(__GLIBCXX__)
|
||||
FILE *fp = nullptr;
|
||||
errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
|
||||
if (err != 0) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
FILE *fp = nullptr;
|
||||
errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
|
||||
if (err != 0) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
FILE *fp = fopen(abs_filename.c_str(), "rb");
|
||||
#endif
|
||||
@@ -2479,7 +2605,20 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
#if defined(__GLIBCXX__) // mingw
|
||||
int file_descriptor =
|
||||
_wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
|
||||
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
|
||||
std::istream f(&wfile_buf);
|
||||
#elif defined(_MSC_VER)
|
||||
std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
|
||||
#else // clang?
|
||||
std::ifstream f(filepath.c_str(), std::ifstream::binary);
|
||||
#endif
|
||||
#else
|
||||
std::ifstream f(filepath.c_str(), std::ifstream::binary);
|
||||
#endif
|
||||
if (!f) {
|
||||
if (err) {
|
||||
(*err) += "File open error : " + filepath + "\n";
|
||||
@@ -2507,7 +2646,6 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
|
||||
out->resize(sz);
|
||||
f.read(reinterpret_cast<char *>(&out->at(0)),
|
||||
static_cast<std::streamsize>(sz));
|
||||
f.close();
|
||||
|
||||
return true;
|
||||
#endif
|
||||
@@ -2515,7 +2653,20 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
|
||||
|
||||
bool WriteWholeFile(std::string *err, const std::string &filepath,
|
||||
const std::vector<unsigned char> &contents, void *) {
|
||||
#ifdef _WIN32
|
||||
#if defined(__GLIBCXX__) // mingw
|
||||
int file_descriptor =
|
||||
_wopen(UTF8ToWchar(filepath).c_str(), _O_WRONLY | _O_BINARY);
|
||||
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
|
||||
std::ostream f(&wfile_buf);
|
||||
#elif defined(_MSC_VER)
|
||||
std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
|
||||
#else // clang?
|
||||
std::ofstream f(filepath.c_str(), std::ofstream::binary);
|
||||
#endif
|
||||
#else
|
||||
std::ofstream f(filepath.c_str(), std::ofstream::binary);
|
||||
#endif
|
||||
if (!f) {
|
||||
if (err) {
|
||||
(*err) += "File open error for writing : " + filepath + "\n";
|
||||
@@ -2532,7 +2683,6 @@ bool WriteWholeFile(std::string *err, const std::string &filepath,
|
||||
return false;
|
||||
}
|
||||
|
||||
f.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2675,6 +2825,7 @@ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(syoyo): Allow empty buffer? #229
|
||||
if (data.empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -3593,7 +3744,10 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
|
||||
#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
|
||||
return true;
|
||||
#endif
|
||||
if (!LoadExternalFile(&img, err, warn, uri, basedir, false, 0, false, fs)) {
|
||||
std::string decoded_uri = dlib::urldecode(uri);
|
||||
if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
|
||||
/* required */ false, /* required bytes */ 0,
|
||||
/* checksize */ false, fs)) {
|
||||
if (warn) {
|
||||
(*warn) += "Failed to load external 'uri' for image[" +
|
||||
std::to_string(image_idx) + "] name = [" + image->name +
|
||||
@@ -3815,9 +3969,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
|
||||
}
|
||||
} else {
|
||||
// External .bin file.
|
||||
std::string decoded_uri = dlib::urldecode(buffer->uri);
|
||||
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
|
||||
buffer->uri, basedir, true, byteLength, true,
|
||||
fs)) {
|
||||
decoded_uri, basedir, /* required */ true,
|
||||
byteLength, /* checkSize */ true, fs)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3859,8 +4014,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
|
||||
}
|
||||
} else {
|
||||
// Assume external .bin file.
|
||||
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, buffer->uri,
|
||||
basedir, true, byteLength, true, fs)) {
|
||||
std::string decoded_uri = dlib::urldecode(buffer->uri);
|
||||
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
|
||||
basedir, /* required */ true, byteLength,
|
||||
/* checkSize */ true, fs)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -5199,7 +5356,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
||||
|
||||
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
|
||||
defined(_CPPUNWIND)) && \
|
||||
not defined(TINYGLTF_NOEXCEPTION)
|
||||
!defined(TINYGLTF_NOEXCEPTION)
|
||||
try {
|
||||
JsonParse(v, json_str, json_str_length, true);
|
||||
|
||||
@@ -5472,7 +5629,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
||||
|
||||
// Assign missing bufferView target types
|
||||
// - Look for missing Mesh indices
|
||||
// - Look for missing bufferView targets
|
||||
// - Look for missing Mesh attributes
|
||||
for (auto &mesh : model->meshes) {
|
||||
for (auto &primitive : mesh.primitives) {
|
||||
if (primitive.indices >
|
||||
@@ -5500,14 +5657,13 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
||||
// we could optionally check if acessors' bufferView type is Scalar, as
|
||||
// it should be
|
||||
}
|
||||
}
|
||||
}
|
||||
// find any missing targets, must be an array buffer type if not fulfilled
|
||||
// from previous check
|
||||
for (auto &bufferView : model->bufferViews) {
|
||||
if (bufferView.target == 0) // missing target type
|
||||
{
|
||||
bufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
|
||||
|
||||
for (auto &attribute : primitive.attributes) {
|
||||
model
|
||||
->bufferViews[size_t(
|
||||
model->accessors[size_t(attribute.second)].bufferView)]
|
||||
.target = TINYGLTF_TARGET_ARRAY_BUFFER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6225,18 +6381,45 @@ static void SerializeValue(const std::string &key, const Value &value,
|
||||
static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
|
||||
json &o) {
|
||||
std::string header = "data:application/octet-stream;base64,";
|
||||
std::string encodedData =
|
||||
base64_encode(&data[0], static_cast<unsigned int>(data.size()));
|
||||
SerializeStringProperty("uri", header + encodedData, o);
|
||||
if (data.size() > 0) {
|
||||
std::string encodedData =
|
||||
base64_encode(&data[0], static_cast<unsigned int>(data.size()));
|
||||
SerializeStringProperty("uri", header + encodedData, o);
|
||||
} else {
|
||||
// Issue #229
|
||||
// size 0 is allowd. Just emit mime header.
|
||||
SerializeStringProperty("uri", header, o);
|
||||
}
|
||||
}
|
||||
|
||||
static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
|
||||
const std::string &binFilename) {
|
||||
#ifdef _WIN32
|
||||
#if defined(__GLIBCXX__) // mingw
|
||||
int file_descriptor =
|
||||
_wopen(UTF8ToWchar(binFilename).c_str(), _O_WRONLY | _O_BINARY);
|
||||
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
|
||||
std::ostream output(&wfile_buf);
|
||||
if (!wfile_buf.is_open()) return false;
|
||||
#elif defined(_MSC_VER)
|
||||
std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
|
||||
if (!output.is_open()) return false;
|
||||
#else
|
||||
std::ofstream output(binFilename.c_str(), std::ofstream::binary);
|
||||
if (!output.is_open()) return false;
|
||||
output.write(reinterpret_cast<const char *>(&data[0]),
|
||||
std::streamsize(data.size()));
|
||||
output.close();
|
||||
#endif
|
||||
#else
|
||||
std::ofstream output(binFilename.c_str(), std::ofstream::binary);
|
||||
if (!output.is_open()) return false;
|
||||
#endif
|
||||
if (data.size() > 0) {
|
||||
output.write(reinterpret_cast<const char *>(&data[0]),
|
||||
std::streamsize(data.size()));
|
||||
} else {
|
||||
// Issue #229
|
||||
// size 0 will be still valid buffer data.
|
||||
// write empty file.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -6307,7 +6490,8 @@ static void SerializeGltfAccessor(Accessor &accessor, json &o) {
|
||||
SerializeNumberProperty<size_t>("count", accessor.count, o);
|
||||
SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
|
||||
SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
|
||||
SerializeValue("normalized", Value(accessor.normalized), o);
|
||||
if (accessor.normalized)
|
||||
SerializeValue("normalized", Value(accessor.normalized), o);
|
||||
std::string type;
|
||||
switch (accessor.type) {
|
||||
case TINYGLTF_TYPE_SCALAR:
|
||||
@@ -6423,6 +6607,18 @@ static void SerializeGltfAsset(Asset &asset, json &o) {
|
||||
SerializeExtensionMap(asset.extensions, o);
|
||||
}
|
||||
|
||||
static void SerializeGltfBufferBin(Buffer &buffer, json &o,
|
||||
std::vector<unsigned char> &binBuffer) {
|
||||
SerializeNumberProperty("byteLength", buffer.data.size(), o);
|
||||
binBuffer = buffer.data;
|
||||
|
||||
if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
|
||||
|
||||
if (buffer.extras.Type() != NULL_TYPE) {
|
||||
SerializeValue("extras", buffer.extras, o);
|
||||
}
|
||||
}
|
||||
|
||||
static void SerializeGltfBuffer(Buffer &buffer, json &o) {
|
||||
SerializeNumberProperty("byteLength", buffer.data.size(), o);
|
||||
SerializeGltfBufferData(buffer.data, o);
|
||||
@@ -6481,6 +6677,7 @@ static void SerializeGltfImage(Image &image, json &o) {
|
||||
SerializeStringProperty("mimeType", image.mimeType, o);
|
||||
SerializeNumberProperty<int>("bufferView", image.bufferView, o);
|
||||
} else {
|
||||
// TODO(syoyo): dlib::urilencode?
|
||||
SerializeStringProperty("uri", image.uri, o);
|
||||
}
|
||||
|
||||
@@ -6597,7 +6794,8 @@ static void SerializeGltfMaterial(Material &material, json &o) {
|
||||
SerializeStringProperty("alphaMode", material.alphaMode, o);
|
||||
}
|
||||
|
||||
JsonAddMember(o, "doubleSided", json(material.doubleSided));
|
||||
if (material.doubleSided != false)
|
||||
JsonAddMember(o, "doubleSided", json(material.doubleSided));
|
||||
|
||||
if (material.normalTexture.index > -1) {
|
||||
json texinfo;
|
||||
@@ -6701,7 +6899,7 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) {
|
||||
JsonAddMember(primitive, "targets", std::move(targets));
|
||||
}
|
||||
|
||||
SerializeExtensionMap(gltfPrimitive.extensions, o);
|
||||
SerializeExtensionMap(gltfPrimitive.extensions, primitive);
|
||||
|
||||
if (gltfPrimitive.extras.Type() != NULL_TYPE) {
|
||||
SerializeValue("extras", gltfPrimitive.extras, primitive);
|
||||
@@ -7090,28 +7288,58 @@ static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
|
||||
|
||||
static bool WriteGltfFile(const std::string &output,
|
||||
const std::string &content) {
|
||||
#ifdef _WIN32
|
||||
#if defined(_MSC_VER)
|
||||
std::ofstream gltfFile(UTF8ToWchar(output).c_str());
|
||||
#elif defined(__GLIBCXX__)
|
||||
int file_descriptor =
|
||||
_wopen(UTF8ToWchar(output).c_str(), _O_WRONLY | _O_BINARY);
|
||||
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
|
||||
std::ostream gltfFile(&wfile_buf);
|
||||
if (!wfile_buf.is_open()) return false;
|
||||
#else
|
||||
std::ofstream gltfFile(output.c_str());
|
||||
if (!gltfFile.is_open()) return false;
|
||||
#endif
|
||||
#else
|
||||
std::ofstream gltfFile(output.c_str());
|
||||
if (!gltfFile.is_open()) return false;
|
||||
#endif
|
||||
return WriteGltfStream(gltfFile, content);
|
||||
}
|
||||
|
||||
static void WriteBinaryGltfStream(std::ostream &stream,
|
||||
const std::string &content) {
|
||||
const std::string &content,
|
||||
const std::vector<unsigned char> &binBuffer) {
|
||||
const std::string header = "glTF";
|
||||
const int version = 2;
|
||||
const int padding_size = content.size() % 4;
|
||||
|
||||
// 12 bytes for header, JSON content length, 8 bytes for JSON chunk info,
|
||||
// padding
|
||||
const int length = 12 + 8 + int(content.size()) + padding_size;
|
||||
// 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());
|
||||
|
||||
// 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
|
||||
// Chunk data must be located at 4-byte boundary.
|
||||
const uint32_t length =
|
||||
12 + 8 + roundUp(uint32_t(content.size()), 4) +
|
||||
(binBuffer.size() ? (8 + roundUp(uint32_t(binBuffer.size()), 4)) : 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 int model_length = int(content.size()) + padding_size;
|
||||
const int model_format = 0x4E4F534A;
|
||||
const uint32_t model_length = uint32_t(content.size()) + padding_size;
|
||||
const uint32_t model_format = 0x4E4F534A;
|
||||
stream.write(reinterpret_cast<const char *>(&model_length),
|
||||
sizeof(model_length));
|
||||
stream.write(reinterpret_cast<const char *>(&model_format),
|
||||
@@ -7123,12 +7351,46 @@ static void WriteBinaryGltfStream(std::ostream &stream,
|
||||
const std::string padding = std::string(size_t(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;
|
||||
stream.write(reinterpret_cast<const char *>(&bin_length),
|
||||
sizeof(bin_length));
|
||||
stream.write(reinterpret_cast<const char *>(&bin_format),
|
||||
sizeof(bin_format));
|
||||
stream.write(reinterpret_cast<const char *>(binBuffer.data()),
|
||||
std::streamsize(binBuffer.size()));
|
||||
// Chunksize must be multiplies of 4, so pad with zeroes
|
||||
if (bin_padding_size > 0) {
|
||||
const std::vector<unsigned char> padding =
|
||||
std::vector<unsigned char>(size_t(bin_padding_size), 0);
|
||||
stream.write(reinterpret_cast<const char *>(padding.data()),
|
||||
std::streamsize(padding.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteBinaryGltfFile(const std::string &output,
|
||||
const std::string &content) {
|
||||
const std::string &content,
|
||||
const std::vector<unsigned char> &binBuffer) {
|
||||
#ifdef _WIN32
|
||||
#if defined(_MSC_VER)
|
||||
std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
|
||||
#elif defined(__GLIBCXX__)
|
||||
int file_descriptor =
|
||||
_wopen(UTF8ToWchar(output).c_str(), _O_WRONLY | _O_BINARY);
|
||||
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
|
||||
std::ostream gltfFile(&wfile_buf);
|
||||
#else
|
||||
std::ofstream gltfFile(output.c_str(), std::ios::binary);
|
||||
WriteBinaryGltfStream(gltfFile, content);
|
||||
#endif
|
||||
#else
|
||||
std::ofstream gltfFile(output.c_str(), std::ios::binary);
|
||||
#endif
|
||||
WriteBinaryGltfStream(gltfFile, content, binBuffer);
|
||||
}
|
||||
|
||||
bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
|
||||
@@ -7141,11 +7403,16 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
|
||||
|
||||
// BUFFERS
|
||||
std::vector<std::string> usedUris;
|
||||
std::vector<unsigned char> binBuffer;
|
||||
json buffers;
|
||||
JsonReserveArray(buffers, model->buffers.size());
|
||||
for (unsigned int i = 0; i < model->buffers.size(); ++i) {
|
||||
json buffer;
|
||||
SerializeGltfBuffer(model->buffers[i], 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));
|
||||
}
|
||||
JsonAddMember(output, "buffers", std::move(buffers));
|
||||
@@ -7170,7 +7437,7 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
|
||||
}
|
||||
|
||||
if (writeBinary) {
|
||||
WriteBinaryGltfStream(stream, JsonToString(output));
|
||||
WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
|
||||
} else {
|
||||
WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
|
||||
}
|
||||
@@ -7201,11 +7468,14 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
|
||||
|
||||
// BUFFERS
|
||||
std::vector<std::string> usedUris;
|
||||
std::vector<unsigned char> binBuffer;
|
||||
json buffers;
|
||||
JsonReserveArray(buffers, model->buffers.size());
|
||||
for (unsigned int i = 0; i < model->buffers.size(); ++i) {
|
||||
json buffer;
|
||||
if (embedBuffers) {
|
||||
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;
|
||||
@@ -7254,7 +7524,7 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
|
||||
}
|
||||
|
||||
if (writeBinary) {
|
||||
WriteBinaryGltfFile(filename, JsonToString(output));
|
||||
WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
|
||||
} else {
|
||||
WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user