Cleanup
parent
fd94cd6701
commit
abd41af202
|
@ -0,0 +1,21 @@
|
|||
use crate::physics::Pfloat;
|
||||
|
||||
pub const ZOOM_MIN: Pfloat = 200.0;
|
||||
pub const ZOOM_MAX: Pfloat = 2000.0;
|
||||
|
||||
// Z-axis range for starfield stars
|
||||
pub const STARFIELD_PARALLAX_MIN: f32 = 100.0;
|
||||
pub const STARFIELD_PARALLAX_MAX: f32 = 200.0;
|
||||
// Size of a square starfield tile, in game units.
|
||||
// A tile of size PARALLAX_MAX * screen-size-in-game-units
|
||||
// will completely cover a (square) screen. This depends on zoom!
|
||||
//
|
||||
// Use a value smaller than zoom_max for debug.
|
||||
pub const STARFIELD_SIZE: u64 = STARFIELD_PARALLAX_MAX as u64 * ZOOM_MAX as u64;
|
||||
// Average number of stars per game unit
|
||||
pub const STARFIELD_DENSITY: f64 = 0.01;
|
||||
// Number of stars in one starfield tile
|
||||
// Must fit inside an i32
|
||||
pub const STARFIELD_COUNT: u64 = (STARFIELD_SIZE as f64 * STARFIELD_DENSITY) as u64;
|
||||
|
||||
pub const CONTENT_ROOT: &'static str = "./content";
|
|
@ -0,0 +1,13 @@
|
|||
use cgmath::Point2;
|
||||
|
||||
use crate::physics::Pfloat;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Camera {
|
||||
/// Camera center
|
||||
pub pos: Point2<Pfloat>,
|
||||
|
||||
/// Camera zoom
|
||||
/// (How many game units tall is the viewport?)
|
||||
pub zoom: Pfloat,
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use cgmath::{Deg, Point2};
|
||||
|
||||
use crate::{physics::Pfloat, render::SpriteTexture, Sprite, Spriteable};
|
||||
use crate::{physics::Pfloat, render::Sprite, render::SpriteTexture, render::Spriteable};
|
||||
|
||||
pub struct Doodad {
|
||||
pub sprite: SpriteTexture,
|
|
@ -0,0 +1,84 @@
|
|||
use cgmath::Deg;
|
||||
use std::time::Instant;
|
||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
||||
|
||||
use super::{ship::ShipKind, Camera, InputStatus, Ship, System};
|
||||
use crate::{consts, content::Content, physics::Pfloat, render::Sprite, render::Spriteable};
|
||||
|
||||
pub struct Game {
|
||||
pub input: InputStatus,
|
||||
pub last_update: Instant,
|
||||
pub player: Ship,
|
||||
pub system: System,
|
||||
pub camera: Camera,
|
||||
}
|
||||
|
||||
impl Game {
|
||||
pub fn new(ct: Content) -> Self {
|
||||
Game {
|
||||
last_update: Instant::now(),
|
||||
input: InputStatus::new(),
|
||||
player: Ship::new(ShipKind::Gypsum, (0.0, 0.0).into()),
|
||||
camera: Camera {
|
||||
pos: (0.0, 0.0).into(),
|
||||
zoom: 500.0,
|
||||
},
|
||||
system: System::new(&ct.systems[0]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_key(&mut self, state: &ElementState, key: &VirtualKeyCode) {
|
||||
self.input.process_key(state, key)
|
||||
}
|
||||
|
||||
pub fn process_click(&mut self, state: &ElementState, key: &MouseButton) {
|
||||
self.input.process_click(state, key)
|
||||
}
|
||||
|
||||
pub fn process_scroll(&mut self, delta: &MouseScrollDelta, phase: &TouchPhase) {
|
||||
self.input.process_scroll(delta, phase)
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
let t: Pfloat = self.last_update.elapsed().as_secs_f32();
|
||||
|
||||
if self.input.key_thrust {
|
||||
self.player.body.thrust(50.0 * t);
|
||||
}
|
||||
|
||||
if self.input.key_right {
|
||||
self.player.body.rot(Deg(35.0) * t);
|
||||
}
|
||||
|
||||
if self.input.key_left {
|
||||
self.player.body.rot(Deg(-35.0) * t);
|
||||
}
|
||||
|
||||
if self.input.v_scroll != 0.0 {
|
||||
self.camera.zoom =
|
||||
(self.camera.zoom + self.input.v_scroll).clamp(consts::ZOOM_MIN, consts::ZOOM_MAX);
|
||||
self.input.v_scroll = 0.0;
|
||||
}
|
||||
|
||||
self.player.body.tick(t);
|
||||
self.camera.pos = self.player.body.pos;
|
||||
|
||||
self.last_update = Instant::now();
|
||||
}
|
||||
|
||||
pub fn get_sprites(&self) -> Vec<Sprite> {
|
||||
let mut sprites: Vec<Sprite> = Vec::new();
|
||||
|
||||
sprites.append(&mut self.system.get_sprites());
|
||||
sprites.push(self.player.get_sprite());
|
||||
|
||||
// Make sure sprites are drawn in the correct order
|
||||
// (note the reversed a, b in the comparator)
|
||||
//
|
||||
// TODO: use a gpu depth buffer with parallax as z-coordinate?
|
||||
// Might be overkill.
|
||||
sprites.sort_by(|a, b| b.parallax.total_cmp(&a.parallax));
|
||||
|
||||
return sprites;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
mod camera;
|
||||
mod doodad;
|
||||
mod game;
|
||||
mod inputstatus;
|
||||
mod ship;
|
||||
mod system;
|
||||
|
||||
pub use camera::Camera;
|
||||
pub use doodad::Doodad;
|
||||
pub use game::Game;
|
||||
pub use inputstatus::InputStatus;
|
||||
pub use ship::Ship;
|
||||
pub use system::System;
|
|
@ -1,6 +1,8 @@
|
|||
use cgmath::Point2;
|
||||
|
||||
use crate::{physics::Pfloat, physics::PhysBody, render::SpriteTexture, Sprite, Spriteable};
|
||||
use crate::{
|
||||
physics::Pfloat, physics::PhysBody, render::Sprite, render::SpriteTexture, render::Spriteable,
|
||||
};
|
||||
|
||||
pub enum ShipKind {
|
||||
Gypsum,
|
|
@ -1,10 +1,11 @@
|
|||
use crate::{
|
||||
content, physics::Pfloat, render::SpriteTexture, Doodad, Sprite, Spriteable, STARFIELD_COUNT,
|
||||
STARFIELD_PARALLAX_MAX, STARFIELD_PARALLAX_MIN, STARFIELD_SIZE,
|
||||
};
|
||||
use cgmath::{Point2, Vector2};
|
||||
use rand::{self, Rng};
|
||||
|
||||
use super::Doodad;
|
||||
use crate::{
|
||||
consts, content, physics::Pfloat, render::Sprite, render::SpriteTexture, render::Spriteable,
|
||||
};
|
||||
|
||||
pub struct StarfieldStar {
|
||||
/// Star coordinates, in world space.
|
||||
/// These are relative to the center of a starfield tile.
|
||||
|
@ -28,17 +29,18 @@ pub struct System {
|
|||
impl System {
|
||||
pub fn new(ct: &content::system::System) -> Self {
|
||||
let mut rng = rand::thread_rng();
|
||||
let sz = STARFIELD_SIZE as f32 / 2.0;
|
||||
let sz = consts::STARFIELD_SIZE as f32 / 2.0;
|
||||
let mut s = System {
|
||||
name: ct.name.clone(),
|
||||
bodies: Vec::new(),
|
||||
starfield: (0..STARFIELD_COUNT)
|
||||
starfield: (0..consts::STARFIELD_COUNT)
|
||||
.map(|_| StarfieldStar {
|
||||
pos: Point2 {
|
||||
x: rng.gen_range(-sz..=sz),
|
||||
y: rng.gen_range(-sz..=sz),
|
||||
},
|
||||
parallax: rng.gen_range(STARFIELD_PARALLAX_MIN..STARFIELD_PARALLAX_MAX),
|
||||
parallax: rng
|
||||
.gen_range(consts::STARFIELD_PARALLAX_MIN..consts::STARFIELD_PARALLAX_MAX),
|
||||
size: rng.gen_range(0.2..0.8), // TODO: configurable
|
||||
tint: Vector2 {
|
||||
x: rng.gen_range(0.0..=1.0),
|
174
src/main.rs
174
src/main.rs
|
@ -1,174 +1,29 @@
|
|||
mod consts;
|
||||
mod content;
|
||||
mod doodad;
|
||||
mod inputstatus;
|
||||
mod game;
|
||||
mod physics;
|
||||
mod render;
|
||||
mod ship;
|
||||
mod system;
|
||||
|
||||
use anyhow::Result;
|
||||
use cgmath::{Deg, Point2};
|
||||
use content::Content;
|
||||
use std::time::Instant;
|
||||
use winit::{
|
||||
event::{
|
||||
ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, TouchPhase,
|
||||
VirtualKeyCode, WindowEvent,
|
||||
},
|
||||
event::{Event, KeyboardInput, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
doodad::Doodad,
|
||||
inputstatus::InputStatus,
|
||||
physics::Pfloat,
|
||||
render::{GPUState, SpriteTexture},
|
||||
ship::{Ship, ShipKind},
|
||||
system::System,
|
||||
};
|
||||
fn main() -> Result<()> {
|
||||
let content = content::load_content_dir(consts::CONTENT_ROOT)?;
|
||||
let game = game::Game::new(content);
|
||||
|
||||
pub const ZOOM_MIN: Pfloat = 200.0;
|
||||
pub const ZOOM_MAX: Pfloat = 2000.0;
|
||||
|
||||
// Z-axis range for starfield stars
|
||||
pub const STARFIELD_PARALLAX_MIN: f32 = 100.0;
|
||||
pub const STARFIELD_PARALLAX_MAX: f32 = 200.0;
|
||||
// Size of a square starfield tile, in game units.
|
||||
// A tile of size PARALLAX_MAX * screen-size-in-game-units
|
||||
// will completely cover a (square) screen. This depends on zoom!
|
||||
//
|
||||
// Use a value smaller than zoom_max for debug.
|
||||
pub const STARFIELD_SIZE: u64 = STARFIELD_PARALLAX_MAX as u64 * ZOOM_MAX as u64;
|
||||
// Average number of stars per game unit
|
||||
pub const STARFIELD_DENSITY: f64 = 0.01;
|
||||
// Number of stars in one starfield tile
|
||||
// Must fit inside an i32
|
||||
pub const STARFIELD_COUNT: u64 = (STARFIELD_SIZE as f64 * STARFIELD_DENSITY) as u64;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Camera {
|
||||
/// Camera center
|
||||
pos: Point2<Pfloat>,
|
||||
|
||||
/// Camera zoom
|
||||
/// (How many game units tall is the viewport?)
|
||||
zoom: Pfloat,
|
||||
pollster::block_on(run(game))?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
trait Spriteable {
|
||||
fn get_sprite(&self) -> Sprite;
|
||||
}
|
||||
|
||||
struct Sprite {
|
||||
/// Name of the sprite to draw
|
||||
texture: SpriteTexture,
|
||||
|
||||
/// This object's position, in world coordinates.
|
||||
pos: Point2<Pfloat>,
|
||||
|
||||
/// The size of this sprite,
|
||||
/// given as height in world units.
|
||||
size: Pfloat,
|
||||
|
||||
/// Scale factor.
|
||||
/// if this is 1, sprite height is exactly self.size.
|
||||
scale: Pfloat,
|
||||
|
||||
/// This sprite's rotation
|
||||
/// (relative to north, measured ccw)
|
||||
angle: Deg<Pfloat>,
|
||||
|
||||
/// Parallax factor.
|
||||
/// More positive => farther away
|
||||
/// More negative => closer
|
||||
/// Zero: no parallax.
|
||||
parallax: Pfloat,
|
||||
}
|
||||
|
||||
struct Game {
|
||||
input: InputStatus,
|
||||
last_update: Instant,
|
||||
player: Ship,
|
||||
system: System,
|
||||
camera: Camera,
|
||||
}
|
||||
|
||||
impl Game {
|
||||
fn new(ct: Content) -> Self {
|
||||
Game {
|
||||
last_update: Instant::now(),
|
||||
input: InputStatus::new(),
|
||||
player: Ship::new(ShipKind::Gypsum, (0.0, 0.0).into()),
|
||||
camera: Camera {
|
||||
pos: (0.0, 0.0).into(),
|
||||
zoom: 500.0,
|
||||
},
|
||||
system: System::new(&ct.systems[0]),
|
||||
}
|
||||
}
|
||||
|
||||
fn process_key(&mut self, state: &ElementState, key: &VirtualKeyCode) {
|
||||
self.input.process_key(state, key)
|
||||
}
|
||||
|
||||
fn process_click(&mut self, state: &ElementState, key: &MouseButton) {
|
||||
self.input.process_click(state, key)
|
||||
}
|
||||
|
||||
fn process_scroll(&mut self, delta: &MouseScrollDelta, phase: &TouchPhase) {
|
||||
self.input.process_scroll(delta, phase)
|
||||
}
|
||||
|
||||
fn update(&mut self) {
|
||||
let t: Pfloat = self.last_update.elapsed().as_secs_f32();
|
||||
|
||||
if self.input.key_thrust {
|
||||
self.player.body.thrust(50.0 * t);
|
||||
}
|
||||
|
||||
if self.input.key_right {
|
||||
self.player.body.rot(Deg(35.0) * t);
|
||||
}
|
||||
|
||||
if self.input.key_left {
|
||||
self.player.body.rot(Deg(-35.0) * t);
|
||||
}
|
||||
|
||||
if self.input.v_scroll != 0.0 {
|
||||
self.camera.zoom = (self.camera.zoom + self.input.v_scroll).clamp(ZOOM_MIN, ZOOM_MAX);
|
||||
self.input.v_scroll = 0.0;
|
||||
}
|
||||
|
||||
println!("{:?}", self.player.body.pos);
|
||||
self.player.body.tick(t);
|
||||
self.camera.pos = self.player.body.pos;
|
||||
|
||||
self.last_update = Instant::now();
|
||||
}
|
||||
|
||||
fn get_sprites(&self) -> Vec<Sprite> {
|
||||
let mut sprites: Vec<Sprite> = Vec::new();
|
||||
|
||||
sprites.append(&mut self.system.get_sprites());
|
||||
sprites.push(self.player.get_sprite());
|
||||
|
||||
// Make sure sprites are drawn in the correct order
|
||||
// (note the reversed a, b in the comparator)
|
||||
//
|
||||
// TODO: use a gpu depth buffer with parallax as z-coordinate?
|
||||
// Might be overkill.
|
||||
sprites.sort_by(|a, b| b.parallax.total_cmp(&a.parallax));
|
||||
|
||||
return sprites;
|
||||
}
|
||||
}
|
||||
|
||||
async fn run(mut game: Game) -> Result<()> {
|
||||
async fn run(mut game: game::Game) -> Result<()> {
|
||||
let event_loop = EventLoop::new();
|
||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
|
||||
let mut gpu = GPUState::new(window).await?;
|
||||
let mut gpu = render::GPUState::new(window).await?;
|
||||
|
||||
gpu.update_starfield_buffer(&game);
|
||||
|
||||
|
@ -229,12 +84,3 @@ async fn run(mut game: Game) -> Result<()> {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let content = content::load_content_dir("./content")?;
|
||||
println!("{:?}", content);
|
||||
let game = Game::new(content);
|
||||
|
||||
pollster::block_on(run(game))?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
use crate::consts;
|
||||
|
||||
// We can draw at most this many sprites on the screen.
|
||||
// TODO: compile-time option
|
||||
pub const SPRITE_INSTANCE_LIMIT: u64 = 100;
|
||||
|
||||
// Must be small enough to fit in an i32
|
||||
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
|
||||
pub const SHADER_MAIN_VERTEX: &'static str = "vertex_main";
|
||||
pub const SHADER_MAIN_FRAGMENT: &'static str = "fragment_main";
|
|
@ -5,18 +5,18 @@ use std::{iter, rc::Rc};
|
|||
use wgpu;
|
||||
use winit::{self, dpi::PhysicalSize, window::Window};
|
||||
|
||||
use crate::{Game, STARFIELD_COUNT, STARFIELD_PARALLAX_MIN, STARFIELD_SIZE, ZOOM_MAX, ZOOM_MIN};
|
||||
|
||||
use super::{
|
||||
consts::{SPRITE_INSTANCE_LIMIT, STARFIELD_INSTANCE_LIMIT},
|
||||
globaldata::{GlobalData, GlobalDataContent},
|
||||
pipeline::PipelineBuilder,
|
||||
texturearray::TextureArray,
|
||||
vertexbuffer::{
|
||||
data::{SPRITE_INDICES, SPRITE_VERTICES},
|
||||
consts::{SPRITE_INDICES, SPRITE_VERTICES},
|
||||
types::{SpriteInstance, StarfieldInstance, TexturedVertex},
|
||||
VertexBuffer,
|
||||
},
|
||||
};
|
||||
use crate::{consts, game::Game};
|
||||
|
||||
pub struct GPUState {
|
||||
device: wgpu::Device,
|
||||
|
@ -43,13 +43,6 @@ struct VertexBuffers {
|
|||
}
|
||||
|
||||
impl GPUState {
|
||||
// We can draw at most this many sprites on the screen.
|
||||
// TODO: compile-time option
|
||||
pub const SPRITE_INSTANCE_LIMIT: u64 = 100;
|
||||
|
||||
// Must be small enough to fit in an i32
|
||||
pub const STARFIELD_INSTANCE_LIMIT: u64 = STARFIELD_COUNT * 24;
|
||||
|
||||
pub async fn new(window: Window) -> Result<Self> {
|
||||
let window_size = window.inner_size();
|
||||
let window_aspect = window_size.width as f32 / window_size.height as f32;
|
||||
|
@ -119,7 +112,7 @@ impl GPUState {
|
|||
&device,
|
||||
Some(SPRITE_VERTICES),
|
||||
Some(SPRITE_INDICES),
|
||||
Self::SPRITE_INSTANCE_LIMIT,
|
||||
SPRITE_INSTANCE_LIMIT,
|
||||
)),
|
||||
|
||||
starfield: Rc::new(VertexBuffer::new::<TexturedVertex, StarfieldInstance>(
|
||||
|
@ -127,7 +120,7 @@ impl GPUState {
|
|||
&device,
|
||||
Some(SPRITE_VERTICES),
|
||||
Some(SPRITE_INDICES),
|
||||
Self::STARFIELD_INSTANCE_LIMIT,
|
||||
STARFIELD_INSTANCE_LIMIT,
|
||||
)),
|
||||
};
|
||||
|
||||
|
@ -217,8 +210,8 @@ impl GPUState {
|
|||
|
||||
for s in game.get_sprites() {
|
||||
// Parallax is computed here, so we can check if this sprite is visible.
|
||||
let pos =
|
||||
(s.pos - game.camera.pos.to_vec()) / (s.parallax + game.camera.zoom / ZOOM_MIN);
|
||||
let pos = (s.pos - game.camera.pos.to_vec())
|
||||
/ (s.parallax + game.camera.zoom / consts::ZOOM_MIN);
|
||||
let texture = self.texture_array.get_sprite_texture(s.texture);
|
||||
|
||||
// Game dimensions of this sprite post-scale.
|
||||
|
@ -246,7 +239,7 @@ impl GPUState {
|
|||
}
|
||||
|
||||
// Enforce sprite limit
|
||||
if instances.len() as u64 > Self::SPRITE_INSTANCE_LIMIT {
|
||||
if instances.len() as u64 > SPRITE_INSTANCE_LIMIT {
|
||||
// TODO: no panic, handle this better.
|
||||
unreachable!("Sprite limit exceeded!")
|
||||
}
|
||||
|
@ -259,17 +252,17 @@ impl GPUState {
|
|||
///
|
||||
/// Starfield data rarely changes, so this is called only when it's needed.
|
||||
pub fn update_starfield_buffer(&mut self, game: &Game) {
|
||||
let sz = STARFIELD_SIZE as f32;
|
||||
let sz = consts::STARFIELD_SIZE as f32;
|
||||
|
||||
// Compute window size in starfield tiles
|
||||
let mut nw_tile: Point2<i32> = {
|
||||
// Game coordinates (relative to camera) of nw corner of screen.
|
||||
let clip_nw = Point2::from((self.window_aspect, 1.0)) * ZOOM_MAX;
|
||||
let clip_nw = Point2::from((self.window_aspect, 1.0)) * consts::ZOOM_MAX;
|
||||
|
||||
// Parallax correction.
|
||||
// Also, adjust v for mod to work properly
|
||||
// (v is centered at 0)
|
||||
let v: Point2<f32> = clip_nw * STARFIELD_PARALLAX_MIN;
|
||||
let v: Point2<f32> = clip_nw * consts::STARFIELD_PARALLAX_MIN;
|
||||
let v_adj: Point2<f32> = (v.x + (sz / 2.0), v.y + (sz / 2.0)).into();
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
@ -293,8 +286,8 @@ impl GPUState {
|
|||
|
||||
// Truncate tile grid to buffer size
|
||||
// (The window won't be full of stars if our instance limit is too small)
|
||||
while ((nw_tile.x * 2 + 1) * (nw_tile.y * 2 + 1) * STARFIELD_COUNT as i32)
|
||||
> Self::STARFIELD_INSTANCE_LIMIT as i32
|
||||
while ((nw_tile.x * 2 + 1) * (nw_tile.y * 2 + 1) * consts::STARFIELD_COUNT as i32)
|
||||
> STARFIELD_INSTANCE_LIMIT as i32
|
||||
{
|
||||
nw_tile -= Vector2::from((1, 1));
|
||||
}
|
||||
|
@ -319,7 +312,7 @@ impl GPUState {
|
|||
}
|
||||
|
||||
// Enforce starfield limit
|
||||
if instances.len() as u64 > Self::STARFIELD_INSTANCE_LIMIT {
|
||||
if instances.len() as u64 > STARFIELD_INSTANCE_LIMIT {
|
||||
unreachable!("Starfield limit exceeded!")
|
||||
}
|
||||
|
||||
|
@ -377,7 +370,7 @@ impl GPUState {
|
|||
],
|
||||
window_aspect: [self.window_aspect, 0.0],
|
||||
starfield_texture: [self.texture_array.get_starfield_texture().index, 0],
|
||||
starfield_tile_size: [STARFIELD_SIZE as f32, 0.0],
|
||||
starfield_tile_size: [consts::STARFIELD_SIZE as f32, 0.0],
|
||||
}]),
|
||||
);
|
||||
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
mod consts;
|
||||
mod globaldata;
|
||||
mod gpustate;
|
||||
mod pipeline;
|
||||
mod sprite;
|
||||
mod texturearray;
|
||||
mod vertexbuffer;
|
||||
|
||||
pub use gpustate::GPUState;
|
||||
pub use sprite::{Sprite, Spriteable};
|
||||
|
||||
/// A handle to a sprite texture
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SpriteTexture(pub String);
|
||||
|
||||
/*
|
||||
// API correction matrix.
|
||||
// cgmath uses OpenGL's matrix format, which
|
||||
// needs to be converted to wgpu's matrix format.
|
||||
/*
|
||||
#[rustfmt::skip]
|
||||
const OPENGL_TO_WGPU_MATRIX: Matrix4<f32> = Matrix4::new(
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::rc::Rc;
|
||||
use wgpu;
|
||||
|
||||
use super::consts::{SHADER_MAIN_FRAGMENT, SHADER_MAIN_VERTEX};
|
||||
use super::vertexbuffer::VertexBuffer;
|
||||
|
||||
pub struct PipelineBuilder<'a> {
|
||||
|
@ -19,9 +20,6 @@ pub struct PipelineBuilder<'a> {
|
|||
}
|
||||
|
||||
impl<'a> PipelineBuilder<'a> {
|
||||
const VERTEX_MAIN: &'static str = "vertex_shader_main";
|
||||
const FRAGMENT_MAIN: &'static str = "fragment_shader_main";
|
||||
|
||||
pub fn new(label: &'a str, device: &'a wgpu::Device) -> Self {
|
||||
Self {
|
||||
label,
|
||||
|
@ -98,13 +96,13 @@ impl<'a> PipelineBuilder<'a> {
|
|||
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: Self::VERTEX_MAIN,
|
||||
entry_point: SHADER_MAIN_VERTEX,
|
||||
buffers: &self.vertex_buffer.unwrap().layout,
|
||||
},
|
||||
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: Self::FRAGMENT_MAIN,
|
||||
entry_point: SHADER_MAIN_FRAGMENT,
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: self.format.unwrap(),
|
||||
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// Vertex shader
|
||||
struct InstanceInput {
|
||||
@location(2) rotation_matrix_0: vec2<f32>,
|
||||
@location(3) rotation_matrix_1: vec2<f32>,
|
||||
|
@ -32,8 +31,16 @@ struct GlobalUniform {
|
|||
};
|
||||
|
||||
|
||||
@group(0) @binding(0)
|
||||
var texture_array: binding_array<texture_2d<f32>>;
|
||||
@group(0) @binding(1)
|
||||
var sampler_array: binding_array<sampler>;
|
||||
|
||||
|
||||
|
||||
|
||||
@vertex
|
||||
fn vertex_shader_main(
|
||||
fn vertex_main(
|
||||
vertex: VertexInput,
|
||||
instance: InstanceInput,
|
||||
) -> VertexOutput {
|
||||
|
@ -71,15 +78,8 @@ fn vertex_shader_main(
|
|||
}
|
||||
|
||||
|
||||
// Fragment shader
|
||||
@group(0) @binding(0)
|
||||
var texture_array: binding_array<texture_2d<f32>>;
|
||||
@group(0) @binding(1)
|
||||
var sampler_array: binding_array<sampler>;
|
||||
|
||||
|
||||
@fragment
|
||||
fn fragment_shader_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
fn fragment_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
return textureSampleLevel(
|
||||
texture_array[in.index],
|
||||
sampler_array[in.index],
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// Vertex shader
|
||||
struct InstanceInput {
|
||||
@location(2) position: vec2<f32>,
|
||||
@location(3) parallax: f32,
|
||||
|
@ -29,12 +28,20 @@ struct GlobalUniform {
|
|||
};
|
||||
|
||||
|
||||
@group(0) @binding(0)
|
||||
var texture_array: binding_array<texture_2d<f32>>;
|
||||
@group(0) @binding(1)
|
||||
var sampler_array: binding_array<sampler>;
|
||||
|
||||
|
||||
|
||||
|
||||
fn fmod(x: vec2<f32>, m: f32) -> vec2<f32> {
|
||||
return x - floor(x / m) * m;
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vertex_shader_main(
|
||||
fn vertex_main(
|
||||
vertex: VertexInput,
|
||||
instance: InstanceInput,
|
||||
) -> VertexOutput {
|
||||
|
@ -95,15 +102,8 @@ fn vertex_shader_main(
|
|||
return out;
|
||||
}
|
||||
|
||||
|
||||
@group(0) @binding(0)
|
||||
var texture_array: binding_array<texture_2d<f32>>;
|
||||
@group(0) @binding(1)
|
||||
var sampler_array: binding_array<sampler>;
|
||||
|
||||
// Fragment shader
|
||||
@fragment
|
||||
fn fragment_shader_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
fn fragment_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
let b = 0.8 - (in.tint.x / 1.5); // brightness
|
||||
let c = in.tint.y; // color
|
||||
// TODO: saturation
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
use cgmath::{Deg, Point2};
|
||||
|
||||
use super::SpriteTexture;
|
||||
use crate::physics::Pfloat;
|
||||
|
||||
pub struct Sprite {
|
||||
/// Name of the sprite to draw
|
||||
pub texture: SpriteTexture,
|
||||
|
||||
/// This object's position, in world coordinates.
|
||||
pub pos: Point2<Pfloat>,
|
||||
|
||||
/// The size of this sprite,
|
||||
/// given as height in world units.
|
||||
pub size: Pfloat,
|
||||
|
||||
/// Scale factor.
|
||||
/// if this is 1, sprite height is exactly self.size.
|
||||
pub scale: Pfloat,
|
||||
|
||||
/// This sprite's rotation
|
||||
/// (relative to north, measured ccw)
|
||||
pub angle: Deg<Pfloat>,
|
||||
|
||||
/// Parallax factor.
|
||||
/// Corresponds to z-distance, and affects
|
||||
/// position and scale.
|
||||
pub parallax: Pfloat,
|
||||
}
|
||||
|
||||
pub trait Spriteable {
|
||||
fn get_sprite(&self) -> Sprite;
|
||||
}
|
|
@ -2,10 +2,11 @@ use anyhow::Result;
|
|||
use std::{collections::HashMap, num::NonZeroU32, path::PathBuf};
|
||||
use wgpu::BindGroupLayout;
|
||||
|
||||
use super::{
|
||||
super::consts::TEXTURE_INDEX_PATH, loader::TextureLoader, Texture, TextureArrayConfig,
|
||||
};
|
||||
use crate::render::SpriteTexture;
|
||||
|
||||
use super::{loader::TextureLoader, Texture, TextureArrayConfig};
|
||||
|
||||
pub struct TextureArray {
|
||||
pub bind_group: wgpu::BindGroup,
|
||||
pub bind_group_layout: BindGroupLayout,
|
||||
|
@ -14,8 +15,6 @@ pub struct TextureArray {
|
|||
}
|
||||
|
||||
impl TextureArray {
|
||||
const INDEX_PATH: &'static str = "assets";
|
||||
|
||||
pub fn get_starfield_texture(&self) -> Texture {
|
||||
return self.get_texture(&self.config.starfield);
|
||||
}
|
||||
|
@ -33,7 +32,7 @@ impl TextureArray {
|
|||
|
||||
pub fn new(device: &wgpu::Device, queue: &wgpu::Queue) -> Result<Self> {
|
||||
// Load all textures
|
||||
let loader = TextureLoader::load(device, queue, &PathBuf::from(Self::INDEX_PATH))?;
|
||||
let loader = TextureLoader::load(device, queue, &PathBuf::from(TEXTURE_INDEX_PATH))?;
|
||||
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use anyhow::Result;
|
||||
use std::{collections::HashMap, fs::File, io::Read, path::Path, path::PathBuf};
|
||||
|
||||
use super::{rawtexture::RawTexture, Texture, TextureArrayConfig};
|
||||
use super::{
|
||||
super::consts::TEXTURE_INDEX_NAME, rawtexture::RawTexture, Texture, TextureArrayConfig,
|
||||
};
|
||||
|
||||
mod fields {
|
||||
|
||||
|
@ -64,12 +66,10 @@ pub struct TextureLoader {
|
|||
}
|
||||
|
||||
impl TextureLoader {
|
||||
const INDEX_NAME: &'static str = "_index.toml";
|
||||
|
||||
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(Self::INDEX_NAME))?
|
||||
let _ = File::open(index_path.join(TEXTURE_INDEX_NAME))?
|
||||
.read_to_string(&mut texture_index_string);
|
||||
toml::from_str(&texture_index_string)?
|
||||
};
|
||||
|
@ -79,6 +79,7 @@ impl TextureLoader {
|
|||
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();
|
||||
|
@ -157,7 +158,7 @@ impl TextureLoader {
|
|||
let sub_root = texture_root.join(&path);
|
||||
let index: fields::SubIndex = {
|
||||
let mut texture_index_string = String::new();
|
||||
let _ = File::open(sub_root.join(Self::INDEX_NAME))?
|
||||
let _ = File::open(sub_root.join(TEXTURE_INDEX_NAME))?
|
||||
.read_to_string(&mut texture_index_string);
|
||||
toml::from_str(&texture_index_string)?
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
pub mod data;
|
||||
pub mod consts;
|
||||
pub mod types;
|
||||
mod vertexbuffer;
|
||||
|
||||
|
|
Loading…
Reference in New Issue