From e5f1b3f7d70d07a9e535775b4fb9d27d4b8a1e85 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 34a5b8372610964c3cdd85fb6530dad880334119 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 8c9ae15ca83f62f7128e24c00d0f380338897ee4 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 634ab23649054dd2e7a57121eaf8f4d150da167b 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 7eebbb6d68e53157fab4bfb47be23c7174122ccf 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 4accacb87707627d32123b6cfefed36061b47faf 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 fa9f361ab21942c8a692a300cd4dd4f817386e0f 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 29c307fef5708feabaf9c4ae4816aa8ccc5450a8 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 f161e7c0488210f54740bf3beb9cdc610fa747d9 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 ff0233dc63200efa44fc463ba25c8b905340a59d 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 478c77d6f9822141448ef564605f304987a474d8 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 741d8f4ba69940182f6d0d94c36152fb457cb059 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 5cff80f10d9d75ab33aba7ee810b9ab7bbfe27ab 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 0c4d18ee9979c98e05a308a9b27bd8b112e83e98 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 7ddfe918c6d779a31b845e2e3f2d8055d1384402 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 4c1bb4fb8738745e316eb4a4ffd30c8628450201 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 c1d4f36bb136e6f34c0b5b901234f0a0e10a6e95 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 d2cdff20703105b8e0587b4df348e8860a3b68d2 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 b0b17ed287a99bcde1da59be3e7b7278438e90d0 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 853477d8d126bcc705026873ed8b67a7b5195979 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' ) +