diff --git a/crates/comet_resources/src/graphic_resource_manager.rs b/crates/comet_resources/src/graphic_resource_manager.rs index f0dda72..9198b34 100644 --- a/crates/comet_resources/src/graphic_resource_manager.rs +++ b/crates/comet_resources/src/graphic_resource_manager.rs @@ -1,248 +1,248 @@ -use std::{ - collections::HashMap, path::Path -}; +use std::{collections::HashMap, path::Path}; -use wgpu::{naga, Device, FilterMode, Queue, ShaderModule, TextureFormat, TextureUsages}; -use wgpu::naga::ShaderStage; -use comet_log::info; -use crate::{font, texture, Texture}; use crate::font::Font; use crate::texture_atlas::{TextureAtlas, TextureRegion}; +use crate::{font, texture, Texture}; +use comet_log::info; +use wgpu::naga::ShaderStage; +use wgpu::{naga, Device, FilterMode, Queue, ShaderModule, TextureFormat, TextureUsages}; pub struct GraphicResourceManager { - texture_atlas: TextureAtlas, - fonts: Vec, - data_files: HashMap, - shaders: HashMap + texture_atlas: TextureAtlas, + fonts: Vec, + data_files: HashMap, + shaders: HashMap, } impl GraphicResourceManager { - pub fn new() -> Self { - Self { - texture_atlas: TextureAtlas::empty(), - fonts: Vec::new(), - data_files: HashMap::new(), - shaders: HashMap::new() - } - } + pub fn new() -> Self { + Self { + texture_atlas: TextureAtlas::empty(), + fonts: Vec::new(), + data_files: HashMap::new(), + shaders: HashMap::new(), + } + } - pub fn texture_atlas(&self) -> &TextureAtlas { - &self.texture_atlas - } + pub fn texture_atlas(&self) -> &TextureAtlas { + &self.texture_atlas + } - pub fn texture_locations(&self) -> &HashMap { - &self.texture_atlas.textures() - } + pub fn texture_locations(&self) -> &HashMap { + &self.texture_atlas.textures() + } - pub fn data_files(&self) -> &HashMap { - &self.data_files - } + pub fn data_files(&self) -> &HashMap { + &self.data_files + } - pub fn fonts(&self) -> &Vec { - &self.fonts - } + pub fn fonts(&self) -> &Vec { + &self.fonts + } - pub fn get_glyph(&self, font: &str, ch: char) -> Option<&TextureRegion> { - self.fonts.iter().find(|f| f.name() == font).and_then(|f| f.get_glyph(ch)) - } + pub fn get_glyph(&self, font: &str, ch: char) -> Option<&TextureRegion> { + self.fonts + .iter() + .find(|f| f.name() == font) + .and_then(|f| f.get_glyph(ch)) + } - pub fn set_texture_atlas(&mut self, texture_atlas: TextureAtlas) { - self.texture_atlas = texture_atlas; + pub fn set_texture_atlas(&mut self, texture_atlas: TextureAtlas) { + self.texture_atlas = texture_atlas; - // This is just for testing purposes - //self.texture_locations.insert("normal_comet.png".to_string(), ([0,0], [15,15])); - //self.texture_locations.insert("green_comet.png".to_string(), ([0,15], [15,31])); - } + // This is just for testing purposes + //self.texture_locations.insert("normal_comet.png".to_string(), ([0,0], [15,15])); + //self.texture_locations.insert("green_comet.png".to_string(), ([0,15], [15,31])); + } - pub fn create_texture_atlas(&mut self, paths: Vec) { - self.texture_atlas = TextureAtlas::from_texture_paths(paths) - } + pub fn create_texture_atlas(&mut self, paths: Vec) { + self.texture_atlas = TextureAtlas::from_texture_paths(paths) + } - pub fn load_string(&self, file_name: &str) -> anyhow::Result { - let path = Path::new(std::env::var("OUT_DIR")?.as_str()) - .join("res") - .join(file_name); - let txt = std::fs::read_to_string(path)?; + pub fn load_string(&self, file_name: &str) -> anyhow::Result { + let base_path = std::env::var("OUT_DIR") + .map(|p| Path::new(&p).to_path_buf()) + .unwrap_or_else(|_| Path::new(".").to_path_buf()); - Ok(txt) - } + let path = base_path.join(file_name); + let txt = std::fs::read_to_string(&path) + .map_err(|e| anyhow::anyhow!("Failed to load {}: {}", path.display(), e))?; - pub fn load_binary(&self, file_name: &str) -> anyhow::Result> { - let path = Path::new(std::env::var("OUT_DIR")?.as_str()) - .join("res") - .join(file_name); - let data = std::fs::read(path)?; + Ok(txt) + } - Ok(data) - } + pub fn load_binary(&self, file_name: &str) -> anyhow::Result> { + let path = Path::new(std::env::var("OUT_DIR")?.as_str()) + .join("res") + .join(file_name); + let data = std::fs::read(path)?; - pub fn load_texture( - &self, - file_name: &str, - is_normal_map: bool, - device: &Device, - queue: &Queue, - ) -> anyhow::Result { - let data = self.load_binary(file_name)?; - Texture::from_bytes(device, queue, &data, file_name, is_normal_map) - } + Ok(data) + } - /// `file_name` is the full name, so with the extension - /// `shader_stage` is only needed if it is a GLSL shader, so default to None if it isn't GLSL - pub fn load_shader( - &mut self, - shader_stage: Option, - file_name: &str, - device: &Device - ) -> anyhow::Result<()> { - let shader_source = self.load_string(file_name)?; + pub fn load_texture( + &self, + file_name: &str, + is_normal_map: bool, + device: &Device, + queue: &Queue, + ) -> anyhow::Result { + let data = self.load_binary(file_name)?; + Texture::from_bytes(device, queue, &data, file_name, is_normal_map) + } - let module = match file_name.split('.').last() { - Some ("wgsl") => { - device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some(file_name.clone()), - source: wgpu::ShaderSource::Wgsl(shader_source.into()) - }) - }, - Some("glsl") => { - if let Some(stage) = shader_stage { - device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some(file_name.clone()), - source: wgpu::ShaderSource::Glsl { - shader: shader_source.into(), - stage, - defines: naga::FastHashMap::default() - } - }) - } - else { - return Err(anyhow::anyhow!("GLSL shader needs a stage")) - } + /// `file_name` is the full name, so with the extension + /// `shader_stage` is only needed if it is a GLSL shader, so default to None if it isn't GLSL + pub fn load_shader( + &mut self, + shader_stage: Option, + file_name: &str, + device: &Device, + ) -> anyhow::Result<()> { + let shader_source = self.load_string(file_name)?; - } - _ => return Err(anyhow::anyhow!("Unsupported shader type")), - }; + let module = match file_name.split('.').last() { + Some("wgsl") => device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some(file_name), + source: wgpu::ShaderSource::Wgsl(shader_source.into()), + }), + Some("glsl") => { + if let Some(stage) = shader_stage { + device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some(file_name), + source: wgpu::ShaderSource::Glsl { + shader: shader_source.into(), + stage, + defines: naga::FastHashMap::default(), + }, + }) + } else { + return Err(anyhow::anyhow!("GLSL shader needs a stage")); + } + } + _ => return Err(anyhow::anyhow!("Unsupported shader type")), + }; - self.shaders.insert(file_name.to_string(), module); - Ok(()) - } + self.shaders.insert(file_name.to_string(), module); + Ok(()) + } - pub fn get_shader(&self, shader: &str) -> Option<&ShaderModule> { - self.shaders.get(shader) - } + pub fn get_shader(&self, shader: &str) -> Option<&ShaderModule> { + self.shaders.get(shader) + } - pub fn load_font(&mut self, path: &str, size: f32) { - info!("Loading font: {}", path); - let font = Font::new(path, size); - info!("Font {} loaded!", font.name()); - self.fonts.push(font); - } + pub fn load_font(&mut self, path: &str, size: f32) { + info!("Loading font: {}", path); + let font = Font::new(path, size); + info!("Font {} loaded!", font.name()); + self.fonts.push(font); + } - /*pub async fn load_model( - &self, - file_name: &str, - device: &wgpu::Device, - queue: &wgpu::Queue, - layout: &wgpu::BindGroupLayout, - ) -> anyhow::Result { - let obj_text = self.load_string(file_name).await?; - let obj_cursor = Cursor::new(obj_text); - let mut obj_reader = BufReader::new(obj_cursor); + /*pub async fn load_model( + &self, + file_name: &str, + device: &wgpu::Device, + queue: &wgpu::Queue, + layout: &wgpu::BindGroupLayout, + ) -> anyhow::Result { + let obj_text = self.load_string(file_name).await?; + let obj_cursor = Cursor::new(obj_text); + let mut obj_reader = BufReader::new(obj_cursor); - let (models, obj_materials) = tobj::load_obj_buf_async( - &mut obj_reader, - &tobj::LoadOptions { - triangulate: true, - single_index: true, - ..Default::default() - }, - |p| async move { - let mat_text = self.load_string(&p).await.unwrap(); - tobj::load_mtl_buf(&mut BufReader::new(Cursor::new(mat_text))) - }, - ) - .await?; + let (models, obj_materials) = tobj::load_obj_buf_async( + &mut obj_reader, + &tobj::LoadOptions { + triangulate: true, + single_index: true, + ..Default::default() + }, + |p| async move { + let mat_text = self.load_string(&p).await.unwrap(); + tobj::load_mtl_buf(&mut BufReader::new(Cursor::new(mat_text))) + }, + ) + .await?; - let mut materials = Vec::new(); - for m in obj_materials? { - let diffuse_texture = self.load_texture(&m.diffuse_texture, false, device, queue).await?; - let normal_texture = self.load_texture(&m.normal_texture, true, device, queue).await?; - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&diffuse_texture.view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), - }, - ], - label: None, - }); + let mut materials = Vec::new(); + for m in obj_materials? { + let diffuse_texture = self.load_texture(&m.diffuse_texture, false, device, queue).await?; + let normal_texture = self.load_texture(&m.normal_texture, true, device, queue).await?; + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&diffuse_texture.view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), + }, + ], + label: None, + }); - materials.push(model::Material { - name: m.name, - diffuse_texture, - bind_group, - }); - } + materials.push(model::Material { + name: m.name, + diffuse_texture, + bind_group, + }); + } - let meshes = models - .into_iter() - .map(|m| { - let vertices = (0..m.mesh.positions.len() / 3) - .map(|i| { - if m.mesh.normals.is_empty() { - model::ModelVertex { - position: [ - m.mesh.positions[i * 3], - m.mesh.positions[i * 3 + 1], - m.mesh.positions[i * 3 + 2], - ], - tex_coords: [m.mesh.texcoords[i * 2], 1.0 - m.mesh.texcoords[i * 2 + 1]], - normal: [0.0, 0.0, 0.0], - } - } else { - model::ModelVertex { - position: [ - m.mesh.positions[i * 3], - m.mesh.positions[i * 3 + 1], - m.mesh.positions[i * 3 + 2], - ], - tex_coords: [m.mesh.texcoords[i * 2], 1.0 - m.mesh.texcoords[i * 2 + 1]], - normal: [ - m.mesh.normals[i * 3], - m.mesh.normals[i * 3 + 1], - m.mesh.normals[i * 3 + 2], - ], - } - } - }) - .collect::>(); + let meshes = models + .into_iter() + .map(|m| { + let vertices = (0..m.mesh.positions.len() / 3) + .map(|i| { + if m.mesh.normals.is_empty() { + model::ModelVertex { + position: [ + m.mesh.positions[i * 3], + m.mesh.positions[i * 3 + 1], + m.mesh.positions[i * 3 + 2], + ], + tex_coords: [m.mesh.texcoords[i * 2], 1.0 - m.mesh.texcoords[i * 2 + 1]], + normal: [0.0, 0.0, 0.0], + } + } else { + model::ModelVertex { + position: [ + m.mesh.positions[i * 3], + m.mesh.positions[i * 3 + 1], + m.mesh.positions[i * 3 + 2], + ], + tex_coords: [m.mesh.texcoords[i * 2], 1.0 - m.mesh.texcoords[i * 2 + 1]], + normal: [ + m.mesh.normals[i * 3], + m.mesh.normals[i * 3 + 1], + m.mesh.normals[i * 3 + 2], + ], + } + } + }) + .collect::>(); - let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some(&format!("{:?} Vertex Buffer", file_name)), - contents: bytemuck::cast_slice(&vertices), - usage: wgpu::BufferUsages::VERTEX, - }); - let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some(&format!("{:?} Index Buffer", file_name)), - contents: bytemuck::cast_slice(&m.mesh.indices), - usage: wgpu::BufferUsages::INDEX, - }); + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(&format!("{:?} Vertex Buffer", file_name)), + contents: bytemuck::cast_slice(&vertices), + usage: wgpu::BufferUsages::VERTEX, + }); + let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(&format!("{:?} Index Buffer", file_name)), + contents: bytemuck::cast_slice(&m.mesh.indices), + usage: wgpu::BufferUsages::INDEX, + }); - model::Mesh { - name: file_name.to_string(), - vertex_buffer, - index_buffer, - num_elements: m.mesh.indices.len() as u32, - material: m.mesh.material_id.unwrap_or(0), - } - }) - .collect::>(); + model::Mesh { + name: file_name.to_string(), + vertex_buffer, + index_buffer, + num_elements: m.mesh.indices.len() as u32, + material: m.mesh.material_id.unwrap_or(0), + } + }) + .collect::>(); - Ok(model::Model { meshes, materials }) - }*/ + Ok(model::Model { meshes, materials }) + }*/ } diff --git a/crates/comet_resources/src/texture.rs b/crates/comet_resources/src/texture.rs index ea75f1b..ca204e2 100644 --- a/crates/comet_resources/src/texture.rs +++ b/crates/comet_resources/src/texture.rs @@ -1,325 +1,310 @@ use anyhow::*; use image::{DynamicImage, GenericImageView, RgbaImage}; -use wgpu::{Device, Queue}; #[derive(Debug)] pub struct Texture { - #[allow(unused)] - pub texture: wgpu::Texture, - pub view: wgpu::TextureView, - pub sampler: wgpu::Sampler, - pub size: wgpu::Extent3d, + #[allow(unused)] + pub texture: wgpu::Texture, + pub view: wgpu::TextureView, + pub sampler: wgpu::Sampler, + pub size: wgpu::Extent3d, } impl Texture { - pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; + pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; - pub fn create_depth_texture( - device: &wgpu::Device, - config: &wgpu::SurfaceConfiguration, - label: &str, - ) -> Self { - let size = wgpu::Extent3d { - width: config.width.max(1), - height: config.height.max(1), - depth_or_array_layers: 1, - }; - let desc = wgpu::TextureDescriptor { - label: Some(label), - size, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: Self::DEPTH_FORMAT, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, - view_formats: &[Self::DEPTH_FORMAT], - }; - let texture = device.create_texture(&desc); - let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); - let sampler = device.create_sampler(&wgpu::SamplerDescriptor { - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter: wgpu::FilterMode::Linear, - min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Nearest, - compare: Some(wgpu::CompareFunction::LessEqual), - lod_min_clamp: 0.0, - lod_max_clamp: 100.0, - ..Default::default() - }); + pub fn create_depth_texture( + device: &wgpu::Device, + config: &wgpu::SurfaceConfiguration, + label: &str, + ) -> Self { + let size = wgpu::Extent3d { + width: config.width.max(1), + height: config.height.max(1), + depth_or_array_layers: 1, + }; + let desc = wgpu::TextureDescriptor { + label: Some(label), + size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: Self::DEPTH_FORMAT, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[Self::DEPTH_FORMAT], + }; + let texture = device.create_texture(&desc); + let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::FilterMode::Nearest, + compare: Some(wgpu::CompareFunction::LessEqual), + lod_min_clamp: 0.0, + lod_max_clamp: 100.0, + ..Default::default() + }); - Self { - texture, - view, - sampler, - size, // NEW! - } - } + Self { + texture, + view, + sampler, + size, + } + } - #[allow(dead_code)] - pub fn from_bytes( - device: &wgpu::Device, - queue: &wgpu::Queue, - bytes: &[u8], - label: &str, - is_normal_map: bool, - ) -> Result { - let img = image::load_from_memory(bytes)?; - Self::from_image(device, queue, &img, Some(label), is_normal_map) - } + #[allow(dead_code)] + pub fn from_bytes( + device: &wgpu::Device, + queue: &wgpu::Queue, + bytes: &[u8], + label: &str, + is_normal_map: bool, + ) -> Result { + let img = image::load_from_memory(bytes)?; + Self::from_image(device, queue, &img, Some(label), is_normal_map) + } - pub fn from_image( - device: &wgpu::Device, - queue: &wgpu::Queue, - img: &image::DynamicImage, - label: Option<&str>, - is_normal_map: bool, - ) -> Result { - let dimensions = img.dimensions(); - let rgba = img.to_rgba8(); + pub fn from_image( + device: &wgpu::Device, + queue: &wgpu::Queue, + img: &image::DynamicImage, + label: Option<&str>, + is_normal_map: bool, + ) -> Result { + let dimensions = img.dimensions(); + let rgba = img.to_rgba8(); - let format = if is_normal_map { - wgpu::TextureFormat::Rgba8Unorm - } else { - wgpu::TextureFormat::Rgba8UnormSrgb - }; - let usage = wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST; - let size = wgpu::Extent3d { - width: img.width(), - height: img.height(), - depth_or_array_layers: 1, - }; - let texture = Self::create_2d_texture( - device, - size.width, - size.height, - format, - usage, - wgpu::FilterMode::Nearest, - label, - ); + let format = if is_normal_map { + wgpu::TextureFormat::Rgba8Unorm + } else { + wgpu::TextureFormat::Rgba8UnormSrgb + }; + let usage = wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST; + let size = wgpu::Extent3d { + width: img.width(), + height: img.height(), + depth_or_array_layers: 1, + }; + let texture = Self::create_2d_texture( + device, + size.width, + size.height, + format, + usage, + wgpu::FilterMode::Nearest, + label, + ); - queue.write_texture( - wgpu::ImageCopyTexture { - aspect: wgpu::TextureAspect::All, - texture: &texture.texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - }, - &rgba, - wgpu::ImageDataLayout { - offset: 0, - bytes_per_row: Some(4 * dimensions.0), - rows_per_image: Some(dimensions.1), - }, - size, - ); + queue.write_texture( + wgpu::ImageCopyTexture { + aspect: wgpu::TextureAspect::All, + texture: &texture.texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + }, + &rgba, + wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(4 * dimensions.0), + rows_per_image: Some(dimensions.1), + }, + size, + ); - Ok(texture) - } + Ok(texture) + } - pub(crate) fn create_2d_texture( - device: &wgpu::Device, - width: u32, - height: u32, - format: wgpu::TextureFormat, - usage: wgpu::TextureUsages, - mag_filter: wgpu::FilterMode, - label: Option<&str>, - ) -> Self { - let size = wgpu::Extent3d { - width, - height, - depth_or_array_layers: 1, - }; - Self::create_texture( - device, - label, - size, - format, - usage, - wgpu::TextureDimension::D2, - mag_filter, - ) - } + pub(crate) fn create_2d_texture( + device: &wgpu::Device, + width: u32, + height: u32, + format: wgpu::TextureFormat, + usage: wgpu::TextureUsages, + mag_filter: wgpu::FilterMode, + label: Option<&str>, + ) -> Self { + let size = wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }; + Self::create_texture( + device, + label, + size, + format, + usage, + wgpu::TextureDimension::D2, + mag_filter, + ) + } - pub fn create_texture( - device: &wgpu::Device, - label: Option<&str>, - size: wgpu::Extent3d, - format: wgpu::TextureFormat, - usage: wgpu::TextureUsages, - dimension: wgpu::TextureDimension, - mag_filter: wgpu::FilterMode, - ) -> Self { - let texture = device.create_texture(&wgpu::TextureDescriptor { - label, - size, - mip_level_count: 1, - sample_count: 1, - dimension, - format, - usage, - view_formats: &[], - }); + pub fn create_texture( + device: &wgpu::Device, + label: Option<&str>, + size: wgpu::Extent3d, + format: wgpu::TextureFormat, + usage: wgpu::TextureUsages, + dimension: wgpu::TextureDimension, + mag_filter: wgpu::FilterMode, + ) -> Self { + let texture = device.create_texture(&wgpu::TextureDescriptor { + label, + size, + mip_level_count: 1, + sample_count: 1, + dimension, + format, + usage, + view_formats: &[], + }); - let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); - let sampler = device.create_sampler(&wgpu::SamplerDescriptor { - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter, - min_filter: wgpu::FilterMode::Nearest, - mipmap_filter: wgpu::FilterMode::Nearest, - ..Default::default() - }); + let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter, + min_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + ..Default::default() + }); - Self { - texture, - view, - sampler, - size, // NEW! - } - } + Self { + texture, + view, + sampler, + size, // NEW! + } + } - pub fn to_image( - &self, - device: &wgpu::Device, - queue: &wgpu::Queue, - ) -> Result { - // Size of the texture - let width = self.size.width; - let height = self.size.height; + pub fn to_image(&self, device: &wgpu::Device, queue: &wgpu::Queue) -> Result { + let width = self.size.width; + let height = self.size.height; - // Calculate the size of the texture in bytes - let texture_size_bytes = (4 * width * height) as wgpu::BufferAddress; + let texture_size_bytes = (4 * width * height) as wgpu::BufferAddress; - // Create a buffer for reading the texture data back from the GPU - let buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: Some("Texture Readback Buffer"), - size: texture_size_bytes, - usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, - mapped_at_creation: false, - }); + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Texture Readback Buffer"), + size: texture_size_bytes, + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); - // Create a command encoder to copy the texture data to the buffer - let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Texture to Buffer Encoder"), - }); + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Texture to Buffer Encoder"), + }); - // Define the copy operation from the texture to the buffer - encoder.copy_texture_to_buffer( - wgpu::ImageCopyTexture { - texture: &self.texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - aspect: wgpu::TextureAspect::All, - }, - wgpu::ImageCopyBuffer { - buffer: &buffer, - layout: wgpu::ImageDataLayout { - offset: 0, - bytes_per_row: Some(4 * width), - rows_per_image: Some(height), - }, - }, - self.size, - ); + encoder.copy_texture_to_buffer( + wgpu::ImageCopyTexture { + texture: &self.texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::ImageCopyBuffer { + buffer: &buffer, + layout: wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(4 * width), + rows_per_image: Some(height), + }, + }, + self.size, + ); - // Submit the command to the queue - queue.submit(Some(encoder.finish())); + queue.submit(Some(encoder.finish())); - // Wait for the GPU to finish the operation - let buffer_slice = buffer.slice(..); - buffer_slice.map_async(wgpu::MapMode::Read, |result| { - if let Err(e) = result { - eprintln!("Failed to map buffer: {:?}", e); - } - }); + let buffer_slice = buffer.slice(..); + buffer_slice.map_async(wgpu::MapMode::Read, |result| { + if let Err(e) = result { + eprintln!("Failed to map buffer: {:?}", e); + } + }); - // Get the buffer data - let data = buffer_slice.get_mapped_range(); + let data = buffer_slice.get_mapped_range(); - // Convert the raw data into an image::RgbaImage - let image = RgbaImage::from_raw(width, height, data.to_vec()) - .ok_or_else(|| anyhow!("Failed to create image from raw texture data"))?; + let image = RgbaImage::from_raw(width, height, data.to_vec()) + .ok_or_else(|| anyhow!("Failed to create image from raw texture data"))?; - // Unmap the buffer now that we're done with it - buffer.unmap(); + buffer.unmap(); - // Convert the RgbaImage into a DynamicImage - Ok(DynamicImage::ImageRgba8(image)) - } + Ok(DynamicImage::ImageRgba8(image)) + } } pub struct CubeTexture { - texture: wgpu::Texture, - sampler: wgpu::Sampler, - view: wgpu::TextureView, + texture: wgpu::Texture, + sampler: wgpu::Sampler, + view: wgpu::TextureView, } impl CubeTexture { - pub fn create_2d( - device: &wgpu::Device, - width: u32, - height: u32, - format: wgpu::TextureFormat, - mip_level_count: u32, - usage: wgpu::TextureUsages, - mag_filter: wgpu::FilterMode, - label: Option<&str>, - ) -> Self { - let texture = device.create_texture(&wgpu::TextureDescriptor { - label, - size: wgpu::Extent3d { - width, - height, - // A cube has 6 sides, so we need 6 layers - depth_or_array_layers: 6, - }, - mip_level_count, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format, - usage, - view_formats: &[], - }); + pub fn create_2d( + device: &wgpu::Device, + width: u32, + height: u32, + format: wgpu::TextureFormat, + mip_level_count: u32, + usage: wgpu::TextureUsages, + mag_filter: wgpu::FilterMode, + label: Option<&str>, + ) -> Self { + let texture = device.create_texture(&wgpu::TextureDescriptor { + label, + size: wgpu::Extent3d { + width, + height, + // A cube has 6 sides, so we need 6 layers + depth_or_array_layers: 6, + }, + mip_level_count, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format, + usage, + view_formats: &[], + }); - let view = texture.create_view(&wgpu::TextureViewDescriptor { - label, - dimension: Some(wgpu::TextureViewDimension::Cube), - array_layer_count: Some(6), - ..Default::default() - }); + let view = texture.create_view(&wgpu::TextureViewDescriptor { + label, + dimension: Some(wgpu::TextureViewDimension::Cube), + array_layer_count: Some(6), + ..Default::default() + }); - let sampler = device.create_sampler(&wgpu::SamplerDescriptor { - label, - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter, - min_filter: wgpu::FilterMode::Nearest, - mipmap_filter: wgpu::FilterMode::Nearest, - ..Default::default() - }); + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + label, + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter, + min_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + ..Default::default() + }); - Self { - texture, - sampler, - view, - } - } + Self { + texture, + sampler, + view, + } + } - pub fn texture(&self) -> &wgpu::Texture { - &self.texture - } + pub fn texture(&self) -> &wgpu::Texture { + &self.texture + } - pub fn view(&self) -> &wgpu::TextureView { - &self.view - } + pub fn view(&self) -> &wgpu::TextureView { + &self.view + } + + pub fn sampler(&self) -> &wgpu::Sampler { + &self.sampler + } +} - pub fn sampler(&self) -> &wgpu::Sampler { - &self.sampler - } -} \ No newline at end of file