diff --git a/include/fastgltf/core.hpp b/include/fastgltf/core.hpp index 35ed90b53..8b2c2f9c7 100644 --- a/include/fastgltf/core.hpp +++ b/include/fastgltf/core.hpp @@ -270,6 +270,7 @@ namespace fastgltf { enum class ExportOptions : std::uint64_t { None = 0, + /** * Calls fastgltf::validate for the passed asset before writing. */ @@ -279,6 +280,12 @@ namespace fastgltf { * Pretty-prints the outputted JSON. This option is ignored for binary glTFs. */ PrettyPrintJson = 1 << 2, + + /** + * This is used by the internal GLB exporter to let the JSON exporter know to treat the first buffer + * as a embedded buffer. This will most likely not be useful for any user. + */ + ExportAsGLB = 1 << 3, }; // clang-format on @@ -804,6 +811,7 @@ namespace fastgltf { class Exporter { protected: Error errorCode = Error::None; + ExportOptions options = ExportOptions::None; std::filesystem::path bufferFolder = ""; std::filesystem::path imageFolder = ""; diff --git a/src/fastgltf.cpp b/src/fastgltf.cpp index 2f0969da2..f9f71269a 100644 --- a/src/fastgltf.cpp +++ b/src/fastgltf.cpp @@ -3639,13 +3639,17 @@ void fg::Exporter::writeBuffers(const Asset& asset, std::string& json) { for (auto it = asset.buffers.begin(); it != asset.buffers.end(); ++it) { json += '{'; - auto bufferIdx = std::distance(asset.buffers.begin(), it); + auto bufferIdx = static_cast(std::distance(asset.buffers.begin(), it)); std::visit(visitor { [&](auto arg) { // Covers BufferView and CustomBuffer. errorCode = Error::InvalidGltf; }, [&](const sources::Vector& vector) { + if (bufferIdx == 0 && hasBit(options, ExportOptions::ExportAsGLB)) { + bufferPaths.emplace_back(std::nullopt); + return; + } auto path = getBufferFilePath(asset, bufferIdx); json += std::string(R"("uri":")") + path.string() + '"' + ','; bufferPaths.emplace_back(path); @@ -4301,9 +4305,10 @@ fs::path fg::Exporter::getImageFilePath(const Asset& asset, std::size_t index, M return imageFolder / (name + std::to_string(index) + std::string(extension)); } -fg::Expected> fg::Exporter::writeGltfJson(const Asset& asset, ExportOptions options) { +fg::Expected> fg::Exporter::writeGltfJson(const Asset& asset, ExportOptions nOptions) { bufferPaths.clear(); imagePaths.clear(); + options = nOptions; if (hasBit(options, ExportOptions::ValidateAsset)) { auto validation = validate(asset); @@ -4362,10 +4367,12 @@ fg::Expected> fg::Exporter::writeGltfJson(const As return Expected { std::move(result) }; } -fg::Expected>> fg::Exporter::writeGltfBinary(const Asset& asset, ExportOptions options) { +fg::Expected>> fg::Exporter::writeGltfBinary(const Asset& asset, ExportOptions nOptions) { bufferPaths.clear(); imagePaths.clear(); + options = nOptions; + options |= ExportOptions::ExportAsGLB; options &= (~ExportOptions::PrettyPrintJson); ExportResult> result; @@ -4379,8 +4386,10 @@ fg::Expected>> fg::Exporter::writeGltfBi result.bufferPaths = std::move(json.bufferPaths); result.imagePaths = std::move(json.imagePaths); - const bool withEmbeddedBuffer = false; - //const bool withEmbeddedBuffer = !asset.buffers.empty() || asset.buffers.front().byteLength < std::numeric_limits::max(); + const bool withEmbeddedBuffer = !asset.buffers.empty() + && std::get_if(&asset.buffers.front().data) != nullptr // We only support writing Vectors as embedded buffers + && asset.buffers.front().byteLength < std::numeric_limits::max(); + std::size_t binarySize = sizeof(BinaryGltfHeader) + sizeof(BinaryGltfChunk) + json.output.size(); if (withEmbeddedBuffer) { binarySize += sizeof(BinaryGltfChunk) + asset.buffers.front().byteLength; @@ -4405,6 +4414,18 @@ fg::Expected>> fg::Exporter::writeGltfBi write(json.output.data(), json.output.size() * sizeof(decltype(json.output)::value_type)); + if (withEmbeddedBuffer) { + const auto& buffer = asset.buffers.front(); + + BinaryGltfChunk dataChunk; + dataChunk.chunkType = binaryGltfDataChunkMagic; + dataChunk.chunkLength = buffer.byteLength; + write(&dataChunk, sizeof dataChunk); + + const auto* vector = std::get_if(&buffer.data); + write(vector->bytes.data(), buffer.byteLength); + } + return Expected { std::move(result) }; }