From 31a4ccaebb338deac016748e3cdfef1e2e5361fe Mon Sep 17 00:00:00 2001 From: Alexis Breust Date: Thu, 14 Dec 2017 16:11:12 +0100 Subject: [PATCH] Added support for generating glb2 (binary glTF 2) --- code/Exporter.cpp | 5 ++- code/glTF2AssetWriter.h | 1 + code/glTF2AssetWriter.inl | 92 +++++++++++++++++++++++++++++++++++++++ code/glTF2Exporter.cpp | 14 +++++- 4 files changed, 110 insertions(+), 2 deletions(-) diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 0281e4e99..cb496490f 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -92,6 +92,7 @@ void ExportScene3DS(const char*, IOSystem*, const aiScene*, const ExportProperti void ExportSceneGLTF(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneGLB(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneGLTF2(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneGLB2(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*); @@ -151,6 +152,8 @@ Exporter::ExportFormatEntry gExporters[] = aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf2", &ExportSceneGLTF2, aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), + Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2", "glb2", &ExportSceneGLB2, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), #endif #ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER @@ -420,7 +423,7 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId; ASSIMP_END_EXCEPTION_REGION(aiReturn); - + return AI_FAILURE; } diff --git a/code/glTF2AssetWriter.h b/code/glTF2AssetWriter.h index bce2b1bd1..b4e7ffc2e 100644 --- a/code/glTF2AssetWriter.h +++ b/code/glTF2AssetWriter.h @@ -81,6 +81,7 @@ public: AssetWriter(Asset& asset); void WriteFile(const char* path); + void WriteGLBFile(const char* path); }; } diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index 8b2769a37..aaef7bccb 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -561,6 +561,98 @@ namespace glTF2 { } } + inline void AssetWriter::WriteGLBFile(const char* path) + { + std::unique_ptr outfile(mAsset.OpenFile(path, "wb", true)); + + if (outfile == 0) { + throw DeadlyExportError("Could not open output file: " + std::string(path)); + } + + // Padding with zeros make a invalid JSON for the gltf online validator + uint8_t padding[] = { '\n', '\n', '\n' }; + + // Adapt JSON so that it is not pointing to an external file, + // as this is required by the GLB spec'. + mDoc["buffers"][0].RemoveMember("uri"); + + // + // JSON chunk + // + + StringBuffer docBuffer; + Writer writer(docBuffer); + mDoc.Accept(writer); + + uint32_t jsonChunkLength = (docBuffer.GetSize() + 3) & ~3; // Round up to next multiple of 4 + auto paddingLength = jsonChunkLength - docBuffer.GetSize(); + std::cout << paddingLength << std::endl; + + GLB_Chunk jsonChunk; + jsonChunk.chunkLength = jsonChunkLength; + jsonChunk.chunkType = ChunkType_JSON; + AI_SWAP4(jsonChunk.chunkLength); + + outfile->Seek(sizeof(GLB_Header), aiOrigin_SET); + if (outfile->Write(&jsonChunk, 1, sizeof(GLB_Chunk)) != sizeof(GLB_Chunk)) { + throw DeadlyExportError("Failed to write scene data header!"); + } + if (outfile->Write(docBuffer.GetString(), 1, docBuffer.GetSize()) != docBuffer.GetSize()) { + throw DeadlyExportError("Failed to write scene data!"); + } + if (paddingLength && outfile->Write(padding, 1, paddingLength) != paddingLength) { + throw DeadlyExportError("Failed to write scene data padding!"); + } + + // + // Binary chunk + // + + uint32_t binaryChunkLength = 0; + if (mAsset.buffers.Size() > 0) { + Ref b = mAsset.buffers.Get(0u); + if (b->byteLength > 0) { + binaryChunkLength = (b->byteLength + 3) & ~3; // Round up to next multiple of 4 + auto paddingLength = binaryChunkLength - b->byteLength; + + GLB_Chunk binaryChunk; + binaryChunk.chunkLength = binaryChunkLength; + binaryChunk.chunkType = ChunkType_BIN; + AI_SWAP4(binaryChunk.chunkLength); + + size_t bodyOffset = sizeof(GLB_Header) + sizeof(GLB_Chunk) + jsonChunk.chunkLength; + outfile->Seek(bodyOffset, aiOrigin_SET); + if (outfile->Write(&binaryChunk, 1, sizeof(GLB_Chunk)) != sizeof(GLB_Chunk)) { + throw DeadlyExportError("Failed to write body data header!"); + } + if (outfile->Write(b->GetPointer(), 1, b->byteLength) != b->byteLength) { + throw DeadlyExportError("Failed to write body data!"); + } + if (paddingLength && outfile->Write(padding, 1, paddingLength) != paddingLength) { + throw DeadlyExportError("Failed to write body data padding!"); + } + } + } + + // + // Header + // + + GLB_Header header; + memcpy(header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)); + + header.version = 2; + AI_SWAP4(header.version); + + header.length = uint32_t(sizeof(GLB_Header) + 2 * sizeof(GLB_Chunk) + jsonChunkLength + binaryChunkLength); + AI_SWAP4(header.length); + + outfile->Seek(0, aiOrigin_SET); + if (outfile->Write(&header, 1, sizeof(GLB_Header)) != sizeof(GLB_Header)) { + throw DeadlyExportError("Failed to write the header!"); + } + } + inline void AssetWriter::WriteMetadata() { Value asset; diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index c1a803c1f..82b95195f 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -77,6 +77,14 @@ namespace Assimp { glTF2Exporter exporter(pFile, pIOSystem, pScene, pProperties, false); } + // ------------------------------------------------------------------------------------------------ + // Worker function for exporting a scene to GLB. Prototyped and registered in Exporter.cpp + void ExportSceneGLB2(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) + { + // invoke the exporter + glTF2Exporter exporter(pFile, pIOSystem, pScene, pProperties, true); + } + } // end of namespace Assimp glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const aiScene* pScene, @@ -118,7 +126,11 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai AssetWriter writer(*mAsset); - writer.WriteFile(filename); + if (isBinary) { + writer.WriteGLBFile(filename); + } else { + writer.WriteFile(filename); + } } /*