405 lines
12 KiB
Rust
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(),
|
|
}
|
|
}
|
|
}
|