parent
abd41af202
commit
a5b3932e9d
|
@ -4,9 +4,8 @@ name = "12 Autumn above"
|
|||
|
||||
[object.star]
|
||||
sprite = "star::star"
|
||||
position = [0.0, 0.0]
|
||||
position = [0.0, 0.0, 20.0]
|
||||
size = 1000
|
||||
parallax = 20.0
|
||||
|
||||
|
||||
[object.earth]
|
||||
|
@ -14,8 +13,8 @@ sprite = "planet::earth"
|
|||
position.center = "star"
|
||||
position.radius = 4000
|
||||
position.angle = 0
|
||||
position.z = 10.0
|
||||
size = 1000
|
||||
parallax = 10.0
|
||||
|
||||
|
||||
[object.luna]
|
||||
|
@ -23,6 +22,6 @@ sprite = "planet::luna"
|
|||
position.center = "earth"
|
||||
position.radius = 1600
|
||||
position.angle = 135
|
||||
position.z = 7.8
|
||||
size = 500
|
||||
angle = -45
|
||||
parallax = 7.8
|
||||
|
|
|
@ -3,19 +3,29 @@ 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
|
||||
/// Z-axis range for starfield stars
|
||||
/// This does not affect scale.
|
||||
pub const STARFIELD_Z_MIN: f32 = 100.0;
|
||||
pub const STARFIELD_Z_MAX: f32 = 200.0;
|
||||
|
||||
/// Size range for starfield stars, in game units.
|
||||
/// This is scaled for zoom, but NOT for distance.
|
||||
pub const STARFIELD_SIZE_MIN: f32 = 0.2;
|
||||
pub const STARFIELD_SIZE_MAX: f32 = 1.8;
|
||||
|
||||
/// Size of a square starfield tile, in game units.
|
||||
/// A tile of size STARFIELD_Z_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_Z_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
|
||||
|
||||
/// 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;
|
||||
|
||||
/// Root directory of game content
|
||||
pub const CONTENT_ROOT: &'static str = "./content";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use anyhow::{bail, Context, Result};
|
||||
use cgmath::{Deg, Point2};
|
||||
use cgmath::{Deg, Point3};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use crate::physics::{Pfloat, Polar};
|
||||
|
@ -27,7 +27,6 @@ pub(in crate::content) mod toml {
|
|||
pub position: Position,
|
||||
|
||||
pub size: Pfloat,
|
||||
pub parallax: Pfloat,
|
||||
|
||||
pub radius: Option<Pfloat>,
|
||||
pub angle: Option<Pfloat>,
|
||||
|
@ -37,24 +36,25 @@ pub(in crate::content) mod toml {
|
|||
#[serde(untagged)]
|
||||
pub enum Position {
|
||||
Polar(PolarCoords),
|
||||
Cartesian(Coordinates),
|
||||
Cartesian(CoordinatesThree),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct PolarCoords {
|
||||
pub center: Coordinates,
|
||||
pub center: CoordinatesTwo,
|
||||
pub radius: Pfloat,
|
||||
pub angle: Pfloat,
|
||||
pub z: Pfloat,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Coordinates {
|
||||
pub enum CoordinatesTwo {
|
||||
Label(String),
|
||||
Coords([Pfloat; 2]),
|
||||
}
|
||||
|
||||
impl ToString for Coordinates {
|
||||
impl ToString for CoordinatesTwo {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Self::Label(s) => s.to_owned(),
|
||||
|
@ -62,6 +62,44 @@ pub(in crate::content) mod toml {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CoordinatesTwo {
|
||||
/// Transform a CoordinatesThree into a CoordinatesTwo by adding a NaN z component.
|
||||
/// Labels are not changed.
|
||||
pub fn to_three(&self) -> CoordinatesThree {
|
||||
match self {
|
||||
Self::Label(s) => CoordinatesThree::Label(s.clone()),
|
||||
Self::Coords(v) => CoordinatesThree::Coords([v[0], v[1], f32::NAN]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum CoordinatesThree {
|
||||
Label(String),
|
||||
Coords([Pfloat; 3]),
|
||||
}
|
||||
|
||||
impl ToString for CoordinatesThree {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Self::Label(s) => s.to_owned(),
|
||||
Self::Coords(v) => format!("{:?}", v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CoordinatesThree {
|
||||
/// Transform a CoordinatesThree into a CoordinatesTwo by deleting z component.
|
||||
/// Labels are not changed.
|
||||
pub fn to_two(&self) -> CoordinatesTwo {
|
||||
match self {
|
||||
Self::Label(s) => CoordinatesTwo::Label(s.clone()),
|
||||
Self::Coords(v) => CoordinatesTwo::Coords([v[0], v[1]]),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -73,20 +111,20 @@ pub struct System {
|
|||
#[derive(Debug)]
|
||||
pub struct Object {
|
||||
pub sprite: String,
|
||||
pub position: Point2<f32>,
|
||||
pub position: Point3<f32>,
|
||||
pub size: Pfloat,
|
||||
pub parallax: Pfloat,
|
||||
pub angle: Deg<Pfloat>,
|
||||
}
|
||||
|
||||
// Helper function for resolve_position, never called on its own.
|
||||
fn resolve_coordinates(
|
||||
objects: &HashMap<String, toml::Object>,
|
||||
cor: &toml::Coordinates,
|
||||
cor: &toml::CoordinatesThree,
|
||||
mut cycle_detector: HashSet<String>,
|
||||
) -> Result<Point2<f32>> {
|
||||
) -> Result<Point3<f32>> {
|
||||
match cor {
|
||||
toml::Coordinates::Coords(c) => Ok((*c).into()),
|
||||
toml::Coordinates::Label(l) => {
|
||||
toml::CoordinatesThree::Coords(c) => Ok((*c).into()),
|
||||
toml::CoordinatesThree::Label(l) => {
|
||||
if cycle_detector.contains(l) {
|
||||
bail!(
|
||||
"Found coordinate cycle: `{}`",
|
||||
|
@ -111,19 +149,28 @@ fn resolve_coordinates(
|
|||
}
|
||||
}
|
||||
|
||||
/// Given an object, resolve it's position as a Point3.
|
||||
fn resolve_position(
|
||||
objects: &HashMap<String, toml::Object>,
|
||||
obj: &toml::Object,
|
||||
cycle_detector: HashSet<String>,
|
||||
) -> Result<Point2<f32>> {
|
||||
) -> Result<Point3<f32>> {
|
||||
match &obj.position {
|
||||
toml::Position::Cartesian(c) => Ok(resolve_coordinates(objects, c, cycle_detector)?),
|
||||
toml::Position::Polar(p) => Ok(Polar {
|
||||
center: resolve_coordinates(&objects, &p.center, cycle_detector)?,
|
||||
toml::Position::Cartesian(c) => Ok(resolve_coordinates(objects, &c, cycle_detector)?),
|
||||
toml::Position::Polar(p) => {
|
||||
let r = resolve_coordinates(&objects, &p.center.to_three(), cycle_detector)?;
|
||||
let plane = Polar {
|
||||
center: (r.x, r.y).into(),
|
||||
radius: p.radius,
|
||||
angle: Deg(p.angle),
|
||||
}
|
||||
.to_cartesian()),
|
||||
.to_cartesian();
|
||||
Ok(Point3 {
|
||||
x: plane.x,
|
||||
y: plane.y,
|
||||
z: p.z,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,7 +187,6 @@ impl System {
|
|||
position: resolve_position(&value.object, &obj, cycle_detector)
|
||||
.with_context(|| format!("In object {:#?}", label))?,
|
||||
size: obj.size,
|
||||
parallax: obj.parallax,
|
||||
angle: Deg(obj.angle.unwrap_or(0.0)),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use cgmath::{Deg, Point2};
|
||||
use cgmath::{Deg, Point3};
|
||||
|
||||
use crate::{physics::Pfloat, render::Sprite, render::SpriteTexture, render::Spriteable};
|
||||
|
||||
pub struct Doodad {
|
||||
pub sprite: SpriteTexture,
|
||||
pub pos: Point2<Pfloat>,
|
||||
pub parallax: Pfloat,
|
||||
pub pos: Point3<Pfloat>,
|
||||
pub size: Pfloat,
|
||||
pub angle: Deg<Pfloat>,
|
||||
}
|
||||
|
@ -18,7 +17,6 @@ impl Spriteable for Doodad {
|
|||
pos: self.pos,
|
||||
angle: self.angle,
|
||||
size: self.size,
|
||||
parallax: self.parallax,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,9 +75,8 @@ impl Game {
|
|||
// 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));
|
||||
// TODO: use a gpu depth buffer instead.
|
||||
sprites.sort_by(|a, b| b.pos.z.total_cmp(&a.pos.z));
|
||||
|
||||
return sprites;
|
||||
}
|
||||
|
|
|
@ -41,11 +41,10 @@ impl Ship {
|
|||
impl Spriteable for Ship {
|
||||
fn get_sprite(&self) -> Sprite {
|
||||
return Sprite {
|
||||
pos: self.body.pos,
|
||||
pos: (self.body.pos.x, self.body.pos.y, 1.0).into(),
|
||||
texture: self.kind.sprite(),
|
||||
angle: self.body.angle,
|
||||
scale: 1.0,
|
||||
parallax: 1.0,
|
||||
size: self.kind.size(),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use cgmath::{Point2, Vector2};
|
||||
use cgmath::{Point3, Vector2};
|
||||
use rand::{self, Rng};
|
||||
|
||||
use super::Doodad;
|
||||
|
@ -9,14 +9,14 @@ use crate::{
|
|||
pub struct StarfieldStar {
|
||||
/// Star coordinates, in world space.
|
||||
/// These are relative to the center of a starfield tile.
|
||||
pub pos: Point2<Pfloat>,
|
||||
pub pos: Point3<Pfloat>,
|
||||
|
||||
// TODO: z-coordinate?
|
||||
pub parallax: Pfloat,
|
||||
/// Height in game units.
|
||||
/// Will be scaled for zoom, but not for distance.
|
||||
pub size: Pfloat,
|
||||
|
||||
/// Color/brightness variation.
|
||||
/// See shader.
|
||||
/// Color/brightness variation. Random between 0 and 1.
|
||||
/// Used in starfield shader.
|
||||
pub tint: Vector2<Pfloat>,
|
||||
}
|
||||
|
||||
|
@ -35,13 +35,12 @@ impl System {
|
|||
bodies: Vec::new(),
|
||||
starfield: (0..consts::STARFIELD_COUNT)
|
||||
.map(|_| StarfieldStar {
|
||||
pos: Point2 {
|
||||
pos: Point3 {
|
||||
x: rng.gen_range(-sz..=sz),
|
||||
y: rng.gen_range(-sz..=sz),
|
||||
z: rng.gen_range(consts::STARFIELD_Z_MIN..consts::STARFIELD_Z_MAX),
|
||||
},
|
||||
parallax: rng
|
||||
.gen_range(consts::STARFIELD_PARALLAX_MIN..consts::STARFIELD_PARALLAX_MAX),
|
||||
size: rng.gen_range(0.2..0.8), // TODO: configurable
|
||||
size: rng.gen_range(consts::STARFIELD_SIZE_MIN..consts::STARFIELD_SIZE_MAX),
|
||||
tint: Vector2 {
|
||||
x: rng.gen_range(0.0..=1.0),
|
||||
y: rng.gen_range(0.0..=1.0),
|
||||
|
@ -55,7 +54,6 @@ impl System {
|
|||
pos: o.position,
|
||||
sprite: SpriteTexture(o.sprite.to_owned()),
|
||||
size: o.size,
|
||||
parallax: o.parallax,
|
||||
angle: o.angle,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,23 +21,29 @@ pub struct GlobalDataContent {
|
|||
pub camera_position: [f32; 2],
|
||||
|
||||
/// Camera zoom value, in game units.
|
||||
/// Only first component has meaning.
|
||||
/// Second component is ignored.
|
||||
pub camera_zoom: [f32; 2],
|
||||
|
||||
/// Camera zoom min and max.
|
||||
pub camera_zoom_limits: [f32; 2],
|
||||
|
||||
/// Size ratio of window, in physical pixels
|
||||
pub window_size: [f32; 2],
|
||||
|
||||
// Aspect ration of window
|
||||
/// Only first component has meaning.
|
||||
/// Second component is ignored.
|
||||
pub window_aspect: [f32; 2],
|
||||
|
||||
/// Texture index of starfield sprites
|
||||
/// Only first component has meaning.
|
||||
/// Second component is ignored.
|
||||
pub starfield_texture: [u32; 2],
|
||||
|
||||
// Size of (square) starfield tiles, in game units
|
||||
/// Only first component has meaning.
|
||||
/// Second component is ignored.
|
||||
pub starfield_tile_size: [f32; 2],
|
||||
|
||||
// Min and max starfield star size, in game units
|
||||
pub starfield_size_limits: [f32; 2],
|
||||
}
|
||||
|
||||
impl GlobalDataContent {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use anyhow::Result;
|
||||
use bytemuck;
|
||||
use cgmath::{EuclideanSpace, Matrix2, Point2, Vector2};
|
||||
use cgmath::{EuclideanSpace, Matrix2, Point2, Vector2, Vector3};
|
||||
use std::{iter, rc::Rc};
|
||||
use wgpu;
|
||||
use winit::{self, dpi::PhysicalSize, window::Window};
|
||||
|
@ -16,7 +16,7 @@ use super::{
|
|||
VertexBuffer,
|
||||
},
|
||||
};
|
||||
use crate::{consts, game::Game};
|
||||
use crate::{consts, game::Game, physics::Pfloat};
|
||||
|
||||
pub struct GPUState {
|
||||
device: wgpu::Device,
|
||||
|
@ -209,14 +209,20 @@ impl GPUState {
|
|||
let clip_sw = Point2::from((self.window_aspect, -1.0)) * game.camera.zoom;
|
||||
|
||||
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 / consts::ZOOM_MIN);
|
||||
// Compute post-parallax position and distance-adjusted scale.
|
||||
// We do this here so we can check if a sprite is on the screen.
|
||||
let pos: Point2<Pfloat> = {
|
||||
(Point2 {
|
||||
x: s.pos.x,
|
||||
y: s.pos.y,
|
||||
} - game.camera.pos.to_vec())
|
||||
/ (s.pos.z + game.camera.zoom / consts::ZOOM_MIN)
|
||||
};
|
||||
let texture = self.texture_array.get_sprite_texture(s.texture);
|
||||
|
||||
// Game dimensions of this sprite post-scale.
|
||||
// Don't divide by 2, we use this later.
|
||||
let height = s.size * s.scale / s.parallax;
|
||||
let height = s.size * s.scale / s.pos.z;
|
||||
let width = height * texture.aspect;
|
||||
|
||||
// Don't draw (or compute matrices for)
|
||||
|
@ -233,7 +239,7 @@ impl GPUState {
|
|||
position: pos.into(),
|
||||
aspect: texture.aspect,
|
||||
rotation: Matrix2::from_angle(s.angle).into(),
|
||||
height,
|
||||
size: height,
|
||||
texture_index: texture.index,
|
||||
})
|
||||
}
|
||||
|
@ -262,7 +268,7 @@ impl GPUState {
|
|||
// Parallax correction.
|
||||
// Also, adjust v for mod to work properly
|
||||
// (v is centered at 0)
|
||||
let v: Point2<f32> = clip_nw * consts::STARFIELD_PARALLAX_MIN;
|
||||
let v: Point2<f32> = clip_nw * consts::STARFIELD_Z_MIN;
|
||||
let v_adj: Point2<f32> = (v.x + (sz / 2.0), v.y + (sz / 2.0)).into();
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
@ -296,14 +302,14 @@ impl GPUState {
|
|||
let mut instances = Vec::new();
|
||||
for x in (-nw_tile.x)..=nw_tile.x {
|
||||
for y in (-nw_tile.y)..=nw_tile.y {
|
||||
let offset = Vector2 {
|
||||
let offset = Vector3 {
|
||||
x: sz * x as f32,
|
||||
y: sz * y as f32,
|
||||
z: 0.0,
|
||||
};
|
||||
for s in &game.system.starfield {
|
||||
instances.push(StarfieldInstance {
|
||||
position: (s.pos + offset).into(),
|
||||
parallax: s.parallax,
|
||||
size: s.size,
|
||||
tint: s.tint.into(),
|
||||
})
|
||||
|
@ -364,6 +370,7 @@ impl GPUState {
|
|||
bytemuck::cast_slice(&[GlobalDataContent {
|
||||
camera_position: game.camera.pos.into(),
|
||||
camera_zoom: [game.camera.zoom, 0.0],
|
||||
camera_zoom_limits: [consts::ZOOM_MIN, consts::ZOOM_MAX],
|
||||
window_size: [
|
||||
self.window_size.width as f32,
|
||||
self.window_size.height as f32,
|
||||
|
@ -371,6 +378,7 @@ impl GPUState {
|
|||
window_aspect: [self.window_aspect, 0.0],
|
||||
starfield_texture: [self.texture_array.get_starfield_texture().index, 0],
|
||||
starfield_tile_size: [consts::STARFIELD_SIZE as f32, 0.0],
|
||||
starfield_size_limits: [consts::STARFIELD_SIZE_MIN, consts::STARFIELD_SIZE_MAX],
|
||||
}]),
|
||||
);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ struct InstanceInput {
|
|||
@location(2) rotation_matrix_0: vec2<f32>,
|
||||
@location(3) rotation_matrix_1: vec2<f32>,
|
||||
@location(4) position: vec2<f32>,
|
||||
@location(5) height: f32,
|
||||
@location(5) size: f32,
|
||||
@location(6) aspect: f32,
|
||||
@location(7) texture_idx: u32,
|
||||
};
|
||||
|
@ -24,10 +24,12 @@ var<uniform> global: GlobalUniform;
|
|||
struct GlobalUniform {
|
||||
camera_position: vec2<f32>,
|
||||
camera_zoom: vec2<f32>,
|
||||
camera_zoom_limits: vec2<f32>,
|
||||
window_size: vec2<f32>,
|
||||
window_aspect: vec2<f32>,
|
||||
starfield_texture: vec2<u32>,
|
||||
starfield_tile_size: vec2<f32>,
|
||||
starfield_size_limits: vec2<f32>,
|
||||
};
|
||||
|
||||
|
||||
|
@ -47,7 +49,7 @@ fn vertex_main(
|
|||
|
||||
// Apply sprite aspect ratio & scale factor
|
||||
// This must be done *before* rotation.
|
||||
let scale = instance.height / global.camera_zoom.x;
|
||||
let scale = instance.size / global.camera_zoom.x;
|
||||
var pos: vec2<f32> = vec2<f32>(
|
||||
vertex.position.x * instance.aspect * scale,
|
||||
vertex.position.y * scale
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
struct InstanceInput {
|
||||
@location(2) position: vec2<f32>,
|
||||
@location(3) parallax: f32,
|
||||
@location(4) size: f32,
|
||||
@location(5) tint: vec2<f32>,
|
||||
@location(2) position: vec3<f32>,
|
||||
@location(3) size: f32,
|
||||
@location(4) tint: vec2<f32>,
|
||||
};
|
||||
|
||||
struct VertexInput {
|
||||
|
@ -21,10 +20,12 @@ var<uniform> global: GlobalUniform;
|
|||
struct GlobalUniform {
|
||||
camera_position: vec2<f32>,
|
||||
camera_zoom: vec2<f32>,
|
||||
camera_zoom_limits: vec2<f32>,
|
||||
window_size: vec2<f32>,
|
||||
window_aspect: vec2<f32>,
|
||||
starfield_texture: vec2<u32>,
|
||||
starfield_tile_size: vec2<f32>,
|
||||
starfield_size_limits: vec2<f32>,
|
||||
};
|
||||
|
||||
|
||||
|
@ -62,19 +63,37 @@ fn vertex_main(
|
|||
)
|
||||
);
|
||||
|
||||
|
||||
let zoom_min_times = (
|
||||
global.camera_zoom.x / global.camera_zoom_limits.x
|
||||
);
|
||||
|
||||
// Hide n% of the smallest stars
|
||||
// If we wanted a constant number of stars on the screen, we would do
|
||||
// `let hide_fraction = 1.0 - 1.0 / (zoom_min_times * zoom_min_times);`
|
||||
// We, however, don't want this: a bigger screen should have more stars,
|
||||
// but not *too* many. We thus scale linearly.
|
||||
let hide_fraction = 1.0 - 1.0 / (zoom_min_times * 0.8);
|
||||
|
||||
// Hide some stars at large zoom levels.
|
||||
if (
|
||||
instance.size < (
|
||||
hide_fraction * (global.starfield_size_limits.y - global.starfield_size_limits.x)
|
||||
+ (global.starfield_size_limits.x)
|
||||
)
|
||||
) {
|
||||
out.position = vec4<f32>(2.0, 2.0, 0.0, 1.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// Apply sprite aspect ratio & scale factor
|
||||
// also applies screen aspect ratio
|
||||
// Note that we do NOT scale for distance here---this is intentional.
|
||||
var scale: f32 = instance.size / global.camera_zoom.x;
|
||||
|
||||
// Minimum scale to prevent flicker at large zoom levels
|
||||
var real_size = scale * global.window_size.xy;
|
||||
// TODO: configurable.
|
||||
// Uniform distribution!
|
||||
if (real_size.x < 0.5 || real_size.y < 0.5) {
|
||||
// If this star is too small, don't even show it
|
||||
out.position = vec4<f32>(2.0, 2.0, 0.0, 1.0);
|
||||
return out;
|
||||
}
|
||||
if (real_size.x < 2.0 || real_size.y < 2.0) {
|
||||
// Otherwise, clamp to a minimum scale
|
||||
scale = 2.0 / max(global.window_size.x, global.window_size.y);
|
||||
|
@ -89,16 +108,16 @@ fn vertex_main(
|
|||
// World position relative to camera
|
||||
// (Note that instance position is in a different
|
||||
// coordinate system than usual)
|
||||
let camera_pos = (instance.position + tile_center) - global.camera_position.xy;
|
||||
let camera_pos = (instance.position.xy + tile_center) - global.camera_position.xy;
|
||||
|
||||
// Translate
|
||||
pos = pos + (
|
||||
// Don't forget to correct distance for screen aspect ratio too!
|
||||
(camera_pos / (global.camera_zoom.x * (instance.parallax)))
|
||||
(camera_pos / (global.camera_zoom.x * (instance.position.z)))
|
||||
/ vec2<f32>(global.window_aspect.x, 1.0)
|
||||
);
|
||||
|
||||
out.position = vec4<f32>(pos, 0.0, 1.0) * instance.parallax;
|
||||
out.position = vec4<f32>(pos, 0.0, 1.0) * instance.position.z;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use cgmath::{Deg, Point2};
|
||||
use cgmath::{Deg, Point3};
|
||||
|
||||
use super::SpriteTexture;
|
||||
use crate::physics::Pfloat;
|
||||
|
@ -8,7 +8,7 @@ pub struct Sprite {
|
|||
pub texture: SpriteTexture,
|
||||
|
||||
/// This object's position, in world coordinates.
|
||||
pub pos: Point2<Pfloat>,
|
||||
pub pos: Point3<Pfloat>,
|
||||
|
||||
/// The size of this sprite,
|
||||
/// given as height in world units.
|
||||
|
@ -21,11 +21,6 @@ pub struct Sprite {
|
|||
/// 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 {
|
||||
|
|
|
@ -36,15 +36,20 @@ impl BufferObject for TexturedVertex {
|
|||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct StarfieldInstance {
|
||||
/// Position in origin field tile.
|
||||
/// note that this is DIFFERENT from
|
||||
/// the way we provide sprite positions!
|
||||
pub position: [f32; 2],
|
||||
|
||||
/// Parallax factor (same unit as usual)
|
||||
pub parallax: f32,
|
||||
/// Position in the starfield.
|
||||
///
|
||||
/// This is NOT world position, i.e, different from sprite positioning!
|
||||
/// The x and y coordinates here represent position relative to the center
|
||||
/// of a starfield tile in world units, which is converted to world position
|
||||
/// by the starfield vertex shader.
|
||||
pub position: [f32; 3],
|
||||
|
||||
/// Star size, in world units. This does NOT scale with distance,
|
||||
/// unlike sprite size.
|
||||
pub size: f32,
|
||||
|
||||
/// Parameters for this star's color variation,
|
||||
/// see the starfield fragment shader.
|
||||
pub tint: [f32; 2],
|
||||
}
|
||||
|
||||
|
@ -58,24 +63,18 @@ impl BufferObject for StarfieldInstance {
|
|||
wgpu::VertexAttribute {
|
||||
offset: 0,
|
||||
shader_location: 2,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
},
|
||||
// Parallax
|
||||
wgpu::VertexAttribute {
|
||||
offset: mem::size_of::<[f32; 2]>() as wgpu::BufferAddress,
|
||||
shader_location: 3,
|
||||
format: wgpu::VertexFormat::Float32,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
// Size
|
||||
wgpu::VertexAttribute {
|
||||
offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
|
||||
shader_location: 4,
|
||||
shader_location: 3,
|
||||
format: wgpu::VertexFormat::Float32,
|
||||
},
|
||||
// Tint
|
||||
wgpu::VertexAttribute {
|
||||
offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress,
|
||||
shader_location: 5,
|
||||
shader_location: 4,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
},
|
||||
],
|
||||
|
@ -91,10 +90,13 @@ pub struct SpriteInstance {
|
|||
pub rotation: [[f32; 2]; 2],
|
||||
|
||||
/// World position, relative to camera
|
||||
/// Note that this does NOT contain z-distance,
|
||||
/// since sprite parallax and distance scaling
|
||||
/// is applied beforehand.
|
||||
pub position: [f32; 2],
|
||||
|
||||
/// Height of (unrotated) sprite in world units
|
||||
pub height: f32,
|
||||
pub size: f32,
|
||||
|
||||
// Sprite aspect ratio (width / height)
|
||||
pub aspect: f32,
|
||||
|
@ -129,7 +131,7 @@ impl BufferObject for SpriteInstance {
|
|||
shader_location: 4,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
},
|
||||
// Height
|
||||
// Size
|
||||
wgpu::VertexAttribute {
|
||||
offset: mem::size_of::<[f32; 6]>() as wgpu::BufferAddress,
|
||||
shader_location: 5,
|
||||
|
|
Loading…
Reference in New Issue