#[macro_use]
extern crate failure;
#[macro_use]
extern crate serde_derive;
pub use self::export::*;
use crate::bounding_box::BoundingBox;
use crate::material::Material;
use serde_json;
use serde_json::Error;
use std::collections::HashMap;
mod bounding_box;
mod combine_indices;
mod export;
mod material;
mod y_up;
#[derive(Debug, Fail)]
pub enum BlenderError {
#[fail(
display = "There was an issue while exporting meshes: Blender stderr output: {}",
_0
)]
Stderr(String),
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[cfg_attr(test, derive(Default))]
#[serde(deny_unknown_fields)]
pub struct BlenderMesh {
pub vertex_positions: Vec<f32>,
pub vertex_position_indices: Vec<u16>,
pub num_vertices_in_each_face: Vec<u8>,
pub vertex_normals: Vec<f32>,
pub vertex_normal_indices: Option<Vec<u16>>,
pub vertex_uvs: Option<Vec<f32>>,
pub vertex_uv_indices: Option<Vec<u16>>,
pub texture_name: Option<String>,
pub armature_name: Option<String>,
pub vertex_group_indices: Option<Vec<u8>>,
pub vertex_group_weights: Option<Vec<f32>>,
pub num_groups_for_each_vertex: Option<Vec<u8>>,
pub bounding_box: BoundingBox,
pub(self) materials: HashMap<String, Material>,
}
impl BlenderMesh {
pub fn from_json(json_str: &str) -> Result<BlenderMesh, Error> {
serde_json::from_str(json_str)
}
}
impl BlenderMesh {
pub fn triangulate(&mut self) {
let mut triangulated_position_indices = vec![];
let mut triangulated_face_vertex_counts = vec![];
let mut face_pointer = 0;
for num_verts_in_face in self.num_vertices_in_each_face.iter() {
match num_verts_in_face {
&3 => {
triangulated_face_vertex_counts.push(3);
triangulated_position_indices.push(self.vertex_position_indices[face_pointer]);
triangulated_position_indices
.push(self.vertex_position_indices[face_pointer + 1]);
triangulated_position_indices
.push(self.vertex_position_indices[face_pointer + 2]);
face_pointer += 3;
}
&4 => {
triangulated_face_vertex_counts.push(3);
triangulated_face_vertex_counts.push(3);
triangulated_position_indices.push(self.vertex_position_indices[face_pointer]);
triangulated_position_indices
.push(self.vertex_position_indices[face_pointer + 1]);
triangulated_position_indices
.push(self.vertex_position_indices[face_pointer + 2]);
triangulated_position_indices.push(self.vertex_position_indices[face_pointer]);
triangulated_position_indices
.push(self.vertex_position_indices[face_pointer + 2]);
triangulated_position_indices
.push(self.vertex_position_indices[face_pointer + 3]);
face_pointer += 4;
}
_ => {
panic!("blender-mesh currently only supports triangulating faces with 3 or 4 vertices");
}
}
}
self.vertex_position_indices = triangulated_position_indices;
self.num_vertices_in_each_face = triangulated_face_vertex_counts;
}
}
impl BlenderMesh {
pub fn set_groups_per_vertex(&mut self, count: u8) {
let mut normalized_group_indices = vec![];
let mut normalized_group_weights = vec![];
let mut current_index: u32 = 0;
{
let indices = self.vertex_group_indices.as_mut().unwrap();
let weights = self.vertex_group_weights.as_mut().unwrap();
self.num_groups_for_each_vertex = Some(
self.num_groups_for_each_vertex
.as_ref()
.unwrap()
.iter()
.map(|group_count| {
let mut vertex_indices = vec![];
let mut vertex_weights = vec![];
for index in current_index..(current_index + *group_count as u32) {
vertex_indices.push(index);
vertex_weights.push(weights[index as usize]);
}
vertex_weights.sort_by(|a, b| b.partial_cmp(a).unwrap());
vertex_indices.sort_by(|a, b| {
weights[*b as usize]
.partial_cmp(&weights[*a as usize])
.unwrap()
});
let mut vertex_indices: Vec<u8> = vertex_indices
.iter()
.map(|i| indices[*i as usize])
.collect();
vertex_indices.resize(count as usize, 0);
vertex_weights.resize(count as usize, 0.0);
normalized_group_indices.append(&mut vertex_indices);
normalized_group_weights.append(&mut vertex_weights);
current_index += *group_count as u32;
count
})
.collect(),
);
}
self.vertex_group_indices = Some(normalized_group_indices);
self.vertex_group_weights = Some(normalized_group_weights);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn triangulate_faces() {
let mut start_mesh = BlenderMesh {
vertex_position_indices: vec![0, 1, 2, 3, 4, 5, 6, 7],
num_vertices_in_each_face: vec![4, 4],
..BlenderMesh::default()
};
start_mesh.triangulate();
let triangulated_mesh = start_mesh;
let expected_mesh = BlenderMesh {
vertex_position_indices: vec![0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7],
num_vertices_in_each_face: vec![3, 3, 3, 3],
..BlenderMesh::default()
};
assert_eq!(triangulated_mesh, expected_mesh);
}
#[test]
fn set_joints_per_vert() {
let mut start_mesh = BlenderMesh {
vertex_group_indices: Some(vec![0, 2, 3, 4, 0, 1, 3, 2]),
num_groups_for_each_vertex: Some(vec![1, 3, 4]),
vertex_group_weights: Some(vec![1.0, 0.5, 0.2, 0.3, 0.6, 0.15, 0.1, 0.15]),
..BlenderMesh::default()
};
start_mesh.set_groups_per_vertex(3);
let three_joints_per_vert = start_mesh;
let expected_mesh = BlenderMesh {
vertex_group_indices: Some(vec![0, 0, 0, 2, 4, 3, 0, 1, 2]),
num_groups_for_each_vertex: Some(vec![3, 3, 3]),
vertex_group_weights: Some(vec![1.0, 0.0, 0.0, 0.5, 0.3, 0.2, 0.6, 0.15, 0.15]),
..BlenderMesh::default()
};
assert_eq!(three_joints_per_vert, expected_mesh);
}
}