From a9d86c1af4e5a75a18eb69c60a11e99767614582 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Fri, 8 Nov 2019 14:45:16 +0900 Subject: [PATCH 01/21] Add URL of Vulkan-Samples. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1a4c33c..7b32797 100644 --- a/README.md +++ b/README.md @@ -75,9 +75,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 From 45cac787091451e7471c0fbcb21c261cec2fed30 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 9 Nov 2019 20:42:55 +0900 Subject: [PATCH 02/21] Fix utf8 filepath on MinGW based on PR 222. --- tiny_gltf.h | 74 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index 36b10d1..d327b1b 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -1541,6 +1541,13 @@ class TinyGLTF { #undef NOMINMAX #endif +#if defined(__GLIBCXX__) // mingw + +#include // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf) +#include // _O_RDONLY + +#endif + #elif !defined(__ANDROID__) #include #endif @@ -2392,13 +2399,26 @@ 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 = fopen(abs_filename.c_str(), "rb"); + FILE *fp = nullptr; + errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb"); + if (err != 0) { + return false; + } +#endif + +#else + FILE *fp = nullptr; + errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb"); + if (err != 0) { + return false; + } #endif if (fp) { ret = true; @@ -2489,7 +2509,15 @@ bool ReadWholeFile(std::vector *out, std::string *err, } #else #ifdef _WIN32 +#if defined(__GLIBCXX__) // mingw + int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY); + __gnu_cxx::stdio_filebuf 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 @@ -2520,7 +2548,6 @@ bool ReadWholeFile(std::vector *out, std::string *err, out->resize(sz); f.read(reinterpret_cast(&out->at(0)), static_cast(sz)); - f.close(); return true; #endif @@ -2529,7 +2556,15 @@ bool ReadWholeFile(std::vector *out, std::string *err, bool WriteWholeFile(std::string *err, const std::string &filepath, const std::vector &contents, void *) { #ifdef _WIN32 +#if defined(__GLIBCXX__) // mingw + int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), _O_WRONLY | _O_BINARY); + __gnu_cxx::stdio_filebuf 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 @@ -2549,7 +2584,6 @@ bool WriteWholeFile(std::string *err, const std::string &filepath, return false; } - f.close(); return true; } @@ -6250,14 +6284,24 @@ static void SerializeGltfBufferData(const std::vector &data, static bool SerializeGltfBufferData(const std::vector &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 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); -#endif if (!output.is_open()) return false; +#endif +#else + std::ofstream output(binFilename.c_str(), std::ofstream::binary); + if (!output.is_open()) return false; +#endif output.write(reinterpret_cast(&data[0]), std::streamsize(data.size())); - output.close(); return true; } @@ -7113,11 +7157,21 @@ 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 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()); -#endif 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); } @@ -7154,7 +7208,13 @@ static void WriteBinaryGltfStream(std::ostream &stream, static void WriteBinaryGltfFile(const std::string &output, const std::string &content) { #ifdef _WIN32 +#if defined(_MSC_VER) std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary); +#elif defined(__GLIBCXX__) + std::ofstream gltfFile(output.c_str(), std::ios::binary); +#else + std::ofstream gltfFile(output.c_str(), std::ios::binary); +#endif #else std::ofstream gltfFile(output.c_str(), std::ios::binary); #endif From 125d8e50a967f8b2958b491f9d51b09f52ed0de9 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 9 Nov 2019 20:52:56 +0900 Subject: [PATCH 03/21] fopen_s -> fopen in linux(posix) code path. --- tiny_gltf.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index d327b1b..e84725a 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -1415,6 +1415,7 @@ class TinyGLTF { #include //#include #ifndef TINYGLTF_NO_FS +#include #include #endif #include @@ -2414,11 +2415,7 @@ bool FileExists(const std::string &abs_filename, void *) { #endif #else - FILE *fp = nullptr; - errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb"); - if (err != 0) { - return false; - } + FILE *fp = fopen(abs_filename.c_str(), "rb"); #endif if (fp) { ret = true; From 4ab0386d0926aa4e0630a0e1ade1dee6f2aa92a6 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sun, 10 Nov 2019 15:31:17 +0900 Subject: [PATCH 04/21] Fix MinGW code path reused linux code path. --- tiny_gltf.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index e84725a..f9ef35d 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -7208,7 +7208,9 @@ static void WriteBinaryGltfFile(const std::string &output, #if defined(_MSC_VER) std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary); #elif defined(__GLIBCXX__) - std::ofstream gltfFile(output.c_str(), std::ios::binary); + int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), _O_WRONLY | _O_BINARY); + __gnu_cxx::stdio_filebuf wfile_buf(file_descriptor, std::ios_base::in); + std::ostream gltfFile(&wfile_buf); #else std::ofstream gltfFile(output.c_str(), std::ios::binary); #endif From 1d2052068074bbf4d64ba993ff68ad5c0820797b Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 16 Nov 2019 17:00:17 +0900 Subject: [PATCH 05/21] Correct computation of padding size. Fixes #224. --- tiny_gltf.h | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index 4e09cff..67ef7e1 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -7124,11 +7124,25 @@ static void WriteBinaryGltfStream(std::ostream &stream, const std::string &content) { 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(content.size(), 4) - 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 int length = 12 + 8 + roundUp(content.size(), 4); stream.write(header.c_str(), std::streamsize(header.size())); stream.write(reinterpret_cast(&version), sizeof(version)); From 06d2fbdae640487fad362112b214db526df9eb51 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sun, 17 Nov 2019 02:11:59 +0900 Subject: [PATCH 06/21] `not` is alternative keyword and not recommended to use. Fixes #225 --- tiny_gltf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index cc09e77..d866984 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -5247,7 +5247,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); From ae9364902a973cbb72a8ec15ea5e883a64fa4969 Mon Sep 17 00:00:00 2001 From: Eero Pajarre Date: Mon, 18 Nov 2019 12:59:05 +0200 Subject: [PATCH 07/21] Added Accessor initializers Added Accessor intializers Now byteOffset and normalized are initialized to their default values. componentType and Type are initialized to bad values on purpose, they must be set when loading or creating the model. --- tiny_gltf.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index d866984..720f7f3 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -887,8 +887,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) From 2e8a115d7e364ed8526a0b748ba7fb1aa8ad8094 Mon Sep 17 00:00:00 2001 From: Eero Pajarre Date: Mon, 18 Nov 2019 13:09:25 +0200 Subject: [PATCH 08/21] Only serialize Accessor.normalized if it is true --- tiny_gltf.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index 720f7f3..3fac9d2 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -6374,7 +6374,8 @@ static void SerializeGltfAccessor(Accessor &accessor, json &o) { SerializeNumberProperty("count", accessor.count, o); SerializeNumberArrayProperty("min", accessor.minValues, o); SerializeNumberArrayProperty("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: From 7b0d81bf1214f3c40e2d40cdbf667f28df7953a5 Mon Sep 17 00:00:00 2001 From: Eero Pajarre Date: Mon, 18 Nov 2019 14:15:48 +0200 Subject: [PATCH 09/21] Added support for BIN chunk when saving in glb format If saving in glb (binary) format the first buffer in model is saved as gbl chunk number 1 in binary format. This operation is not performed if the first buffer as an url specified. --- tiny_gltf.h | 55 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index 3fac9d2..fee068c 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -6491,6 +6491,18 @@ static void SerializeGltfAsset(Asset &asset, json &o) { SerializeExtensionMap(asset.extensions, o); } + static void SerializeGltfBufferBin(Buffer &buffer, json &o, + std::vector &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); @@ -7179,7 +7191,8 @@ static bool WriteGltfFile(const std::string &output, } static void WriteBinaryGltfStream(std::ostream &stream, - const std::string &content) { + const std::string &content, + const std::vector &binBuffer) { const std::string header = "glTF"; const int version = 2; @@ -7200,7 +7213,8 @@ static void WriteBinaryGltfStream(std::ostream &stream, // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info. // Chunk data must be located at 4-byte boundary. - const int length = 12 + 8 + roundUp(content.size(), 4); + const int length = 12 + 8 + roundUp(content.size(), 4)+ + (binBuffer.size()?(8+roundUp(binBuffer.size(),4)) : 0); stream.write(header.c_str(), std::streamsize(header.size())); stream.write(reinterpret_cast(&version), sizeof(version)); @@ -7220,10 +7234,27 @@ 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(binBuffer.size(), 4) - binBuffer.size(); + // BIN chunk info, then BIN data + const int bin_length = int(binBuffer.size()) + bin_padding_size; + const int bin_format = 0x004e4942; + stream.write(reinterpret_cast(&bin_length), + sizeof(bin_length)); + stream.write(reinterpret_cast(&bin_format), + sizeof(bin_format)); + stream.write((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 padding = std::vector(size_t(bin_padding_size), 0); + stream.write((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 &binBuffer) { #ifdef _WIN32 #if defined(_MSC_VER) std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary); @@ -7237,7 +7268,7 @@ static void WriteBinaryGltfFile(const std::string &output, #else std::ofstream gltfFile(output.c_str(), std::ios::binary); #endif - WriteBinaryGltfStream(gltfFile, content); + WriteBinaryGltfStream(gltfFile, content,binBuffer); } bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, @@ -7250,11 +7281,16 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, // BUFFERS std::vector usedUris; + std::vector 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)); @@ -7279,7 +7315,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)); } @@ -7310,11 +7346,14 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, // BUFFERS std::vector usedUris; + std::vector 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; @@ -7363,7 +7402,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))); } From 49651fe3d94ad1808e7767edcabb7157cc4c333c Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sun, 24 Nov 2019 20:50:35 +0900 Subject: [PATCH 10/21] Update READE. --- README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7b32797..16de69a 100644 --- a/README.md +++ b/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 @@ -160,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. From 3eb65e269a7a805c4a294547804ee6ce3bb80b9b Mon Sep 17 00:00:00 2001 From: zhaozhiquan Date: Wed, 18 Dec 2019 11:28:57 +0800 Subject: [PATCH 11/21] Fix the issue that the extension of primitive written as extension of mesh. --- tiny_gltf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index fee068c..6303642 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -6782,7 +6782,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); From a11f6e19399f6af67d7c57909e8ce99d20beb369 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 19 Dec 2019 14:16:48 +0900 Subject: [PATCH 12/21] Allow empty buffer when serializing glTF buffer data. --- tiny_gltf.h | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index 6303642..dae84ed 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -2728,6 +2728,7 @@ bool DecodeDataURI(std::vector *out, std::string &mime_type, } } + // TODO(syoyo): Allow empty buffer? #229 if (data.empty()) { return false; } @@ -6278,9 +6279,15 @@ static void SerializeValue(const std::string &key, const Value &value, static void SerializeGltfBufferData(const std::vector &data, json &o) { std::string header = "data:application/octet-stream;base64,"; - std::string encodedData = - base64_encode(&data[0], static_cast(data.size())); - SerializeStringProperty("uri", header + encodedData, o); + if (data.size() > 0) { + std::string encodedData = + base64_encode(&data[0], static_cast(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 &data, @@ -6302,8 +6309,14 @@ static bool SerializeGltfBufferData(const std::vector &data, std::ofstream output(binFilename.c_str(), std::ofstream::binary); if (!output.is_open()) return false; #endif - output.write(reinterpret_cast(&data[0]), - std::streamsize(data.size())); + if (data.size() > 0) { + output.write(reinterpret_cast(&data[0]), + std::streamsize(data.size())); + } else { + // Issue #229 + // size 0 will be still valid buffer data. + // write empty file. + } return true; } From d09788d10f506af12baa291c2c36360d9b077fbd Mon Sep 17 00:00:00 2001 From: Marcin Kacprzak Date: Thu, 2 Jan 2020 13:00:48 +0100 Subject: [PATCH 13/21] Do not set target on bufferView pointing to animation data --- tiny_gltf.h | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index dae84ed..2d968ed 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -5526,7 +5526,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 > @@ -5554,14 +5554,11 @@ 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[model->accessors[attribute.second].bufferView] + .target = TINYGLTF_TARGET_ARRAY_BUFFER; + } } } From 4e47bc77991e8f78c6232317a7050d060b8ce30e Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 2 Jan 2020 22:07:25 +0900 Subject: [PATCH 14/21] Fill BufferView variables with initial/invalid values just in case. --- tiny_gltf.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index 2d968ed..80a0bd2 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -797,12 +797,12 @@ 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 = + 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; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] + 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 +810,9 @@ 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), target(0), byteOffset(0), byteLength(0), byteStride(0), dracoDecoded(false) {} DEFAULT_METHODS(BufferView) bool operator==(const BufferView &) const; }; From 1da4e5d633d4777810568910c039ef559ca1fe46 Mon Sep 17 00:00:00 2001 From: Jacek Date: Mon, 6 Jan 2020 14:36:57 -0600 Subject: [PATCH 15/21] Reserve space for animation samplers in serialization --- tiny_gltf.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tiny_gltf.h b/tiny_gltf.h index e2bd4a6..9405656 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -6074,6 +6074,7 @@ static void SerializeGltfAnimation(Animation &animation, json &o) { { json samplers; + JsonReserveArray(samplers, animation.samplers.size()); for (unsigned int i = 0; i < animation.samplers.size(); ++i) { json sampler; AnimationSampler gltfSampler = animation.samplers[i]; From 379bb612f118e33f1a61ab3fed93ff9848866647 Mon Sep 17 00:00:00 2001 From: Dado Date: Tue, 7 Jan 2020 14:41:12 +0000 Subject: [PATCH 16/21] BufferView ctor now uses correct members order. tiny_gltf.h line 815 (Moved target(0) to second to last) --- tiny_gltf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index 80a0bd2..2027e23 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -812,7 +812,7 @@ struct BufferView { bool dracoDecoded{false}; // Flag indicating this has been draco decoded - BufferView() : buffer(-1), target(0), byteOffset(0), byteLength(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; }; From 72f4a55edd54742bca1a71ade8ac70afca1d3f07 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Wed, 8 Jan 2020 00:40:41 +0900 Subject: [PATCH 17/21] Suppress clang warnings. Fixes #234 --- tiny_gltf.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index 80a0bd2..44c017e 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -812,7 +812,7 @@ struct BufferView { bool dracoDecoded{false}; // Flag indicating this has been draco decoded - BufferView() : buffer(-1), target(0), byteOffset(0), byteLength(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; }; @@ -5556,7 +5556,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } for (auto &attribute : primitive.attributes) { - model->bufferViews[model->accessors[attribute.second].bufferView] + model->bufferViews[size_t(model->accessors[size_t(attribute.second)].bufferView)] .target = TINYGLTF_TARGET_ARRAY_BUFFER; } } @@ -7219,20 +7219,20 @@ static void WriteBinaryGltfStream(std::ostream &stream, return numToRound + multiple - remainder; }; - const uint32_t padding_size = roundUp(content.size(), 4) - content.size(); + 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 int length = 12 + 8 + roundUp(content.size(), 4)+ - (binBuffer.size()?(8+roundUp(binBuffer.size(),4)) : 0); + 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(&version), sizeof(version)); stream.write(reinterpret_cast(&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(&model_length), sizeof(model_length)); stream.write(reinterpret_cast(&model_format), @@ -7245,19 +7245,19 @@ static void WriteBinaryGltfStream(std::ostream &stream, stream.write(padding.c_str(), std::streamsize(padding.size())); } if (binBuffer.size() > 0){ - const uint32_t bin_padding_size = roundUp(binBuffer.size(), 4) - binBuffer.size(); + const uint32_t bin_padding_size = roundUp(uint32_t(binBuffer.size()), 4) - uint32_t(binBuffer.size()); // BIN chunk info, then BIN data - const int bin_length = int(binBuffer.size()) + bin_padding_size; - const int bin_format = 0x004e4942; + const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size; + const uint32_t bin_format = 0x004e4942; stream.write(reinterpret_cast(&bin_length), sizeof(bin_length)); stream.write(reinterpret_cast(&bin_format), sizeof(bin_format)); - stream.write((const char *)binBuffer.data(), std::streamsize(binBuffer.size())); + stream.write(reinterpret_cast(binBuffer.data()), std::streamsize(binBuffer.size())); // Chunksize must be multiplies of 4, so pad with zeroes if (bin_padding_size > 0) { const std::vector padding = std::vector(size_t(bin_padding_size), 0); - stream.write((const char *)padding.data(), std::streamsize(padding.size())); + stream.write(reinterpret_cast(padding.data()), std::streamsize(padding.size())); } } } From aa3c5a1cadccbaa9caaa95e0a0caf68c10ae31f0 Mon Sep 17 00:00:00 2001 From: Frank Galligan Date: Mon, 13 Jan 2020 15:06:56 -0800 Subject: [PATCH 18/21] Fix loading images with spaces on Linux This change quotes the string before it is passed into wordexp. This addresses issue https://github.com/syoyo/tinygltf/issues/236 --- .../ 2x2 image has multiple spaces.png | Bin 0 -> 79 bytes .../2x2 image has spaces.png | Bin 0 -> 79 bytes .../CubeImageUriMultipleSpaces.gltf | 171 ++++++++++++++++++ .../CubeImageUriSpaces/CubeImageUriSpaces.bin | Bin 0 -> 804 bytes .../CubeImageUriSpaces.gltf | 171 ++++++++++++++++++ tests/tester.cc | 26 +++ tiny_gltf.h | 4 +- 7 files changed, 371 insertions(+), 1 deletion(-) create mode 100644 models/CubeImageUriSpaces/ 2x2 image has multiple spaces.png create mode 100644 models/CubeImageUriSpaces/2x2 image has spaces.png create mode 100644 models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf create mode 100644 models/CubeImageUriSpaces/CubeImageUriSpaces.bin create mode 100644 models/CubeImageUriSpaces/CubeImageUriSpaces.gltf diff --git a/models/CubeImageUriSpaces/ 2x2 image has multiple spaces.png b/models/CubeImageUriSpaces/ 2x2 image has multiple spaces.png new file mode 100644 index 0000000000000000000000000000000000000000..67d8040da398071e257745c9fbd6b0e50f38ac3e GIT binary patch literal 79 zcmeAS@N?(olHy`uVBq!ia0vp^Od!kwBL7~QRScxWJY5_^IIbt}IDFv1>Ag*kX{_IW b$FE_Ka%72`@Y|^osDi=M)z4*}Q$iB}^U)UT literal 0 HcmV?d00001 diff --git a/models/CubeImageUriSpaces/2x2 image has spaces.png b/models/CubeImageUriSpaces/2x2 image has spaces.png new file mode 100644 index 0000000000000000000000000000000000000000..67d8040da398071e257745c9fbd6b0e50f38ac3e GIT binary patch literal 79 zcmeAS@N?(olHy`uVBq!ia0vp^Od!kwBL7~QRScxWJY5_^IIbt}IDFv1>Ag*kX{_IW b$FE_Ka%72`@Y|^osDi=M)z4*}Q$iB}^U)UT literal 0 HcmV?d00001 diff --git a/models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf b/models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf new file mode 100644 index 0000000..49d32ec --- /dev/null +++ b/models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf @@ -0,0 +1,171 @@ +{ + "asset": { + "version": "2.0" + }, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "scene": 0, + "nodes": [ + { + "mesh": 0 + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 1, + "TEXCOORD_0": 3 + }, + "indices": 0, + "mode": 4, + "material": 0 + } + ] + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0, + "texCoord": 0 + }, + "baseColorFactor": [ + 1, + 1, + 1, + 1 + ], + "metallicFactor": 1, + "roughnessFactor": 1 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE" + } + ], + "textures": [ + { + "source": 0, + "sampler": 0 + } + ], + "samplers": [ + { + "wrapS": 33071, + "wrapT": 33071 + } + ], + "images": [ + { + "uri": " 2x2 image has multiple spaces.png " + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5121, + "count": 36, + "normalized": false, + "max": [ + 23 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "normalized": false, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "normalized": false, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "normalized": false, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "type": "VEC2" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 36 + }, + { + "buffer": 0, + "byteOffset": 36, + "byteLength": 288 + }, + { + "buffer": 0, + "byteOffset": 324, + "byteLength": 288 + }, + { + "buffer": 0, + "byteOffset": 612, + "byteLength": 192 + } + ], + "buffers": [ + { + "byteLength": 804, + "uri": "CubeImageUriSpaces.bin" + } + ] +} diff --git a/models/CubeImageUriSpaces/CubeImageUriSpaces.bin b/models/CubeImageUriSpaces/CubeImageUriSpaces.bin new file mode 100644 index 0000000000000000000000000000000000000000..20598674468b499a36efb07ea0d8e8630903261f GIT binary patch literal 804 zcmb780SDZePpRIo6lZ`8nc{%+Ujo> zDnLtB;w8^}4=v#DRC|2S#~RJV`N!MS2@!NhiT~p<3nY*C$?SPvMsKB0 Date: Fri, 17 Jan 2020 16:15:03 +0900 Subject: [PATCH 19/21] Possible fix for error: invalid path 'models/CubeImageUriSpaces/ 2x2 image has multiple spaces.png ' on Windows(NTFS? dislikes extra white space after the file extension ) --- ...ng => 2x2 image has multiple spaces.png} | Bin .../CubeImageUriMultipleSpaces.gltf | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename models/CubeImageUriSpaces/{ 2x2 image has multiple spaces.png => 2x2 image has multiple spaces.png} (100%) diff --git a/models/CubeImageUriSpaces/ 2x2 image has multiple spaces.png b/models/CubeImageUriSpaces/ 2x2 image has multiple spaces.png similarity index 100% rename from models/CubeImageUriSpaces/ 2x2 image has multiple spaces.png rename to models/CubeImageUriSpaces/ 2x2 image has multiple spaces.png diff --git a/models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf b/models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf index 49d32ec..e10b904 100644 --- a/models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf +++ b/models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf @@ -69,7 +69,7 @@ ], "images": [ { - "uri": " 2x2 image has multiple spaces.png " + "uri": " 2x2 image has multiple spaces.png" } ], "accessors": [ From a0a62bde1c335b3a83186aef20b3196e12a9bd36 Mon Sep 17 00:00:00 2001 From: sammyKhan Date: Fri, 17 Jan 2020 13:41:16 +0100 Subject: [PATCH 20/21] Initialize defaultScene to -1 To prevent undefined behavior if the model is serialized without defaultScene being set explicitly. --- tiny_gltf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index 91c77b5..ebc7e5f 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -1141,7 +1141,7 @@ class Model { std::vector scenes; std::vector lights; - int defaultScene; + int defaultScene = -1; std::vector extensionsUsed; std::vector extensionsRequired; From 28ad4ab7b889f103ebd75789722a7c836aa5a61b Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 18 Jan 2020 20:31:34 +0900 Subject: [PATCH 21/21] Add clang fuzzer tester. --- README.md | 4 ++++ tests/fuzzer/README.md | 46 +++++++++++++++++++++++++++++++++++++++ tests/fuzzer/fuzz_gltf.cc | 33 ++++++++++++++++++++++++++++ tests/fuzzer/meson.build | 9 ++++++++ 4 files changed, 92 insertions(+) create mode 100644 tests/fuzzer/README.md create mode 100644 tests/fuzzer/fuzz_gltf.cc create mode 100644 tests/fuzzer/meson.build diff --git a/README.md b/README.md index 16de69a..bd476d5 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,10 @@ $ ./tester $ ./tester_noexcept ``` +### Fuzzing tests + +See `tests/fuzzer` for details. + ## Third party licenses * json.hpp : Licensed under the MIT License . Copyright (c) 2013-2017 Niels Lohmann . diff --git a/tests/fuzzer/README.md b/tests/fuzzer/README.md new file mode 100644 index 0000000..b7c5223 --- /dev/null +++ b/tests/fuzzer/README.md @@ -0,0 +1,46 @@ +# Fuzzing test + +Do fuzzing test for TinyGLTF API. + +## Supported API + +* [x] LoadASCIIFromMemory +* [ ] LoadBinaryFromMemory + +## Requirements + +* meson +* clang with fuzzer support(`-fsanitize=fuzzer`. at least clang 8.0 should work) + +## Setup + +### Ubuntu 18.04 + +``` +$ sudo apt install clang++-8 +$ sudo apt install libfuzzer-8-dev +``` + +Optionally, if you didn't set `update-alternatives` you can set `clang++` to point to `clang++8` + +``` +$ sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 10 +$ sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 10 +``` + +## How to compile + +``` +$ CXX=clang++ CC=clang meson build +$ cd build +$ ninja +``` + +## How to run + +Increase memory limit. e.g. `-rss_limit_mb=50000` + +``` +$ ./fuzz_gltf -rss_limit_mb=20000 -jobs 4 +``` + diff --git a/tests/fuzzer/fuzz_gltf.cc b/tests/fuzzer/fuzz_gltf.cc new file mode 100644 index 0000000..73c87b1 --- /dev/null +++ b/tests/fuzzer/fuzz_gltf.cc @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_WRITE_IMPLEMENTATION +#define TINYGLTF_IMPLEMENTATION +#include "tiny_gltf.h" + +static void parse_intCoding4(const uint8_t *data, size_t size) +{ + + tinygltf::Model model; + tinygltf::TinyGLTF ctx; + std::string err; + std::string warn; + + const char *str = reinterpret_cast(data); + + bool ret = ctx.LoadASCIIFromString(&model, &err, &warn, str, size, /* base_dir */"" ); + (void)ret; + +} + +extern "C" +int LLVMFuzzerTestOneInput(std::uint8_t const* data, std::size_t size) +{ + parse_intCoding4(data, size); + return 0; +} + diff --git a/tests/fuzzer/meson.build b/tests/fuzzer/meson.build new file mode 100644 index 0000000..c69ed13 --- /dev/null +++ b/tests/fuzzer/meson.build @@ -0,0 +1,9 @@ +project('fuzz_tinygltf', 'cpp', default_options : ['cpp_std=c++11']) + +incdirs = include_directories('../../') +executable('fuzz_gltf', + 'fuzz_gltf.cc', + include_directories : incdirs, + cpp_args : '-fsanitize=address,fuzzer', + link_args : '-fsanitize=address,fuzzer' ) +