mod material;
mod mode;
mod vertex;
use crate::utils::*;
use cgmath::*;
use std::sync::Arc;
pub use material::*;
pub use mode::*;
pub use vertex::*;
#[derive(Clone, Debug, Default)]
pub struct Model {
#[cfg(feature = "names")]
pub(crate) mesh_name: Option<String>,
#[cfg(feature = "extras")]
pub(crate) mesh_extras: gltf::json::extras::Extras,
#[cfg(feature = "extras")]
pub(crate) primitive_extras: gltf::json::extras::Extras,
pub(crate) primitive_index: usize,
pub(crate) vertices: Vec<Vertex>,
pub(crate) indices: Option<Vec<u32>>,
pub(crate) mode: Mode,
pub(crate) material: Arc<Material>,
pub(crate) has_normals: bool,
pub(crate) has_tangents: bool,
pub(crate) has_tex_coords: bool,
#[cfg(feature = "vertex-color")]
pub(crate) has_colors: bool,
}
impl Model {
#[cfg(feature = "names")]
pub fn mesh_name(&self) -> Option<&str> {
self.mesh_name.as_deref()
}
pub fn primitive_index(&self) -> usize {
self.primitive_index
}
#[cfg(feature = "extras")]
pub fn mesh_extras(&self) -> &gltf::json::extras::Extras {
&self.mesh_extras
}
#[cfg(feature = "extras")]
pub fn primitive_extras(&self) -> &gltf::json::extras::Extras {
&self.primitive_extras
}
pub fn material(&self) -> Arc<Material> {
self.material.clone()
}
pub fn vertices(&self) -> &Vec<Vertex> {
&self.vertices
}
pub fn indices(&self) -> Option<&Vec<u32>> {
self.indices.as_ref()
}
pub fn mode(&self) -> Mode {
self.mode.clone()
}
pub fn triangles(&self) -> Result<Vec<Triangle>, BadMode> {
let mut triangles = vec![];
let indices = (0..self.vertices.len() as u32).collect();
let indices = self.indices().unwrap_or(&indices);
match self.mode {
Mode::Triangles => {
for i in (0..indices.len()).step_by(3) {
triangles.push([
self.vertices[indices[i] as usize],
self.vertices[indices[i + 1] as usize],
self.vertices[indices[i + 2] as usize],
]);
}
}
Mode::TriangleStrip => {
for i in 0..(indices.len() - 2) {
triangles.push([
self.vertices[indices[i] as usize + i % 2],
self.vertices[indices[i + 1 - i % 2] as usize],
self.vertices[indices[i + 2] as usize],
]);
}
}
Mode::TriangleFan => {
for i in 1..(indices.len() - 1) {
triangles.push([
self.vertices[indices[0] as usize],
self.vertices[indices[i] as usize],
self.vertices[indices[i + 1] as usize],
]);
}
}
_ => return Err(BadMode { mode: self.mode() }),
}
Ok(triangles)
}
pub fn lines(&self) -> Result<Vec<Line>, BadMode> {
let mut lines = vec![];
let indices = (0..self.vertices.len() as u32).collect();
let indices = self.indices().unwrap_or(&indices);
match self.mode {
Mode::Lines => {
for i in (0..indices.len()).step_by(2) {
lines.push([
self.vertices[indices[i] as usize],
self.vertices[indices[i + 1] as usize],
]);
}
}
Mode::LineStrip | Mode::LineLoop => {
for i in 0..(indices.len() - 1) {
lines.push([
self.vertices[indices[i] as usize],
self.vertices[indices[i + 1] as usize],
]);
}
}
_ => return Err(BadMode { mode: self.mode() }),
}
if self.mode == Mode::LineLoop {
lines.push([
self.vertices[indices[0] as usize],
self.vertices[indices[indices.len() - 1] as usize],
]);
}
Ok(lines)
}
pub fn points(&self) -> Result<&Vec<Vertex>, BadMode> {
match self.mode {
Mode::Points => Ok(&self.vertices),
_ => Err(BadMode { mode: self.mode() }),
}
}
pub fn has_normals(&self) -> bool {
self.has_normals
}
pub fn has_tangents(&self) -> bool {
self.has_tangents
}
pub fn has_tex_coords(&self) -> bool {
self.has_tex_coords
}
#[cfg(feature = "vertex-color")]
pub fn has_colors(&self) -> bool {
self.has_colors
}
fn apply_transform_position(pos: [f32; 3], transform: &Matrix4<f32>) -> Vector3<f32> {
let pos = Vector4::new(pos[0], pos[1], pos[2], 1.);
let res = transform * pos;
Vector3::new(res.x / res.w, res.y / res.w, res.z / res.w)
}
fn apply_transform_vector(vec: [f32; 3], transform: &Matrix4<f32>) -> Vector3<f32> {
let vec = Vector4::new(vec[0], vec[1], vec[2], 0.);
(transform * vec).truncate()
}
fn apply_transform_tangent(tangent: [f32; 4], transform: &Matrix4<f32>) -> Vector4<f32> {
let tang = Vector4::new(tangent[0], tangent[1], tangent[2], 0.);
let mut tang = transform * tang;
tang[3] = tangent[3];
tang
}
pub(crate) fn load(
mesh: &gltf::Mesh,
primitive_index: usize,
primitive: gltf::Primitive,
transform: &Matrix4<f32>,
data: &mut GltfData,
) -> Self {
#[cfg(not(feature = "names"))]
{
let _ = mesh;
}
let buffers = &data.buffers;
let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()]));
let indices = reader
.read_indices()
.map(|indices| indices.into_u32().collect());
let mut vertices: Vec<_> = reader
.read_positions()
.unwrap_or_else(|| panic!("The model primitive doesn't contain positions"))
.map(|pos| Vertex {
position: Self::apply_transform_position(pos, transform),
..Default::default()
})
.collect();
let has_normals = if let Some(normals) = reader.read_normals() {
for (i, normal) in normals.enumerate() {
vertices[i].normal = Self::apply_transform_vector(normal, transform).normalize();
}
true
} else {
false
};
let has_tangents = if let Some(tangents) = reader.read_tangents() {
for (i, tangent) in tangents.enumerate() {
let tangent = Self::apply_transform_tangent(tangent, transform);
vertices[i].tangent = tangent.truncate().normalize().extend(tangent.w);
}
true
} else {
false
};
let has_tex_coords = if let Some(tex_coords) = reader.read_tex_coords(0) {
for (i, tex_coords) in tex_coords.into_f32().enumerate() {
vertices[i].tex_coords = Vector2::from(tex_coords);
}
true
} else {
false
};
#[cfg(feature = "vertex-color")]
let has_colors = if let Some(colors) = reader.read_colors(0) {
for (i, color) in colors.into_rgba_u16().enumerate() {
vertices[i].color = Vector4::from(color);
}
true
} else {
false
};
Model {
#[cfg(feature = "names")]
mesh_name: mesh.name().map(String::from),
#[cfg(feature = "extras")]
mesh_extras: mesh.extras().clone(),
#[cfg(feature = "extras")]
primitive_extras: primitive.extras().clone(),
primitive_index,
vertices,
indices,
material: Material::load(primitive.material(), data),
mode: primitive.mode().into(),
has_normals,
has_tangents,
has_tex_coords,
#[cfg(feature = "vertex-color")]
has_colors,
}
}
}