Renamed content fields,

reworked texture indexing
master
Mark 2024-01-04 17:17:55 -08:00
parent 10f9776108
commit c382431747
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
25 changed files with 240 additions and 217 deletions

View File

@ -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

View File

@ -3,5 +3,5 @@
space.engine = 20
engine.thrust = 100
engine.flare_texture = "flare::ion"
engine.flare_sprite = "flare::ion"
steering.power = 20

View File

@ -1,5 +1,5 @@
[ship."Gypsum"]
sprite_texture = "ship::gypsum"
sprite = "ship::gypsum"
size = 100
mass = 1
hull = 200

View File

@ -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 = [

View File

@ -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

View File

@ -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;

View File

@ -17,6 +17,8 @@ readme = { workspace = true }
workspace = true
[dependencies]
galactica-packer = { workspace = true }
serde = { workspace = true }
toml = { workspace = true }
anyhow = { workspace = true }

View File

@ -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<H: std::hash::Hasher>(&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)
}

View File

@ -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<HashMap<String, ship::syntax::Ship>>,
pub system: Option<HashMap<String, system::syntax::System>>,
pub outfit: Option<HashMap<String, outfit::syntax::Outfit>>,
pub texture: Option<HashMap<String, texture::syntax::Texture>>,
pub sprite: Option<HashMap<String, sprite::syntax::Sprite>>,
pub faction: Option<HashMap<String, faction::syntax::Faction>>,
}
@ -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<part::texture::Texture>,
/// Sprites
pub sprites: Vec<part::sprite::Sprite>,
/// 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<String, handle::TextureHandle>,
sprite_index: HashMap<String, handle::SpriteHandle>,
/// The texture to use for starfield stars
starfield_handle: Option<handle::TextureHandle>,
starfield_handle: Option<handle::SpriteHandle>,
/// Keeps track of which images are in which texture
sprite_atlas: SpriteAtlas,
/// Outfits
outfits: Vec<part::outfit::Outfit>,
@ -148,6 +152,7 @@ impl Content {
pub fn load_dir(
path: PathBuf,
texture_root: PathBuf,
atlas_index: PathBuf,
starfield_texture_name: String,
) -> Result<Self> {
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];
}

View File

@ -59,9 +59,9 @@ pub struct Faction {
}
impl crate::Build for Faction {
type InputSyntax = HashMap<String, syntax::Faction>;
type InputSyntaxType = HashMap<String, syntax::Faction>;
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<String> = factions.keys().map(|x| x.to_owned()).collect();

View File

@ -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<syntax::ProjectileParticle>,
) -> Result<Option<ProjectileParticle>> {
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<String, syntax::Gun>;
type InputSyntaxType = HashMap<String, syntax::Gun>;
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,

View File

@ -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};

View File

@ -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<TextureHandle>,
pub engine_flare_sprite: Option<SpriteHandle>,
/// How much space this outfit requires
pub space: OutfitSpace,
}
impl crate::Build for Outfit {
type InputSyntax = HashMap<String, syntax::Outfit>;
type InputSyntaxType = HashMap<String, syntax::Outfit>;
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

View File

@ -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<Engine>,
pub guns: Vec<Gun>,
@ -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<String, syntax::Ship>;
type InputSyntaxType = HashMap<String, syntax::Ship>;
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,

View File

@ -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<PathBuf>,
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<PathBuf>,
/// 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<String, syntax::Texture>;
impl crate::Build for Sprite {
type InputSyntaxType = HashMap<String, syntax::Sprite>;
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
)
}

View File

@ -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<String, syntax::System>;
type InputSyntaxType = HashMap<String, syntax::System>;
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,

View File

@ -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(),

View File

@ -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();

View File

@ -33,7 +33,7 @@ impl ShipGun {
pub struct OutfitStatSum {
pub engine_thrust: f32,
pub steer_power: f32,
pub engine_flare_textures: Vec<content::TextureHandle>,
pub engine_flare_sprites: Vec<content::SpriteHandle>,
}
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,
})

View File

@ -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,
});

View File

@ -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<f32>,
pub size: f32,
pub angle: Deg<f32>,
@ -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,

View File

@ -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<f32> = 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,

View File

@ -44,7 +44,7 @@ impl ProjectileWorldObject {
let ang: Deg<f32> = 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,

View File

@ -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,

View File

@ -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,