use anyhow::Result; use cgmath::Matrix4; use gltf_loader::prelude::*; use itertools::izip; use utilities::{impl_reprc, prelude::*}; use vulkan_rs::prelude::*; use crate::vertextypes::prelude::*; use crate::{mesh::AssetMesh, scene_type::SceneType}; use super::imageformatchecker::ImageFormatChecker; use std::sync::{Arc, Mutex}; impl_reprc!( #[derive(Debug, Copy)] pub struct MatrixBuffer { #[assume_reprc] m: Matrix4, } ); impl From> for MatrixBuffer { fn from(m: Matrix4) -> MatrixBuffer { Self { m } } } #[derive(Debug)] pub struct Asset { device: Arc, pub gltf_file: String, pub meshes: Vec, pub animations: Arc>>, pub bounding_box: GltfBoundingBox, } impl Asset { pub fn new( device: &Arc, queue: &Arc>, render_type: SceneType, gltf_asset: GltfAsset, gltf_file: &str, ) -> Result { // create images let mut images = Vec::new(); for image_data in gltf_asset.image_data { images.push(ImageFormatChecker::new(image_data).build(device, queue)?); } let mut bounding_box = GltfBoundingBox { min: [0.0, 0.0, 0.0], max: [0.0, 0.0, 0.0], }; // create vulkan buffers and connect images to the primitives // save it into self defined mesh list let mut meshes = Vec::new(); for mesh in &gltf_asset.meshes { let mut asset_mesh = AssetMesh::new_custom_model(device, render_type, mesh.model_matrix())?; asset_mesh.set_node_index(mesh.node_index); if let Some(name) = &mesh.name { asset_mesh.set_name(name); } if !gltf_asset.animations.is_empty() { asset_mesh.add_bone_buffer()?; } for primitive in mesh.primitives() { asset_mesh.primitive_types.push(Self::build_primitive( device, primitive, &images, &asset_mesh.model_buffer, &asset_mesh.bone_buffer, render_type, )?); Self::update_bounding_box(&mut bounding_box, &primitive.bounding_box); } meshes.push(asset_mesh); } Ok(Asset { device: device.clone(), gltf_file: gltf_file.to_string(), meshes, animations: Arc::new(gltf_asset.animations), bounding_box, }) } /// Takes a primitive, checks all internals loaded from the glTF file /// /// e.g. /// /// ``` /// match primitive.positions { /// Some(ref positions) => handle case when positions are present /// None => handle case when positions are NOT present /// } /// ``` /// /// That is what I do for every member in primitives, a huge tree of matches fn build_primitive( device: &Arc, primitive: &Primitive, images: &[Arc], model_buffer: &Arc>, bone_buffer: &Option>>, render_type: SceneType, ) -> Result { match ( &primitive.positions, &primitive.normals, &primitive.color_texture_info, &primitive.normal_texture_info, &primitive.tangents, &primitive.joint_indices, &primitive.weights, bone_buffer, ) { ( Some(positions), Some(normals), Some(color_texture), Some(normal_texture), Some(tangents), Some(joint_indices), Some(weights), Some(bone_buffer), ) => { let mut vertices = Vec::new(); for (position, normal, tangents, uv, joints, weight) in izip!( positions, normals, tangents, &color_texture.texture_coordinates, joint_indices, weights ) { vertices.push(PositionNormalColorUVNormalUVBone::new( *position, *normal, *tangents, *uv, *joints, *weight, )); } let buffer = Self::create_buffer(device, &primitive.indices, vertices)?; PrimitiveDataTypes::new( device, buffer, Some(images[color_texture.image_index].clone()), Some(images[normal_texture.image_index].clone()), model_buffer, Some(bone_buffer), render_type, primitive.material, true, ) } (Some(positions), None, None, _, _, _, _, _) => { let vertices = positions.iter().map(|p| PositionOnly::new(*p)).collect(); let buffer = Self::create_buffer(device, &primitive.indices, vertices)?; PrimitiveDataTypes::new( device, buffer, None, None, model_buffer, None, render_type, primitive.material, true, ) } (Some(positions), None, Some(color_texture), _, _, _, _, _) => { let mut vertices = Vec::new(); for (position, uv) in izip!(positions, &color_texture.texture_coordinates) { vertices.push(PositionColorUV::new(*position, *uv)); } let buffer = Self::create_buffer(device, &primitive.indices, vertices)?; PrimitiveDataTypes::new( device, buffer, Some(images[color_texture.image_index].clone()), None, model_buffer, None, render_type, primitive.material, true, ) } (Some(positions), Some(normals), None, _, _, _, _, _) => { let mut vertices = Vec::new(); for (position, normal) in izip!(positions, normals) { vertices.push(PositionNormal::new(*position, *normal)); } let buffer = Self::create_buffer(device, &primitive.indices, vertices)?; PrimitiveDataTypes::new( device, buffer, None, None, model_buffer, None, render_type, primitive.material, true, ) } (Some(positions), Some(normals), Some(color_texture), None, _, None, None, _) => { let mut vertices = Vec::new(); for (position, normal, uv) in izip!(positions, normals, &color_texture.texture_coordinates) { vertices.push(PositionNormalColorUV::new(*position, *normal, *uv)); } let buffer = Self::create_buffer(device, &primitive.indices, vertices)?; PrimitiveDataTypes::new( device, buffer, Some(images[color_texture.image_index].clone()), None, model_buffer, None, render_type, primitive.material, true, ) } ( Some(positions), Some(normals), Some(color_texture), Some(normal_texture), Some(tangents), None, None, _, ) => { let mut vertices = Vec::new(); for (position, normal, tangent, uv) in izip!( positions, normals, tangents, &color_texture.texture_coordinates ) { vertices.push(PositionNormalColorUVNormalUV::new( *position, *normal, *tangent, *uv, )); } let buffer = Self::create_buffer(device, &primitive.indices, vertices)?; PrimitiveDataTypes::new( device, buffer, Some(images[color_texture.image_index].clone()), Some(images[normal_texture.image_index].clone()), model_buffer, None, render_type, primitive.material, true, ) } ( Some(positions), Some(normals), Some(color_texture), None, _, Some(joint_indices), Some(weights), Some(bone_buffer), ) => { let mut vertices = Vec::new(); for (position, normal, uv, joints, weights) in izip!( positions, normals, &color_texture.texture_coordinates, joint_indices, weights ) { vertices.push(PositionNormalColorUVBone::new( *position, *normal, *uv, *joints, *weights, )); } let buffer = Self::create_buffer(device, &primitive.indices, vertices)?; PrimitiveDataTypes::new( device, buffer, Some(images[color_texture.image_index].clone()), None, model_buffer, Some(bone_buffer), render_type, primitive.material, true, ) } _ => todo!( "combination: Pos({}) Norm({}) ColUV({}) NormUV({}) Tang({}) Joint({}) Weight({}) Bones({})", primitive.positions.is_some(), primitive.normals.is_some(), primitive.color_texture_info.is_some(), primitive.normal_texture_info.is_some(), primitive.tangents.is_some(), primitive.joint_indices.is_some(), primitive.weights.is_some(), bone_buffer.is_some(), ), } } pub(crate) fn create_buffer( device: &Arc, indices: &Option>, vertices: Vec, ) -> Result>> { let data = match indices { Some(indices) => { let mut indexed_vertices: Vec = Vec::new(); for index in indices { indexed_vertices.push(vertices[*index as usize]); } indexed_vertices } None => vertices, }; Buffer::builder() .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) .set_memory_usage(MemoryUsage::CpuOnly) .set_data(&data) .build(device.clone()) } fn update_bounding_box(target_bb: &mut GltfBoundingBox, bb: &GltfBoundingBox) { for i in 0..3 { if bb.min[i] < target_bb.min[i] { target_bb.min[i] = bb.min[i]; } } for i in 0..3 { if bb.max[i] > target_bb.max[i] { target_bb.max[i] = bb.max[i]; } } } } impl Clone for Asset { fn clone(&self) -> Self { let meshes = self .meshes .iter() .filter_map(|m| match m.separated_copy() { Ok(c) => Some(c), Err(msg) => { println!("{}", msg); None } }) .collect(); Asset { device: self.device.clone(), gltf_file: self.gltf_file.clone(), meshes, animations: self.animations.clone(), bounding_box: self.bounding_box.clone(), } } }