Pārlūkot izejas kodu

Merge pull request #1584 from pdaehne/master

[glTF2] Implemented reading binary glTF2 (glb) files
Kim Kulling 7 gadi atpakaļ
vecāks
revīzija
4502d72ee6
3 mainītis faili ar 120 papildinājumiem un 16 dzēšanām
  1. 25 4
      code/glTF2Asset.h
  2. 91 8
      code/glTF2Asset.inl
  3. 4 4
      code/glTF2Importer.cpp

+ 25 - 4
code/glTF2Asset.h

@@ -192,15 +192,31 @@ namespace glTF2
         #include "./../include/assimp/Compiler/pushpack1.h"
     #endif
 
+    //! For binary .glb files
+    //! 12-byte header (+ the JSON + a "body" data section)
+    struct GLB_Header
+    {
+        uint8_t magic[4];     //!< Magic number: "glTF"
+        uint32_t version;     //!< Version number (always 2 as of the last update)
+        uint32_t length;      //!< Total length of the Binary glTF, including header, scene, and body, in bytes
+    } PACK_STRUCT;
+
+    struct GLB_Chunk
+    {
+        uint32_t chunkLength;
+        uint32_t chunkType;
+    } PACK_STRUCT;
+
     #ifdef ASSIMP_API
         #include "./../include/assimp/Compiler/poppack1.h"
     #endif
 
 
-    //! Values for the GLB_Header::sceneFormat field
-    enum SceneFormat
+    //! Values for the GLB_Chunk::chunkType field
+    enum ChunkType
     {
-        SceneFormat_JSON = 0
+        ChunkType_JSON = 0x4E4F534A,
+        ChunkType_BIN  = 0x004E4942
     };
 
     //! Values for the mesh primitive modes
@@ -1086,7 +1102,10 @@ namespace glTF2
         }
 
         //! Main function
-        void Load(const std::string& file);
+        void Load(const std::string& file, bool isBinary = false);
+
+        //! Enables binary encoding on the asset
+        void SetAsBinary();
 
         //! Search for an available name, starting from the given strings
         std::string FindUniqueID(const std::string& str, const char* suffix);
@@ -1095,6 +1114,8 @@ namespace glTF2
             { return mBodyBuffer; }
 
     private:
+        void ReadBinaryHeader(IOStream& stream, std::vector<char>& sceneData);
+
         void ReadExtensionsUsed(Document& doc);
 
         IOStream* OpenFile(std::string path, const char* mode, bool absolute = false);

+ 91 - 8
code/glTF2Asset.inl

@@ -1037,7 +1037,72 @@ inline void AssetMetadata::Read(Document& doc)
 // Asset methods implementation
 //
 
