mod3d_gltf/glb.rs
1/// A GLB file contains a 12-byte header; a chunk 0 (JSON); an optional
2/// chunk 1 (binary data)
3///
4/// The 12-byte header is 0x46546C67_u32 ; 0x00000002_u32; byte_length_u32
5///
6/// byte_length_u32 must equal the byte size of the GLB file content
7///
8/// Chunk 0 has an 8-byte header that is byte_length_u32; 0x4E4F534A_u32,
9/// followed by data
10///
11/// byte_length_u32 must equal the byte size of the data - must be a
12/// multiple of 4 bytes
13///
14/// Chunk 1 (optional) has an 8-byte header that is byte_length_u32;
15/// 0x004E4942_32, followed by data
16///
17/// byte_length_u32 must equal the byte size of the data - must be a
18/// multiple of 4 bytes
19///
20///
21/// A function is required that takes a GLB file and returns a Gltf Json
22/// Value and invokes a callback on the chunk 1 file data to get an
23/// Option<Vec<u8>> of chunk 1
24///
25/// A function is required that takes a Json file and returns a Gltf Json
26/// Value
27///
28/// A method on the Gltf Json Value is required that takes invokes
29/// callbacks for each buffer data in the file
30///
31/// A method on the Gltf Json Value is required that turns it into a Gltf
32/// descriptor
33///
34/// Methods are required on the Gltf descriptor that access the
35/// scenes; the default scene; the nodes by name; the skeletons; etc.
36use serde_json::json;
37use serde_json::Value as JsonValue;
38
39use crate::{Error, Gltf, Result};
40
41struct GlbLoader<'file, F: std::io::Read, B> {
42 file: &'file mut F,
43 max_json_length: usize,
44 byte_length: u32,
45 json_value: JsonValue,
46 buffer_0: Option<B>,
47}
48impl<'file, F: std::io::Read, B> GlbLoader<'file, F, B> {
49 //cp new
50 /// Create a new GlbLoader given a file and maximum length
51 /// expected of the Json within it
52 fn new(file: &'file mut F, max_json_length: usize) -> Self {
53 let byte_length = 0;
54 let json_value = json!(null);
55 let buffer_0 = None;
56 Self {
57 file,
58 max_json_length,
59 byte_length,
60 json_value,
61 buffer_0,
62 }
63 }
64
65 //mp read_glb_hdr
66 /// Read the GLB header from the file for the loader and validate
67 /// it
68 fn read_glb_hdr(&mut self) -> Result<()> {
69 let mut hdr = [0; 12];
70 self.file.read_exact(&mut hdr)?;
71 if hdr[0..8] != [0x67, 0x6c, 0x54, 0x46, 0x02, 0x00, 0x00, 0x00] {
72 return Err(Error::GlbHdr);
73 }
74 let byte_length = (hdr[8] as u32)
75 | ((hdr[9] as u32) << 8)
76 | ((hdr[10] as u32) << 16)
77 | ((hdr[11] as u32) << 24);
78 self.byte_length = byte_length;
79 Ok(())
80 }
81
82 //mp read_json
83 /// Read the Json chunk of the GLB file (assuming the header has
84 /// been read), and validate the lengtht of it, and parse the Json
85 ///
86 /// Do not at this point attempt to validate the Json to be Gltf Json
87 fn read_json(&mut self) -> Result<()> {
88 let mut hdr = [0; 8];
89 self.file.read_exact(&mut hdr).map_err(Error::GlbJsonIo)?;
90 if hdr[4..8] != [0x4a, 0x53, 0x4f, 0x4e] {
91 return Err(Error::GlbJsonHdr);
92 }
93 let json_byte_length = (hdr[0] as u32)
94 | ((hdr[1] as u32) << 8)
95 | ((hdr[2] as u32) << 16)
96 | ((hdr[3] as u32) << 24);
97 let json_byte_length = json_byte_length as usize;
98 if json_byte_length > self.max_json_length {
99 return Err(Error::GlbJsonHdr);
100 }
101 let mut buffer = vec![0; json_byte_length];
102 let bytes_read = self.file.read(&mut buffer)?;
103 if bytes_read < json_byte_length {
104 return Err(Error::GlbJsonLength);
105 }
106 self.json_value = serde_json::from_str(std::str::from_utf8(&buffer)?)?;
107 Ok(())
108 }
109
110 //mp read_buffer
111 /// Read the 'binary' buffer chunk of the GLB if it is present
112 ///
113 /// This does *NOT* support GLB files with a Json chunk and an
114 /// extension chunk without a binary chunk. It consumes the header
115 /// of the next Glb chunk and checks that it is a binary chunk; if
116 /// no header is there (no binary) then all is okay, but if there
117 /// is no binary chunk but there is an extension chunk then its
118 /// header will have been consumed
119 fn read_buffer<BR>(&mut self, buf_reader: &BR) -> Result<()>
120 where
121 BR: Fn(&mut F, usize) -> std::result::Result<Option<B>, std::io::Error>,
122 {
123 let mut hdr = [0; 8];
124 let n = self.file.read(&mut hdr).map_err(Error::GlbJsonIo)?;
125 if n == 0 {
126 return Ok(());
127 }
128 if hdr[4..8] != [0x42, 0x49, 0x4e, 0x00] {
129 return Err(Error::GlbBinHdr);
130 }
131 let bin_byte_length = (hdr[0] as u32)
132 | ((hdr[1] as u32) << 8)
133 | ((hdr[2] as u32) << 16)
134 | ((hdr[3] as u32) << 24);
135 let bin_byte_length = bin_byte_length as usize;
136 self.buffer_0 = buf_reader(self.file, bin_byte_length).map_err(Error::GlbBinIo)?;
137 Ok(())
138 }
139
140 //dp into_gltf
141 /// Drop the borrow of the file and return a GltfJsonValue and the
142 /// binary buffer loaded from the Glb file (if any)
143 fn into_gltf(self) -> Result<(Gltf, Option<B>)> {
144 let gltf_json_value = Gltf::of_json_value(self.json_value)?;
145 Ok((gltf_json_value, self.buffer_0))
146 }
147}
148
149pub fn glb_load<F, B, BR>(file: &mut F, b: &BR, max_json_length: usize) -> Result<(Gltf, Option<B>)>
150where
151 F: std::io::Read,
152 BR: Fn(&mut F, usize) -> std::result::Result<Option<B>, std::io::Error>,
153{
154 let mut loader = GlbLoader::new(file, max_json_length);
155 loader.read_glb_hdr()?;
156 loader.read_json()?;
157 loader.read_buffer(b)?;
158 loader.into_gltf()
159}