diff --git a/src/app.rs b/src/app.rs index 03c5b0d..792d374 100644 --- a/src/app.rs +++ b/src/app.rs @@ -495,10 +495,7 @@ impl VulkanContext { println!("Couldn't find material {material_id:?} for {mesh_id:?}"); DEFAULT_BASE_COLOR }, - |material| { - println!("Found material {material_id:?} for {mesh_id:?}, with base color {:?}", material.base_color); - material.base_color - } + |material| material.base_color ); builder diff --git a/src/assets/asset_manager.rs b/src/assets/asset_manager.rs index 1582d64..2537ee4 100644 --- a/src/assets/asset_manager.rs +++ b/src/assets/asset_manager.rs @@ -1,15 +1,16 @@ -use std::collections::HashMap; -use std::path::Path; -use std::sync::Arc; -use anyhow::Result; -use vulkano::buffer::Subbuffer; -use vulkano::memory::allocator::MemoryAllocator; -use crate::assets::loaders::material::mat_loader::MaterialLoader; +use crate::assets::loaders::mat_loader::MaterialLoader; use crate::assets::loaders::mesh_loader::MeshLoader; use crate::common::material::{Material, MaterialRegistry}; use crate::common::mesh::{MeshHandle, MeshRegistry}; use crate::common::scene::Scene; +use crate::common::texture::TextureRegistry; use crate::gpu::vertex3::Vertex3; +use anyhow::Result; +use std::collections::HashMap; +use std::path::Path; +use std::sync::Arc; +use vulkano::buffer::Subbuffer; +use vulkano::memory::allocator::MemoryAllocator; /// A manager that controls all assets used for display. It handles loading all meshes, materials, /// and textures for now. @@ -24,6 +25,8 @@ pub struct AssetManager { mesh_registry: MeshRegistry, /// The asset manager's [`MaterialRegistry`]. material_registry: MaterialRegistry, + /// The asset manager's [`TextureRegistry`]. + texture_registry: TextureRegistry, // Loaders mesh_loader: MeshLoader, @@ -39,6 +42,7 @@ impl AssetManager { allocator, mesh_registry, material_registry, + texture_registry: TextureRegistry::new(), mesh_loader: MeshLoader::new(), material_loader: MaterialLoader::new(), } diff --git a/src/assets/loaders/common.rs b/src/assets/loaders/common.rs new file mode 100644 index 0000000..e38bdae --- /dev/null +++ b/src/assets/loaders/common.rs @@ -0,0 +1,64 @@ +pub struct MeshLoadInfo { + /// The mesh's vertices. + pub vertices: Vec<[f32; 3]>, + /// The mesh's vertex normals. + pub normals: Vec<[f32; 3]>, + /// The mesh's vertex indices. + pub indices: Vec, + /// The texture coordinates. Note that these may also be used on 3D textures. + pub uvs: Vec<[f32; 3]>, + /// The contiguous ranges of `indices` that should be drawn with a single material, in the + /// order they were encountered in the source file. + pub submeshes: Vec, +} + +/// A contiguous range within a [`MeshInfo`]'s `indices`, along with the material it should be +/// drawn with. Ranges are local to a single loaded file - a [`crate::common::mesh::MeshRegistry`] +/// rewrites these into offsets within its own shared index buffer. +#[derive(Debug, Clone)] +pub struct Submesh { + /// The index of the material this submesh uses, as defined by the source file (e.g. a glTF + /// material index). `None` if the file did not assign a material to this range. + pub material_index: Option, + /// The offset of this submesh's first index within `MeshInfo::indices`. + pub index_start: usize, + /// The number of indices that make up this submesh. + pub index_count: usize, +} + + +/// Contains all information required for the application to load a new material. +pub struct MaterialLoadInfo { + /// The ID of this material + pub name: String, + /// The material uniforms. These should be defined on every imported material. + pub uniforms: MaterialUniforms, + /// The ID of the base color map, if any. + pub base_color_map: Option, + /// The ID of the roughness map, if any. This is ignored for now. + pub roughness_map: Option, + /// The ID of the normal map, if any. This is ignored for now. + pub normal_map: Option, +} + +/// A struct composed of the material uniforms. These uniforms will typically scale the material +/// texture maps if there are any. Refer to [`MaterialInfo`] for more information. +pub struct MaterialUniforms { + /// The base color of the material. This scales the base color texture, if any. + /// This value must be specified in normalized RGBA format (all values between 0 and 1). + pub base_color: [f32; 4], + /// The metalness of the material. This scales the metalness texture, if any. This value should + /// be specified as a normalized float between 0 and 1, based on an 8-bit number space. + /// + /// This is ignored until the shaders are updated. + pub metalness_factor: f32, + /// The roughness of the material. This scales the roughness texture, if any. This value should + /// be specified as a normalized float between 0 and 1, based on an 8-bit number space. + /// + /// This is ignored until the shaders are updated. + pub roughness_factor: f32, +} + +/// A struct containing all information about a loaded texture. +pub struct TextureLoadInfo { +} diff --git a/src/assets/loaders/mesh/gltf_loader.rs b/src/assets/loaders/gltf_loader.rs similarity index 61% rename from src/assets/loaders/mesh/gltf_loader.rs rename to src/assets/loaders/gltf_loader.rs index a61d4c2..423f9ab 100644 --- a/src/assets/loaders/mesh/gltf_loader.rs +++ b/src/assets/loaders/gltf_loader.rs @@ -1,8 +1,10 @@ -use crate::assets::loaders::mesh_loader::{MeshFileLoader, MeshInfo, Submesh}; use anyhow::Result; -use gltf::buffer::Data; -use nalgebra::{Matrix4, Vector4}; use std::path::Path; +use gltf::buffer::Data; +use gltf::image::Source; +use gltf::Texture; +use nalgebra::{Matrix4, Vector4}; +use crate::assets::loaders::common::{MaterialLoadInfo, MaterialUniforms, MeshLoadInfo, Submesh, TextureLoadInfo}; pub struct GltfLoader; @@ -83,10 +85,19 @@ impl GltfLoader { ); } } -} -impl MeshFileLoader for GltfLoader { - fn load_mesh(&self, path: &Path) -> Result { + /// Returns a stable identifier for a texture's image, meant to be resolved by a texture + /// loader later. External images are identified by their URI; images embedded in the file + /// itself (e.g. in a `.glb`) have no URI, so they're identified by their image index instead. + fn texture_id(texture: &Texture) -> String { + let image = texture.source(); + match image.source() { + Source::Uri { uri, .. } => uri.to_string(), + Source::View { .. } => format!("embedded_image_{}", image.index()), + } + } + + fn load_mesh(&self, path: &Path) -> Result { let (document, buffers, _) = gltf::import(path)?; let mut vertices = Vec::new(); @@ -117,12 +128,59 @@ impl MeshFileLoader for GltfLoader { } } - Ok(MeshInfo { + let info = MeshLoadInfo { vertices, indices, normals, uvs, submeshes, - }) + }; + + Ok(info) + } + + fn load_materials(&self, path: &Path) -> anyhow::Result> { + let (document, _buffers, _images) = gltf::import(path)?; + + // Collected in document order, so a material's position here matches the + // `primitive.material().index()` that mesh loading recorded for its submeshes. + Ok(document + .materials() + .enumerate() + .map(|(index, material)| { + let pbr = material.pbr_metallic_roughness(); + + let name = material + .name() + .map(str::to_string) + .unwrap_or_else(|| format!("material_{index}")); + + let base_color_map = pbr + .base_color_texture() + .map(|info| Self::texture_id(&info.texture())); + let roughness_map = pbr + .metallic_roughness_texture() + .map(|info| Self::texture_id(&info.texture())); + let normal_map = material + .normal_texture() + .map(|info| Self::texture_id(&info.texture())); + + MaterialLoadInfo { + name, + uniforms: MaterialUniforms { + base_color: pbr.base_color_factor(), + metalness_factor: pbr.metallic_factor(), + roughness_factor: pbr.roughness_factor(), + }, + base_color_map, + roughness_map, + normal_map, + } + }) + .collect()) + } + + fn load_texture(&self, path: &Path) -> Result { + todo!() } } diff --git a/src/assets/loaders/material/gltf_mat_loader.rs b/src/assets/loaders/material/gltf_mat_loader.rs deleted file mode 100644 index 814240b..0000000 --- a/src/assets/loaders/material/gltf_mat_loader.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::assets::loaders::material::mat_loader::{MaterialFileLoader, MaterialInfo, MaterialUniforms}; -use gltf::image::Source; -use gltf::texture::Texture; -use std::path::Path; - -pub struct GltfMaterialLoader; - -impl GltfMaterialLoader { - /// Returns a stable identifier for a texture's image, meant to be resolved by a texture - /// loader later. External images are identified by their URI; images embedded in the file - /// itself (e.g. in a `.glb`) have no URI, so they're identified by their image index instead. - fn texture_id(texture: &Texture) -> String { - let image = texture.source(); - match image.source() { - Source::Uri { uri, .. } => uri.to_string(), - Source::View { .. } => format!("embedded_image_{}", image.index()), - } - } -} - -impl MaterialFileLoader for GltfMaterialLoader { - fn load_materials(&self, path: &Path) -> anyhow::Result> { - let (document, _buffers, _images) = gltf::import(path)?; - - // Collected in document order, so a material's position here matches the - // `primitive.material().index()` that mesh loading recorded for its submeshes. - Ok(document - .materials() - .enumerate() - .map(|(index, material)| { - let pbr = material.pbr_metallic_roughness(); - - let name = material - .name() - .map(str::to_string) - .unwrap_or_else(|| format!("material_{index}")); - - let base_color_map = pbr - .base_color_texture() - .map(|info| Self::texture_id(&info.texture())); - let roughness_map = pbr - .metallic_roughness_texture() - .map(|info| Self::texture_id(&info.texture())); - let normal_map = material - .normal_texture() - .map(|info| Self::texture_id(&info.texture())); - - MaterialInfo { - name, - uniforms: MaterialUniforms { - base_color: pbr.base_color_factor(), - metalness_factor: pbr.metallic_factor(), - roughness_factor: pbr.roughness_factor(), - }, - base_color_map, - roughness_map, - normal_map, - } - }) - .collect()) - } -} diff --git a/src/assets/loaders/material/mat_loader.rs b/src/assets/loaders/material/mat_loader.rs deleted file mode 100644 index a7b4789..0000000 --- a/src/assets/loaders/material/mat_loader.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::assets::loaders::material::gltf_mat_loader::GltfMaterialLoader; -use anyhow::Result; -use std::collections::HashMap; -use std::path::Path; -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum MaterialLoaderError { - #[error("file extension is missing")] - MissingExtension, - #[error("extension {0} is not supported")] - ExtensionNotSupported(String) -} - -/// A material loader struct that handles multiple different file types. -/// List of supported and planned file formats: -/// - `.gltf` -/// - `.mtl` (planned, not implemented) -pub struct MaterialLoader { - loaders: HashMap>, -} - -impl MaterialLoader { - /// Creates a new `MaterialLoader` struct. - pub fn new() -> Self { - let mut loaders = HashMap::new(); - loaders.insert("gltf".into(), Box::new(GltfMaterialLoader) as Box); - - Self { loaders } - } - - /// Loads all materials from the given path. Refer to the [`MaterialLoader`] for more information - /// on supported material files and which files to target. - pub fn load_materials>(&mut self, path: P) -> Result> { - let path = path.as_ref(); - let file_ext = path.extension().and_then(|ext| ext.to_str()) - .ok_or(MaterialLoaderError::MissingExtension)?; - - self.loaders.get(file_ext) - .ok_or(MaterialLoaderError::ExtensionNotSupported(file_ext.to_string()))? - .load_materials(path) - } -} - -/// Contains all information required for the application to load a new material. -pub struct MaterialInfo { - /// The ID of this material - pub name: String, - /// The material uniforms. These should be defined on every imported material. - pub uniforms: MaterialUniforms, - /// The ID of the base color map, if any. - pub base_color_map: Option, - /// The ID of the roughness map, if any. This is ignored for now. - pub roughness_map: Option, - /// The ID of the normal map, if any. This is ignored for now. - pub normal_map: Option, -} - -/// A struct composed of the material uniforms. These uniforms will typically scale the material -/// texture maps if there are any. Refer to [`MaterialInfo`] for more information. -pub struct MaterialUniforms { - /// The base color of the material. This scales the base color texture, if any. - /// This value must be specified in normalized RGBA format (all values between 0 and 1). - pub base_color: [f32; 4], - /// The metalness of the material. This scales the metalness texture, if any. This value should - /// be specified as a normalized float between 0 and 1, based on an 8-bit number space. - /// - /// This is ignored until the shaders are updated. - pub metalness_factor: f32, - /// The roughness of the material. This scales the roughness texture, if any. This value should - /// be specified as a normalized float between 0 and 1, based on an 8-bit number space. - /// - /// This is ignored until the shaders are updated. - pub roughness_factor: f32, -} - -/// A trait that must be implemented on all material loaders. Ensures consistent material -/// import/load information. -pub trait MaterialFileLoader { - fn load_materials(&self, path: &Path) -> anyhow::Result>; -} diff --git a/src/assets/loaders/material/mod.rs b/src/assets/loaders/material/mod.rs deleted file mode 100644 index d88479d..0000000 --- a/src/assets/loaders/material/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod mat_loader; -pub mod gltf_mat_loader; diff --git a/src/assets/loaders/mesh/mesh_loader.rs b/src/assets/loaders/mesh/mesh_loader.rs deleted file mode 100644 index d87a40c..0000000 --- a/src/assets/loaders/mesh/mesh_loader.rs +++ /dev/null @@ -1,126 +0,0 @@ -use super::gltf_loader::*; -use crate::assets::loaders::mesh::obj_loader::ObjLoader; -use anyhow::Result; -use std::collections::HashMap; -use std::path::Path; -use thiserror::Error; - -pub struct MeshInfo { - /// The mesh's vertices. - pub vertices: Vec<[f32; 3]>, - /// The mesh's vertex normals. - pub normals: Vec<[f32; 3]>, - /// The mesh's vertex indices. - pub indices: Vec, - /// The texture coordinates. Note that these may also be used on 3D textures. - pub uvs: Vec<[f32; 3]>, - /// The contiguous ranges of `indices` that should be drawn with a single material, in the - /// order they were encountered in the source file. - pub submeshes: Vec, -} - -/// A contiguous range within a [`MeshInfo`]'s `indices`, along with the material it should be -/// drawn with. Ranges are local to a single loaded file - a [`crate::common::mesh::MeshRegistry`] -/// rewrites these into offsets within its own shared index buffer. -#[derive(Debug, Clone)] -pub struct Submesh { - /// The index of the material this submesh uses, as defined by the source file (e.g. a glTF - /// material index). `None` if the file did not assign a material to this range. - pub material_index: Option, - /// The offset of this submesh's first index within `MeshInfo::indices`. - pub index_start: usize, - /// The number of indices that make up this submesh. - pub index_count: usize, -} - -pub trait MeshFileLoader { - fn load_mesh(&self, path: &Path) -> Result; -} - -#[derive(Error, Debug)] -pub enum MeshLoaderError { - #[error("file extension is missing")] - MissingExtension, - #[error("extension {0} is not supported")] - ExtensionNotSupported(String), -} - -/// A mesh loader struct that handles multiple different file types. The list of supported and -/// planned file formats follows: -/// - `.gltf` -/// - `.obj` -pub struct MeshLoader { - /// The list of mesh loaders. - loaders: HashMap>, -} - -impl MeshLoader { - /// Creates a new `MeshLoader` instance. - pub fn new() -> Self { - let mut loaders = HashMap::new(); - loaders.insert( - "gltf".into(), - Box::new(GltfLoader) as Box, - ); - loaders.insert( - "glb".into(), - Box::new(GltfLoader) as Box, - ); - loaders.insert("obj".into(), Box::new(ObjLoader)); - - Self { loaders } - } - - /// Loads a mesh from the given path. Note that materials are not automatically loaded through - /// this function, and must be manually loaded. Refer to the [`MeshLoader`] for more information - /// on what file types are supported. - pub fn load_mesh>(&self, path: &P) -> Result { - let ext = path - .as_ref() - .extension() - .and_then(|ext| ext.to_str()) - .ok_or(MeshLoaderError::MissingExtension)?; - - println!("Found extension {}, continuing with loading", ext); - - self.loaders - .get(ext) - .ok_or(MeshLoaderError::ExtensionNotSupported(ext.to_string()))? - .load_mesh(path.as_ref()) - } -} - -#[cfg(test)] -pub mod test_load_meshes { - use super::*; - - fn load_gltf(loader: &MeshLoader) -> Result { - loader.load_mesh(&Path::new("../../../meshes/laat_gltf/scene.gltf")) - } - - fn load_obj(loader: &MeshLoader) -> Result { - loader.load_mesh(&Path::new("../../../meshes/laat_gunship/scene.obj")) - } - - #[test] - fn compare_obj_gltf() -> Result<()> { - // Create the mesh loader - let loader = MeshLoader::new(); - - let obj = load_obj(&loader)?; - let gltf = load_gltf(&loader)?; - - // Compare mesh information between the two MeshInfo structs. - obj.vertices.iter() - .zip(gltf.vertices.iter()) - .for_each(|(&a, &b)| assert_eq!(a, b)); - obj.indices.iter() - .zip(gltf.indices.iter()) - .for_each(|(&a, &b)| assert_eq!(a, b)); - obj.normals.iter() - .zip(gltf.normals.iter()) - .for_each(|(&a, &b)| assert_eq!(a, b)); - - Ok(()) - } -} diff --git a/src/assets/loaders/mesh/mod.rs b/src/assets/loaders/mesh/mod.rs deleted file mode 100644 index a29e1a2..0000000 --- a/src/assets/loaders/mesh/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod gltf_loader; -pub mod mesh_loader; -mod obj_loader; diff --git a/src/assets/loaders/mod.rs b/src/assets/loaders/mod.rs index 266a8cb..09ebefa 100644 --- a/src/assets/loaders/mod.rs +++ b/src/assets/loaders/mod.rs @@ -1,4 +1,4 @@ -pub mod mesh; -pub mod material; - -pub use mesh::*; \ No newline at end of file +pub mod model_loader; +pub mod gltf_loader; +mod obj_loader; +pub mod common; diff --git a/src/assets/loaders/model_loader.rs b/src/assets/loaders/model_loader.rs new file mode 100644 index 0000000..50fdb5b --- /dev/null +++ b/src/assets/loaders/model_loader.rs @@ -0,0 +1,49 @@ +use anyhow::Result; +use std::collections::HashMap; +use std::path::Path; +use crate::assets::loaders::common::{MaterialLoadInfo, MeshLoadInfo, TextureLoadInfo}; + +/// A trait for loading a model from a given path. +pub trait LoadModel { + /// Loads a model from the given path, returning a [`ModelLoadInformation`] if successful. + fn load_model(&self, path: &Path, options: ModelLoadOptions) -> Result; +} + +/// The options applicable to loading a model. +#[derive(Default, Copy, Clone, Debug)] +pub struct ModelLoadOptions { + /// Whether to load the full mesh. + pub load_mesh: bool, + /// Whether to load all materials from the file. + pub load_materials: bool, + /// Whether to load all textures from the file. + pub load_textures: bool, +} + +/// Contains the information about the mesh, materials, and textures that this model has loaded. +/// Each type of information will only be loaded if the caller indicates that they should be. +pub struct ModelLoadInfo { + /// The loaded mesh, if applicable. + pub mesh: Option, + /// The list of loaded materials, if applicable. + pub materials: Option>, + /// The list of loaded textures, if applicable. + pub textures: Option>, +} + +/// A model loader struct that handles loading model information for various file types. +pub struct ModelLoader { + /// The list of loaders, keyed by file type extension. + loaders: HashMap> +} + +impl ModelLoader { + /// Creates a new `ModelLoader` instance. + pub fn new() -> Self { + let mut loaders = HashMap::new(); + + Self { + loaders, + } + } +} diff --git a/src/assets/loaders/mesh/obj_loader.rs b/src/assets/loaders/obj_loader.rs similarity index 82% rename from src/assets/loaders/mesh/obj_loader.rs rename to src/assets/loaders/obj_loader.rs index 463b270..440110b 100644 --- a/src/assets/loaders/mesh/obj_loader.rs +++ b/src/assets/loaders/obj_loader.rs @@ -1,11 +1,11 @@ -use crate::assets::loaders::mesh_loader::{MeshFileLoader, MeshInfo, Submesh}; +use crate::assets::loaders::mesh_loader::{LoadMesh, MeshLoadInfo, Submesh}; use std::path::Path; use wavefront::Obj; pub struct ObjLoader; -impl MeshFileLoader for ObjLoader { - fn load_mesh(&self, path: &Path) -> anyhow::Result { +impl LoadMesh for ObjLoader { + fn load_mesh(&self, path: &Path) -> anyhow::Result { let obj_info = Obj::from_file(path).expect("Couldn't read wavefront file"); let index_list = obj_info @@ -25,7 +25,7 @@ impl MeshFileLoader for ObjLoader { index_count: index_list.len(), }]; - Ok(MeshInfo { + Ok(MeshLoadInfo { vertices: obj_info.positions().to_vec(), normals: obj_info.normals().to_vec(), indices: index_list, diff --git a/src/common/material.rs b/src/common/material.rs index 40649a8..e0bc005 100644 --- a/src/common/material.rs +++ b/src/common/material.rs @@ -1,4 +1,4 @@ -use crate::assets::loaders::material::mat_loader::MaterialInfo; +use crate::assets::loaders::mat_loader::MaterialLoadInfo; use std::collections::HashMap; pub struct MaterialRegistry { @@ -36,11 +36,11 @@ pub struct Material { pub base_color_texture: Option, } -impl From for Material { +impl From for Material { /// Converts loader output into a storable `Material`. `MaterialInfo::roughness_map` has no /// equivalent here yet - glTF packs it with the metallic map, and there's no texture loading /// in place to make use of either. - fn from(info: MaterialInfo) -> Self { + fn from(info: MaterialLoadInfo) -> Self { Self { base_color: info.uniforms.base_color, metallic: info.uniforms.metalness_factor, diff --git a/src/common/mesh.rs b/src/common/mesh.rs index 816ff4b..e7075cd 100644 --- a/src/common/mesh.rs +++ b/src/common/mesh.rs @@ -1,4 +1,4 @@ -use crate::assets::loaders::mesh_loader::MeshInfo; +use crate::assets::loaders::mesh_loader::MeshLoadInfo; use crate::gpu::vertex3::Vertex3; use std::collections::HashMap; use std::sync::Arc; @@ -84,7 +84,7 @@ impl MeshRegistry { /// material has actually been registered under that id yet. fn append_mesh( mesh_id: &str, - info: MeshInfo, + info: MeshLoadInfo, verts: &mut Vec, indices: &mut Vec, ) -> MeshHandle { @@ -136,7 +136,7 @@ impl MeshRegistry { /// This is far less efficient than batching a whole scene through [`Self::load_scene`], since /// every call reallocates and re-uploads the full vertex/index buffers - fine for loading a /// handful of models up front, not for anything performance-sensitive. - pub fn register_mesh(&mut self, mesh_id: String, info: MeshInfo) { + pub fn register_mesh(&mut self, mesh_id: String, info: MeshLoadInfo) { let handle = Self::append_mesh( &mesh_id, info, diff --git a/src/common/mod.rs b/src/common/mod.rs index e804f93..372686f 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -4,3 +4,4 @@ pub mod material; pub mod mesh; pub mod render_batch; pub mod scene; +pub mod texture; diff --git a/src/common/texture.rs b/src/common/texture.rs new file mode 100644 index 0000000..5521f99 --- /dev/null +++ b/src/common/texture.rs @@ -0,0 +1,33 @@ +use std::collections::HashMap; +use std::sync::Arc; +use vulkano::image::view::ImageView; +use vulkano::image::sampler::Sampler; + +/// A texture handle that contains the texture's [`ImageView`] and [`Sampler`]. +pub struct TextureHandle { + /// The texture's [`ImageView`]. + pub image_view: Arc, + /// The [`Sampler`] associated with the image. This may be a shared sampler, + /// as they do not contain image-specific information. + pub sampler: Arc +} + +pub struct TextureRegistry { + textures: HashMap, +} + +impl TextureRegistry { + pub fn new() -> Self { + TextureRegistry { + textures: HashMap::new(), + } + } + + pub fn register(&mut self, id: String, texture: TextureHandle) { + self.textures.insert(id, texture); + } + + pub fn get(&self, id: &str) -> Option<&TextureHandle> { + self.textures.get(id) + } +} diff --git a/src/gpu/material.rs b/src/gpu/material.rs new file mode 100644 index 0000000..21c0717 --- /dev/null +++ b/src/gpu/material.rs @@ -0,0 +1,9 @@ +use vulkano::buffer::BufferContents; + +/// Mirrors the `MaterialPushConstants` block in `triangle_frag.glsl`. Pushed once per submesh +/// draw call, since each submesh may use a different material. +#[derive(BufferContents, Debug, Clone, Copy)] +#[repr(C)] +pub struct MaterialPushConstants { + pub base_color: [f32; 4], +}