1use std::fs::File;
11use std::io::{self, Write, Seek};
12use byteorder::{LittleEndian, WriteBytesExt};
13use serde_json;
14
15use crate::error::{GltfError, Result};
16use crate::models::*;
17
18pub struct GltfBuilder {
27 pub gltf: Gltf,
28 pub buffer_data: Vec<u8>,
29}
30
31impl GltfBuilder {
32 pub fn new() -> Self {
34 let mut gltf = Gltf::default();
36
37 let asset = Asset {
39 version: "2.0".to_string(),
40 generator: Some("Rust glTF Export Library".to_string()),
41 copyright: None,
42 };
43
44 gltf.asset = asset;
45
46 gltf.scenes = Some(Vec::new());
48 gltf.nodes = Some(Vec::new());
49 gltf.meshes = Some(Vec::new());
50 gltf.accessors = Some(Vec::new());
51 gltf.buffer_views = Some(Vec::new());
52 gltf.buffers = Some(Vec::new());
53
54 let buffer = Buffer {
56 byte_length: 0, uri: None, };
59
60 if let Some(buffers) = &mut gltf.buffers {
61 buffers.push(buffer);
62 }
63
64 GltfBuilder {
65 gltf,
66 buffer_data: Vec::new(),
67 }
68 }
69
70 pub fn add_scene(&mut self, name: Option<String>, nodes: Option<Vec<usize>>) -> usize {
72 let scene = Scene {
73 name,
74 nodes,
75 };
76
77 if let Some(scenes) = &mut self.gltf.scenes {
78 let index = scenes.len();
79 scenes.push(scene);
80
81 if self.gltf.scene.is_none() {
83 self.gltf.scene = Some(0);
84 }
85
86 index
87 } else {
88 self.gltf.scenes = Some(vec![scene]);
89 self.gltf.scene = Some(0);
90 0
91 }
92 }
93
94 pub fn add_node(&mut self, name: Option<String>, mesh: Option<usize>,
96 translation: Option<[f32; 3]>, rotation: Option<[f32; 4]>,
97 scale: Option<[f32; 3]>) -> usize {
98 let node = Node {
99 name,
100 mesh,
101 translation,
102 rotation,
103 scale,
104 matrix: None,
105 children: None,
106 };
107
108 if let Some(nodes) = &mut self.gltf.nodes {
109 let index = nodes.len();
110 nodes.push(node);
111 index
112 } else {
113 self.gltf.nodes = Some(vec![node]);
114 0
115 }
116 }
117
118 pub fn add_node_with_children(&mut self, name: Option<String>, mesh: Option<usize>,
120 translation: Option<[f32; 3]>, rotation: Option<[f32; 4]>,
121 scale: Option<[f32; 3]>, children: Vec<usize>) -> usize {
122 let node = Node {
123 name,
124 mesh,
125 translation,
126 rotation,
127 scale,
128 matrix: None,
129 children: Some(children),
130 };
131
132 if let Some(nodes) = &mut self.gltf.nodes {
133 let index = nodes.len();
134 nodes.push(node);
135 index
136 } else {
137 self.gltf.nodes = Some(vec![node]);
138 0
139 }
140 }
141
142 pub fn add_child_to_node(&mut self, parent_index: usize, child_index: usize) -> Result<()> {
144 if let Some(nodes) = &mut self.gltf.nodes {
145 if parent_index < nodes.len() && child_index < nodes.len() {
146 let parent = &mut nodes[parent_index];
147
148 if let Some(children) = &mut parent.children {
149 if !children.contains(&child_index) {
150 children.push(child_index);
151 }
152 } else {
153 parent.children = Some(vec![child_index]);
154 }
155
156 Ok(())
157 } else {
158 Err(GltfError::InvalidIndex)
159 }
160 } else {
161 Err(GltfError::InvalidData("No nodes in document".to_string()))
162 }
163 }
164
165 pub fn create_node_hierarchy(&mut self, parent_name: Option<String>,
167 parent_translation: Option<[f32; 3]>,
168 parent_rotation: Option<[f32; 4]>,
169 parent_scale: Option<[f32; 3]>,
170 child_indices: Vec<usize>) -> usize {
171 self.add_node_with_children(
173 parent_name,
174 None, parent_translation,
176 parent_rotation,
177 parent_scale,
178 child_indices
179 )
180 }
181
182 pub fn add_mesh(&mut self, name: Option<String>, primitives: Vec<Primitive>) -> usize {
184 let mesh = Mesh {
185 name,
186 primitives,
187 };
188
189 if let Some(meshes) = &mut self.gltf.meshes {
190 let index = meshes.len();
191 meshes.push(mesh);
192 index
193 } else {
194 self.gltf.meshes = Some(vec![mesh]);
195 0
196 }
197 }
198
199 pub(crate) fn add_accessor(&mut self, buffer_view: usize, component_type: usize,
201 count: usize, type_: String, byte_offset: Option<usize>,
202 min: Option<Vec<f32>>, max: Option<Vec<f32>>) -> usize {
203 let accessor = Accessor {
204 buffer_view: buffer_view,
205 component_type: component_type,
206 count,
207 type_,
208 byte_offset: byte_offset,
209 min,
210 max,
211 normalized: None,
212 };
213
214 if let Some(accessors) = &mut self.gltf.accessors {
215 let index = accessors.len();
216 accessors.push(accessor);
217 index
218 } else {
219 self.gltf.accessors = Some(vec![accessor]);
220 0
221 }
222 }
223
224 pub(crate) fn add_buffer_view(&mut self, byte_offset: usize, byte_length: usize,
226 target: Option<usize>) -> usize {
227 let buffer_view = BufferView {
228 buffer: 0, byte_offset: byte_offset,
230 byte_length: byte_length,
231 byte_stride: None,
232 target,
233 };
234
235 if let Some(buffer_views) = &mut self.gltf.buffer_views {
236 let index = buffer_views.len();
237 buffer_views.push(buffer_view);
238 index
239 } else {
240 self.gltf.buffer_views = Some(vec![buffer_view]);
241 0
242 }
243 }
244
245 pub(crate) fn add_buffer_data(&mut self, data: &[u8]) -> (usize, usize) {
247 while self.buffer_data.len() % 4 != 0 {
249 self.buffer_data.push(0);
250 }
251
252 let byte_offset = self.buffer_data.len();
253 let byte_length = data.len();
254
255 self.buffer_data.extend_from_slice(data);
256
257 if let Some(buffers) = &mut self.gltf.buffers {
259 if !buffers.is_empty() {
260 buffers[0].byte_length = self.buffer_data.len();
261 }
262 }
263
264 (byte_offset, byte_length)
265 }
266
267 pub fn export_glb(&self, path: &str) -> Result<()> {
269 let mut file = File::create(path)?;
270
271 file.write_all(b"glTF")?;
273 file.write_u32::<LittleEndian>(2)?; let length_pos = file.stream_position()?;
277 file.write_u32::<LittleEndian>(0)?; let json = serde_json::to_string(&self.gltf)?;
281 let json_len = json.len();
282 let json_pad = (4 - (json_len % 4)) % 4; file.write_u32::<LittleEndian>((json_len + json_pad) as u32)?; file.write_u32::<LittleEndian>(0x4E4F534A)?; file.write_all(json.as_bytes())?;
287
288 for _ in 0..json_pad {
290 file.write_u8(0x20)?; }
292
293 if !self.buffer_data.is_empty() {
295 let bin_len = self.buffer_data.len();
296 let bin_pad = (4 - (bin_len % 4)) % 4; file.write_u32::<LittleEndian>((bin_len + bin_pad) as u32)?; file.write_u32::<LittleEndian>(0x004E4942)?; file.write_all(&self.buffer_data)?;
301
302 for _ in 0..bin_pad {
304 file.write_u8(0)?;
305 }
306 }
307
308 let current_pos = file.stream_position()?;
310 file.seek(io::SeekFrom::Start(length_pos))?;
311 file.write_u32::<LittleEndian>(current_pos as u32)?;
312
313 Ok(())
314 }
315}