-inline void Asset::Load(const std::string& pFile)
+inline void Asset::ReadBinaryHeader(IOStream& stream, std::vector<char>& sceneData)
+{
+    GLB_Header header;
+    if (stream.Read(&header, sizeof(header), 1) != 1) {
+        throw DeadlyImportError("GLTF: Unable to read the file header");
+    }
+
+    if (strncmp((char*)header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)) != 0) {
+        throw DeadlyImportError("GLTF: Invalid binary glTF file");
+    }
+
+    AI_SWAP4(header.version);
+    asset.version = to_string(header.version);
+    if (header.version != 2) {
+        throw DeadlyImportError("GLTF: Unsupported binary glTF version");
+    }
+
+    GLB_Chunk chunk;
+    if (stream.Read(&chunk, sizeof(chunk), 1) != 1) {
+        throw DeadlyImportError("GLTF: Unable to read JSON chunk");
+    }
+
+    AI_SWAP4(chunk.chunkLength);
+    AI_SWAP4(chunk.chunkType);
+
+    if (chunk.chunkType != ChunkType_JSON) {
+        throw DeadlyImportError("GLTF: JSON chunk missing");
+    }
+
+    // read the scene data
+
+    mSceneLength = chunk.chunkLength;
+    sceneData.resize(mSceneLength + 1);
+    sceneData[mSceneLength] = '\0';
+
+    if (stream.Read(&sceneData[0], 1, mSceneLength) != mSceneLength) {
+        throw DeadlyImportError("GLTF: Could not read the file contents");
+    }
+
+    uint32_t padding = ((chunk.chunkLength + 3) & ~3) - chunk.chunkLength;
+    if (padding > 0) {
+        stream.Seek(padding, aiOrigin_CUR);
+    }
+
+    AI_SWAP4(header.length);
+    mBodyOffset = 12 + 8 + chunk.chunkLength + padding + 8;
+    if (header.length >= mBodyOffset) {
+        if (stream.Read(&chunk, sizeof(chunk), 1) != 1) {
+            throw DeadlyImportError("GLTF: Unable to read BIN chunk");
+        }
+
+        AI_SWAP4(chunk.chunkLength);
+        AI_SWAP4(chunk.chunkType);
+
+        if (chunk.chunkType != ChunkType_BIN) {
+            throw DeadlyImportError("GLTF: BIN chunk missing");
+        }
+
+        mBodyLength = chunk.chunkLength;
+    }
+    else {
+        mBodyOffset = mBodyLength = 0;
+    }
+}
+
+inline void Asset::Load(const std::string& pFile, bool isBinary)
 {
     mCurrentAssetDir.clear();
     int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\')));
@@ -1048,16 +1113,25 @@ inline void Asset::Load(const std::string& pFile)
         throw DeadlyImportError("GLTF: Could not open file for reading");
     }
 
-    mSceneLength = stream->FileSize();
-    mBodyLength = 0;
+    // is binary? then read the header
+    std::vector<char> sceneData;
+    if (isBinary) {
+        SetAsBinary(); // also creates the body buffer
+        ReadBinaryHeader(*stream, sceneData);
+    }
+    else {
+        mSceneLength = stream->FileSize();
+        mBodyLength = 0;
 
-    // read the scene data
 
-    std::vector<char> sceneData(mSceneLength + 1);
-    sceneData[mSceneLength] = '\0';
+        // read the scene data
 
-    if (stream->Read(&sceneData[0], 1, mSceneLength) != mSceneLength) {
-        throw DeadlyImportError("GLTF: Could not read the file contents");
+        sceneData.resize(mSceneLength + 1);
+        sceneData[mSceneLength] = '\0';
+
+        if (stream->Read(&sceneData[0], 1, mSceneLength) != mSceneLength) {
+            throw DeadlyImportError("GLTF: Could not read the file contents");
+        }
     }
 
 
@@ -1110,6 +1184,15 @@ inline void Asset::Load(const std::string& pFile)
     }
 }
 
+inline void Asset::SetAsBinary()
+{
+    if (!mBodyBuffer) {
+        mBodyBuffer = buffers.Create("binary_glTF");
+        mBodyBuffer->MarkAsSpecial();
+    }
+}
+
+
 inline void Asset::ReadExtensionsUsed(Document& doc)
 {
     Value* extsUsed = FindArray(doc, "extensionsUsed");

+ 4 - 4
code/glTF2Importer.cpp

@@ -74,7 +74,7 @@ static const aiImporterDesc desc = {
     "",
     "",
     "",
-    aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
+    aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
     0,
     0,
     0,
@@ -103,13 +103,13 @@ bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool
 {
     const std::string &extension = GetExtension(pFile);
 
-    if (extension != "gltf") // We currently can't read glTF2 binary files (.glb), yet
+    if (extension != "gltf" && extension != "glb")
         return false;
 
     if (checkSig && pIOHandler) {
         glTF2::Asset asset(pIOHandler);
         try {
-            asset.Load(pFile);
+            asset.Load(pFile, extension == "glb");
             std::string version = asset.asset.version;
             return !version.empty() && version[0] == '2';
         } catch (...) {
@@ -639,7 +639,7 @@ void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IO
 
     // read the asset file
     glTF2::Asset asset(pIOHandler);
-    asset.Load(pFile);
+    asset.Load(pFile, GetExtension(pFile) == "glb");
 
     //
     // Copy the data out