Added collision path and improved texture loading
parent
8be7cdf5a3
commit
3c62907fb6
|
@ -579,6 +579,8 @@ version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cgmath",
|
"cgmath",
|
||||||
|
"image",
|
||||||
|
"nalgebra",
|
||||||
"serde",
|
"serde",
|
||||||
"toml",
|
"toml",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
|
@ -598,8 +600,6 @@ dependencies = [
|
||||||
"pollster",
|
"pollster",
|
||||||
"rand",
|
"rand",
|
||||||
"rapier2d",
|
"rapier2d",
|
||||||
"serde",
|
|
||||||
"toml",
|
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"winit",
|
"winit",
|
||||||
|
|
|
@ -37,8 +37,6 @@ galactica-content = { path = "crates/content" }
|
||||||
|
|
||||||
# Files
|
# Files
|
||||||
image = { version = "0.24", features = ["png"] }
|
image = { version = "0.24", features = ["png"] }
|
||||||
toml = "0.8.8"
|
|
||||||
serde = { version = "1.0.193", features = ["derive"] }
|
|
||||||
# Graphics
|
# Graphics
|
||||||
winit = "0.28"
|
winit = "0.28"
|
||||||
wgpu = "0.18"
|
wgpu = "0.18"
|
||||||
|
|
2
assets
2
assets
|
@ -1 +1 @@
|
||||||
Subproject commit 3360f44d6ac26a103fddc794d46fe945b49ce27c
|
Subproject commit 711e4fd58100ee31e41c3c27273f7caa706d8d91
|
|
@ -1,7 +1,59 @@
|
||||||
[ship."Gypsum"]
|
[ship."Gypsum"]
|
||||||
sprite = "ship::gypsum"
|
sprite = "ship::gypsum"
|
||||||
size = 100
|
size = 100
|
||||||
|
aspect = 0.47
|
||||||
hull = 200
|
hull = 200
|
||||||
|
|
||||||
engines = [{ x = 0.0, y = -1.05, size = 50.0 }]
|
engines = [{ x = 0.0, y = -1.05, size = 50.0 }]
|
||||||
guns = [{ x = 0.0, y = 1 }, { x = 0.1, y = 0.80 }, { x = -0.1, y = 0.80 }]
|
guns = [{ x = 0.0, y = 1 }, { x = 0.1, y = 0.80 }, { x = -0.1, y = 0.80 }]
|
||||||
|
|
||||||
|
|
||||||
|
collision.points = [
|
||||||
|
#[rustfmt:skip],
|
||||||
|
[0.53921, 1.0000],
|
||||||
|
[0.53921, 0.29343],
|
||||||
|
[0.63725, 0.29343],
|
||||||
|
[0.63725, -0.03088],
|
||||||
|
[0.90019, -0.03088],
|
||||||
|
[0.90019, -0.61776],
|
||||||
|
[0.66666, -0.61776],
|
||||||
|
[0.66666, -0.86486],
|
||||||
|
[0.24509, -0.86486],
|
||||||
|
[0.24509, -1.00000],
|
||||||
|
|
||||||
|
[-0.24509, -1.00000],
|
||||||
|
[-0.24509, -0.86486],
|
||||||
|
[-0.66666, -0.86486],
|
||||||
|
[-0.66666, -0.61776],
|
||||||
|
[-0.90019, -0.61776],
|
||||||
|
[-0.90019, -0.03088],
|
||||||
|
[-0.63725, -0.03088],
|
||||||
|
[-0.63725, 0.29343],
|
||||||
|
[-0.53921, 0.29343],
|
||||||
|
[-0.53921, 1.0000],
|
||||||
|
]
|
||||||
|
|
||||||
|
# TODO: generate this automatically
|
||||||
|
collision.indices = [
|
||||||
|
#[rustfmt:skip],
|
||||||
|
[0, 1],
|
||||||
|
[1, 2],
|
||||||
|
[2, 3],
|
||||||
|
[3, 4],
|
||||||
|
[4, 5],
|
||||||
|
[5, 6],
|
||||||
|
[6, 7],
|
||||||
|
[7, 8],
|
||||||
|
[8, 9],
|
||||||
|
[9, 10],
|
||||||
|
[10, 11],
|
||||||
|
[11, 12],
|
||||||
|
[12, 13],
|
||||||
|
[13, 14],
|
||||||
|
[14, 15],
|
||||||
|
[15, 16],
|
||||||
|
[16, 17],
|
||||||
|
[17, 18],
|
||||||
|
[18, 19],
|
||||||
|
[19, 0],
|
||||||
|
]
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
[texture."starfield"]
|
||||||
|
path = "starfield.png"
|
||||||
|
|
||||||
|
[texture."star::star"]
|
||||||
|
path = "star/B-09.png"
|
||||||
|
|
||||||
|
[texture."flare::ion"]
|
||||||
|
path = "flare/1.png"
|
||||||
|
|
||||||
|
[texture."planet::earth"]
|
||||||
|
path = "planet/earth.png"
|
||||||
|
|
||||||
|
[texture."planet::luna"]
|
||||||
|
path = "planet/luna.png"
|
||||||
|
|
||||||
|
[texture."projectile::blaster"]
|
||||||
|
path = "projectile/blaster.png"
|
||||||
|
|
||||||
|
[texture."ship::gypsum"]
|
||||||
|
path = "ship/gypsum.png"
|
|
@ -9,3 +9,5 @@ serde = { version = "1.0.193", features = ["derive"] }
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
cgmath = "0.18.0"
|
cgmath = "0.18.0"
|
||||||
walkdir = "2.4.0"
|
walkdir = "2.4.0"
|
||||||
|
nalgebra = "0.32.3"
|
||||||
|
image = { version = "0.24", features = ["png"] }
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
use anyhow::Result;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
|
||||||
|
use crate::{Content, TextureHandle};
|
||||||
|
|
||||||
pub(super) mod syntax {
|
pub(super) mod syntax {
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -29,26 +33,30 @@ pub struct Engine {
|
||||||
/// The flare sprite this engine creates.
|
/// The flare sprite this engine creates.
|
||||||
/// Its location and size is determined by a ship's
|
/// Its location and size is determined by a ship's
|
||||||
/// engine points.
|
/// engine points.
|
||||||
pub flare_sprite: String,
|
pub flare_sprite: TextureHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Build for Engine {
|
impl super::Build for Engine {
|
||||||
fn build(root: &super::syntax::Root) -> Result<Vec<Self>> {
|
type InputSyntax = HashMap<String, syntax::Engine>;
|
||||||
let engine = if let Some(engine) = &root.engine {
|
|
||||||
engine
|
|
||||||
} else {
|
|
||||||
return Ok(vec![]);
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut out = Vec::new();
|
fn build(engine: Self::InputSyntax, ct: &mut Content) -> Result<()> {
|
||||||
for (engine_name, engine) in engine {
|
for (engine_name, engine) in engine {
|
||||||
out.push(Self {
|
let th = match ct.texture_index.get(&engine.flare.sprite) {
|
||||||
name: engine_name.to_owned(),
|
None => bail!(
|
||||||
|
"In engine `{}`: texture `{}` doesn't exist",
|
||||||
|
engine_name,
|
||||||
|
engine.flare.sprite
|
||||||
|
),
|
||||||
|
Some(t) => *t,
|
||||||
|
};
|
||||||
|
|
||||||
|
ct.engines.push(Self {
|
||||||
|
name: engine_name,
|
||||||
thrust: engine.thrust,
|
thrust: engine.thrust,
|
||||||
flare_sprite: engine.flare.sprite.clone(),
|
flare_sprite: th,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(out);
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
use anyhow::Result;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
use cgmath::Deg;
|
use cgmath::Deg;
|
||||||
|
|
||||||
|
use crate::{Content, TextureHandle};
|
||||||
|
|
||||||
pub(super) mod syntax {
|
pub(super) mod syntax {
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
// Raw serde syntax structs.
|
// Raw serde syntax structs.
|
||||||
|
@ -55,7 +59,7 @@ pub struct Gun {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Projectile {
|
pub struct Projectile {
|
||||||
/// The projectile sprite
|
/// The projectile sprite
|
||||||
pub sprite: String,
|
pub sprite: TextureHandle,
|
||||||
|
|
||||||
/// The average size of this projectile
|
/// The average size of this projectile
|
||||||
/// (height in game units)
|
/// (height in game units)
|
||||||
|
@ -79,22 +83,26 @@ pub struct Projectile {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Build for Gun {
|
impl super::Build for Gun {
|
||||||
fn build(root: &super::syntax::Root) -> Result<Vec<Self>> {
|
type InputSyntax = HashMap<String, syntax::Gun>;
|
||||||
let gun = if let Some(gun) = &root.gun {
|
|
||||||
gun
|
|
||||||
} else {
|
|
||||||
return Ok(vec![]);
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut out = Vec::new();
|
fn build(gun: Self::InputSyntax, ct: &mut Content) -> Result<()> {
|
||||||
for (gun_name, gun) in gun {
|
for (gun_name, gun) in gun {
|
||||||
out.push(Self {
|
let th = match ct.texture_index.get(&gun.projectile.sprite) {
|
||||||
name: gun_name.to_owned(),
|
None => bail!(
|
||||||
|
"In gun `{}`: texture `{}` doesn't exist",
|
||||||
|
gun_name,
|
||||||
|
gun.projectile.sprite
|
||||||
|
),
|
||||||
|
Some(t) => *t,
|
||||||
|
};
|
||||||
|
|
||||||
|
ct.guns.push(Self {
|
||||||
|
name: gun_name,
|
||||||
spread: Deg(gun.spread),
|
spread: Deg(gun.spread),
|
||||||
rate: gun.rate,
|
rate: gun.rate,
|
||||||
rate_rng: gun.rate_rng,
|
rate_rng: gun.rate_rng,
|
||||||
projectile: Projectile {
|
projectile: Projectile {
|
||||||
sprite: gun.projectile.sprite.to_owned(),
|
sprite: th,
|
||||||
size: gun.projectile.size,
|
size: gun.projectile.size,
|
||||||
size_rng: gun.projectile.size_rng,
|
size_rng: gun.projectile.size_rng,
|
||||||
speed: gun.projectile.speed,
|
speed: gun.projectile.speed,
|
||||||
|
@ -106,6 +114,6 @@ impl super::Build for Gun {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(out);
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,22 +7,30 @@ mod engine;
|
||||||
mod gun;
|
mod gun;
|
||||||
mod ship;
|
mod ship;
|
||||||
mod system;
|
mod system;
|
||||||
|
mod texture;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
pub use engine::Engine;
|
pub use engine::Engine;
|
||||||
pub use gun::{Gun, Projectile};
|
pub use gun::{Gun, Projectile};
|
||||||
pub use ship::{EnginePoint, GunPoint, Ship};
|
pub use ship::{EnginePoint, GunPoint, Ship};
|
||||||
pub use system::{Object, System};
|
pub use system::{Object, System};
|
||||||
|
pub use texture::Texture;
|
||||||
|
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use std::collections::HashMap;
|
use std::{
|
||||||
use std::{fs::File, io::Read, path::Path};
|
cmp::Eq,
|
||||||
|
collections::HashMap,
|
||||||
|
fs::File,
|
||||||
|
hash::Hash,
|
||||||
|
io::Read,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
use toml;
|
use toml;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
mod syntax {
|
mod syntax {
|
||||||
use super::HashMap;
|
use super::{bail, HashMap, Result};
|
||||||
use super::{engine, gun, ship, system};
|
use super::{engine, gun, ship, system, texture};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -31,16 +39,135 @@ mod syntax {
|
||||||
pub ship: Option<HashMap<String, ship::syntax::Ship>>,
|
pub ship: Option<HashMap<String, ship::syntax::Ship>>,
|
||||||
pub system: Option<HashMap<String, system::syntax::System>>,
|
pub system: Option<HashMap<String, system::syntax::System>>,
|
||||||
pub engine: Option<HashMap<String, engine::syntax::Engine>>,
|
pub engine: Option<HashMap<String, engine::syntax::Engine>>,
|
||||||
|
pub texture: Option<HashMap<String, texture::syntax::Texture>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Root {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
gun: None,
|
||||||
|
ship: None,
|
||||||
|
system: None,
|
||||||
|
engine: None,
|
||||||
|
texture: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merge(&mut self, other: Root) -> Result<()> {
|
||||||
|
// Insert if not exists
|
||||||
|
// TODO: replace with a macro and try_insert once that is stable
|
||||||
|
if let Some(a) = other.gun {
|
||||||
|
if self.gun.is_none() {
|
||||||
|
self.gun = Some(a);
|
||||||
|
} else {
|
||||||
|
let sg = self.gun.as_mut().unwrap();
|
||||||
|
for (k, v) in a {
|
||||||
|
if sg.contains_key(&k) {
|
||||||
|
bail!("Repeated gun name {k}");
|
||||||
|
} else {
|
||||||
|
sg.insert(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(a) = other.ship {
|
||||||
|
if self.ship.is_none() {
|
||||||
|
self.ship = Some(a);
|
||||||
|
} else {
|
||||||
|
let sg = self.ship.as_mut().unwrap();
|
||||||
|
for (k, v) in a {
|
||||||
|
if sg.contains_key(&k) {
|
||||||
|
bail!("Repeated ship name {k}");
|
||||||
|
} else {
|
||||||
|
sg.insert(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(a) = other.system {
|
||||||
|
if self.system.is_none() {
|
||||||
|
self.system = Some(a);
|
||||||
|
} else {
|
||||||
|
let sg = self.system.as_mut().unwrap();
|
||||||
|
for (k, v) in a {
|
||||||
|
if sg.contains_key(&k) {
|
||||||
|
bail!("Repeated system name {k}");
|
||||||
|
} else {
|
||||||
|
sg.insert(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(a) = other.engine {
|
||||||
|
if self.engine.is_none() {
|
||||||
|
self.engine = Some(a);
|
||||||
|
} else {
|
||||||
|
let sg = self.engine.as_mut().unwrap();
|
||||||
|
for (k, v) in a {
|
||||||
|
if sg.contains_key(&k) {
|
||||||
|
bail!("Repeated engine name {k}");
|
||||||
|
} else {
|
||||||
|
sg.insert(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(a) = other.texture {
|
||||||
|
if self.texture.is_none() {
|
||||||
|
self.texture = Some(a);
|
||||||
|
} else {
|
||||||
|
let sg = self.texture.as_mut().unwrap();
|
||||||
|
for (k, v) in a {
|
||||||
|
if sg.contains_key(&k) {
|
||||||
|
bail!("Repeated texture name {k}");
|
||||||
|
} else {
|
||||||
|
sg.insert(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Build {
|
trait Build {
|
||||||
|
type InputSyntax;
|
||||||
|
|
||||||
/// Build a processed System struct from raw serde data
|
/// Build a processed System struct from raw serde data
|
||||||
fn build(root: &syntax::Root) -> Result<Vec<Self>>
|
fn build(root: Self::InputSyntax, ct: &mut Content) -> Result<()>
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a specific texture defined in the content dir.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct TextureHandle {
|
||||||
|
/// The index of this texture in content.textures
|
||||||
|
pub index: usize,
|
||||||
|
|
||||||
|
/// The aspect ratio of this texture (width / height)
|
||||||
|
pub aspect: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for TextureHandle {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.index.hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for TextureHandle {}
|
||||||
|
impl PartialEq for TextureHandle {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.index.eq(&other.index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents generic game content, not connected to any game objects.
|
/// Represents generic game content, not connected to any game objects.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Content {
|
pub struct Content {
|
||||||
|
@ -55,24 +182,22 @@ pub struct Content {
|
||||||
|
|
||||||
/// Engine outfits
|
/// Engine outfits
|
||||||
pub engines: Vec<engine::Engine>,
|
pub engines: Vec<engine::Engine>,
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! quick_name_dup_check {
|
/// Textures
|
||||||
($array:expr, $root:ident, $build:expr) => {{
|
pub textures: Vec<texture::Texture>,
|
||||||
let mut p = $build(&$root)?;
|
|
||||||
for s in &$array {
|
/// Map strings to texture handles
|
||||||
for o in &p {
|
/// This is never used outside this crate.
|
||||||
if s.name == o.name {
|
texture_index: HashMap<String, TextureHandle>,
|
||||||
bail!(
|
|
||||||
"Error parsing content: duplicate ship names `{}` and `{}`",
|
/// The texture to use for starfield stars
|
||||||
s.name,
|
starfield_handle: Option<TextureHandle>,
|
||||||
o.name
|
|
||||||
)
|
/// Root directory for textures
|
||||||
}
|
texture_root: PathBuf,
|
||||||
}
|
|
||||||
}
|
/// Name of starfield texture
|
||||||
$array.append(&mut p);
|
starfield_texture_name: String,
|
||||||
}};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Content {
|
impl Content {
|
||||||
|
@ -83,22 +208,28 @@ impl Content {
|
||||||
return Ok(toml::from_str(&file_string)?);
|
return Ok(toml::from_str(&file_string)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_root(&mut self, root: syntax::Root) -> Result<()> {
|
/// Get the texture handle for the starfield texture
|
||||||
quick_name_dup_check!(self.systems, root, system::System::build);
|
pub fn get_starfield_handle(&self) -> TextureHandle {
|
||||||
quick_name_dup_check!(self.guns, root, gun::Gun::build);
|
match self.starfield_handle {
|
||||||
quick_name_dup_check!(self.ships, root, ship::Ship::build);
|
Some(h) => h,
|
||||||
quick_name_dup_check!(self.engines, root, engine::Engine::build);
|
None => unreachable!("Starfield texture hasn't been loaded yet!"),
|
||||||
return Ok(());
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a texture from a handle
|
||||||
|
pub fn get_texture(&self, h: TextureHandle) -> &texture::Texture {
|
||||||
|
// 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];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load content from a directory.
|
/// Load content from a directory.
|
||||||
pub fn load_dir(path: &str) -> Result<Self> {
|
pub fn load_dir(
|
||||||
let mut content = Self {
|
path: PathBuf,
|
||||||
systems: Vec::new(),
|
texture_root: PathBuf,
|
||||||
ships: Vec::new(),
|
starfield_texture_name: String,
|
||||||
guns: Vec::new(),
|
) -> Result<Self> {
|
||||||
engines: Vec::new(),
|
let mut root = syntax::Root::new();
|
||||||
};
|
|
||||||
|
|
||||||
for e in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
|
for e in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
|
||||||
if e.metadata().unwrap().is_file() {
|
if e.metadata().unwrap().is_file() {
|
||||||
|
@ -117,14 +248,43 @@ impl Content {
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = e.path();
|
let path = e.path();
|
||||||
let root = Self::try_parse(path)
|
let this_root = Self::try_parse(path)
|
||||||
.with_context(|| format!("Could not load {:#?}", e.path()))?;
|
.with_context(|| format!("Could not read {}", path.display()))?;
|
||||||
content
|
|
||||||
.add_root(root)
|
root.merge(this_root)
|
||||||
.with_context(|| format!("Could not parse {}", path.display()))?;
|
.with_context(|| format!("Could not parse {}", path.display()))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut content = Self {
|
||||||
|
systems: Vec::new(),
|
||||||
|
ships: Vec::new(),
|
||||||
|
guns: Vec::new(),
|
||||||
|
engines: Vec::new(),
|
||||||
|
textures: Vec::new(),
|
||||||
|
texture_index: HashMap::new(),
|
||||||
|
starfield_handle: None,
|
||||||
|
texture_root,
|
||||||
|
starfield_texture_name,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Order here matters, usually
|
||||||
|
if root.texture.is_some() {
|
||||||
|
texture::Texture::build(root.texture.take().unwrap(), &mut content)?;
|
||||||
|
}
|
||||||
|
if root.ship.is_some() {
|
||||||
|
ship::Ship::build(root.ship.take().unwrap(), &mut content)?;
|
||||||
|
}
|
||||||
|
if root.gun.is_some() {
|
||||||
|
gun::Gun::build(root.gun.take().unwrap(), &mut content)?;
|
||||||
|
}
|
||||||
|
if root.engine.is_some() {
|
||||||
|
engine::Engine::build(root.engine.take().unwrap(), &mut content)?;
|
||||||
|
}
|
||||||
|
if root.system.is_some() {
|
||||||
|
system::System::build(root.system.take().unwrap(), &mut content)?;
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(content);
|
return Ok(content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
use anyhow::Result;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
use cgmath::Point2;
|
use cgmath::Point2;
|
||||||
|
use nalgebra::{point, Point};
|
||||||
|
|
||||||
|
use crate::{Content, TextureHandle};
|
||||||
|
|
||||||
pub(super) mod syntax {
|
pub(super) mod syntax {
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -13,6 +18,14 @@ pub(super) mod syntax {
|
||||||
pub engines: Vec<Engine>,
|
pub engines: Vec<Engine>,
|
||||||
pub guns: Vec<Gun>,
|
pub guns: Vec<Gun>,
|
||||||
pub hull: f32,
|
pub hull: f32,
|
||||||
|
pub collision: Collision,
|
||||||
|
pub aspect: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Collision {
|
||||||
|
pub points: Vec<[f32; 2]>,
|
||||||
|
pub indices: Vec<[u32; 2]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -39,7 +52,7 @@ pub struct Ship {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
||||||
/// This ship's sprite
|
/// This ship's sprite
|
||||||
pub sprite: String,
|
pub sprite: TextureHandle,
|
||||||
|
|
||||||
/// The size of this ship.
|
/// The size of this ship.
|
||||||
/// Measured as unrotated height,
|
/// Measured as unrotated height,
|
||||||
|
@ -56,6 +69,19 @@ pub struct Ship {
|
||||||
|
|
||||||
/// This ship's hull strength
|
/// This ship's hull strength
|
||||||
pub hull: f32,
|
pub hull: f32,
|
||||||
|
|
||||||
|
/// Collision shape for this ship
|
||||||
|
pub collision: Collision,
|
||||||
|
|
||||||
|
/// Remove later
|
||||||
|
pub aspect: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collision shape for this ship
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Collision {
|
||||||
|
pub points: Vec<Point<f32, 2>>,
|
||||||
|
pub indices: Vec<[u32; 2]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An engine point on a ship.
|
/// An engine point on a ship.
|
||||||
|
@ -80,20 +106,26 @@ pub struct GunPoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Build for Ship {
|
impl super::Build for Ship {
|
||||||
fn build(root: &super::syntax::Root) -> Result<Vec<Self>> {
|
type InputSyntax = HashMap<String, syntax::Ship>;
|
||||||
let ship = if let Some(ship) = &root.ship {
|
|
||||||
ship
|
|
||||||
} else {
|
|
||||||
return Ok(vec![]);
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut out = Vec::new();
|
fn build(ship: Self::InputSyntax, ct: &mut Content) -> Result<()> {
|
||||||
for (ship_name, ship) in ship {
|
for (ship_name, ship) in ship {
|
||||||
let size = ship.size;
|
let size = ship.size;
|
||||||
|
let aspect = ship.aspect;
|
||||||
|
|
||||||
out.push(Self {
|
let th = match ct.texture_index.get(&ship.sprite) {
|
||||||
|
None => bail!(
|
||||||
|
"In ship `{}`: texture `{}` doesn't exist",
|
||||||
|
ship_name,
|
||||||
|
ship.sprite
|
||||||
|
),
|
||||||
|
Some(t) => *t,
|
||||||
|
};
|
||||||
|
|
||||||
|
ct.ships.push(Self {
|
||||||
|
aspect,
|
||||||
name: ship_name.to_owned(),
|
name: ship_name.to_owned(),
|
||||||
sprite: ship.sprite.to_owned(),
|
sprite: th,
|
||||||
size,
|
size,
|
||||||
hull: ship.hull,
|
hull: ship.hull,
|
||||||
engines: ship
|
engines: ship
|
||||||
|
@ -101,8 +133,8 @@ impl super::Build for Ship {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| EnginePoint {
|
.map(|e| EnginePoint {
|
||||||
pos: Point2 {
|
pos: Point2 {
|
||||||
x: e.x * size,
|
x: e.x * size * aspect / 2.0,
|
||||||
y: e.y * size,
|
y: e.y * size / 2.0,
|
||||||
},
|
},
|
||||||
size: e.size,
|
size: e.size,
|
||||||
})
|
})
|
||||||
|
@ -112,14 +144,23 @@ impl super::Build for Ship {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| GunPoint {
|
.map(|e| GunPoint {
|
||||||
pos: Point2 {
|
pos: Point2 {
|
||||||
x: e.x * size,
|
x: e.x * size * aspect / 2.0,
|
||||||
y: e.y * size,
|
y: e.y * size / 2.0,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
collision: Collision {
|
||||||
|
indices: ship.collision.indices.clone(),
|
||||||
|
points: ship
|
||||||
|
.collision
|
||||||
|
.points
|
||||||
|
.iter()
|
||||||
|
.map(|x| point![x[0] * (size / 2.0) * aspect, x[1] * size / 2.0])
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(out);
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use anyhow::{bail, Context, Result};
|
||||||
use cgmath::{Deg, Point3};
|
use cgmath::{Deg, Point3};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use crate::util::Polar;
|
use crate::{util::Polar, Content, TextureHandle};
|
||||||
|
|
||||||
pub(super) mod syntax {
|
pub(super) mod syntax {
|
||||||
use super::HashMap;
|
use super::HashMap;
|
||||||
|
@ -94,7 +94,7 @@ pub struct System {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Object {
|
pub struct Object {
|
||||||
/// This object's sprite
|
/// This object's sprite
|
||||||
pub sprite: String,
|
pub sprite: TextureHandle,
|
||||||
|
|
||||||
/// This object's size.
|
/// This object's size.
|
||||||
/// Measured as height in game units.
|
/// Measured as height in game units.
|
||||||
|
@ -175,23 +175,27 @@ fn resolve_position(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Build for System {
|
impl super::Build for System {
|
||||||
fn build(root: &super::syntax::Root) -> Result<Vec<Self>> {
|
type InputSyntax = HashMap<String, syntax::System>;
|
||||||
let system = if let Some(system) = &root.system {
|
|
||||||
system
|
|
||||||
} else {
|
|
||||||
return Ok(vec![]);
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut out = Vec::new();
|
fn build(system: Self::InputSyntax, ct: &mut Content) -> Result<()> {
|
||||||
for (system_name, system) in system {
|
for (system_name, system) in system {
|
||||||
let mut objects = Vec::new();
|
let mut objects = Vec::new();
|
||||||
|
|
||||||
for (label, obj) in &system.object {
|
for (label, obj) in &system.object {
|
||||||
let mut cycle_detector = HashSet::new();
|
let mut cycle_detector = HashSet::new();
|
||||||
cycle_detector.insert(label.to_owned());
|
cycle_detector.insert(label.clone());
|
||||||
|
|
||||||
|
let th = match ct.texture_index.get(&obj.sprite) {
|
||||||
|
None => bail!(
|
||||||
|
"In system `{}`: texture `{}` doesn't exist",
|
||||||
|
system_name,
|
||||||
|
obj.sprite
|
||||||
|
),
|
||||||
|
Some(t) => *t,
|
||||||
|
};
|
||||||
|
|
||||||
objects.push(Object {
|
objects.push(Object {
|
||||||
sprite: obj.sprite.clone(),
|
sprite: th,
|
||||||
position: resolve_position(&system.object, &obj, cycle_detector)
|
position: resolve_position(&system.object, &obj, cycle_detector)
|
||||||
.with_context(|| format!("In object {:#?}", label))?,
|
.with_context(|| format!("In object {:#?}", label))?,
|
||||||
size: obj.size,
|
size: obj.size,
|
||||||
|
@ -199,12 +203,12 @@ impl super::Build for System {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
out.push(Self {
|
ct.systems.push(Self {
|
||||||
name: system_name.to_owned(),
|
name: system_name,
|
||||||
objects,
|
objects,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(out);
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
|
use anyhow::{bail, Context, Result};
|
||||||
|
use image::io::Reader;
|
||||||
|
|
||||||
|
use crate::{Content, TextureHandle};
|
||||||
|
|
||||||
|
pub(super) mod syntax {
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
// Raw serde syntax structs.
|
||||||
|
// These are never seen by code outside this crate.
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Texture {
|
||||||
|
pub path: PathBuf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a texture that may be used in the game.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Texture {
|
||||||
|
/// The name of this texture
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// The handle for this texture
|
||||||
|
pub handle: TextureHandle,
|
||||||
|
|
||||||
|
/// The path to this texture's image file
|
||||||
|
pub path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::Build for Texture {
|
||||||
|
type InputSyntax = HashMap<String, syntax::Texture>;
|
||||||
|
|
||||||
|
fn build(texture: Self::InputSyntax, ct: &mut Content) -> Result<()> {
|
||||||
|
for (texture_name, t) in texture {
|
||||||
|
let path = ct.texture_root.join(t.path);
|
||||||
|
let reader = Reader::open(&path).with_context(|| {
|
||||||
|
format!(
|
||||||
|
"Failed to read texture `{}` from file `{}`",
|
||||||
|
texture_name,
|
||||||
|
path.display()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let dim = reader.into_dimensions().with_context(|| {
|
||||||
|
format!(
|
||||||
|
"Failed to dimensions of texture `{}` from file `{}`",
|
||||||
|
texture_name,
|
||||||
|
path.display()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let h = TextureHandle {
|
||||||
|
index: ct.textures.len(),
|
||||||
|
aspect: dim.0 as f32 / dim.1 as f32,
|
||||||
|
};
|
||||||
|
ct.texture_index.insert(texture_name.clone(), h);
|
||||||
|
|
||||||
|
if texture_name == ct.starfield_texture_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.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ct.textures.push(Self {
|
||||||
|
name: texture_name,
|
||||||
|
path: path,
|
||||||
|
handle: h,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ct.starfield_handle.is_none() {
|
||||||
|
bail!(
|
||||||
|
"Could not find a starfield texture (name: `{}`)",
|
||||||
|
ct.starfield_texture_name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,5 +25,11 @@ pub const STARFIELD_DENSITY: f64 = 0.01;
|
||||||
/// Must fit inside an i32
|
/// Must fit inside an i32
|
||||||
pub const STARFIELD_COUNT: u64 = (STARFIELD_SIZE as f64 * STARFIELD_DENSITY) as u64;
|
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";
|
||||||
|
|
||||||
/// Root directory of game content
|
/// Root directory of game content
|
||||||
pub const CONTENT_ROOT: &'static str = "./content";
|
pub const CONTENT_ROOT: &'static str = "./content";
|
||||||
|
|
||||||
|
/// Root directory of game textures
|
||||||
|
pub const TEXTURE_ROOT: &'static str = "./assets";
|
||||||
|
|
|
@ -40,7 +40,7 @@ impl Game {
|
||||||
);
|
);
|
||||||
|
|
||||||
let h2 = physics.add_ship(&ct.ships[0], vec![], Point2 { x: 300.0, y: 300.0 });
|
let h2 = physics.add_ship(&ct.ships[0], vec![], Point2 { x: 300.0, y: 300.0 });
|
||||||
let h3 = physics.add_ship(
|
let _h3 = physics.add_ship(
|
||||||
&ct.ships[0],
|
&ct.ships[0],
|
||||||
vec![outfits::ShipOutfit::Gun(outfits::ShipGun::new(
|
vec![outfits::ShipOutfit::Gun(outfits::ShipGun::new(
|
||||||
ct.guns[0].clone(),
|
ct.guns[0].clone(),
|
||||||
|
@ -54,7 +54,7 @@ impl Game {
|
||||||
|
|
||||||
let mut shipbehaviors: Vec<Box<dyn ShipBehavior>> = Vec::new();
|
let mut shipbehaviors: Vec<Box<dyn ShipBehavior>> = Vec::new();
|
||||||
shipbehaviors.push(shipbehavior::Player::new(h1));
|
shipbehaviors.push(shipbehavior::Player::new(h1));
|
||||||
shipbehaviors.push(shipbehavior::Point::new(h3));
|
//shipbehaviors.push(shipbehavior::Point::new(h3));
|
||||||
shipbehaviors.push(shipbehavior::Dummy::new(h2));
|
shipbehaviors.push(shipbehavior::Dummy::new(h2));
|
||||||
|
|
||||||
Game {
|
Game {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use cgmath::{Deg, Point3};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
content::{self, EnginePoint, GunPoint},
|
content::{self, EnginePoint, GunPoint},
|
||||||
render::{SpriteTexture, SubSprite},
|
render::SubSprite,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Represents a gun attached to a specific ship at a certain gunpoint.
|
/// Represents a gun attached to a specific ship at a certain gunpoint.
|
||||||
|
@ -103,7 +103,7 @@ impl<'a> ShipOutfits {
|
||||||
// TODO: better way to pick flare texture
|
// TODO: better way to pick flare texture
|
||||||
self.engine_flare_sprites.clear();
|
self.engine_flare_sprites.clear();
|
||||||
let t = if let Some(e) = self.iter_engines().next() {
|
let t = if let Some(e) = self.iter_engines().next() {
|
||||||
SpriteTexture(e.flare_sprite.clone())
|
e.flare_sprite
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -116,7 +116,7 @@ impl<'a> ShipOutfits {
|
||||||
y: p.pos.y,
|
y: p.pos.y,
|
||||||
z: 1.0,
|
z: 1.0,
|
||||||
},
|
},
|
||||||
texture: t.clone(),
|
texture: t,
|
||||||
angle: Deg(0.0),
|
angle: Deg(0.0),
|
||||||
size: p.size,
|
size: p.size,
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,7 +2,7 @@ use cgmath::{Point3, Vector2};
|
||||||
use rand::{self, Rng};
|
use rand::{self, Rng};
|
||||||
|
|
||||||
use super::SystemObject;
|
use super::SystemObject;
|
||||||
use crate::{consts, content, render::Sprite, render::SpriteTexture};
|
use crate::{consts, content, render::Sprite};
|
||||||
|
|
||||||
pub struct StarfieldStar {
|
pub struct StarfieldStar {
|
||||||
/// Star coordinates, in world space.
|
/// Star coordinates, in world space.
|
||||||
|
@ -50,7 +50,7 @@ impl System {
|
||||||
for o in &ct.objects {
|
for o in &ct.objects {
|
||||||
s.bodies.push(SystemObject {
|
s.bodies.push(SystemObject {
|
||||||
pos: o.position,
|
pos: o.position,
|
||||||
sprite: SpriteTexture(o.sprite.to_owned()),
|
sprite: o.sprite,
|
||||||
size: o.size,
|
size: o.size,
|
||||||
angle: o.angle,
|
angle: o.angle,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use cgmath::{Deg, Point3};
|
use cgmath::{Deg, Point3};
|
||||||
|
use galactica_content::TextureHandle;
|
||||||
|
|
||||||
use crate::{render::Sprite, render::SpriteTexture};
|
use crate::render::Sprite;
|
||||||
|
|
||||||
pub struct SystemObject {
|
pub struct SystemObject {
|
||||||
pub sprite: SpriteTexture,
|
pub sprite: TextureHandle,
|
||||||
pub pos: Point3<f32>,
|
pub pos: Point3<f32>,
|
||||||
pub size: f32,
|
pub size: f32,
|
||||||
pub angle: Deg<f32>,
|
pub angle: Deg<f32>,
|
||||||
|
|
16
src/main.rs
16
src/main.rs
|
@ -6,6 +6,8 @@ mod physics;
|
||||||
mod render;
|
mod render;
|
||||||
mod shipbehavior;
|
mod shipbehavior;
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use galactica_content as content;
|
use galactica_content as content;
|
||||||
use winit::{
|
use winit::{
|
||||||
|
@ -16,18 +18,22 @@ use winit::{
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
// TODO: error if missing
|
// TODO: error if missing
|
||||||
let content = content::Content::load_dir(consts::CONTENT_ROOT)?;
|
let content = content::Content::load_dir(
|
||||||
let game = game::Game::new(content);
|
PathBuf::from(consts::CONTENT_ROOT),
|
||||||
|
PathBuf::from(consts::TEXTURE_ROOT),
|
||||||
|
consts::STARFIELD_TEXTURE_NAME.to_owned(),
|
||||||
|
)?;
|
||||||
|
|
||||||
pollster::block_on(run(game))?;
|
pollster::block_on(run(content))?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(mut game: game::Game) -> Result<()> {
|
async fn run(content: content::Content) -> Result<()> {
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new();
|
||||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||||
|
|
||||||
let mut gpu = render::GPUState::new(window).await?;
|
let mut gpu = render::GPUState::new(window, &content).await?;
|
||||||
|
let mut game = game::Game::new(content);
|
||||||
|
|
||||||
gpu.update_starfield_buffer(&game);
|
gpu.update_starfield_buffer(&game);
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
use cgmath::Point3;
|
use cgmath::Point3;
|
||||||
|
use galactica_content::TextureHandle;
|
||||||
use rapier2d::{
|
use rapier2d::{
|
||||||
dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle},
|
dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle},
|
||||||
geometry::{ColliderBuilder, ColliderHandle},
|
geometry::{ColliderBuilder, ColliderHandle},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{physics::util, render::Sprite};
|
||||||
physics::util,
|
|
||||||
render::{Sprite, SpriteTexture},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct ProjectileBuilder {
|
pub struct ProjectileBuilder {
|
||||||
pub rigid_body: RigidBodyBuilder,
|
pub rigid_body: RigidBodyBuilder,
|
||||||
pub collider: ColliderBuilder,
|
pub collider: ColliderBuilder,
|
||||||
pub sprite: SpriteTexture,
|
pub sprite: TextureHandle,
|
||||||
pub lifetime: f32,
|
pub lifetime: f32,
|
||||||
pub size: f32,
|
pub size: f32,
|
||||||
pub damage: f32,
|
pub damage: f32,
|
||||||
|
@ -35,7 +33,7 @@ impl ProjectileBuilder {
|
||||||
pub struct Projectile {
|
pub struct Projectile {
|
||||||
pub rigid_body: RigidBodyHandle,
|
pub rigid_body: RigidBodyHandle,
|
||||||
pub collider: ColliderHandle,
|
pub collider: ColliderHandle,
|
||||||
pub sprite: SpriteTexture,
|
pub sprite: TextureHandle,
|
||||||
pub lifetime: f32,
|
pub lifetime: f32,
|
||||||
pub size: f32,
|
pub size: f32,
|
||||||
pub damage: f32,
|
pub damage: f32,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use cgmath::{Deg, EuclideanSpace, Matrix2, Rad, Vector2};
|
use cgmath::{Deg, EuclideanSpace, Matrix2, Rad, Vector2};
|
||||||
|
use content::TextureHandle;
|
||||||
use nalgebra::vector;
|
use nalgebra::vector;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rapier2d::{
|
use rapier2d::{
|
||||||
|
@ -12,7 +13,7 @@ use crate::{
|
||||||
content,
|
content,
|
||||||
game::outfits,
|
game::outfits,
|
||||||
physics::{util, ShipHandle},
|
physics::{util, ShipHandle},
|
||||||
render::{Sprite, SpriteTexture},
|
render::Sprite,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ShipTickResult {
|
pub struct ShipTickResult {
|
||||||
|
@ -41,7 +42,7 @@ pub struct Ship {
|
||||||
pub physics_handle: ShipHandle,
|
pub physics_handle: ShipHandle,
|
||||||
outfits: outfits::ShipOutfits,
|
outfits: outfits::ShipOutfits,
|
||||||
|
|
||||||
sprite: SpriteTexture,
|
sprite: TextureHandle,
|
||||||
size: f32,
|
size: f32,
|
||||||
pub hull: f32,
|
pub hull: f32,
|
||||||
pub controls: ShipControls,
|
pub controls: ShipControls,
|
||||||
|
@ -61,7 +62,7 @@ impl Ship {
|
||||||
Ship {
|
Ship {
|
||||||
physics_handle,
|
physics_handle,
|
||||||
outfits: o,
|
outfits: o,
|
||||||
sprite: SpriteTexture(c.sprite.clone()),
|
sprite: c.sprite,
|
||||||
size: c.size,
|
size: c.size,
|
||||||
hull: c.hull,
|
hull: c.hull,
|
||||||
controls: ShipControls::new(),
|
controls: ShipControls::new(),
|
||||||
|
@ -107,7 +108,7 @@ impl Ship {
|
||||||
out.push(ProjectileBuilder {
|
out.push(ProjectileBuilder {
|
||||||
rigid_body: p_r,
|
rigid_body: p_r,
|
||||||
collider: p_c,
|
collider: p_c,
|
||||||
sprite: SpriteTexture(g.kind.projectile.sprite.clone()),
|
sprite: g.kind.projectile.sprite,
|
||||||
lifetime: g.kind.projectile.lifetime
|
lifetime: g.kind.projectile.lifetime
|
||||||
+ rng.gen_range(
|
+ rng.gen_range(
|
||||||
-g.kind.projectile.lifetime_rng..=g.kind.projectile.lifetime_rng,
|
-g.kind.projectile.lifetime_rng..=g.kind.projectile.lifetime_rng,
|
||||||
|
@ -132,11 +133,11 @@ impl Ship {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.controls.right {
|
if self.controls.right {
|
||||||
r.apply_torque_impulse(500.0 * t, true);
|
r.apply_torque_impulse(-500.0 * t, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.controls.left {
|
if self.controls.left {
|
||||||
r.apply_torque_impulse(-500.0 * t, true);
|
r.apply_torque_impulse(500.0 * t, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let p = if self.controls.guns {
|
let p = if self.controls.guns {
|
||||||
|
|
|
@ -84,10 +84,15 @@ impl Physics {
|
||||||
outfits: Vec<outfits::ShipOutfit>,
|
outfits: Vec<outfits::ShipOutfit>,
|
||||||
position: Point2<f32>,
|
position: Point2<f32>,
|
||||||
) -> ShipHandle {
|
) -> ShipHandle {
|
||||||
|
let cl = ColliderBuilder::convex_decomposition(
|
||||||
|
&ct.collision.points[..],
|
||||||
|
&ct.collision.indices[..],
|
||||||
|
)
|
||||||
|
.mass(1.0);
|
||||||
|
|
||||||
let rb = RigidBodyBuilder::dynamic()
|
let rb = RigidBodyBuilder::dynamic()
|
||||||
.translation(vector![position.x, position.y])
|
.translation(vector![position.x, position.y])
|
||||||
.can_sleep(false);
|
.can_sleep(false);
|
||||||
let cl = ColliderBuilder::ball(50.0).restitution(0.7).mass(1.0);
|
|
||||||
|
|
||||||
let r = self.wrapper.rigid_body_set.insert(rb.build());
|
let r = self.wrapper.rigid_body_set.insert(rb.build());
|
||||||
let c = self.wrapper.collider_set.insert_with_parent(
|
let c = self.wrapper.collider_set.insert_with_parent(
|
||||||
|
|
|
@ -11,10 +11,10 @@ pub fn rigidbody_position(r: &RigidBody) -> cgmath::Point2<f32> {
|
||||||
|
|
||||||
pub fn rigidbody_angle(r: &RigidBody) -> Deg<f32> {
|
pub fn rigidbody_angle(r: &RigidBody) -> Deg<f32> {
|
||||||
Vector2 {
|
Vector2 {
|
||||||
x: r.rotation().re,
|
x: r.rotation().im,
|
||||||
y: r.rotation().im,
|
y: r.rotation().re,
|
||||||
}
|
}
|
||||||
.angle(Vector2 { x: 1.0, y: 0.0 })
|
.angle(Vector2 { x: 0.0, y: 1.0 })
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,6 @@ pub const SPRITE_INSTANCE_LIMIT: u64 = 500;
|
||||||
// Must be small enough to fit in an i32
|
// Must be small enough to fit in an i32
|
||||||
pub const STARFIELD_INSTANCE_LIMIT: u64 = consts::STARFIELD_COUNT * 24;
|
pub const STARFIELD_INSTANCE_LIMIT: u64 = consts::STARFIELD_COUNT * 24;
|
||||||
|
|
||||||
/// Texture index file name
|
|
||||||
pub const TEXTURE_INDEX_NAME: &'static str = "_index.toml";
|
|
||||||
|
|
||||||
/// Texture index dir
|
|
||||||
pub const TEXTURE_INDEX_PATH: &'static str = "./assets";
|
|
||||||
|
|
||||||
/// Shader entry points
|
/// Shader entry points
|
||||||
pub const SHADER_MAIN_VERTEX: &'static str = "vertex_main";
|
pub const SHADER_MAIN_VERTEX: &'static str = "vertex_main";
|
||||||
pub const SHADER_MAIN_FRAGMENT: &'static str = "fragment_main";
|
pub const SHADER_MAIN_FRAGMENT: &'static str = "fragment_main";
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bytemuck;
|
use bytemuck;
|
||||||
use cgmath::{Deg, EuclideanSpace, Matrix4, Point2, Vector2, Vector3};
|
use cgmath::{Deg, EuclideanSpace, Matrix4, Point2, Vector2, Vector3};
|
||||||
|
use galactica_content::Content;
|
||||||
use std::{iter, rc::Rc};
|
use std::{iter, rc::Rc};
|
||||||
use wgpu;
|
use wgpu;
|
||||||
use winit::{self, dpi::PhysicalSize, window::Window};
|
use winit::{self, dpi::PhysicalSize, window::Window};
|
||||||
|
@ -45,7 +46,7 @@ struct VertexBuffers {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GPUState {
|
impl GPUState {
|
||||||
pub async fn new(window: Window) -> Result<Self> {
|
pub async fn new(window: Window, ct: &Content) -> Result<Self> {
|
||||||
let window_size = window.inner_size();
|
let window_size = window.inner_size();
|
||||||
let window_aspect = window_size.width as f32 / window_size.height as f32;
|
let window_aspect = window_size.width as f32 / window_size.height as f32;
|
||||||
|
|
||||||
|
@ -128,7 +129,7 @@ impl GPUState {
|
||||||
|
|
||||||
// Load uniforms
|
// Load uniforms
|
||||||
let global_data = GlobalData::new(&device);
|
let global_data = GlobalData::new(&device);
|
||||||
let texture_array = TextureArray::new(&device, &queue)?;
|
let texture_array = TextureArray::new(&device, &queue, ct)?;
|
||||||
|
|
||||||
// Make sure these match the indices in each shader
|
// Make sure these match the indices in each shader
|
||||||
let bind_group_layouts = &[
|
let bind_group_layouts = &[
|
||||||
|
@ -215,7 +216,7 @@ impl GPUState {
|
||||||
} - game.camera.pos.to_vec())
|
} - game.camera.pos.to_vec())
|
||||||
/ s.pos.z
|
/ s.pos.z
|
||||||
};
|
};
|
||||||
let texture = self.texture_array.get_sprite_texture(s.texture);
|
let texture = self.texture_array.get_texture(s.texture);
|
||||||
|
|
||||||
// Game dimensions of this sprite post-scale.
|
// Game dimensions of this sprite post-scale.
|
||||||
// Don't divide by 2, we use this later.
|
// Don't divide by 2, we use this later.
|
||||||
|
@ -298,7 +299,7 @@ impl GPUState {
|
||||||
parent_pos: Point2<f32>,
|
parent_pos: Point2<f32>,
|
||||||
parent_angle: Deg<f32>,
|
parent_angle: Deg<f32>,
|
||||||
) {
|
) {
|
||||||
let texture = self.texture_array.get_sprite_texture(s.texture);
|
let texture = self.texture_array.get_texture(s.texture);
|
||||||
let scale = s.size / (s.pos.z * game.camera.zoom);
|
let scale = s.size / (s.pos.z * game.camera.zoom);
|
||||||
let sprite_aspect_and_scale =
|
let sprite_aspect_and_scale =
|
||||||
Matrix4::from_nonuniform_scale(texture.aspect * scale, scale, 1.0);
|
Matrix4::from_nonuniform_scale(texture.aspect * scale, scale, 1.0);
|
||||||
|
|
|
@ -8,9 +8,3 @@ mod vertexbuffer;
|
||||||
|
|
||||||
pub use gpustate::GPUState;
|
pub use gpustate::GPUState;
|
||||||
pub use sprite::{Sprite, SubSprite};
|
pub use sprite::{Sprite, SubSprite};
|
||||||
|
|
||||||
/// A handle to a sprite texture
|
|
||||||
/// TODO: This should be easy to copy,
|
|
||||||
/// but string references create unnecessary complexity
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SpriteTexture(pub String);
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use cgmath::{Deg, Point3};
|
use cgmath::{Deg, Point3};
|
||||||
|
use galactica_content::TextureHandle;
|
||||||
use super::SpriteTexture;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Sprite {
|
pub struct Sprite {
|
||||||
/// The sprite texture to draw
|
/// The sprite texture to draw
|
||||||
pub texture: SpriteTexture,
|
pub texture: TextureHandle,
|
||||||
|
|
||||||
/// This object's position, in world coordinates.
|
/// This object's position, in world coordinates.
|
||||||
pub pos: Point3<f32>,
|
pub pos: Point3<f32>,
|
||||||
|
@ -25,7 +24,7 @@ pub struct Sprite {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SubSprite {
|
pub struct SubSprite {
|
||||||
/// The sprite texture to draw
|
/// The sprite texture to draw
|
||||||
pub texture: SpriteTexture,
|
pub texture: TextureHandle,
|
||||||
|
|
||||||
/// This object's position, in world coordinates.
|
/// This object's position, in world coordinates.
|
||||||
/// This is relative to this sprite's parent.
|
/// This is relative to this sprite's parent.
|
||||||
|
|
|
@ -1,38 +1,48 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::{collections::HashMap, num::NonZeroU32, path::PathBuf};
|
use galactica_content::{Content, TextureHandle};
|
||||||
|
use std::{collections::HashMap, fs::File, io::Read, num::NonZeroU32};
|
||||||
use wgpu::BindGroupLayout;
|
use wgpu::BindGroupLayout;
|
||||||
|
|
||||||
use super::{
|
use super::{rawtexture::RawTexture, Texture};
|
||||||
super::consts::TEXTURE_INDEX_PATH, loader::TextureLoader, Texture, TextureArrayConfig,
|
|
||||||
};
|
|
||||||
use crate::render::SpriteTexture;
|
|
||||||
|
|
||||||
pub struct TextureArray {
|
pub struct TextureArray {
|
||||||
pub bind_group: wgpu::BindGroup,
|
pub bind_group: wgpu::BindGroup,
|
||||||
pub bind_group_layout: BindGroupLayout,
|
pub bind_group_layout: BindGroupLayout,
|
||||||
textures: HashMap<String, Texture>,
|
starfield_handle: TextureHandle,
|
||||||
config: TextureArrayConfig,
|
textures: HashMap<TextureHandle, Texture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureArray {
|
impl TextureArray {
|
||||||
pub fn get_starfield_texture(&self) -> Texture {
|
pub fn get_starfield_texture(&self) -> Texture {
|
||||||
return self.get_texture(&self.config.starfield);
|
*self.textures.get(&self.starfield_handle).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sprite_texture(&self, sprite: SpriteTexture) -> Texture {
|
pub fn get_texture(&self, handle: TextureHandle) -> Texture {
|
||||||
return self.get_texture(&sprite.0);
|
match self.textures.get(&handle) {
|
||||||
}
|
|
||||||
|
|
||||||
fn get_texture(&self, name: &str) -> Texture {
|
|
||||||
match self.textures.get(name) {
|
|
||||||
Some(x) => *x,
|
Some(x) => *x,
|
||||||
None => self.get_texture(&self.config.error),
|
None => unreachable!("Tried to get a texture that doesn't exist"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(device: &wgpu::Device, queue: &wgpu::Queue) -> Result<Self> {
|
pub fn new(device: &wgpu::Device, queue: &wgpu::Queue, ct: &Content) -> Result<Self> {
|
||||||
// Load all textures
|
// Load all textures
|
||||||
let loader = TextureLoader::load(device, queue, &PathBuf::from(TEXTURE_INDEX_PATH))?;
|
let mut texture_data = Vec::new();
|
||||||
|
let mut textures = HashMap::new();
|
||||||
|
|
||||||
|
for t in &ct.textures {
|
||||||
|
let mut f = File::open(&t.path)?;
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
f.read_to_end(&mut bytes)?;
|
||||||
|
textures.insert(
|
||||||
|
t.handle,
|
||||||
|
Texture {
|
||||||
|
index: texture_data.len() as u32,
|
||||||
|
aspect: t.handle.aspect,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
texture_data.push(RawTexture::from_bytes(&device, &queue, &bytes, &t.name)?);
|
||||||
|
}
|
||||||
|
|
||||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||||
|
@ -56,7 +66,7 @@ impl TextureArray {
|
||||||
view_dimension: wgpu::TextureViewDimension::D2,
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||||
},
|
},
|
||||||
count: NonZeroU32::new(loader.texture_data.len() as u32),
|
count: NonZeroU32::new(texture_data.len() as u32),
|
||||||
},
|
},
|
||||||
// Texture sampler
|
// Texture sampler
|
||||||
wgpu::BindGroupLayoutEntry {
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
@ -68,7 +78,7 @@ impl TextureArray {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
let views: Vec<&wgpu::TextureView> = loader.texture_data.iter().map(|x| &x.view).collect();
|
let views: Vec<&wgpu::TextureView> = texture_data.iter().map(|x| &x.view).collect();
|
||||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
label: Some("TextureArray bind group"),
|
label: Some("TextureArray bind group"),
|
||||||
layout: &bind_group_layout,
|
layout: &bind_group_layout,
|
||||||
|
@ -88,8 +98,8 @@ impl TextureArray {
|
||||||
return Ok(Self {
|
return Ok(Self {
|
||||||
bind_group,
|
bind_group,
|
||||||
bind_group_layout,
|
bind_group_layout,
|
||||||
textures: loader.textures,
|
textures: textures,
|
||||||
config: loader.config,
|
starfield_handle: ct.get_starfield_handle(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,193 +0,0 @@
|
||||||
use anyhow::Result;
|
|
||||||
use std::{collections::HashMap, fs::File, io::Read, path::Path, path::PathBuf};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
super::consts::TEXTURE_INDEX_NAME, rawtexture::RawTexture, Texture, TextureArrayConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod fields {
|
|
||||||
|
|
||||||
use super::{File, HashMap, Path, PathBuf, RawTexture, Read, Result, TextureArrayConfig};
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
// Config is used outside of this file,
|
|
||||||
// so it is defined in mod.rs.
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Index {
|
|
||||||
pub config: TextureArrayConfig,
|
|
||||||
pub include: Option<HashMap<PathBuf, String>>,
|
|
||||||
pub texture: Vec<Texture>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Index {
|
|
||||||
pub fn to_sub(self) -> (TextureArrayConfig, SubIndex) {
|
|
||||||
(
|
|
||||||
self.config,
|
|
||||||
SubIndex {
|
|
||||||
include: self.include,
|
|
||||||
texture: self.texture,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct SubIndex {
|
|
||||||
pub include: Option<HashMap<PathBuf, String>>,
|
|
||||||
pub texture: Vec<Texture>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Texture {
|
|
||||||
pub name: String,
|
|
||||||
pub path: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Texture {
|
|
||||||
pub fn read(
|
|
||||||
&self,
|
|
||||||
device: &wgpu::Device,
|
|
||||||
queue: &wgpu::Queue,
|
|
||||||
root: &Path,
|
|
||||||
) -> Result<RawTexture> {
|
|
||||||
let mut f = File::open(root.join(&self.path))?;
|
|
||||||
let mut bytes = Vec::new();
|
|
||||||
f.read_to_end(&mut bytes)?;
|
|
||||||
RawTexture::from_bytes(&device, &queue, &bytes, &self.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TextureLoader {
|
|
||||||
pub textures: HashMap<String, Texture>,
|
|
||||||
pub texture_data: Vec<RawTexture>,
|
|
||||||
pub config: TextureArrayConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TextureLoader {
|
|
||||||
pub fn load(device: &wgpu::Device, queue: &wgpu::Queue, index_path: &Path) -> Result<Self> {
|
|
||||||
let index: fields::Index = {
|
|
||||||
let mut texture_index_string = String::new();
|
|
||||||
let _ = File::open(index_path.join(TEXTURE_INDEX_NAME))?
|
|
||||||
.read_to_string(&mut texture_index_string);
|
|
||||||
toml::from_str(&texture_index_string)?
|
|
||||||
};
|
|
||||||
|
|
||||||
let (config, index) = index.to_sub();
|
|
||||||
let texture_raw =
|
|
||||||
Self::inner_load_textures(device, queue, index, index_path, "".to_string())?;
|
|
||||||
|
|
||||||
// TODO: validate configuration
|
|
||||||
// TODO: handle error states
|
|
||||||
|
|
||||||
let mut textures = HashMap::new();
|
|
||||||
let mut texture_data = Vec::new();
|
|
||||||
let mut i = 0;
|
|
||||||
for (name, data) in texture_raw {
|
|
||||||
textures.insert(
|
|
||||||
name,
|
|
||||||
Texture {
|
|
||||||
index: i,
|
|
||||||
aspect: data.dimensions.0 as f32 / data.dimensions.1 as f32,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
texture_data.push(data);
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(Self {
|
|
||||||
textures,
|
|
||||||
texture_data,
|
|
||||||
config,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inner_load_textures(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
queue: &wgpu::Queue,
|
|
||||||
index: fields::SubIndex,
|
|
||||||
texture_root: &Path,
|
|
||||||
prefix: String,
|
|
||||||
) -> Result<HashMap<String, RawTexture>> {
|
|
||||||
// Load all textures
|
|
||||||
let mut texture_raw = HashMap::new();
|
|
||||||
for t in index.texture {
|
|
||||||
let data = match t.read(device, queue, &texture_root) {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(_) => {
|
|
||||||
println!(
|
|
||||||
"[WARNING] Failed to load texture `{}` from `{}`: file doesn't exist.",
|
|
||||||
if prefix.is_empty() {
|
|
||||||
t.name
|
|
||||||
} else {
|
|
||||||
format!("{prefix}::{}", t.name)
|
|
||||||
},
|
|
||||||
texture_root.display(),
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if texture_raw.contains_key(&t.name) {
|
|
||||||
println!(
|
|
||||||
"[WARNING] Subindex in `{}` has a duplicate texture `{}`, skipping.",
|
|
||||||
texture_root.display(),
|
|
||||||
if prefix.is_empty() {
|
|
||||||
t.name
|
|
||||||
} else {
|
|
||||||
format!("{prefix}::{}", t.name)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
texture_raw.insert(
|
|
||||||
if prefix.is_empty() {
|
|
||||||
t.name
|
|
||||||
} else {
|
|
||||||
format!("{prefix}::{}", t.name)
|
|
||||||
},
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load included files
|
|
||||||
if let Some(include) = index.include {
|
|
||||||
for (path, sub_prefix) in include {
|
|
||||||
let sub_root = texture_root.join(&path);
|
|
||||||
let index: fields::SubIndex = {
|
|
||||||
let mut texture_index_string = String::new();
|
|
||||||
let _ = File::open(sub_root.join(TEXTURE_INDEX_NAME))?
|
|
||||||
.read_to_string(&mut texture_index_string);
|
|
||||||
toml::from_str(&texture_index_string)?
|
|
||||||
};
|
|
||||||
|
|
||||||
let sub_textures = Self::inner_load_textures(
|
|
||||||
device,
|
|
||||||
queue,
|
|
||||||
index,
|
|
||||||
&sub_root,
|
|
||||||
if prefix.is_empty() {
|
|
||||||
sub_prefix
|
|
||||||
} else {
|
|
||||||
format!("{prefix}::{sub_prefix}")
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
for (s, data) in sub_textures {
|
|
||||||
if texture_raw.contains_key(&s) {
|
|
||||||
println!(
|
|
||||||
"[WARNING] Subindex in `{}` has a duplicate texture `{}`, skipping.",
|
|
||||||
sub_root.display(),
|
|
||||||
&s,
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
texture_raw.insert(s, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(texture_raw);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +1,10 @@
|
||||||
mod array;
|
mod array;
|
||||||
mod loader;
|
|
||||||
mod rawtexture;
|
mod rawtexture;
|
||||||
|
|
||||||
pub use array::TextureArray;
|
pub use array::TextureArray;
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Texture {
|
pub struct Texture {
|
||||||
pub index: u32, // Index in texture array
|
pub index: u32, // Index in texture array
|
||||||
pub aspect: f32, // width / height
|
pub aspect: f32, // width / height
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct TextureArrayConfig {
|
|
||||||
pub error: String,
|
|
||||||
pub starfield: String,
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ use image::GenericImageView;
|
||||||
|
|
||||||
pub(super) struct RawTexture {
|
pub(super) struct RawTexture {
|
||||||
pub(super) view: wgpu::TextureView,
|
pub(super) view: wgpu::TextureView,
|
||||||
pub(super) dimensions: (u32, u32),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RawTexture {
|
impl RawTexture {
|
||||||
|
@ -61,6 +60,6 @@ impl RawTexture {
|
||||||
size,
|
size,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Self { view, dimensions })
|
Ok(Self { view })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue