diff --git a/.gitignore b/.gitignore index ac2fab7..73ab246 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +/cache /target -*.ignore \ No newline at end of file +*.ignore diff --git a/content/ship.toml b/content/ship.toml index 46e8030..986c34e 100644 --- a/content/ship.toml +++ b/content/ship.toml @@ -1,5 +1,5 @@ [ship."Gypsum"] -sprite = "ship::peregrine" +sprite = "ship::gypsum" size = 100 mass = 1 hull = 200 diff --git a/crates/constants/src/lib.rs b/crates/constants/src/lib.rs index b1c7a43..ece78af 100644 --- a/crates/constants/src/lib.rs +++ b/crates/constants/src/lib.rs @@ -1,6 +1,7 @@ #![warn(missing_docs)] //! Compile-time parameters +// TODO: many of these should be moved to a config file or cli option /// Minimum zoom level pub const ZOOM_MIN: f32 = 200.0; @@ -61,3 +62,6 @@ pub const SPRITE_LIMIT: u32 = 1024; /// The maximum number of images we can load pub const IMAGE_LIMIT: u32 = 1024; + +/// Where we should look for packed assets +pub const ASSET_CACHE: &'static str = "./cache"; diff --git a/crates/content/src/lib.rs b/crates/content/src/lib.rs index 2eb45f7..b9197c0 100644 --- a/crates/content/src/lib.rs +++ b/crates/content/src/lib.rs @@ -255,6 +255,11 @@ impl Content { return &self.sprites[h.index as usize]; } + /// Get the list of atlas files we may use + pub fn atlas_files(&self) -> &Vec { + return &self.sprite_atlas.atlas_list; + } + /// Get a sprite from a path pub fn get_image(&self, p: &Path) -> &SpriteAtlasImage { self.sprite_atlas.index.get(p).unwrap() diff --git a/crates/galactica/src/main.rs b/crates/galactica/src/main.rs index c877da0..b874b77 100644 --- a/crates/galactica/src/main.rs +++ b/crates/galactica/src/main.rs @@ -4,9 +4,12 @@ mod inputstatus; pub use galactica_content as content; -use anyhow::Result; -use galactica_constants; -use std::path::PathBuf; +use anyhow::{bail, Result}; +use galactica_constants::{self, ASSET_CACHE}; +use std::{ + fs, + path::{Path, PathBuf}, +}; use winit::{ event::{Event, KeyboardInput, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -14,11 +17,20 @@ use winit::{ }; fn main() -> Result<()> { - // TODO: error if missing + let cache_dir = Path::new(ASSET_CACHE); + fs::create_dir_all(cache_dir)?; + + let atlas_index = cache_dir.join("spriteatlas.toml"); + + if !atlas_index.exists() { + bail!("Could not find sprite atlas!"); + } + + // TODO: pretty error if missing let content = content::Content::load_dir( PathBuf::from(galactica_constants::CONTENT_ROOT), PathBuf::from(galactica_constants::IMAGE_ROOT), - PathBuf::from("spriteatlas.toml"), + atlas_index, galactica_constants::STARFIELD_SPRITE_NAME.to_owned(), )?; diff --git a/crates/packer/src/atlasset.rs b/crates/packer/src/atlasset.rs index ac404f6..80ee0e5 100644 --- a/crates/packer/src/atlasset.rs +++ b/crates/packer/src/atlasset.rs @@ -78,8 +78,10 @@ impl AtlasSet { pub fn write_image(&mut self, path: &Path, dim: [u32; 2]) -> Result { let mut f = File::open(&path)?; let mut bytes = Vec::new(); - f.read_to_end(&mut bytes)?; - let img = image::load_from_memory(&bytes)?; + f.read_to_end(&mut bytes) + .with_context(|| format!("While reading file `{}`", path.display()))?; + let img = image::load_from_memory(&bytes) + .with_context(|| format!("While loading file `{}`", path.display()))?; let mut x = 0; let mut y = 0; @@ -179,7 +181,7 @@ impl AtlasSet { self.index.index.insert( p.to_path_buf(), SpriteAtlasImage { - atlas: atlas_idx, + atlas: atlas_idx as u32, x: x as f32 / self.texture_width as f32, y: y as f32 / self.texture_height as f32, w: dim[0] as f32 / self.texture_width as f32, @@ -190,13 +192,17 @@ impl AtlasSet { return Ok(atlas_idx); } - pub fn save_files(self, atlas_path: F, index_path: &Path) -> Result<()> + pub fn save_files(mut self, atlas_path: F, index_path: &Path) -> Result<()> where F: Fn(usize) -> PathBuf, { // Save atlases for i in 0..self.texture_list.len() { - self.texture_list[i].save(atlas_path(i))?; + let path = atlas_path(i); + self.texture_list[i].save(&path)?; + self.index + .atlas_list + .push(path.file_name().unwrap().to_str().unwrap().to_owned()); } // Save index diff --git a/crates/packer/src/lib.rs b/crates/packer/src/lib.rs index bcc9c2e..95cc6c1 100644 --- a/crates/packer/src/lib.rs +++ b/crates/packer/src/lib.rs @@ -11,7 +11,8 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct SpriteAtlasImage { /// The index of the atlas this image is in - pub atlas: usize, + /// This is an index in SpriteAtlas.atlas_list + pub atlas: u32, /// x-position of this image /// (between 0 and 1, using wgpu texture coordinates) @@ -36,6 +37,9 @@ pub struct SpriteAtlasImage { pub struct SpriteAtlas { /// The images in this atlas pub index: HashMap, + + /// The file names of the atlas textures we've generated + pub atlas_list: Vec, } impl SpriteAtlas { @@ -43,6 +47,7 @@ impl SpriteAtlas { pub fn new() -> Self { Self { index: HashMap::new(), + atlas_list: Vec::new(), } } } diff --git a/crates/packer/src/main.rs b/crates/packer/src/main.rs index f563dc2..37d7bb4 100644 --- a/crates/packer/src/main.rs +++ b/crates/packer/src/main.rs @@ -2,16 +2,16 @@ mod atlasset; use atlasset::AtlasSet; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use image::io::Reader; use std::path::{Path, PathBuf}; use walkdir::WalkDir; // TODO: warning when images have extra transparency -// TODO: don't re-encode. Direct to gpu? -// (maybe not, tiling is slow. Make it work with files first.) -// TODO: path for atlas files // TODO: dynamic packing (for plugins) +// TODO: standalone cli (galactica should ask to run this) +// TODO: randomly assign sprites to textures, for efficiency +// TODO: group images by use case fn main() -> Result<()> { let mut files = Vec::new(); @@ -38,8 +38,11 @@ fn main() -> Result<()> { } let path = e.path().to_path_buf(); - let reader = Reader::open(&path)?; - let dim = reader.into_dimensions()?; + let reader = Reader::open(&path) + .with_context(|| format!("While reading file `{}`", path.display()))?; + let dim = reader.into_dimensions().with_context(|| { + format!("While reading dimensions of file `{}`", path.display()) + })?; files.push((path, [dim.0, dim.1])); total_dim += dim.0 as f64 * dim.1 as f64; } diff --git a/crates/render/shaders/object.wgsl b/crates/render/shaders/object.wgsl index fd382db..769de50 100644 --- a/crates/render/shaders/object.wgsl +++ b/crates/render/shaders/object.wgsl @@ -45,7 +45,7 @@ fn vertex_main( out.position = transform * vec4(vertex.position, 1.0); let t = atlas.data[animate(instance, global.current_time.x)]; - out.texture_index = u32(0); + out.texture_index = t.atlas_texture; out.texture_coords = vec2(t.xpos, t.ypos); if vertex.texture_coords.x == 1.0 { out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y); diff --git a/crates/render/shaders/particle.wgsl b/crates/render/shaders/particle.wgsl index 403dba2..5d1b43d 100644 --- a/crates/render/shaders/particle.wgsl +++ b/crates/render/shaders/particle.wgsl @@ -77,7 +77,7 @@ fn vertex_main( // Compute texture coordinates let t = atlas.data[animate(instance, age)]; - out.texture_index = u32(0); + out.texture_index = u32(t.atlas_texture); out.texture_coords = vec2(t.xpos, t.ypos); if vertex.texture_coords.x == 1.0 { out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y); diff --git a/crates/render/shaders/starfield.wgsl b/crates/render/shaders/starfield.wgsl index 599c2dc..fea16f0 100644 --- a/crates/render/shaders/starfield.wgsl +++ b/crates/render/shaders/starfield.wgsl @@ -111,7 +111,7 @@ fn vertex_main( // Starfield sprites may not be animated let i = sprites.data[global.starfield_sprite.x].first_frame; let t = atlas.data[i]; - out.texture_index = u32(0); + out.texture_index = u32(t.atlas_texture); out.texture_coords = vec2(t.xpos, t.ypos); if vertex.texture_coords.x == 1.0 { out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y); diff --git a/crates/render/shaders/ui.wgsl b/crates/render/shaders/ui.wgsl index 55b3f5c..1f80f89 100644 --- a/crates/render/shaders/ui.wgsl +++ b/crates/render/shaders/ui.wgsl @@ -49,7 +49,7 @@ fn vertex_main( // Pick texture frame let t = atlas.data[animate(instance, global.current_time.x)]; - out.texture_index = u32(0); + out.texture_index = u32(t.atlas_texture); out.texture_coords = vec2(t.xpos, t.ypos); if vertex.texture_coords.x == 1.0 { out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y); diff --git a/crates/render/src/globaluniform/atlascontent.rs b/crates/render/src/globaluniform/atlascontent.rs index 3570764..4d7e8e9 100644 --- a/crates/render/src/globaluniform/atlascontent.rs +++ b/crates/render/src/globaluniform/atlascontent.rs @@ -11,6 +11,11 @@ pub struct ImageLocation { pub ypos: f32, pub width: f32, pub height: f32, + + // The index of the texture this image is in + pub atlas_texture: u32, + + pub _padding: [f32; 3], } #[derive(Debug, Copy, Clone)] diff --git a/crates/render/src/globaluniform/globaluniform.rs b/crates/render/src/globaluniform/globaluniform.rs index 9dbaea3..ce24071 100644 --- a/crates/render/src/globaluniform/globaluniform.rs +++ b/crates/render/src/globaluniform/globaluniform.rs @@ -44,6 +44,12 @@ impl GlobalUniform { ypos: f32, width: f32, height: f32, + + atlas_texture: u32, + + padding_a: f32, + padding_b: f32, + padding_c: f32, }; "#, ); diff --git a/crates/render/src/texturearray.rs b/crates/render/src/texturearray.rs index d3cf856..1da041a 100644 --- a/crates/render/src/texturearray.rs +++ b/crates/render/src/texturearray.rs @@ -4,9 +4,10 @@ use crate::{ }; use anyhow::Result; use bytemuck::Zeroable; +use galactica_constants::ASSET_CACHE; use galactica_packer::SpriteAtlasImage; use image::GenericImageView; -use std::{fs::File, io::Read, num::NonZeroU32}; +use std::{fs::File, io::Read, num::NonZeroU32, path::Path}; use wgpu::BindGroupLayout; pub(crate) struct RawTexture { @@ -94,11 +95,19 @@ impl TextureArray { // Load all textures let mut texture_data = Vec::new(); - println!("opening image"); - let mut f = File::open("atlas-0.bmp")?; - let mut bytes = Vec::new(); - f.read_to_end(&mut bytes)?; - texture_data.push(RawTexture::from_bytes(&device, &queue, &bytes, "Atlas")?); + for a in ct.atlas_files() { + println!("opening {a}"); + let p = Path::new(ASSET_CACHE); + let mut f = File::open(p.join(a))?; + let mut bytes = Vec::new(); + f.read_to_end(&mut bytes)?; + texture_data.push(RawTexture::from_bytes( + &device, + &queue, + &bytes, + &format!("Atlas `{a}`"), + )?); + } let mut image_locations = ImageLocationArray::zeroed(); let mut sprite_data = SpriteDataArray::zeroed(); @@ -125,6 +134,8 @@ impl TextureArray { ypos: image.y, width: image.w, height: image.h, + atlas_texture: image.atlas, + _padding: Default::default(), }; image_counter += 1; } diff --git a/crates/ui/src/radar.rs b/crates/ui/src/radar.rs index a6c03e9..81af430 100644 --- a/crates/ui/src/radar.rs +++ b/crates/ui/src/radar.rs @@ -20,7 +20,7 @@ pub fn build_radar( let hide_range = 0.85; let shrink_distance = 20.0; let system_object_scale = 1.0 / 600.0; - let ship_scale = 1.0 / 10.0; + let ship_scale = 1.0 / 15.0; let (_, player_body) = physics.get_ship_body(player).unwrap(); let player_position = util::rigidbody_position(player_body);