engine/asset/src/asset.rs
2024-08-23 13:22:09 +02:00

405 lines
12 KiB
Rust

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<f32>,
}
);
impl From<Matrix4<f32>> for MatrixBuffer {
fn from(m: Matrix4<f32>) -> MatrixBuffer {
Self { m }
}
}
#[derive(Debug)]
pub struct Asset {
device: Arc<Device>,
pub gltf_file: String,
pub meshes: Vec<AssetMesh>,
pub animations: Arc<Vec<Arc<gltf_loader::prelude::GltfAnimation>>>,
pub bounding_box: GltfBoundingBox,
}
impl Asset {
pub fn new(
device: &Arc<Device>,
queue: &Arc<Mutex<Queue>>,
render_type: SceneType,
gltf_asset: GltfAsset,
gltf_file: &str,
) -> Result<Asset> {
// 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<Device>,
primitive: &Primitive,
images: &[Arc<Image>],
model_buffer: &Arc<Buffer<MatrixBuffer>>,
bone_buffer: &Option<Arc<Buffer<MatrixBuffer>>>,
render_type: SceneType,
) -> Result<PrimitiveDataTypes> {
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<T: ReprC + Clone + Copy + Send + Sync + 'static>(
device: &Arc<Device>,
indices: &Option<Vec<u32>>,
vertices: Vec<T>,
) -> Result<Arc<Buffer<T>>> {
let data = match indices {
Some(indices) => {
let mut indexed_vertices: Vec<T> = 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(),
}
}
}