Browse Source

Merge pull request #1640 from wanadev/gltf2-exporter-improvements

Added support for generating glb2 (binary glTF 2)
Kim Kulling 7 years ago
parent
commit
13dba835a0
4 changed files with 110 additions and 3 deletions
  1. 4 1
      code/Exporter.cpp
  2. 1 0
      code/glTF2AssetWriter.h
  3. 91 0
      code/glTF2AssetWriter.inl
  4. 14 2
      code/glTF2Exporter.cpp

+ 4 - 1
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 (binary)", "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;
 }
 

+ 1 - 0
code/glTF2AssetWriter.h

@@ -81,6 +81,7 @@ public:
     AssetWriter(Asset& asset);
 
     void WriteFile(const char* path);
+    void WriteGLBFile(const char* path);
 };
 
 }

+ 91 - 0
code/glTF2AssetWriter.inl

@@ -561,6 +561,97 @@ namespace glTF2 {
         }
     }
 
+    inline void AssetWriter::WriteGLBFile(const char* path)
+    {
+        std::unique_ptr<IOStream> outfile(mAsset.OpenFile(path, "wb", true));
+
+        if (outfile == 0) {
+            throw DeadlyExportError("Could not open output file: " + std::string(path));
+        }
+
+        // Padding with spaces as required by the spec
+        uint32_t padding = 0x20202020;
+
+        // 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<StringBuffer> writer(docBuffer);
+        mDoc.Accept(writer);
+
+        uint32_t jsonChunkLength = (docBuffer.GetSize() + 3) & ~3; // Round up to next multiple of 4
+        auto paddingLength = jsonChunkLength - docBuffer.GetSize();
+
+        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<Buffer> 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;

+ 14 - 2
code/glTF2Exporter.cpp

@@ -77,10 +77,18 @@ 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,
-                           const ExportProperties* pProperties, bool /*isBinary*/)
+                           const ExportProperties* pProperties, bool isBinary)
     : mFilename(filename)
     , mIOSystem(pIOSystem)
     , mProperties(pProperties)
@@ -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);
+    }
 }
 
 /*