[glTF2] Implemented reading binary glTF2 (glb) files
parent
e1837b6cc8
commit
6cbfd5b977
|
@ -192,15 +192,31 @@ namespace glTF2
|
||||||
#include "./../include/assimp/Compiler/pushpack1.h"
|
#include "./../include/assimp/Compiler/pushpack1.h"
|
||||||
#endif
|
#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
|
#ifdef ASSIMP_API
|
||||||
#include "./../include/assimp/Compiler/poppack1.h"
|
#include "./../include/assimp/Compiler/poppack1.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
//! Values for the GLB_Header::sceneFormat field
|
//! Values for the GLB_Chunk::chunkType field
|
||||||
enum SceneFormat
|
enum ChunkType
|
||||||
{
|
{
|
||||||
SceneFormat_JSON = 0
|
ChunkType_JSON = 0x4E4F534A,
|
||||||
|
ChunkType_BIN = 0x004E4942
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Values for the mesh primitive modes
|
//! Values for the mesh primitive modes
|
||||||
|
@ -1086,7 +1102,10 @@ namespace glTF2
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Main function
|
//! 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
|
//! Search for an available name, starting from the given strings
|
||||||
std::string FindUniqueID(const std::string& str, const char* suffix);
|
std::string FindUniqueID(const std::string& str, const char* suffix);
|
||||||
|
@ -1095,6 +1114,8 @@ namespace glTF2
|
||||||
{ return mBodyBuffer; }
|
{ return mBodyBuffer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void ReadBinaryHeader(IOStream& stream, std::vector<char>& sceneData);
|
||||||
|
|
||||||
void ReadExtensionsUsed(Document& doc);
|
void ReadExtensionsUsed(Document& doc);
|
||||||
|
|
||||||
IOStream* OpenFile(std::string path, const char* mode, bool absolute = false);
|
IOStream* OpenFile(std::string path, const char* mode, bool absolute = false);
|
||||||
|
|
|
@ -1037,7 +1037,72 @@ inline void AssetMetadata::Read(Document& doc)
|
||||||
// Asset methods implementation
|
// 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();
|
mCurrentAssetDir.clear();
|
||||||
int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\')));
|
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");
|
throw DeadlyImportError("GLTF: Could not open file for reading");
|
||||||
}
|
}
|
||||||
|
|
||||||
mSceneLength = stream->FileSize();
|
// is binary? then read the header
|
||||||
mBodyLength = 0;
|
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);
|
// read the scene data
|
||||||
sceneData[mSceneLength] = '\0';
|
|
||||||
|
|
||||||
if (stream->Read(&sceneData[0], 1, mSceneLength) != mSceneLength) {
|
sceneData.resize(mSceneLength + 1);
|
||||||
throw DeadlyImportError("GLTF: Could not read the file contents");
|
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)
|
inline void Asset::ReadExtensionsUsed(Document& doc)
|
||||||
{
|
{
|
||||||
Value* extsUsed = FindArray(doc, "extensionsUsed");
|
Value* extsUsed = FindArray(doc, "extensionsUsed");
|
||||||
|
|
|
@ -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,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -103,13 +103,13 @@ bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool
|
||||||
{
|
{
|
||||||
const std::string &extension = GetExtension(pFile);
|
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;
|
return false;
|
||||||
|
|
||||||
if (checkSig && pIOHandler) {
|
if (checkSig && pIOHandler) {
|
||||||
glTF2::Asset asset(pIOHandler);
|
glTF2::Asset asset(pIOHandler);
|
||||||
try {
|
try {
|
||||||
asset.Load(pFile);
|
asset.Load(pFile, extension == "glb");
|
||||||
std::string version = asset.asset.version;
|
std::string version = asset.asset.version;
|
||||||
return !version.empty() && version[0] == '2';
|
return !version.empty() && version[0] == '2';
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
@ -639,7 +639,7 @@ void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IO
|
||||||
|
|
||||||
// read the asset file
|
// read the asset file
|
||||||
glTF2::Asset asset(pIOHandler);
|
glTF2::Asset asset(pIOHandler);
|
||||||
asset.Load(pFile);
|
asset.Load(pFile, GetExtension(pFile) == "glb");
|
||||||
|
|
||||||
//
|
//
|
||||||
// Copy the data out
|
// Copy the data out
|
||||||
|
|
Loading…
Reference in New Issue