Compare commits
6 Commits
1f154c1a58
...
aa4c6c18b3
Author | SHA1 | Date |
---|---|---|
Mark | aa4c6c18b3 | |
Mark | 1a41b93df6 | |
Mark | 0d55e4e4f7 | |
Mark | eaa00a63f5 | |
Mark | 001c74ba6a | |
Mark | 8dc040cf08 |
|
@ -1,2 +1,3 @@
|
||||||
|
/cache
|
||||||
/target
|
/target
|
||||||
*.ignore
|
*.ignore
|
||||||
|
|
16
TODO.md
16
TODO.md
|
@ -1,10 +1,9 @@
|
||||||
## Specific Jobs
|
## Specific Jobs
|
||||||
- Particle variation
|
- Particle variation
|
||||||
- Animated sprites
|
|
||||||
- UI: health, shield, fuel, heat, energy bars
|
- UI: health, shield, fuel, heat, energy bars
|
||||||
- UI: text arranger
|
- UI: text arranger
|
||||||
- Sound system
|
- Sound system
|
||||||
- Debris on ship death
|
- Ship death animation & debris
|
||||||
|
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
|
@ -57,6 +56,11 @@
|
||||||
- how to target them
|
- how to target them
|
||||||
- where to go
|
- where to go
|
||||||
- etc, extra flags
|
- etc, extra flags
|
||||||
|
- Conditional animations: on fire, on death, etc (tempest)
|
||||||
|
- Higher texture limit (16 x 8096 x 8096 isn't enough)
|
||||||
|
- Fast-load menu, progress bar for the rest
|
||||||
|
- Only load what is needed?
|
||||||
|
- GPU limits? (texture size, texture number)
|
||||||
|
|
||||||
|
|
||||||
## Faction interaction
|
## Faction interaction
|
||||||
|
@ -73,7 +77,6 @@
|
||||||
- Frame timings (compute/render/physics/etc)
|
- Frame timings (compute/render/physics/etc)
|
||||||
- Elegantly handle lost focus
|
- Elegantly handle lost focus
|
||||||
- Pause game
|
- Pause game
|
||||||
- Better player controller? (only one shipbehavior needs inputs)
|
|
||||||
- Clear all `// TODO:` comments littered in the source
|
- Clear all `// TODO:` comments littered in the source
|
||||||
- CLI options (debug, save location, content location, check content)
|
- CLI options (debug, save location, content location, check content)
|
||||||
- Config file and compile options, remove all those consts.
|
- Config file and compile options, remove all those consts.
|
||||||
|
@ -81,6 +84,10 @@
|
||||||
- Sprite optimization: do we need to allocate a new `Vec` every frame? Probably not.
|
- Sprite optimization: do we need to allocate a new `Vec` every frame? Probably not.
|
||||||
- Better error when run outside of directory
|
- Better error when run outside of directory
|
||||||
- Documentation site & front page
|
- Documentation site & front page
|
||||||
|
- Random animation age for objects * ui
|
||||||
|
- Random animation delay/fps?
|
||||||
|
- Fade between animation frames
|
||||||
|
- Better WGSL preprocessor (warning when including a bad file!)
|
||||||
|
|
||||||
## Content
|
## Content
|
||||||
- Angled engines
|
- Angled engines
|
||||||
|
@ -88,7 +95,6 @@
|
||||||
- Turn engine flares
|
- Turn engine flares
|
||||||
- Reverse engine & flares
|
- Reverse engine & flares
|
||||||
- Better ship colliders (need a tool or an algorithm)
|
- Better ship colliders (need a tool or an algorithm)
|
||||||
- Better projectile colliders (box/ball/etc -- shared syntax)
|
|
||||||
- Turrets
|
- Turrets
|
||||||
- Weapons with ammunition
|
- Weapons with ammunition
|
||||||
- Enable/disable weapons
|
- Enable/disable weapons
|
||||||
|
@ -111,7 +117,6 @@
|
||||||
- Important objects affect camera
|
- Important objects affect camera
|
||||||
|
|
||||||
## Visuals
|
## Visuals
|
||||||
- Particles
|
|
||||||
- Dynamic lighting (planets & ships)
|
- Dynamic lighting (planets & ships)
|
||||||
- Motion blur
|
- Motion blur
|
||||||
- Zoom parallax (?)
|
- Zoom parallax (?)
|
||||||
|
@ -120,6 +125,7 @@
|
||||||
- Ship outlines in radar
|
- Ship outlines in radar
|
||||||
- Engine flare ease in/out
|
- Engine flare ease in/out
|
||||||
- Lens flare
|
- Lens flare
|
||||||
|
- Particles when a ship is damaged
|
||||||
|
|
||||||
## Write and Document
|
## Write and Document
|
||||||
- Parallax
|
- Parallax
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[ship."Gypsum"]
|
[ship."Gypsum"]
|
||||||
sprite = "ship::peregrine"
|
sprite = "ship::gypsum"
|
||||||
size = 100
|
size = 100
|
||||||
mass = 1
|
mass = 1
|
||||||
hull = 200
|
hull = 200
|
||||||
|
|
|
@ -20,8 +20,8 @@ file = "projectile/blaster.png"
|
||||||
file = "ship/gypsum.png"
|
file = "ship/gypsum.png"
|
||||||
|
|
||||||
[sprite."ship::peregrine"]
|
[sprite."ship::peregrine"]
|
||||||
duration = 1
|
duration = 1.3
|
||||||
repeat = "repeat"
|
repeat = "reverse"
|
||||||
frames = [
|
frames = [
|
||||||
"ship/peregrine/01.png",
|
"ship/peregrine/01.png",
|
||||||
"ship/peregrine/02.png",
|
"ship/peregrine/02.png",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
//! Compile-time parameters
|
//! Compile-time parameters
|
||||||
|
// TODO: many of these should be moved to a config file or cli option
|
||||||
|
|
||||||
/// Minimum zoom level
|
/// Minimum zoom level
|
||||||
pub const ZOOM_MIN: f32 = 200.0;
|
pub const ZOOM_MIN: f32 = 200.0;
|
||||||
|
@ -61,3 +62,6 @@ pub const SPRITE_LIMIT: u32 = 1024;
|
||||||
|
|
||||||
/// The maximum number of images we can load
|
/// The maximum number of images we can load
|
||||||
pub const IMAGE_LIMIT: u32 = 1024;
|
pub const IMAGE_LIMIT: u32 = 1024;
|
||||||
|
|
||||||
|
/// Where we should look for packed assets
|
||||||
|
pub const ASSET_CACHE: &'static str = "./cache";
|
||||||
|
|
|
@ -255,6 +255,11 @@ impl Content {
|
||||||
return &self.sprites[h.index as usize];
|
return &self.sprites[h.index as usize];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the list of atlas files we may use
|
||||||
|
pub fn atlas_files(&self) -> &Vec<String> {
|
||||||
|
return &self.sprite_atlas.atlas_list;
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a sprite from a path
|
/// Get a sprite from a path
|
||||||
pub fn get_image(&self, p: &Path) -> &SpriteAtlasImage {
|
pub fn get_image(&self, p: &Path) -> &SpriteAtlasImage {
|
||||||
self.sprite_atlas.index.get(p).unwrap()
|
self.sprite_atlas.index.get(p).unwrap()
|
||||||
|
|
|
@ -44,6 +44,10 @@ pub enum RepeatMode {
|
||||||
/// After the first frame, jump to the last frame
|
/// After the first frame, jump to the last frame
|
||||||
#[serde(rename = "repeat")]
|
#[serde(rename = "repeat")]
|
||||||
Repeat,
|
Repeat,
|
||||||
|
|
||||||
|
/// Play this animation in reverse after the last frame
|
||||||
|
#[serde(rename = "reverse")]
|
||||||
|
Reverse,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RepeatMode {
|
impl RepeatMode {
|
||||||
|
@ -51,8 +55,9 @@ impl RepeatMode {
|
||||||
/// Used to pass this enum into shaders
|
/// Used to pass this enum into shaders
|
||||||
pub fn as_int(&self) -> u32 {
|
pub fn as_int(&self) -> u32 {
|
||||||
match self {
|
match self {
|
||||||
Self::Once => 0,
|
Self::Repeat => 0,
|
||||||
Self::Repeat => 1,
|
Self::Once => 1,
|
||||||
|
Self::Reverse => 2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,12 @@ mod inputstatus;
|
||||||
|
|
||||||
pub use galactica_content as content;
|
pub use galactica_content as content;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{bail, Result};
|
||||||
use galactica_constants;
|
use galactica_constants::{self, ASSET_CACHE};
|
||||||
use std::path::PathBuf;
|
use std::{
|
||||||
|
fs,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
use winit::{
|
use winit::{
|
||||||
event::{Event, KeyboardInput, WindowEvent},
|
event::{Event, KeyboardInput, WindowEvent},
|
||||||
event_loop::{ControlFlow, EventLoop},
|
event_loop::{ControlFlow, EventLoop},
|
||||||
|
@ -14,11 +17,20 @@ use winit::{
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
// TODO: error if missing
|
let cache_dir = Path::new(ASSET_CACHE);
|
||||||
|
fs::create_dir_all(cache_dir)?;
|
||||||
|
|
||||||
|
let atlas_index = cache_dir.join("spriteatlas.toml");
|
||||||
|
|
||||||
|
if !atlas_index.exists() {
|
||||||
|
bail!("Could not find sprite atlas!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: pretty error if missing
|
||||||
let content = content::Content::load_dir(
|
let content = content::Content::load_dir(
|
||||||
PathBuf::from(galactica_constants::CONTENT_ROOT),
|
PathBuf::from(galactica_constants::CONTENT_ROOT),
|
||||||
PathBuf::from(galactica_constants::IMAGE_ROOT),
|
PathBuf::from(galactica_constants::IMAGE_ROOT),
|
||||||
PathBuf::from("spriteatlas.toml"),
|
atlas_index,
|
||||||
galactica_constants::STARFIELD_SPRITE_NAME.to_owned(),
|
galactica_constants::STARFIELD_SPRITE_NAME.to_owned(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
|
@ -78,8 +78,10 @@ impl AtlasSet {
|
||||||
pub fn write_image(&mut self, path: &Path, dim: [u32; 2]) -> Result<usize> {
|
pub fn write_image(&mut self, path: &Path, dim: [u32; 2]) -> Result<usize> {
|
||||||
let mut f = File::open(&path)?;
|
let mut f = File::open(&path)?;
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = Vec::new();
|
||||||
f.read_to_end(&mut bytes)?;
|
f.read_to_end(&mut bytes)
|
||||||
let img = image::load_from_memory(&bytes)?;
|
.with_context(|| format!("While reading file `{}`", path.display()))?;
|
||||||
|
let img = image::load_from_memory(&bytes)
|
||||||
|
.with_context(|| format!("While loading file `{}`", path.display()))?;
|
||||||
|
|
||||||
let mut x = 0;
|
let mut x = 0;
|
||||||
let mut y = 0;
|
let mut y = 0;
|
||||||
|
@ -179,7 +181,7 @@ impl AtlasSet {
|
||||||
self.index.index.insert(
|
self.index.index.insert(
|
||||||
p.to_path_buf(),
|
p.to_path_buf(),
|
||||||
SpriteAtlasImage {
|
SpriteAtlasImage {
|
||||||
atlas: atlas_idx,
|
atlas: atlas_idx as u32,
|
||||||
x: x as f32 / self.texture_width as f32,
|
x: x as f32 / self.texture_width as f32,
|
||||||
y: y as f32 / self.texture_height as f32,
|
y: y as f32 / self.texture_height as f32,
|
||||||
w: dim[0] as f32 / self.texture_width as f32,
|
w: dim[0] as f32 / self.texture_width as f32,
|
||||||
|
@ -190,13 +192,17 @@ impl AtlasSet {
|
||||||
return Ok(atlas_idx);
|
return Ok(atlas_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_files<F>(self, atlas_path: F, index_path: &Path) -> Result<()>
|
pub fn save_files<F>(mut self, atlas_path: F, index_path: &Path) -> Result<()>
|
||||||
where
|
where
|
||||||
F: Fn(usize) -> PathBuf,
|
F: Fn(usize) -> PathBuf,
|
||||||
{
|
{
|
||||||
// Save atlases
|
// Save atlases
|
||||||
for i in 0..self.texture_list.len() {
|
for i in 0..self.texture_list.len() {
|
||||||
self.texture_list[i].save(atlas_path(i))?;
|
let path = atlas_path(i);
|
||||||
|
self.texture_list[i].save(&path)?;
|
||||||
|
self.index
|
||||||
|
.atlas_list
|
||||||
|
.push(path.file_name().unwrap().to_str().unwrap().to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save index
|
// Save index
|
||||||
|
|
|
@ -11,7 +11,8 @@ use serde::{Deserialize, Serialize};
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct SpriteAtlasImage {
|
pub struct SpriteAtlasImage {
|
||||||
/// The index of the atlas this image is in
|
/// The index of the atlas this image is in
|
||||||
pub atlas: usize,
|
/// This is an index in SpriteAtlas.atlas_list
|
||||||
|
pub atlas: u32,
|
||||||
|
|
||||||
/// x-position of this image
|
/// x-position of this image
|
||||||
/// (between 0 and 1, using wgpu texture coordinates)
|
/// (between 0 and 1, using wgpu texture coordinates)
|
||||||
|
@ -36,6 +37,9 @@ pub struct SpriteAtlasImage {
|
||||||
pub struct SpriteAtlas {
|
pub struct SpriteAtlas {
|
||||||
/// The images in this atlas
|
/// The images in this atlas
|
||||||
pub index: HashMap<PathBuf, SpriteAtlasImage>,
|
pub index: HashMap<PathBuf, SpriteAtlasImage>,
|
||||||
|
|
||||||
|
/// The file names of the atlas textures we've generated
|
||||||
|
pub atlas_list: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpriteAtlas {
|
impl SpriteAtlas {
|
||||||
|
@ -43,6 +47,7 @@ impl SpriteAtlas {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
index: HashMap::new(),
|
index: HashMap::new(),
|
||||||
|
atlas_list: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,16 @@ mod atlasset;
|
||||||
|
|
||||||
use atlasset::AtlasSet;
|
use atlasset::AtlasSet;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use image::io::Reader;
|
use image::io::Reader;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
// TODO: warning when images have extra transparency
|
// TODO: warning when images have extra transparency
|
||||||
// TODO: don't re-encode. Direct to gpu?
|
|
||||||
// (maybe not, tiling is slow. Make it work with files first.)
|
|
||||||
// TODO: path for atlas files
|
|
||||||
// TODO: dynamic packing (for plugins)
|
// TODO: dynamic packing (for plugins)
|
||||||
|
// TODO: standalone cli (galactica should ask to run this)
|
||||||
|
// TODO: randomly assign sprites to textures, for efficiency
|
||||||
|
// TODO: group images by use case
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let mut files = Vec::new();
|
let mut files = Vec::new();
|
||||||
|
@ -38,8 +38,11 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = e.path().to_path_buf();
|
let path = e.path().to_path_buf();
|
||||||
let reader = Reader::open(&path)?;
|
let reader = Reader::open(&path)
|
||||||
let dim = reader.into_dimensions()?;
|
.with_context(|| format!("While reading file `{}`", path.display()))?;
|
||||||
|
let dim = reader.into_dimensions().with_context(|| {
|
||||||
|
format!("While reading dimensions of file `{}`", path.display())
|
||||||
|
})?;
|
||||||
files.push((path, [dim.0, dim.1]));
|
files.push((path, [dim.0, dim.1]));
|
||||||
total_dim += dim.0 as f64 * dim.1 as f64;
|
total_dim += dim.0 as f64 * dim.1 as f64;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Pick frame of animation from an instance.
|
||||||
|
//
|
||||||
|
// This function assumes that the uniform header has been loaded,
|
||||||
|
// and that `InstanceInput` contains a field `texture_index`
|
||||||
|
fn animate(instance: InstanceInput, age: f32) -> u32 {
|
||||||
|
|
||||||
|
let idx = instance.texture_index;
|
||||||
|
let len = sprites.data[idx].frame_count;
|
||||||
|
let rep = sprites.data[idx].repeatmode;
|
||||||
|
let fps = sprites.data[idx].fps;
|
||||||
|
var frame: u32 = u32(0);
|
||||||
|
|
||||||
|
|
||||||
|
// Repeat
|
||||||
|
if rep == u32(1) {
|
||||||
|
|
||||||
|
frame = u32(min(
|
||||||
|
(age / fps),
|
||||||
|
f32(len) - 1.0
|
||||||
|
));
|
||||||
|
|
||||||
|
// Reverse
|
||||||
|
} else if rep == u32(2) {
|
||||||
|
let x = age / fps;
|
||||||
|
let m = f32(len) * 2.0 - 1.0;
|
||||||
|
// x fmod m
|
||||||
|
frame = u32(x - floor(x / m) * m);
|
||||||
|
|
||||||
|
if frame >= len {
|
||||||
|
frame = len + len - frame - u32(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repeat (default)
|
||||||
|
} else {
|
||||||
|
let x = age / fps;
|
||||||
|
let m = f32(len);
|
||||||
|
// x fmod m
|
||||||
|
frame = u32(x - floor(x / m) * m);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return frame + sprites.data[idx].first_frame;
|
||||||
|
}
|
|
@ -26,39 +26,7 @@ var texture_array: binding_array<texture_2d<f32>>;
|
||||||
var sampler_array: binding_array<sampler>;
|
var sampler_array: binding_array<sampler>;
|
||||||
|
|
||||||
|
|
||||||
fn fmod(x: f32, m: f32) -> f32 {
|
// INCLUDE: animate.wgsl
|
||||||
return x - floor(x / m) * m;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns texture index
|
|
||||||
// TODO: random age
|
|
||||||
// TODO: preprocessor include function
|
|
||||||
// TODO: packed location config, better error
|
|
||||||
// TODO: bounce animations
|
|
||||||
// TODO: animation randomness?
|
|
||||||
fn animate(instance: InstanceInput) -> u32 {
|
|
||||||
// Age doesn't make sense here, so arbitrarily pick zero.
|
|
||||||
let age = global.current_time.x;
|
|
||||||
let len = sprites.data[instance.texture_index].frame_count;
|
|
||||||
let rep = sprites.data[instance.texture_index].repeatmode;
|
|
||||||
let fps = sprites.data[instance.texture_index].fps;
|
|
||||||
var frame: u32 = u32(0);
|
|
||||||
if rep == u32(1) {
|
|
||||||
// Repeat
|
|
||||||
frame = u32(fmod(
|
|
||||||
(age / fps),
|
|
||||||
f32(len)
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
// Once
|
|
||||||
frame = u32(min(
|
|
||||||
(age / fps),
|
|
||||||
f32(len) - 1.0
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return frame + sprites.data[instance.texture_index].first_frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn vertex_main(
|
fn vertex_main(
|
||||||
|
@ -76,8 +44,8 @@ fn vertex_main(
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
out.position = transform * vec4<f32>(vertex.position, 1.0);
|
out.position = transform * vec4<f32>(vertex.position, 1.0);
|
||||||
|
|
||||||
let t = atlas.data[animate(instance)];
|
let t = atlas.data[animate(instance, global.current_time.x)];
|
||||||
out.texture_index = u32(0);
|
out.texture_index = t.atlas_texture;
|
||||||
out.texture_coords = vec2(t.xpos, t.ypos);
|
out.texture_coords = vec2(t.xpos, t.ypos);
|
||||||
if vertex.texture_coords.x == 1.0 {
|
if vertex.texture_coords.x == 1.0 {
|
||||||
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
|
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
|
||||||
|
|
|
@ -29,33 +29,7 @@ var texture_array: binding_array<texture_2d<f32>>;
|
||||||
var sampler_array: binding_array<sampler>;
|
var sampler_array: binding_array<sampler>;
|
||||||
|
|
||||||
|
|
||||||
// Returns texture index
|
// INCLUDE: animate.wgsl
|
||||||
fn animate(instance: InstanceInput) -> u32 {
|
|
||||||
let age = global.current_time.x - instance.created;
|
|
||||||
let len = sprites.data[instance.texture_index].frame_count;
|
|
||||||
let rep = sprites.data[instance.texture_index].repeatmode;
|
|
||||||
let fps = sprites.data[instance.texture_index].fps;
|
|
||||||
var frame: u32 = u32(0);
|
|
||||||
if rep == u32(1) {
|
|
||||||
// Repeat
|
|
||||||
frame = u32(fmod(
|
|
||||||
(age / fps),
|
|
||||||
f32(len)
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
// Once
|
|
||||||
frame = u32(min(
|
|
||||||
(age / fps),
|
|
||||||
f32(len) - 1.0
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return frame + sprites.data[instance.texture_index].first_frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fmod(x: f32, m: f32) -> f32 {
|
|
||||||
return x - floor(x / m) * m;
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn vertex_main(
|
fn vertex_main(
|
||||||
|
@ -66,51 +40,25 @@ fn vertex_main(
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
out.texture_coords = vertex.texture_coords;
|
out.texture_coords = vertex.texture_coords;
|
||||||
|
|
||||||
|
// Skip expired particles
|
||||||
if instance.expires < global.current_time.x {
|
if instance.expires < global.current_time.x {
|
||||||
out.texture_index = u32(0);
|
out.texture_index = u32(0);
|
||||||
|
// Draw off screen
|
||||||
out.position = vec4<f32>(2.0, 2.0, 0.0, 1.0);
|
out.position = vec4<f32>(2.0, 2.0, 0.0, 1.0);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
let age = global.current_time.x - instance.created;
|
let age = global.current_time.x - instance.created;
|
||||||
|
|
||||||
let len = sprites.data[instance.texture_index].frame_count;
|
// Apply transformations
|
||||||
let rep = sprites.data[instance.texture_index].repeatmode;
|
|
||||||
let fps = sprites.data[instance.texture_index].fps;
|
|
||||||
var frame: u32 = u32(0);
|
|
||||||
if rep == u32(1) {
|
|
||||||
// Repeat
|
|
||||||
frame = u32(fmod(
|
|
||||||
(age / fps),
|
|
||||||
f32(len)
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
// Once
|
|
||||||
frame = u32(min(
|
|
||||||
(age / fps),
|
|
||||||
f32(len) - 1.0
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let t = atlas.data[animate(instance)];
|
|
||||||
out.texture_index = u32(0);
|
|
||||||
out.texture_coords = vec2(t.xpos, t.ypos);
|
|
||||||
if vertex.texture_coords.x == 1.0 {
|
|
||||||
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
|
|
||||||
}
|
|
||||||
if vertex.texture_coords.y == 1.0 {
|
|
||||||
out.texture_coords = vec2(out.texture_coords.x, out.texture_coords.y + t.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
let rotation = mat2x2(instance.rotation_0, instance.rotation_1);
|
let rotation = mat2x2(instance.rotation_0, instance.rotation_1);
|
||||||
|
|
||||||
var scale: f32 = instance.size / global.camera_zoom.x;
|
var scale: f32 = instance.size / global.camera_zoom.x;
|
||||||
var pos: vec2<f32> = vec2(vertex.position.x, vertex.position.y);
|
var pos: vec2<f32> = vec2(vertex.position.x, vertex.position.y);
|
||||||
|
|
||||||
pos = pos * vec2<f32>(
|
pos = pos * vec2<f32>(
|
||||||
sprites.data[instance.texture_index].aspect * scale / global.window_aspect.x,
|
sprites.data[instance.texture_index].aspect * scale / global.window_aspect.x,
|
||||||
scale
|
scale
|
||||||
);
|
);
|
||||||
|
|
||||||
pos = rotation * pos;
|
pos = rotation * pos;
|
||||||
|
|
||||||
var ipos: vec2<f32> = (
|
var ipos: vec2<f32> = (
|
||||||
|
@ -125,6 +73,19 @@ fn vertex_main(
|
||||||
);
|
);
|
||||||
|
|
||||||
out.position = vec4<f32>(pos, 0.0, 1.0);
|
out.position = vec4<f32>(pos, 0.0, 1.0);
|
||||||
|
|
||||||
|
|
||||||
|
// Compute texture coordinates
|
||||||
|
let t = atlas.data[animate(instance, age)];
|
||||||
|
out.texture_index = u32(t.atlas_texture);
|
||||||
|
out.texture_coords = vec2(t.xpos, t.ypos);
|
||||||
|
if vertex.texture_coords.x == 1.0 {
|
||||||
|
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
|
||||||
|
}
|
||||||
|
if vertex.texture_coords.y == 1.0 {
|
||||||
|
out.texture_coords = vec2(out.texture_coords.x, out.texture_coords.y + t.height);
|
||||||
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ var sampler_array: binding_array<sampler>;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn fmod(x: vec2<f32>, m: f32) -> vec2<f32> {
|
fn fmod(x: vec2<f32>, m: f32) -> vec2<f32> {
|
||||||
return x - floor(x / m) * m;
|
return x - floor(x / m) * m;
|
||||||
}
|
}
|
||||||
|
@ -108,9 +107,11 @@ fn vertex_main(
|
||||||
|
|
||||||
out.position = vec4<f32>(pos, 0.0, 1.0) * instance.position.z;
|
out.position = vec4<f32>(pos, 0.0, 1.0) * instance.position.z;
|
||||||
|
|
||||||
|
|
||||||
|
// Starfield sprites may not be animated
|
||||||
let i = sprites.data[global.starfield_sprite.x].first_frame;
|
let i = sprites.data[global.starfield_sprite.x].first_frame;
|
||||||
let t = atlas.data[i];
|
let t = atlas.data[i];
|
||||||
out.texture_index = u32(0);
|
out.texture_index = u32(t.atlas_texture);
|
||||||
out.texture_coords = vec2(t.xpos, t.ypos);
|
out.texture_coords = vec2(t.xpos, t.ypos);
|
||||||
if vertex.texture_coords.x == 1.0 {
|
if vertex.texture_coords.x == 1.0 {
|
||||||
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
|
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
|
||||||
|
|
|
@ -27,6 +27,7 @@ var texture_array: binding_array<texture_2d<f32>>;
|
||||||
var sampler_array: binding_array<sampler>;
|
var sampler_array: binding_array<sampler>;
|
||||||
|
|
||||||
|
|
||||||
|
// INCLUDE: animate.wgsl
|
||||||
|
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
|
@ -46,9 +47,9 @@ fn vertex_main(
|
||||||
out.position = transform * vec4<f32>(vertex.position, 1.0);
|
out.position = transform * vec4<f32>(vertex.position, 1.0);
|
||||||
out.color_transform = instance.color_transform;
|
out.color_transform = instance.color_transform;
|
||||||
|
|
||||||
let i = sprites.data[instance.texture_index].first_frame;
|
// Pick texture frame
|
||||||
let t = atlas.data[i];
|
let t = atlas.data[animate(instance, global.current_time.x)];
|
||||||
out.texture_index = u32(0);
|
out.texture_index = u32(t.atlas_texture);
|
||||||
out.texture_coords = vec2(t.xpos, t.ypos);
|
out.texture_coords = vec2(t.xpos, t.ypos);
|
||||||
if vertex.texture_coords.x == 1.0 {
|
if vertex.texture_coords.x == 1.0 {
|
||||||
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
|
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
|
||||||
|
|
|
@ -11,6 +11,11 @@ pub struct ImageLocation {
|
||||||
pub ypos: f32,
|
pub ypos: f32,
|
||||||
pub width: f32,
|
pub width: f32,
|
||||||
pub height: f32,
|
pub height: f32,
|
||||||
|
|
||||||
|
// The index of the texture this image is in
|
||||||
|
pub atlas_texture: u32,
|
||||||
|
|
||||||
|
pub _padding: [f32; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
|
|
@ -44,6 +44,12 @@ impl GlobalUniform {
|
||||||
ypos: f32,
|
ypos: f32,
|
||||||
width: f32,
|
width: f32,
|
||||||
height: f32,
|
height: f32,
|
||||||
|
|
||||||
|
atlas_texture: u32,
|
||||||
|
|
||||||
|
padding_a: f32,
|
||||||
|
padding_b: f32,
|
||||||
|
padding_c: f32,
|
||||||
};
|
};
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
|
@ -60,17 +60,29 @@ struct VertexBuffers {
|
||||||
particle: Rc<VertexBuffer>,
|
particle: Rc<VertexBuffer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Preprocess shader files
|
/// Basic wgsl preprocesser
|
||||||
fn preprocess_shader(
|
fn preprocess_shader(
|
||||||
shader: &str,
|
shader: &str,
|
||||||
global_uniform: &GlobalUniform,
|
global_uniform: &GlobalUniform,
|
||||||
global_uniform_group: u32,
|
global_uniform_group: u32,
|
||||||
) -> String {
|
) -> String {
|
||||||
// Insert common headers
|
// Insert dynamically-generated global definitions
|
||||||
shader.replace(
|
let shader = shader.replace(
|
||||||
"// INCLUDE: global uniform header",
|
"// INCLUDE: global uniform header",
|
||||||
&global_uniform.shader_header(global_uniform_group),
|
&global_uniform.shader_header(global_uniform_group),
|
||||||
)
|
);
|
||||||
|
|
||||||
|
// Insert common functions
|
||||||
|
let shader = shader.replace(
|
||||||
|
"// INCLUDE: animate.wgsl",
|
||||||
|
&include_str!(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/shaders/include/",
|
||||||
|
"animate.wgsl"
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GPUState {
|
impl GPUState {
|
||||||
|
|
|
@ -4,9 +4,10 @@ use crate::{
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bytemuck::Zeroable;
|
use bytemuck::Zeroable;
|
||||||
|
use galactica_constants::ASSET_CACHE;
|
||||||
use galactica_packer::SpriteAtlasImage;
|
use galactica_packer::SpriteAtlasImage;
|
||||||
use image::GenericImageView;
|
use image::GenericImageView;
|
||||||
use std::{fs::File, io::Read, num::NonZeroU32};
|
use std::{fs::File, io::Read, num::NonZeroU32, path::Path};
|
||||||
use wgpu::BindGroupLayout;
|
use wgpu::BindGroupLayout;
|
||||||
|
|
||||||
pub(crate) struct RawTexture {
|
pub(crate) struct RawTexture {
|
||||||
|
@ -94,11 +95,19 @@ impl TextureArray {
|
||||||
// Load all textures
|
// Load all textures
|
||||||
let mut texture_data = Vec::new();
|
let mut texture_data = Vec::new();
|
||||||
|
|
||||||
println!("opening image");
|
for a in ct.atlas_files() {
|
||||||
let mut f = File::open("atlas-0.bmp")?;
|
println!("opening {a}");
|
||||||
let mut bytes = Vec::new();
|
let p = Path::new(ASSET_CACHE);
|
||||||
f.read_to_end(&mut bytes)?;
|
let mut f = File::open(p.join(a))?;
|
||||||
texture_data.push(RawTexture::from_bytes(&device, &queue, &bytes, "Atlas")?);
|
let mut bytes = Vec::new();
|
||||||
|
f.read_to_end(&mut bytes)?;
|
||||||
|
texture_data.push(RawTexture::from_bytes(
|
||||||
|
&device,
|
||||||
|
&queue,
|
||||||
|
&bytes,
|
||||||
|
&format!("Atlas `{a}`"),
|
||||||
|
)?);
|
||||||
|
}
|
||||||
|
|
||||||
let mut image_locations = ImageLocationArray::zeroed();
|
let mut image_locations = ImageLocationArray::zeroed();
|
||||||
let mut sprite_data = SpriteDataArray::zeroed();
|
let mut sprite_data = SpriteDataArray::zeroed();
|
||||||
|
@ -125,6 +134,8 @@ impl TextureArray {
|
||||||
ypos: image.y,
|
ypos: image.y,
|
||||||
width: image.w,
|
width: image.w,
|
||||||
height: image.h,
|
height: image.h,
|
||||||
|
atlas_texture: image.atlas,
|
||||||
|
_padding: Default::default(),
|
||||||
};
|
};
|
||||||
image_counter += 1;
|
image_counter += 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub fn build_radar(
|
||||||
let hide_range = 0.85;
|
let hide_range = 0.85;
|
||||||
let shrink_distance = 20.0;
|
let shrink_distance = 20.0;
|
||||||
let system_object_scale = 1.0 / 600.0;
|
let system_object_scale = 1.0 / 600.0;
|
||||||
let ship_scale = 1.0 / 10.0;
|
let ship_scale = 1.0 / 15.0;
|
||||||
|
|
||||||
let (_, player_body) = physics.get_ship_body(player).unwrap();
|
let (_, player_body) = physics.get_ship_body(player).unwrap();
|
||||||
let player_position = util::rigidbody_position(player_body);
|
let player_position = util::rigidbody_position(player_body);
|
||||||
|
|
Loading…
Reference in New Issue