diff --git a/crates/comet_renderer/src/renderer2d.rs b/crates/comet_renderer/src/renderer2d.rs index 1d1f6b5..049acd0 100644 --- a/crates/comet_renderer/src/renderer2d.rs +++ b/crates/comet_renderer/src/renderer2d.rs @@ -550,6 +550,11 @@ impl<'a> Renderer2D<'a> { self.graphic_resource_manager.texture_atlas().textures().get(&texture_path).unwrap() } + pub fn get_glyph_region(&self, glyph: char, font: String) -> &TextureRegion { + let font_atlas = self.graphic_resource_manager.fonts().iter().find(|f| f.name() == font).unwrap(); + font_atlas.get_glyph(glyph).unwrap() + } + fn create_rectangle(&self, width: f32, height: f32) -> Vec { let (bound_x, bound_y) = ((width/ self.config.width as f32) * 0.5, (height/ self.config.height as f32) * 0.5); @@ -564,7 +569,7 @@ impl<'a> Renderer2D<'a> { /// A function that allows you to set the texture atlas with a list of paths to the textures. /// The old texture atlas will be replaced with the new one. - pub fn set_texture_atlas(&mut self, paths: Vec) { + pub fn set_texture_atlas_by_paths(&mut self, paths: Vec) { self.graphic_resource_manager.create_texture_atlas(paths); self.diffuse_texture = Texture::from_image(&self.device, &self.queue, self.graphic_resource_manager.texture_atlas().atlas(), None, false).unwrap(); @@ -609,6 +614,95 @@ impl<'a> Renderer2D<'a> { self.diffuse_bind_group = diffuse_bind_group; } + fn set_texture_atlas(&mut self) { + self.diffuse_texture = Texture::from_image(&self.device, &self.queue, self.graphic_resource_manager.texture_atlas().atlas(), None, false).unwrap(); + + let texture_bind_group_layout = + self.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + label: Some("texture_bind_group_layout"), + }); + + let diffuse_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &texture_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&self.diffuse_texture.view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&self.diffuse_texture.sampler), + }, + ], + label: Some("diffuse_bind_group"), + }); + + self.diffuse_bind_group = diffuse_bind_group; + } + + fn set_font_atlas(&mut self, font: String) { + let font_atlas = self.graphic_resource_manager.fonts().iter().find(|f| f.name() == font).unwrap(); + self.diffuse_texture = Texture::from_image(&self.device, &self.queue, font_atlas.glyphs().atlas(), None, false).unwrap(); + + let texture_bind_group_layout = + self.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + label: Some("texture_bind_group_layout"), + }); + + let diffuse_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &texture_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&self.diffuse_texture.view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&self.diffuse_texture.sampler), + }, + ], + label: Some("diffuse_bind_group"), + }); + + self.diffuse_bind_group = diffuse_bind_group; + } + fn get_project_root() -> std::io::Result { let path = std::env::current_dir()?; let mut path_ancestors = path.as_path().ancestors(); @@ -634,7 +728,7 @@ impl<'a> Renderer2D<'a> { paths.push(texture_path.clone() + path.unwrap().file_name().to_str().unwrap()); } - self.set_texture_atlas(paths); + self.set_texture_atlas_by_paths(paths); } /// A function that clears the buffers and sets the vertex and index buffer of the `Renderer2D` with the given data. @@ -686,13 +780,13 @@ impl<'a> Renderer2D<'a> { self.vertex_buffer = self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Updated Vertex Buffer"), contents: bytemuck::cast_slice(&self.vertex_data), - usage: wgpu::BufferUsages::VERTEX, + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, }); self.index_buffer = self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Updated Index Buffer"), contents: bytemuck::cast_slice(&self.index_data), - usage: wgpu::BufferUsages::INDEX, + usage: wgpu::BufferUsages::INDEX | BufferUsages::COPY_DST, }); self.num_indices = self.index_data.len() as u32; @@ -706,13 +800,13 @@ impl<'a> Renderer2D<'a> { self.vertex_buffer = self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Updated Vertex Buffer"), contents: bytemuck::cast_slice(&self.vertex_data), - usage: wgpu::BufferUsages::VERTEX, + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, }); self.index_buffer = self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Updated Index Buffer"), contents: bytemuck::cast_slice(&self.index_data), - usage: wgpu::BufferUsages::INDEX, + usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST, }); self.num_indices = self.index_data.len() as u32; @@ -744,35 +838,17 @@ impl<'a> Renderer2D<'a> { } /// A function to draw text at a given position. - pub fn draw_text_at(&mut self, text: &str, position: Point3) { + pub fn draw_text_at(&mut self, text: &str, font: String, position: Point3) { + self.set_font_atlas(font.clone()); + let mut x = position.x(); let mut y = position.y(); for c in text.chars() { - let region = self.get_texture_region(c.to_string()); - let (dim_x, dim_y) = region.dimensions(); - - let (bound_x, bound_y) = - ((dim_x as f32/ self.config.width as f32) * 0.5, (dim_y as f32/ self.config.height as f32) * 0.5); - - let vertices: &mut Vec = &mut vec![ - Vertex :: new ( [-bound_x + x, bound_y + y, 0.0], [region.x0(), region.y0()], [0.0, 0.0, 0.0, 0.0] ), - Vertex :: new ( [-bound_x + x, -bound_y + y, 0.0], [region.x0(), region.y1()], [0.0, 0.0, 0.0, 0.0] ), - Vertex :: new ( [ bound_x + x, -bound_y + y, 0.0], [region.x1(), region.y1()], [0.0, 0.0, 0.0, 0.0] ) , - Vertex :: new ( [ bound_x + x, bound_y + y, 0.0], [region.x1(), region.y0()], [0.0, 0.0, 0.0, 0.0] ) - ]; - - let buffer_size = self.vertex_data.len() as u16; - - let indices: &mut Vec = &mut vec![ - 0 + buffer_size, 1 + buffer_size, 3 + buffer_size, - 1 + buffer_size, 2 + buffer_size, 3 + buffer_size - ]; - - self.push_to_buffers(vertices, indices); - - x += dim_x as f32; + self.draw_texture_at(font.clone() + &c.to_string(), Point3::new(x, y, position.z())); } + + self.set_texture_atlas(); } fn find_priority_camera(&self, cameras: Vec) -> usize { diff --git a/crates/comet_resources/src/font.rs b/crates/comet_resources/src/font.rs index 417970e..5215bd6 100644 --- a/crates/comet_resources/src/font.rs +++ b/crates/comet_resources/src/font.rs @@ -1,16 +1,17 @@ use image::{DynamicImage, Rgba, RgbaImage}; use ab_glyph::{FontArc, PxScale, ScaleFont, Glyph, point, Font as AbFont}; +use crate::texture_atlas::{TextureAtlas, TextureRegion}; pub struct Font { name: String, - glyphs: Vec, + glyphs: TextureAtlas, } impl Font { pub fn new(path: &str, size: f32) -> Self { Font { name: path.to_string(), - glyphs: Self::generate_images(path, size), + glyphs: Self::generate_atlas(path, size) } } @@ -18,31 +19,22 @@ impl Font { &self.name } - pub fn names(&self) -> Vec { - let mut names = Vec::new(); - for code_point in 0x0020..=0x007E { - if let Some(ch) = std::char::from_u32(code_point) { - names.push(ch.to_string()); - } - } - names + pub fn glyphs(&self) -> &TextureAtlas { + &self.glyphs } - pub fn glyph(&self, index: usize) -> &DynamicImage { - &self.glyphs[index] + pub fn get_glyph(&self, ch: char) -> Option<&TextureRegion> { + self.glyphs.textures().get(&ch.to_string()) } - pub fn glyphs(&self) -> Vec { - self.glyphs.clone() - } - - fn generate_images(path: &str, size: f32) -> Vec { + fn generate_atlas(path: &str, size: f32) -> TextureAtlas { let font_data = std::fs::read(path).expect("Failed to read font file"); let font = FontArc::try_from_vec(font_data).expect("Failed to load font"); let scale = PxScale::from(size); let scaled_font = font.as_scaled(scale); + let mut names = Vec::new(); let mut images = Vec::new(); for code_point in 0x0020..=0x007E { @@ -51,6 +43,8 @@ impl Font { continue; } + names.push(ch.to_string()); + let glyph = Glyph { id: font.glyph_id(ch), scale, @@ -80,6 +74,7 @@ impl Font { } } } - images + + TextureAtlas::from_textures(names, images) } } \ No newline at end of file diff --git a/crates/comet_resources/src/graphic_resource_manager.rs b/crates/comet_resources/src/graphic_resource_manager.rs index 9d716ef..c8f7b7f 100644 --- a/crates/comet_resources/src/graphic_resource_manager.rs +++ b/crates/comet_resources/src/graphic_resource_manager.rs @@ -4,12 +4,14 @@ use std::{ 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}; pub struct GraphicResourceManager { texture_atlas: TextureAtlas, - fonts: HashMap, + fonts: Vec, data_files: HashMap, shaders: HashMap } @@ -18,7 +20,7 @@ impl GraphicResourceManager { pub fn new() -> Self { Self { texture_atlas: TextureAtlas::empty(), - fonts: HashMap::new(), + fonts: Vec::new(), data_files: HashMap::new(), shaders: HashMap::new() } @@ -36,6 +38,14 @@ impl GraphicResourceManager { &self.data_files } + 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 set_texture_atlas(&mut self, texture_atlas: TextureAtlas) { self.texture_atlas = texture_atlas; @@ -122,9 +132,10 @@ impl GraphicResourceManager { } pub fn load_font(&mut self, path: &str, size: f32) { - let font = font::Font::new(path, size); - let atlas = TextureAtlas::from_textures(font.glyphs(), font.names()); - self.fonts.insert(font.name().to_string(), atlas); + info!("Loading font: {}", path); + let font = Font::new(path, size); + info!("Font {} loaded!", font.name()); + self.fonts.push(font); } /*pub async fn load_model( diff --git a/crates/comet_resources/src/texture_atlas.rs b/crates/comet_resources/src/texture_atlas.rs index 6595384..9002212 100644 --- a/crates/comet_resources/src/texture_atlas.rs +++ b/crates/comet_resources/src/texture_atlas.rs @@ -166,8 +166,8 @@ impl TextureAtlas { } pub fn from_textures( - textures: Vec, names: Vec, + textures: Vec, ) -> Self { let mut regions: HashMap = HashMap::new();