use std::collections::HashMap; use glium::{ texture::{RawImage2d, SrgbTexture2d}, Display, Program, }; use glob::glob; pub type Textures = HashMap; #[derive(Debug)] pub struct TextureAtlas { pub texture: SrgbTexture2d, rects: HashMap, width: u32, height: u32, } pub struct AtlasUVs { pub bottom_left: [f32; 2], pub bottom_right: [f32; 2], pub top_left: [f32; 2], pub top_right: [f32; 2], } impl TextureAtlas { fn new(display: &Display) -> TextureAtlas { log::info!("Loading textures"); let paths = glob("assets/textures/**/*.png").unwrap(); let items = paths.map(|entry| { let path = entry.unwrap(); let image = image::open(&path).unwrap().to_rgba8(); let image_dimensions = image.dimensions(); let image = image.into_raw(); let name_path = path .with_extension("") .strip_prefix("assets/textures/") .unwrap() .to_owned(); let mut name = String::new(); for part in name_path.iter() { name.push_str(part.to_str().unwrap()); name.push(':'); } name.pop(); crunch::Item::new( (name, image), image_dimensions.0 as usize, image_dimensions.1 as usize, crunch::Rotation::None, ) }); log::info!("Packing textures into atlas"); let (width, height, items) = crunch::pack_into_po2(4096, items).unwrap(); let items = items.into_iter().map(|entry| { let rect = glium::Rect { left: entry.0.x as u32, bottom: entry.0.y as u32, width: entry.0.w as u32, height: entry.0.h as u32, }; let name = entry.1 .0; let image = entry.1 .1; (name, image, rect) }); let mut atlas = TextureAtlas { texture: SrgbTexture2d::empty(display, width as u32, height as u32).unwrap(), rects: HashMap::new(), width: width as u32, height: height as u32, }; for (name, image, rect) in items { atlas.texture.write( rect, RawImage2d::from_raw_rgba_reversed(&image, (rect.width, rect.height)), ); atlas.rects.insert(name, rect); } atlas } pub fn get_uv(&self, name: String) -> AtlasUVs { let rect = self.rects.get(&name).unwrap(); AtlasUVs { bottom_left: Self::coord_to_uv( rect.left, rect.bottom + rect.height, self.width, self.height, ), bottom_right: Self::coord_to_uv( rect.left + rect.width, rect.bottom + rect.height, self.width, self.height, ), top_left: Self::coord_to_uv(rect.left, rect.bottom, self.width, self.height), top_right: Self::coord_to_uv( rect.left + rect.width, rect.bottom, self.width, self.height, ), } } fn coord_to_uv(x: u32, y: u32, width: u32, height: u32) -> [f32; 2] { [x as f32 / width as f32, (y as f32 / height as f32)] } } pub type Shaders = HashMap; fn load_shaders(display: &Display) -> Shaders { log::info!("Loading shaders."); let mut shaders: Shaders = Shaders::new(); for entry in glob("assets/shaders/*").unwrap() { if let Ok(path) = entry { // Operate only on directories if path.is_file() { continue; } let vert_source = std::fs::read_to_string(path.join("vert.glsl")).unwrap(); let frag_source = std::fs::read_to_string(path.join("frag.glsl")).unwrap(); let program = Program::from_source(display, &vert_source, &frag_source, None).unwrap(); let name = path.file_name().unwrap().to_str().unwrap().to_owned(); shaders.insert(name.to_string(), program); log::info!("Loaded shader {:?} from {:?}.", name, path); } } shaders } pub struct Assets { pub texture_atlas: TextureAtlas, pub shaders: Shaders, } impl Assets { pub fn new(display: &Display) -> Assets { Assets { texture_atlas: TextureAtlas::new(display), shaders: load_shaders(display), } } }