From 7eebbb6d68e53157fab4bfb47be23c7174122ccf Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 16 Nov 2019 17:00:17 +0900 Subject: [PATCH 1/5] 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 2/5] `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 3/5] 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 4/5] 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 5/5] 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))); }