// todo: this needs reimplemented. There is no such thing as a binary chunk index, and the chunk may not be in 0, 1, 2 order // Moves stream position to binary chunk location public static ChunkInfo SeekToBinaryChunk(Stream stream, int binaryChunkIndex, long startPosition = 0) { stream.Position = startPosition + 4; // start after magic number chunk GLBHeader header = ParseGLBHeader(stream); uint chunkOffset = 12; // sizeof(GLBHeader) + magic number uint chunkLength = 0; for (int i = 0; i < binaryChunkIndex + 2; ++i) { chunkOffset += chunkLength; stream.Position = chunkOffset; chunkLength = GetUInt32(stream); chunkOffset += 8; // to account for chunk length (4 bytes) and type (4 bytes) } // Load Binary Chunk if (chunkOffset + chunkLength <= header.FileLength) { ChunkFormat chunkType = (ChunkFormat)GetUInt32(stream); if (chunkType != ChunkFormat.BIN) { throw new GLTFHeaderInvalidException("Second chunk must be of type BIN if present"); } return(new ChunkInfo { StartPosition = stream.Position - CHUNK_HEADER_SIZE, Length = chunkLength, Type = chunkType }); } throw new GLTFHeaderInvalidException("File length does not match chunk header."); }
// Moves stream position to binary chunk location public static void SeekToBinaryChunk(Stream stream, int binaryChunkIndex, long startPosition = 0) { stream.Position = startPosition + 4; // start after magic number chunk GLBHeader header = ParseGLBHeader(stream); uint chunkOffset = 12; // sizeof(GLBHeader) + magic number uint chunkLength = 0; for (int i = 0; i < binaryChunkIndex + 2; ++i) { chunkOffset += chunkLength; //vona 5/25/21 //stream.Position = chunkOffset; stream.Position = startPosition + chunkOffset; chunkLength = GetUInt32(stream); chunkOffset += 8; // to account for chunk length (4 bytes) and type (4 bytes) } // Load Binary Chunk if (chunkOffset + chunkLength <= header.FileLength) { uint chunkType = GetUInt32(stream); if (chunkType != (uint)ChunkFormat.BIN) { throw new GLTFHeaderInvalidException("Second chunk must be of type BIN if present"); } } else { throw new GLTFHeaderInvalidException("File length does not match chunk header."); } }
// todo optimize: this can have a caching system where GLTFRoot stores data about JSON offset of a GLB public static void ExtractBinaryChunk(byte[] gltfBinary, int binaryChunkIndex, out byte[] glbBuffer) { GLBHeader header = ParseGLBHeader(gltfBinary); int chunkOffset = 12; // sizeof(GLBHeader) + magic number int chunkLength = 0; for (int i = 0; i < binaryChunkIndex + 2; ++i) { chunkOffset += chunkLength; chunkLength = (int)GetChunkLength(gltfBinary, chunkOffset); chunkOffset += 8; // to account for chunk length (4 bytes) and type (4 bytes) } // Load Binary Chunk if (chunkOffset + chunkLength <= header.FileLength) { uint chunkType = BitConverter.ToUInt32(gltfBinary, chunkOffset - 4); if (chunkType != (uint)ChunkFormat.BIN) { throw new GLTFHeaderInvalidException("Second chunk must be of type BIN if present"); } glbBuffer = new byte[chunkLength]; System.Buffer.BlockCopy(gltfBinary, chunkOffset, glbBuffer, 0, chunkLength); } else { throw new GLTFHeaderInvalidException("File length does not match chunk header."); } }
private static string ParseJsonChunk(byte[] gltfBinary) { GLBHeader header = ParseGLBHeader(gltfBinary); if (header.Version != 2) { throw new GLTFHeaderInvalidException("Unsupported glTF version"); } ; if (header.FileLength != gltfBinary.Length) { throw new GLTFHeaderInvalidException("File length does not match header."); } uint chunkLength = GetChunkLength(gltfBinary, 12); var chunkType = BitConverter.ToUInt32(gltfBinary, 16); if (chunkType != (uint)ChunkFormat.JSON) { throw new GLTFHeaderInvalidException("First chunk must be of type JSON"); } // Load JSON chunk return(System.Text.Encoding.UTF8.GetString(gltfBinary, 20, (int)chunkLength)); }
/// <summary> /// Turns the GLB data contained in a stream into a GLBObject. Will copy to the outStream if specified /// </summary> /// <param name="root">The glTF root to turn into a GLBObject</param> /// <param name="inputGLBStream">The stream the glb came in</param> /// <param name="outStream">If outstream is specified, the glb gets copied to it</param> /// <param name="inputGLBStreamStartPosition">Offset into the buffer that the GLB starts</param> /// <returns>A constructed GLBObject</returns> public static GLBObject ConstructFromGLB(GLTFRoot root, Stream inputGLBStream, Stream outStream = null, long inputGLBStreamStartPosition = 0) { if (outStream != null) { inputGLBStream.Position = inputGLBStreamStartPosition; inputGLBStream.CopyTo(outStream); } else { outStream = inputGLBStream; } // header information is 4 bytes in, past the magic number inputGLBStream.Position = 4 + inputGLBStreamStartPosition; GLBHeader header = GLTFParser.ParseGLBHeader(inputGLBStream); inputGLBStream.Position = GLTFParser.HEADER_SIZE + inputGLBStreamStartPosition; List <ChunkInfo> allChunks = GLTFParser.FindChunks(inputGLBStream); ChunkInfo jsonChunkInfo = new ChunkInfo { Type = ChunkFormat.JSON }; ChunkInfo binaryChunkInfo = new ChunkInfo { Type = ChunkFormat.BIN }; foreach (ChunkInfo chunkInfo in allChunks) { switch (chunkInfo.Type) { case ChunkFormat.JSON: jsonChunkInfo = chunkInfo; break; case ChunkFormat.BIN: binaryChunkInfo = chunkInfo; break; } } if (jsonChunkInfo.Length == 0) { throw new ArgumentException("JSON chunk must exists for valid GLB", nameof(inputGLBStream)); } // todo: compute the initial bufferview list return(new GLBObject { Root = root, Stream = outStream, StreamStartPosition = inputGLBStreamStartPosition, Header = header, JsonChunkInfo = jsonChunkInfo, BinaryChunkInfo = binaryChunkInfo }); }
private static void WriteHeader(Stream stream, GLBHeader header, long streamStartPosition) { stream.Position = streamStartPosition; byte[] magicNumber = BitConverter.GetBytes(GLTFParser.MAGIC_NUMBER); byte[] version = BitConverter.GetBytes(header.Version); byte[] length = BitConverter.GetBytes(header.FileLength); stream.Write(magicNumber, 0, magicNumber.Length); stream.Write(version, 0, version.Length); stream.Write(length, 0, length.Length); }
private static void ParseJsonChunk(Stream stream, long startPosition) { GLBHeader header = ParseGLBHeader(stream); // 4, 8 if (header.Version != 2) { throw new GLTFHeaderInvalidException("Unsupported glTF version"); } ; if (header.FileLength != (stream.Length - startPosition)) { throw new GLTFHeaderInvalidException("File length does not match header."); } ChunkInfo chunkInfo = ParseChunkInfo(stream); if (chunkInfo.Type != ChunkFormat.JSON) { throw new GLTFHeaderInvalidException("First chunk must be of type JSON"); } }
private static void ParseJsonChunk(Stream stream, long startPosition) { GLBHeader header = ParseGLBHeader(stream); // 4, 8 if (header.Version != 2) { throw new GLTFHeaderInvalidException("Unsupported glTF version"); } ; if (header.FileLength != (stream.Length - startPosition)) { throw new GLTFHeaderInvalidException("File length does not match header."); } int chunkLength = (int)GetUInt32(stream); // 12 var chunkType = GetUInt32(stream); // 16 if (chunkType != (uint)ChunkFormat.JSON) { throw new GLTFHeaderInvalidException("First chunk must be of type JSON"); } }