From c382431747e0cc0c98e395e47f683e085a8b0cdd Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 4 Jan 2024 17:17:55 -0800 Subject: [PATCH] Renamed content fields, reworked texture indexing --- content/guns.toml | 8 +- content/outfits.toml | 2 +- content/ship.toml | 2 +- content/{textures.toml => sprite.toml} | 28 ++-- content/system.toml | 6 +- crates/constants/src/lib.rs | 8 +- crates/content/Cargo.toml | 2 + crates/content/src/handle.rs | 14 +- crates/content/src/lib.rs | 89 ++++++++----- crates/content/src/part/faction.rs | 4 +- crates/content/src/part/gun.rs | 34 ++--- crates/content/src/part/mod.rs | 4 +- crates/content/src/part/outfit.rs | 20 +-- crates/content/src/part/ship.rs | 20 +-- .../src/part/{texture.rs => sprite.rs} | 121 +++++++++--------- crates/content/src/part/system.rs | 18 +-- crates/galactica/src/game.rs | 6 +- crates/galactica/src/main.rs | 5 +- crates/gameobject/src/outfits.rs | 18 +-- crates/gameobject/src/system.rs | 2 +- crates/gameobject/src/systemobject.rs | 4 +- crates/ui/src/radar.rs | 34 ++--- crates/world/src/objects/projectile.rs | 2 +- crates/world/src/objects/ship.rs | 2 +- crates/world/src/world.rs | 4 +- 25 files changed, 240 insertions(+), 217 deletions(-) rename content/{textures.toml => sprite.toml} (69%) rename crates/content/src/part/{texture.rs => sprite.rs} (51%) diff --git a/content/guns.toml b/content/guns.toml index 1ee90d1..a3e20f9 100644 --- a/content/guns.toml +++ b/content/guns.toml @@ -7,8 +7,8 @@ rate = 0.2 # Random rate variation (each cooldown is +- this) rate_rng = 0.1 -# TODO: apply force on fire -projectile.sprite_texture = "projectile::blaster" +# TODO: apply force to ship on fire +projectile.sprite = "projectile::blaster" # Height of projectile in game units projectile.size = 6 projectile.size_rng = 0.0 @@ -29,13 +29,13 @@ projectile.force = 0.0 projectile.collider.ball.radius = 2.0 -projectile.impact.texture = "particle::blaster" +projectile.impact.sprite = "particle::explosion" projectile.impact.lifetime = "inherit" projectile.impact.inherit_velocity = "target" projectile.impact.size = 3.0 -projectile.expire.texture = "particle::blaster" +projectile.expire.sprite = "particle::blaster" projectile.expire.lifetime = "inherit" projectile.expire.inherit_velocity = "projectile" projectile.expire.size = 3.0 diff --git a/content/outfits.toml b/content/outfits.toml index 69452bc..8cbda0b 100644 --- a/content/outfits.toml +++ b/content/outfits.toml @@ -3,5 +3,5 @@ space.engine = 20 engine.thrust = 100 -engine.flare_texture = "flare::ion" +engine.flare_sprite = "flare::ion" steering.power = 20 diff --git a/content/ship.toml b/content/ship.toml index 0073436..986c34e 100644 --- a/content/ship.toml +++ b/content/ship.toml @@ -1,5 +1,5 @@ [ship."Gypsum"] -sprite_texture = "ship::gypsum" +sprite = "ship::gypsum" size = 100 mass = 1 hull = 200 diff --git a/content/textures.toml b/content/sprite.toml similarity index 69% rename from content/textures.toml rename to content/sprite.toml index 42b5ca9..95a8eb8 100644 --- a/content/textures.toml +++ b/content/sprite.toml @@ -1,40 +1,40 @@ -[texture."starfield"] +[sprite."starfield"] file = "starfield.png" -[texture."star::star"] +[sprite."star::star"] file = "star/B-09.png" -[texture."flare::ion"] +[sprite."flare::ion"] file = "flare/1.png" -[texture."planet::earth"] +[sprite."planet::earth"] file = "planet/earth.png" -[texture."planet::luna"] +[sprite."planet::luna"] file = "planet/luna.png" -[texture."projectile::blaster"] +[sprite."projectile::blaster"] file = "projectile/blaster.png" -[texture."ship::gypsum"] +[sprite."ship::gypsum"] file = "ship/gypsum.png" -[texture."ui::radar"] +[sprite."ui::radar"] file = "ui/radar.png" -[texture."ui::shipblip"] +[sprite."ui::shipblip"] file = "ui/ship-blip.png" -[texture."ui::planetblip"] +[sprite."ui::planetblip"] file = "ui/planet-blip.png" -[texture."ui::radarframe"] +[sprite."ui::radarframe"] file = "ui/radarframe.png" -[texture."ui::centerarrow"] +[sprite."ui::centerarrow"] file = "ui/center-arrow.png" -[texture."particle::blaster"] +[sprite."particle::blaster"] duration = 0.15 repeat = "once" frames = [ @@ -45,7 +45,7 @@ frames = [ ] -[texture."particle::explosion"] +[sprite."particle::explosion"] duration = 0.4 repeat = "once" frames = [ diff --git a/content/system.toml b/content/system.toml index f4c39ce..4debef5 100644 --- a/content/system.toml +++ b/content/system.toml @@ -1,17 +1,17 @@ [system."12 Autumn Above"] -object.star.sprite_texture = "star::star" +object.star.sprite = "star::star" object.star.position = [0.0, 0.0, 30.0] object.star.size = 2000 -object.earth.sprite_texture = "planet::earth" +object.earth.sprite = "planet::earth" object.earth.position.center = "star" object.earth.position.radius = 4000 object.earth.position.angle = 0 object.earth.position.z = 10.0 object.earth.size = 1000 -object.luna.sprite_texture = "planet::luna" +object.luna.sprite = "planet::luna" object.luna.position.center = "earth" object.luna.position.radius = 1600 object.luna.position.angle = 135 diff --git a/crates/constants/src/lib.rs b/crates/constants/src/lib.rs index 8000b68..8745938 100644 --- a/crates/constants/src/lib.rs +++ b/crates/constants/src/lib.rs @@ -35,14 +35,14 @@ pub const STARFIELD_DENSITY: f64 = 0.01; /// Must fit inside an i32 pub const STARFIELD_COUNT: u64 = (STARFIELD_SIZE as f64 * STARFIELD_DENSITY) as u64; -/// Name of starfield texture -pub const STARFIELD_TEXTURE_NAME: &'static str = "starfield"; +/// Name of starfield sprite +pub const STARFIELD_SPRITE_NAME: &'static str = "starfield"; /// Root directory of game content pub const CONTENT_ROOT: &'static str = "./content"; -/// Root directory of game textures -pub const TEXTURE_ROOT: &'static str = "./assets/render"; +/// Root directory of game images +pub const IMAGE_ROOT: &'static str = "./assets/render"; /// We can draw at most this many object sprites on the screen. pub const OBJECT_SPRITE_INSTANCE_LIMIT: u64 = 500; diff --git a/crates/content/Cargo.toml b/crates/content/Cargo.toml index c331037..5fc5ffa 100644 --- a/crates/content/Cargo.toml +++ b/crates/content/Cargo.toml @@ -17,6 +17,8 @@ readme = { workspace = true } workspace = true [dependencies] +galactica-packer = { workspace = true } + serde = { workspace = true } toml = { workspace = true } anyhow = { workspace = true } diff --git a/crates/content/src/handle.rs b/crates/content/src/handle.rs index 350ad40..715636d 100644 --- a/crates/content/src/handle.rs +++ b/crates/content/src/handle.rs @@ -7,24 +7,24 @@ //! in our code. It's managable, but the approach here is simpler and easier to understand. use std::{cmp::Eq, hash::Hash}; -/// A lightweight representation of a +/// A lightweight representation of a sprite #[derive(Debug, Clone, Copy)] -pub struct TextureHandle { - /// The index of this texture in content.textures +pub struct SpriteHandle { + /// The index of this sprite in content.sprites pub(crate) index: usize, - /// The aspect ratio of this texture (width / height) + /// The aspect ratio of this sprite (width / height) pub aspect: f32, } -impl Hash for TextureHandle { +impl Hash for SpriteHandle { fn hash(&self, state: &mut H) { self.index.hash(state) } } -impl Eq for TextureHandle {} -impl PartialEq for TextureHandle { +impl Eq for SpriteHandle {} +impl PartialEq for SpriteHandle { fn eq(&self, other: &Self) -> bool { self.index.eq(&other.index) } diff --git a/crates/content/src/lib.rs b/crates/content/src/lib.rs index 52f7591..af7f2fd 100644 --- a/crates/content/src/lib.rs +++ b/crates/content/src/lib.rs @@ -8,6 +8,7 @@ mod part; mod util; use anyhow::{Context, Result}; +use galactica_packer::{SpriteAtlas, SpriteAtlasImage}; use std::{ collections::HashMap, fs::File, @@ -17,10 +18,10 @@ use std::{ use toml; use walkdir::WalkDir; -pub use handle::{FactionHandle, GunHandle, OutfitHandle, ShipHandle, SystemHandle, TextureHandle}; +pub use handle::{FactionHandle, GunHandle, OutfitHandle, ShipHandle, SpriteHandle, SystemHandle}; pub use part::{ EnginePoint, Faction, Gun, GunPoint, ImpactInheritVelocity, Outfit, OutfitSpace, Projectile, - ProjectileCollider, ProjectileParticle, Relationship, RepeatMode, Ship, System, Texture, + ProjectileCollider, ProjectileParticle, Relationship, RepeatMode, Ship, Sprite, System, }; mod syntax { @@ -28,7 +29,7 @@ mod syntax { use serde::Deserialize; use std::{collections::HashMap, fmt::Display, hash::Hash}; - use crate::part::{faction, gun, outfit, ship, system, texture}; + use crate::part::{faction, gun, outfit, ship, sprite, system}; #[derive(Debug, Deserialize)] pub struct Root { @@ -36,7 +37,7 @@ mod syntax { pub ship: Option>, pub system: Option>, pub outfit: Option>, - pub texture: Option>, + pub sprite: Option>, pub faction: Option>, } @@ -72,7 +73,7 @@ mod syntax { ship: None, system: None, outfit: None, - texture: None, + sprite: None, faction: None, } } @@ -84,8 +85,8 @@ mod syntax { .with_context(|| "while merging systems")?; merge_hashmap(&mut self.outfit, other.outfit) .with_context(|| "while merging outfits")?; - merge_hashmap(&mut self.texture, other.texture) - .with_context(|| "while merging textures")?; + merge_hashmap(&mut self.sprite, other.sprite) + .with_context(|| "while merging sprites")?; merge_hashmap(&mut self.faction, other.faction) .with_context(|| "while merging factions")?; return Ok(()); @@ -94,10 +95,10 @@ mod syntax { } trait Build { - type InputSyntax; + type InputSyntaxType; /// Build a processed System struct from raw serde data - fn build(root: Self::InputSyntax, ct: &mut Content) -> Result<()> + fn build(root: Self::InputSyntaxType, ct: &mut Content) -> Result<()> where Self: Sized; } @@ -106,18 +107,21 @@ trait Build { #[derive(Debug)] pub struct Content { /* Configuration values */ - /// Root directory for textures - texture_root: PathBuf, - /// Name of starfield texture - starfield_texture_name: String, + /// Root directory for image + image_root: PathBuf, + /// Name of starfield sprite + starfield_sprite_name: String, - /// Textures - pub textures: Vec, + /// Sprites + pub sprites: Vec, /// Map strings to texture names. /// This is only necessary because we need to hard-code a few texture names for UI elements. - texture_index: HashMap, + sprite_index: HashMap, /// The texture to use for starfield stars - starfield_handle: Option, + starfield_handle: Option, + + /// Keeps track of which images are in which texture + sprite_atlas: SpriteAtlas, /// Outfits outfits: Vec, @@ -148,6 +152,7 @@ impl Content { pub fn load_dir( path: PathBuf, texture_root: PathBuf, + atlas_index: PathBuf, starfield_texture_name: String, ) -> Result { let mut root = syntax::Root::new(); @@ -177,23 +182,32 @@ impl Content { } } + let atlas: SpriteAtlas = { + let mut file_string = String::new(); + let _ = File::open(atlas_index)?.read_to_string(&mut file_string); + let file_string = file_string.trim(); + toml::from_str(&file_string)? + }; + let mut content = Self { + sprite_atlas: atlas, systems: Vec::new(), ships: Vec::new(), guns: Vec::new(), outfits: Vec::new(), - textures: Vec::new(), + sprites: Vec::new(), factions: Vec::new(), - texture_index: HashMap::new(), + sprite_index: HashMap::new(), starfield_handle: None, - texture_root, - starfield_texture_name, + image_root: texture_root, + starfield_sprite_name: starfield_texture_name, }; // Order here matters, usually - if root.texture.is_some() { - part::texture::Texture::build(root.texture.take().unwrap(), &mut content)?; + if root.sprite.is_some() { + part::sprite::Sprite::build(root.sprite.take().unwrap(), &mut content)?; } + if root.ship.is_some() { part::ship::Ship::build(root.ship.take().unwrap(), &mut content)?; } @@ -216,27 +230,32 @@ impl Content { // Access methods impl Content { - /// Get the texture handle for the starfield texture - pub fn get_starfield_handle(&self) -> TextureHandle { + /// Get the handle for the starfield sprite + pub fn get_starfield_handle(&self) -> SpriteHandle { match self.starfield_handle { Some(h) => h, - None => unreachable!("Starfield texture hasn't been loaded yet!"), + None => unreachable!("Starfield sprite hasn't been loaded yet!"), } } - /// Get a handle from a texture name - pub fn get_texture_handle(&self, name: &str) -> TextureHandle { - return match self.texture_index.get(name) { + /// Get a handle from a sprite name + pub fn get_sprite_handle(&self, name: &str) -> SpriteHandle { + return match self.sprite_index.get(name) { Some(s) => *s, - None => unreachable!("get_texture_handle was called with a bad handle!"), + None => unreachable!("get_sprite_handle was called with a bad name!"), }; } - /// Get a texture from a handle - pub fn get_texture(&self, h: TextureHandle) -> &Texture { + /// Get a sprite from a handle + pub fn get_sprite(&self, h: SpriteHandle) -> &Sprite { // In theory, this could fail if h has a bad index, but that shouldn't ever happen. - // The only TextureHandles that exist should be created by this crate. - return &self.textures[h.index]; + // The only handles that exist should be created by this crate. + return &self.sprites[h.index]; + } + + /// Get a sprite from a path + pub fn get_image(&self, p: &Path) -> &SpriteAtlasImage { + self.sprite_atlas.index.get(p).unwrap() } /// Get an outfit from a handle @@ -249,7 +268,7 @@ impl Content { return &self.guns[h.index]; } - /// Get a texture from a handle + /// Get a ship from a handle pub fn get_ship(&self, h: ShipHandle) -> &Ship { return &self.ships[h.index]; } diff --git a/crates/content/src/part/faction.rs b/crates/content/src/part/faction.rs index a732d68..851674e 100644 --- a/crates/content/src/part/faction.rs +++ b/crates/content/src/part/faction.rs @@ -59,9 +59,9 @@ pub struct Faction { } impl crate::Build for Faction { - type InputSyntax = HashMap; + type InputSyntaxType = HashMap; - fn build(factions: Self::InputSyntax, ct: &mut Content) -> Result<()> { + fn build(factions: Self::InputSyntaxType, ct: &mut Content) -> Result<()> { // Keeps track of position in faction array. // This lets us build FactionHandles before finishing all factions. let faction_names: Vec = factions.keys().map(|x| x.to_owned()).collect(); diff --git a/crates/content/src/part/gun.rs b/crates/content/src/part/gun.rs index 6e66369..56bc56b 100644 --- a/crates/content/src/part/gun.rs +++ b/crates/content/src/part/gun.rs @@ -3,7 +3,7 @@ use cgmath::Deg; use serde::Deserialize; use std::collections::HashMap; -use crate::{handle::TextureHandle, Content}; +use crate::{handle::SpriteHandle, Content}; use crate::OutfitSpace; @@ -23,7 +23,7 @@ pub(crate) mod syntax { #[derive(Debug, Deserialize)] pub struct Projectile { - pub sprite_texture: String, + pub sprite: String, pub size: f32, pub size_rng: f32, pub speed: f32, @@ -40,7 +40,7 @@ pub(crate) mod syntax { #[derive(Debug, Deserialize)] pub struct ProjectileParticle { - pub texture: String, + pub sprite: String, pub lifetime: ParticleLifetime, pub inherit_velocity: super::ImpactInheritVelocity, pub size: f32, @@ -110,7 +110,7 @@ pub struct Gun { #[derive(Debug, Clone)] pub struct Projectile { /// The projectile sprite - pub sprite_texture: TextureHandle, + pub sprite: SpriteHandle, /// The average size of this projectile /// (height in game units) @@ -155,9 +155,9 @@ pub struct Projectile { /// The particle a projectile will spawn when it hits something #[derive(Debug, Clone)] pub struct ProjectileParticle { - /// The texture to use for this particle. + /// The sprite to use for this particle. /// This is most likely animated. - pub texture: TextureHandle, + pub sprite: SpriteHandle, /// How many seconds this particle should live pub lifetime: f32, @@ -174,8 +174,8 @@ fn parse_projectile_particle( p: Option, ) -> Result> { if let Some(impact) = p { - let impact_texture = match ct.texture_index.get(&impact.texture) { - None => bail!("impact texture `{}` doesn't exist", impact.texture), + let impact_sprite_handle = match ct.sprite_index.get(&impact.sprite) { + None => bail!("impact sprite `{}` doesn't exist", impact.sprite), Some(t) => *t, }; @@ -183,8 +183,8 @@ fn parse_projectile_particle( syntax::ParticleLifetime::Seconds(s) => s, syntax::ParticleLifetime::Inherit(s) => { if s == "inherit" { - let t = ct.get_texture(impact_texture); - t.fps * t.frames.len() as f32 + let sprite = ct.get_sprite(impact_sprite_handle); + sprite.fps * sprite.frames.len() as f32 } else { bail!("bad impact lifetime, must be float or \"inherit\"",) } @@ -192,7 +192,7 @@ fn parse_projectile_particle( }; Ok(Some(ProjectileParticle { - texture: impact_texture, + sprite: impact_sprite_handle, lifetime: impact_lifetime, inherit_velocity: impact.inherit_velocity, size: impact.size, @@ -203,15 +203,15 @@ fn parse_projectile_particle( } impl crate::Build for Gun { - type InputSyntax = HashMap; + type InputSyntaxType = HashMap; - fn build(gun: Self::InputSyntax, ct: &mut Content) -> Result<()> { + fn build(gun: Self::InputSyntaxType, ct: &mut Content) -> Result<()> { for (gun_name, gun) in gun { - let projectile_texture = match ct.texture_index.get(&gun.projectile.sprite_texture) { + let projectile_sprite_handle = match ct.sprite_index.get(&gun.projectile.sprite) { None => bail!( - "In gun `{}`: projectile texture `{}` doesn't exist", + "In gun `{}`: projectile sprite `{}` doesn't exist", gun_name, - gun.projectile.sprite_texture + gun.projectile.sprite ), Some(t) => *t, }; @@ -229,7 +229,7 @@ impl crate::Build for Gun { rate_rng: gun.rate_rng, projectile: Projectile { force: gun.projectile.force, - sprite_texture: projectile_texture, + sprite: projectile_sprite_handle, size: gun.projectile.size, size_rng: gun.projectile.size_rng, speed: gun.projectile.speed, diff --git a/crates/content/src/part/mod.rs b/crates/content/src/part/mod.rs index eadcd43..0da4d35 100644 --- a/crates/content/src/part/mod.rs +++ b/crates/content/src/part/mod.rs @@ -5,13 +5,13 @@ pub mod gun; pub mod outfit; mod shared; pub mod ship; +pub mod sprite; pub mod system; -pub mod texture; pub use faction::{Faction, Relationship}; pub use gun::{Gun, ImpactInheritVelocity, Projectile, ProjectileCollider, ProjectileParticle}; pub use outfit::Outfit; pub use shared::OutfitSpace; pub use ship::{EnginePoint, GunPoint, Ship}; +pub use sprite::{RepeatMode, Sprite}; pub use system::{Object, System}; -pub use texture::{RepeatMode, Texture}; diff --git a/crates/content/src/part/outfit.rs b/crates/content/src/part/outfit.rs index c986233..3478bc3 100644 --- a/crates/content/src/part/outfit.rs +++ b/crates/content/src/part/outfit.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use anyhow::{bail, Result}; -use crate::{handle::TextureHandle, Content, OutfitSpace}; +use crate::{handle::SpriteHandle, Content, OutfitSpace}; pub(crate) mod syntax { use crate::part::shared; @@ -20,7 +20,7 @@ pub(crate) mod syntax { #[derive(Debug, Deserialize)] pub struct Engine { pub thrust: f32, - pub flare_texture: String, + pub flare_sprite: String, } #[derive(Debug, Deserialize)] @@ -44,37 +44,37 @@ pub struct Outfit { /// The engine flare sprite this outfit creates. /// Its location and size is determined by a ship's /// engine points. - pub engine_flare_texture: Option, + pub engine_flare_sprite: Option, /// How much space this outfit requires pub space: OutfitSpace, } impl crate::Build for Outfit { - type InputSyntax = HashMap; + type InputSyntaxType = HashMap; - fn build(outfits: Self::InputSyntax, ct: &mut Content) -> Result<()> { + fn build(outfits: Self::InputSyntaxType, ct: &mut Content) -> Result<()> { for (outfit_name, outfit) in outfits { let mut o = Self { name: outfit_name.clone(), engine_thrust: 0.0, steer_power: 0.0, - engine_flare_texture: None, + engine_flare_sprite: None, space: OutfitSpace::from(outfit.space), }; // Engine stats if let Some(engine) = outfit.engine { - let th = match ct.texture_index.get(&engine.flare_texture) { + let th = match ct.sprite_index.get(&engine.flare_sprite) { None => bail!( - "In outfit `{}`: texture `{}` doesn't exist", + "In outfit `{}`: flare sprite `{}` doesn't exist", outfit_name, - engine.flare_texture + engine.flare_sprite ), Some(t) => *t, }; o.engine_thrust = engine.thrust; - o.engine_flare_texture = Some(th); + o.engine_flare_sprite = Some(th); } // Steering stats diff --git a/crates/content/src/part/ship.rs b/crates/content/src/part/ship.rs index 91933ae..b80a90c 100644 --- a/crates/content/src/part/ship.rs +++ b/crates/content/src/part/ship.rs @@ -4,7 +4,7 @@ use anyhow::{bail, Result}; use cgmath::Point2; use nalgebra::{point, Point}; -use crate::{handle::TextureHandle, Content, OutfitSpace}; +use crate::{handle::SpriteHandle, Content, OutfitSpace}; pub(crate) mod syntax { use crate::part::shared; @@ -15,7 +15,7 @@ pub(crate) mod syntax { #[derive(Debug, Deserialize)] pub struct Ship { - pub sprite_texture: String, + pub sprite: String, pub size: f32, pub engines: Vec, pub guns: Vec, @@ -57,7 +57,7 @@ pub struct Ship { pub name: String, /// This ship's sprite - pub sprite_texture: TextureHandle, + pub sprite: SpriteHandle, /// The size of this ship. /// Measured as unrotated height, @@ -123,26 +123,26 @@ pub struct GunPoint { } impl crate::Build for Ship { - type InputSyntax = HashMap; + type InputSyntaxType = HashMap; - fn build(ship: Self::InputSyntax, ct: &mut Content) -> Result<()> { + fn build(ship: Self::InputSyntaxType, ct: &mut Content) -> Result<()> { for (ship_name, ship) in ship { - let th = match ct.texture_index.get(&ship.sprite_texture) { + let handle = match ct.sprite_index.get(&ship.sprite) { None => bail!( - "In ship `{}`: texture `{}` doesn't exist", + "In ship `{}`: sprite `{}` doesn't exist", ship_name, - ship.sprite_texture + ship.sprite ), Some(t) => *t, }; let size = ship.size; - let aspect = th.aspect; + let aspect = ct.get_sprite(handle).aspect; ct.ships.push(Self { aspect, name: ship_name, - sprite_texture: th, + sprite: handle, mass: ship.mass, space: OutfitSpace::from(ship.space), angular_drag: ship.angular_drag, diff --git a/crates/content/src/part/texture.rs b/crates/content/src/part/sprite.rs similarity index 51% rename from crates/content/src/part/texture.rs rename to crates/content/src/part/sprite.rs index 79f8749..f7b85a2 100644 --- a/crates/content/src/part/texture.rs +++ b/crates/content/src/part/sprite.rs @@ -3,7 +3,7 @@ use image::io::Reader; use serde::Deserialize; use std::{collections::HashMap, path::PathBuf}; -use crate::{handle::TextureHandle, Content}; +use crate::{handle::SpriteHandle, Content}; pub(crate) mod syntax { use serde::Deserialize; @@ -16,18 +16,18 @@ pub(crate) mod syntax { #[derive(Debug, Deserialize)] #[serde(untagged)] - pub enum Texture { - Static(StaticTexture), - Frames(FramesTexture), + pub enum Sprite { + Static(StaticSprite), + Frames(FrameSprite), } #[derive(Debug, Deserialize)] - pub struct StaticTexture { + pub struct StaticSprite { pub file: PathBuf, } #[derive(Debug, Deserialize)] - pub struct FramesTexture { + pub struct FrameSprite { pub frames: Vec, pub duration: f32, pub repeat: RepeatMode, @@ -57,90 +57,94 @@ impl RepeatMode { } } -/// Represents a texture that may be used in the game. +/// Represents a sprite that may be used in the game. #[derive(Debug, Clone)] -pub struct Texture { - /// The name of this texture +pub struct Sprite { + /// The name of this sprite pub name: String, - /// The handle for this texture - pub handle: TextureHandle, + /// This sprite's handle + pub handle: SpriteHandle, - /// The frames of this texture - /// (static textures have one frame) + /// The file names of frames of this sprite. + /// unanimated sprites have one frame. pub frames: Vec, - /// The speed of this texture's animation - /// (static textures have zero fps) + /// The speed of this sprite's animation. + /// unanimated sprites have zero fps. pub fps: f32, - /// How to replay this texture's animation + /// How to replay this sprite's animation pub repeat: RepeatMode, + + /// Aspect ratio of this sprite (width / height) + pub aspect: f32, } -impl crate::Build for Texture { - type InputSyntax = HashMap; +impl crate::Build for Sprite { + type InputSyntaxType = HashMap; - fn build(texture: Self::InputSyntax, ct: &mut Content) -> Result<()> { - for (texture_name, t) in texture { + fn build(sprites: Self::InputSyntaxType, ct: &mut Content) -> Result<()> { + for (sprite_name, t) in sprites { match t { - syntax::Texture::Static(t) => { - let file = ct.texture_root.join(t.file); + syntax::Sprite::Static(t) => { + let file = ct.image_root.join(&t.file); let reader = Reader::open(&file).with_context(|| { format!( - "Failed to read texture `{}` from file `{}`", - texture_name, - file.display() + "Failed to read file `{}` in sprite `{}`", + file.display(), + sprite_name, ) })?; let dim = reader.into_dimensions().with_context(|| { format!( - "Failed to get dimensions of texture `{}` from file `{}`", - texture_name, - file.display() + "Failed to get dimensions of file `{}` in sprite `{}`", + file.display(), + sprite_name, ) })?; - let h = TextureHandle { - index: ct.textures.len(), + let h = SpriteHandle { + index: ct.sprites.len(), aspect: dim.0 as f32 / dim.1 as f32, }; - if texture_name == ct.starfield_texture_name { + if sprite_name == ct.starfield_sprite_name { if ct.starfield_handle.is_none() { ct.starfield_handle = Some(h) } else { // This can't happen, since this is a hashmap. - unreachable!("Found two starfield textures! Something is very wrong.") + unreachable!("Found two starfield sprites! Something is very wrong.") } } - ct.texture_index.insert(texture_name.clone(), h); + ct.sprite_index.insert(sprite_name.clone(), h); - ct.textures.push(Self { - name: texture_name, - frames: vec![file], + ct.sprites.push(Self { + name: sprite_name, + frames: vec![t.file], fps: 0.0, handle: h, repeat: RepeatMode::Once, + aspect: dim.0 as f32 / dim.1 as f32, }); } - syntax::Texture::Frames(t) => { + syntax::Sprite::Frames(t) => { let mut dim = None; for f in &t.frames { - let file = ct.texture_root.join(f); + let file = ct.image_root.join(f); let reader = Reader::open(&file).with_context(|| { format!( - "Failed to read texture `{}` from file `{}`", - texture_name, - file.display() + "Failed to read file `{}` in sprite `{}`", + file.display(), + sprite_name, ) })?; let d = reader.into_dimensions().with_context(|| { format!( - "Failed to get dimensions of texture `{}` from file `{}`", - texture_name, - file.display() + "Failed to get dimensions of file `{}` in sprite `{}`", + file.display(), + sprite_name, ) })?; match dim { @@ -148,37 +152,34 @@ impl crate::Build for Texture { Some(e) => { if d != e { bail!( - "Failed to load frames of texture `{}`. Frames have different sizes `{}`", - texture_name, - file.display() + "Failed to load frames of sprite `{}` because frames have different sizes.", + sprite_name, ) } } } } + let dim = dim.unwrap(); - let h = TextureHandle { - index: ct.textures.len(), - aspect: dim.unwrap().0 as f32 / dim.unwrap().1 as f32, + let h = SpriteHandle { + index: ct.sprites.len(), + aspect: dim.0 as f32 / dim.1 as f32, }; - if texture_name == ct.starfield_texture_name { + if sprite_name == ct.starfield_sprite_name { unreachable!("Starfield texture may not be animated") } let fps = t.duration / t.frames.len() as f32; - ct.texture_index.insert(texture_name.clone(), h); - ct.textures.push(Self { - name: texture_name, - frames: t - .frames - .into_iter() - .map(|f| ct.texture_root.join(f)) - .collect(), + ct.sprite_index.insert(sprite_name.clone(), h); + ct.sprites.push(Self { + name: sprite_name, + frames: t.frames, fps, handle: h, repeat: t.repeat, + aspect: dim.0 as f32 / dim.1 as f32, }); } } @@ -187,7 +188,7 @@ impl crate::Build for Texture { if ct.starfield_handle.is_none() { bail!( "Could not find a starfield texture (name: `{}`)", - ct.starfield_texture_name + ct.starfield_sprite_name ) } diff --git a/crates/content/src/part/system.rs b/crates/content/src/part/system.rs index 3a08f43..cf186bc 100644 --- a/crates/content/src/part/system.rs +++ b/crates/content/src/part/system.rs @@ -2,7 +2,7 @@ use anyhow::{bail, Context, Result}; use cgmath::{Deg, Point3}; use std::collections::{HashMap, HashSet}; -use crate::{handle::TextureHandle, util::Polar, Content}; +use crate::{handle::SpriteHandle, util::Polar, Content}; pub(crate) mod syntax { use serde::Deserialize; @@ -17,7 +17,7 @@ pub(crate) mod syntax { #[derive(Debug, Deserialize)] pub struct Object { - pub sprite_texture: String, + pub sprite: String, pub position: Position, pub size: f32, @@ -94,7 +94,7 @@ pub struct System { #[derive(Debug)] pub struct Object { /// This object's sprite - pub sprite_texture: TextureHandle, + pub sprite: SpriteHandle, /// This object's size. /// Measured as height in game units. @@ -175,9 +175,9 @@ fn resolve_position( } impl crate::Build for System { - type InputSyntax = HashMap; + type InputSyntaxType = HashMap; - fn build(system: Self::InputSyntax, ct: &mut Content) -> Result<()> { + fn build(system: Self::InputSyntaxType, ct: &mut Content) -> Result<()> { for (system_name, system) in system { let mut objects = Vec::new(); @@ -185,17 +185,17 @@ impl crate::Build for System { let mut cycle_detector = HashSet::new(); cycle_detector.insert(label.clone()); - let th = match ct.texture_index.get(&obj.sprite_texture) { + let handle = match ct.sprite_index.get(&obj.sprite) { None => bail!( - "In system `{}`: texture `{}` doesn't exist", + "In system `{}`: sprite `{}` doesn't exist", system_name, - obj.sprite_texture + obj.sprite ), Some(t) => *t, }; objects.push(Object { - sprite_texture: th, + sprite: handle, position: resolve_position(&system.object, &obj, cycle_detector) .with_context(|| format!("In object {:#?}", label))?, size: obj.size, diff --git a/crates/galactica/src/game.rs b/crates/galactica/src/game.rs index 4b03a6c..a2e148d 100644 --- a/crates/galactica/src/game.rs +++ b/crates/galactica/src/game.rs @@ -10,7 +10,7 @@ use galactica_behavior::{behavior, ShipBehavior}; use galactica_constants; use galactica_content as content; use galactica_gameobject as object; -use galactica_render::{FrameState, ObjectSprite, ParticleBuilder, UiSprite}; +use galactica_render::{ObjectSprite, ParticleBuilder, RenderState, UiSprite}; use galactica_ui as ui; use galactica_world::{util, ShipPhysicsHandle, World}; @@ -174,8 +174,8 @@ impl Game { self.last_update = Instant::now(); } - pub fn get_frame_state(&mut self) -> FrameState { - FrameState { + pub fn get_frame_state(&mut self) -> RenderState { + RenderState { camera_pos: self.camera.pos, camera_zoom: self.camera.zoom, object_sprites: self.get_object_sprites(), diff --git a/crates/galactica/src/main.rs b/crates/galactica/src/main.rs index 24a772b..c877da0 100644 --- a/crates/galactica/src/main.rs +++ b/crates/galactica/src/main.rs @@ -17,14 +17,15 @@ fn main() -> Result<()> { // TODO: error if missing let content = content::Content::load_dir( PathBuf::from(galactica_constants::CONTENT_ROOT), - PathBuf::from(galactica_constants::TEXTURE_ROOT), + PathBuf::from(galactica_constants::IMAGE_ROOT), PathBuf::from("spriteatlas.toml"), - galactica_constants::STARFIELD_TEXTURE_NAME.to_owned(), + galactica_constants::STARFIELD_SPRITE_NAME.to_owned(), )?; let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); let mut gpu = pollster::block_on(galactica_render::GPUState::new(window, &content))?; + gpu.init(); let mut game = game::Game::new(content); gpu.update_starfield_buffer(); diff --git a/crates/gameobject/src/outfits.rs b/crates/gameobject/src/outfits.rs index 1842559..b9eb6a7 100644 --- a/crates/gameobject/src/outfits.rs +++ b/crates/gameobject/src/outfits.rs @@ -33,7 +33,7 @@ impl ShipGun { pub struct OutfitStatSum { pub engine_thrust: f32, pub steer_power: f32, - pub engine_flare_textures: Vec, + pub engine_flare_sprites: Vec, } impl OutfitStatSum { @@ -41,23 +41,23 @@ impl OutfitStatSum { Self { engine_thrust: 0.0, steer_power: 0.0, - engine_flare_textures: Vec::new(), + engine_flare_sprites: Vec::new(), } } pub fn add(&mut self, o: &content::Outfit) { self.engine_thrust += o.engine_thrust; - if let Some(t) = o.engine_flare_texture { - self.engine_flare_textures.push(t); + if let Some(t) = o.engine_flare_sprite { + self.engine_flare_sprites.push(t); }; self.steer_power += o.steer_power; } pub fn remove(&mut self, o: &content::Outfit) { self.engine_thrust -= o.engine_thrust; - if let Some(t) = o.engine_flare_texture { - self.engine_flare_textures.remove( - self.engine_flare_textures + if let Some(t) = o.engine_flare_sprite { + self.engine_flare_sprites.remove( + self.engine_flare_sprites .iter() .position(|x| *x == t) .unwrap(), @@ -185,7 +185,7 @@ impl<'a> OutfitSet { pub fn update_engine_flares(&mut self) { // TODO: better way to pick flare texture self.engine_flare_sprites.clear(); - let t = if let Some(e) = self.stats.engine_flare_textures.iter().next() { + let s = if let Some(e) = self.stats.engine_flare_sprites.iter().next() { e } else { return; @@ -200,7 +200,7 @@ impl<'a> OutfitSet { y: p.pos.y, z: 1.0, }, - texture: *t, + sprite: *s, angle: Deg(0.0), size: p.size, }) diff --git a/crates/gameobject/src/system.rs b/crates/gameobject/src/system.rs index 90fa05b..268aa0c 100644 --- a/crates/gameobject/src/system.rs +++ b/crates/gameobject/src/system.rs @@ -18,7 +18,7 @@ impl System { for o in &sys.objects { s.bodies.push(SystemObject { pos: o.position, - sprite_texture: o.sprite_texture, + sprite: o.sprite, size: o.size, angle: o.angle, }); diff --git a/crates/gameobject/src/systemobject.rs b/crates/gameobject/src/systemobject.rs index bc12a6c..abe7591 100644 --- a/crates/gameobject/src/systemobject.rs +++ b/crates/gameobject/src/systemobject.rs @@ -4,7 +4,7 @@ use galactica_content as content; use galactica_render::ObjectSprite; pub struct SystemObject { - pub sprite_texture: content::TextureHandle, + pub sprite: content::SpriteHandle, pub pos: Point3, pub size: f32, pub angle: Deg, @@ -13,7 +13,7 @@ pub struct SystemObject { impl SystemObject { pub(crate) fn get_sprite(&self) -> ObjectSprite { return ObjectSprite { - texture: self.sprite_texture, + sprite: self.sprite, pos: self.pos, angle: self.angle, size: self.size, diff --git a/crates/ui/src/radar.rs b/crates/ui/src/radar.rs index 9217e3f..a6c03e9 100644 --- a/crates/ui/src/radar.rs +++ b/crates/ui/src/radar.rs @@ -24,12 +24,12 @@ pub fn build_radar( let (_, player_body) = physics.get_ship_body(player).unwrap(); let player_position = util::rigidbody_position(player_body); - let planet_texture = ct.get_texture_handle("ui::planetblip"); - let ship_texture = ct.get_texture_handle("ui::shipblip"); - let arrow_texture = ct.get_texture_handle("ui::centerarrow"); + let planet_sprite = ct.get_sprite_handle("ui::planetblip"); + let ship_sprite = ct.get_sprite_handle("ui::shipblip"); + let arrow_sprite = ct.get_sprite_handle("ui::centerarrow"); out.push(UiSprite { - texture: ct.get_texture_handle("ui::radar"), + sprite: ct.get_sprite_handle("ui::radar"), pos: AnchoredUiPosition::NwNw(Point2 { x: 10.0, y: -10.0 }), dimensions: Point2 { x: radar_size, @@ -57,7 +57,7 @@ pub fn build_radar( continue; } out.push(UiSprite { - texture: planet_texture, + sprite: planet_sprite, pos: AnchoredUiPosition::NwC( Point2 { x: radar_size / 2.0 + 10.0, @@ -65,7 +65,7 @@ pub fn build_radar( } + (d * (radar_size / 2.0)), ), dimensions: Point2 { - x: planet_texture.aspect, + x: planet_sprite.aspect, y: 1.0, } * size, angle: o.angle, @@ -77,7 +77,7 @@ pub fn build_radar( // Draw ships for (s, r) in physics.iter_ship_body() { let ship = ct.get_ship(s.ship.handle); - let size = (ship.size * ship.sprite_texture.aspect) * ship_scale; + let size = (ship.size * ship.sprite.aspect) * ship_scale; let p = util::rigidbody_position(r); let d = (p - player_position) / radar_range; let m = d.magnitude() + (size / (2.0 * radar_size)); @@ -92,7 +92,7 @@ pub fn build_radar( let f = ct.get_faction(s.ship.faction).color; let f = [f[0], f[1], f[2], 1.0]; out.push(UiSprite { - texture: ship_texture, + sprite: ship_sprite, pos: AnchoredUiPosition::NwC( Point2 { x: radar_size / 2.0 + 10.0, @@ -100,7 +100,7 @@ pub fn build_radar( } + (d * (radar_size / 2.0)), ), dimensions: Point2 { - x: ship_texture.aspect, + x: ship_sprite.aspect, y: 1.0, } * size, angle: -angle, @@ -118,13 +118,13 @@ pub fn build_radar( let d = d * (radar_size / 2.0); let color = Some([0.3, 0.3, 0.3, 1.0]); if m < 0.8 { - let texture = ct.get_texture_handle("ui::radarframe"); + let sprite = ct.get_sprite_handle("ui::radarframe"); let dimensions = Point2 { - x: texture.aspect, + x: sprite.aspect, y: 1.0, } * 7.0f32.min((0.8 - m) * 70.0); out.push(UiSprite { - texture, + sprite, pos: AnchoredUiPosition::NwNw(Point2 { x: (radar_size / 2.0 + 10.0) - d.x, y: (radar_size / -2.0 - 10.0) + d.y, @@ -135,7 +135,7 @@ pub fn build_radar( }); out.push(UiSprite { - texture, + sprite, pos: AnchoredUiPosition::NwSw(Point2 { x: (radar_size / 2.0 + 10.0) - d.x, y: (radar_size / -2.0 - 10.0) - d.y, @@ -146,7 +146,7 @@ pub fn build_radar( }); out.push(UiSprite { - texture, + sprite, pos: AnchoredUiPosition::NwSe(Point2 { x: (radar_size / 2.0 + 10.0) + d.x, y: (radar_size / -2.0 - 10.0) - d.y, @@ -157,7 +157,7 @@ pub fn build_radar( }); out.push(UiSprite { - texture, + sprite, pos: AnchoredUiPosition::NwNe(Point2 { x: (radar_size / 2.0 + 10.0) + d.x, y: (radar_size / -2.0 - 10.0) + d.y, @@ -174,7 +174,7 @@ pub fn build_radar( if m > 200.0 { let player_angle: Deg = q.angle(Vector2 { x: 0.0, y: 1.0 }).into(); out.push(UiSprite { - texture: arrow_texture, + sprite: arrow_sprite, pos: AnchoredUiPosition::NwC( Point2 { x: radar_size / 2.0 + 10.0, @@ -182,7 +182,7 @@ pub fn build_radar( } + ((q.normalize() * 0.865) * (radar_size / 2.0)), ), dimensions: Point2 { - x: arrow_texture.aspect, + x: arrow_sprite.aspect, y: 1.0, } * 10.0, angle: -player_angle, diff --git a/crates/world/src/objects/projectile.rs b/crates/world/src/objects/projectile.rs index 58e7a14..9ef6620 100644 --- a/crates/world/src/objects/projectile.rs +++ b/crates/world/src/objects/projectile.rs @@ -44,7 +44,7 @@ impl ProjectileWorldObject { let ang: Deg = rot.angle(Vector2 { x: 1.0, y: 0.0 }).into(); ObjectSprite { - texture: self.projectile.content.sprite_texture, + sprite: self.projectile.content.sprite, pos: Point3 { x: pos.x, y: pos.y, diff --git a/crates/world/src/objects/ship.rs b/crates/world/src/objects/ship.rs index 3570601..ad1ff15 100644 --- a/crates/world/src/objects/ship.rs +++ b/crates/world/src/objects/ship.rs @@ -86,7 +86,7 @@ impl ShipWorldObject { ObjectSprite { pos: (ship_pos.x, ship_pos.y, 1.0).into(), - texture: s.sprite_texture, + sprite: s.sprite, angle: -ship_ang, size: s.size, diff --git a/crates/world/src/world.rs b/crates/world/src/world.rs index e131a5e..a70bf50 100644 --- a/crates/world/src/world.rs +++ b/crates/world/src/world.rs @@ -189,7 +189,7 @@ impl<'a> World { } }; particles.push(ParticleBuilder { - texture: x.texture, + sprite: x.sprite, pos: Point2 { x: pos.x, y: pos.y }, velocity, angle: -angle, @@ -332,7 +332,7 @@ impl<'a> World { content::ImpactInheritVelocity::Projectile => util::rigidbody_velocity(&pr), }; particles.push(ParticleBuilder { - texture: x.texture, + sprite: x.sprite, pos: Point2 { x: pos.x, y: pos.y }, velocity, angle: -angle,