use std::fs::File;
use std::io::{self, Write, Seek};
use byteorder::{LittleEndian, WriteBytesExt};
use serde_json;
use crate::error::{GltfError, Result};
use crate::models::*;
pub struct GltfBuilder {
pub gltf: Gltf,
pub buffer_data: Vec<u8>,
}
impl GltfBuilder {
pub fn new() -> Self {
let mut gltf = Gltf::default();
let asset = Asset {
version: "2.0".to_string(),
generator: Some("Rust glTF Export Library".to_string()),
copyright: None,
};
gltf.asset = asset;
gltf.scenes = Some(Vec::new());
gltf.nodes = Some(Vec::new());
gltf.meshes = Some(Vec::new());
gltf.accessors = Some(Vec::new());
gltf.buffer_views = Some(Vec::new());
gltf.buffers = Some(Vec::new());
let buffer = Buffer {
byte_length: 0, uri: None, };
if let Some(buffers) = &mut gltf.buffers {
buffers.push(buffer);
}
GltfBuilder {
gltf,
buffer_data: Vec::new(),
}
}
pub fn add_scene(&mut self, name: Option<String>, nodes: Option<Vec<usize>>) -> usize {
let scene = Scene {
name,
nodes,
};
if let Some(scenes) = &mut self.gltf.scenes {
let index = scenes.len();
scenes.push(scene);
if self.gltf.scene.is_none() {
self.gltf.scene = Some(0);
}
index
} else {
self.gltf.scenes = Some(vec![scene]);
self.gltf.scene = Some(0);
0
}
}
pub fn add_node(&mut self, name: Option<String>, mesh: Option<usize>,
translation: Option<[f32; 3]>, rotation: Option<[f32; 4]>,
scale: Option<[f32; 3]>) -> usize {
let node = Node {
name,
mesh,
translation,
rotation,
scale,
matrix: None,
children: None,
};
if let Some(nodes) = &mut self.gltf.nodes {
let index = nodes.len();
nodes.push(node);
index
} else {
self.gltf.nodes = Some(vec![node]);
0
}
}
pub fn add_node_with_children(&mut self, name: Option<String>, mesh: Option<usize>,
translation: Option<[f32; 3]>, rotation: Option<[f32; 4]>,
scale: Option<[f32; 3]>, children: Vec<usize>) -> usize {
let node = Node {
name,
mesh,
translation,
rotation,
scale,
matrix: None,
children: Some(children),
};
if let Some(nodes) = &mut self.gltf.nodes {
let index = nodes.len();
nodes.push(node);
index
} else {
self.gltf.nodes = Some(vec![node]);
0
}
}
pub fn add_child_to_node(&mut self, parent_index: usize, child_index: usize) -> Result<()> {
if let Some(nodes) = &mut self.gltf.nodes {
if parent_index < nodes.len() && child_index < nodes.len() {
let parent = &mut nodes[parent_index];
if let Some(children) = &mut parent.children {
if !children.contains(&child_index) {
children.push(child_index);
}
} else {
parent.children = Some(vec![child_index]);
}
Ok(())
} else {
Err(GltfError::InvalidIndex)
}
} else {
Err(GltfError::InvalidData("No nodes in document".to_string()))
}
}
pub fn create_node_hierarchy(&mut self, parent_name: Option<String>,
parent_translation: Option<[f32; 3]>,
parent_rotation: Option<[f32; 4]>,
parent_scale: Option<[f32; 3]>,
child_indices: Vec<usize>) -> usize {
self.add_node_with_children(
parent_name,
None, parent_translation,
parent_rotation,
parent_scale,
child_indices
)
}
pub fn add_mesh(&mut self, name: Option<String>, primitives: Vec<Primitive>) -> usize {
let mesh = Mesh {
name,
primitives,
};
if let Some(meshes) = &mut self.gltf.meshes {
let index = meshes.len();
meshes.push(mesh);
index
} else {
self.gltf.meshes = Some(vec![mesh]);
0
}
}
pub(crate) fn add_accessor(&mut self, buffer_view: usize, component_type: usize,
count: usize, type_: String, byte_offset: Option<usize>,
min: Option<Vec<f32>>, max: Option<Vec<f32>>) -> usize {
let accessor = Accessor {
buffer_view: buffer_view,
component_type: component_type,
count,
type_,
byte_offset: byte_offset,
min,
max,
normalized: None,
};
if let Some(accessors) = &mut self.gltf.accessors {
let index = accessors.len();
accessors.push(accessor);
index
} else {
self.gltf.accessors = Some(vec![accessor]);
0
}
}
pub(crate) fn add_buffer_view(&mut self, byte_offset: usize, byte_length: usize,
target: Option<usize>) -> usize {
let buffer_view = BufferView {
buffer: 0, byte_offset: byte_offset,
byte_length: byte_length,
byte_stride: None,
target,
};
if let Some(buffer_views) = &mut self.gltf.buffer_views {
let index = buffer_views.len();
buffer_views.push(buffer_view);
index
} else {
self.gltf.buffer_views = Some(vec![buffer_view]);
0
}
}
pub(crate) fn add_buffer_data(&mut self, data: &[u8]) -> (usize, usize) {
while self.buffer_data.len() % 4 != 0 {
self.buffer_data.push(0);
}
let byte_offset = self.buffer_data.len();
let byte_length = data.len();
self.buffer_data.extend_from_slice(data);
if let Some(buffers) = &mut self.gltf.buffers {
if !buffers.is_empty() {
buffers[0].byte_length = self.buffer_data.len();
}
}
(byte_offset, byte_length)
}
pub fn export_glb(&self, path: &str) -> Result<()> {
let mut file = File::create(path)?;
file.write_all(b"glTF")?;
file.write_u32::<LittleEndian>(2)?;
let length_pos = file.stream_position()?;
file.write_u32::<LittleEndian>(0)?;
let json = serde_json::to_string(&self.gltf)?;
let json_len = json.len();
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())?;
for _ in 0..json_pad {
file.write_u8(0x20)?; }
if !self.buffer_data.is_empty() {
let bin_len = self.buffer_data.len();
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)?;
for _ in 0..bin_pad {
file.write_u8(0)?;
}
}
let current_pos = file.stream_position()?;
file.seek(io::SeekFrom::Start(length_pos))?;
file.write_u32::<LittleEndian>(current_pos as u32)?;
Ok(())
}
}