Compare commits

...

4 Commits

Author SHA1 Message Date
Mark 0af265040c
Added subsprites 2023-12-26 22:33:00 -08:00
Mark 370d4c9bc9
Flare edits 2023-12-26 22:31:52 -08:00
Mark e5d399c3c5
Added basic ship config 2023-12-26 22:21:18 -08:00
Mark 1bd78d3cfd
Added assets 2023-12-26 11:27:38 -08:00
17 changed files with 325 additions and 170 deletions

2
assets

@ -1 +1 @@
Subproject commit 7b00da4f1971908d389d906fe537bfecd3d03b50 Subproject commit 7f9886acae8ab62827821ba4f5271689f9a67d4d

7
content/ship.toml Normal file
View File

@ -0,0 +1,7 @@
# content type: ship
[ship]
name = "Gypsum"
sprite = "ship::gypsum"
size = 100
engines = [{ x = 0.0, y = -105, size = 50.0 }]

View File

@ -6,28 +6,29 @@ use super::{syntax, ContentType};
#[derive(Debug)] #[derive(Debug)]
pub struct Content { pub struct Content {
pub systems: Vec<syntax::system::System>, pub systems: Vec<syntax::system::System>,
pub ships: Vec<syntax::ship::Ship>,
} }
impl Content { impl Content {
pub fn new(cv: Vec<(PathBuf, ContentType)>) -> Result<Self> { pub fn new(cv: Vec<(PathBuf, ContentType)>) -> Result<Self> {
let mut content = Self { let mut systems = Vec::new();
systems: Vec::new(), let mut ships = Vec::new();
};
// These methods check intra-file consistency // These methods check intra-file consistency
for (p, c) in cv { for (p, c) in cv {
match c { match c {
ContentType::System(v) => content ContentType::System(v) => systems.push(
.add_system(v) syntax::system::System::parse(v)
.with_context(|| format!("Could not parse {}", p.display()))?, .with_context(|| format!("Could not parse {}", p.display()))?,
}; ),
ContentType::Ship(v) => ships.push(
syntax::ship::Ship::parse(v)
.with_context(|| format!("Could not parse {}", p.display()))?,
),
}
} }
return Ok(content); return Ok(Self { systems, ships });
}
fn add_system(&mut self, toml: syntax::system::toml::SystemRoot) -> Result<()> {
self.systems.push(syntax::system::System::parse(toml)?);
return Ok(());
} }
} }

View File

@ -6,6 +6,7 @@ use super::syntax;
#[derive(Debug)] #[derive(Debug)]
pub enum ContentType { pub enum ContentType {
System(syntax::system::toml::SystemRoot), System(syntax::system::toml::SystemRoot),
Ship(syntax::ship::toml::ShipRoot),
} }
// TODO: check content without loading game // TODO: check content without loading game
@ -26,6 +27,7 @@ impl ContentType {
return Ok(match &type_spec[..] { return Ok(match &type_spec[..] {
"system" => Some(Self::System(toml::from_str(&file_string)?)), "system" => Some(Self::System(toml::from_str(&file_string)?)),
"ship" => Some(Self::Ship(toml::from_str(&file_string)?)),
_ => bail!("Invalid content type `{}`", type_spec), _ => bail!("Invalid content type `{}`", type_spec),
}); });
} }

View File

@ -4,6 +4,7 @@ mod syntax;
pub use content::Content; pub use content::Content;
pub use contenttype::ContentType; pub use contenttype::ContentType;
pub use syntax::ship;
pub use syntax::system; pub use syntax::system;
use anyhow::{Context, Result}; use anyhow::{Context, Result};

View File

@ -1,2 +1,3 @@
#![allow(dead_code)] #![allow(dead_code)]
pub mod ship;
pub mod system; pub mod system;

View File

@ -0,0 +1,60 @@
use anyhow::Result;
use cgmath::Point2;
/// Toml file syntax
pub(in crate::content) mod toml {
use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct ShipRoot {
pub ship: Ship,
}
#[derive(Debug, Deserialize)]
pub struct Ship {
pub name: String,
pub sprite: String,
pub size: f32,
pub engines: Vec<Engine>,
}
#[derive(Debug, Deserialize)]
pub struct Engine {
pub x: f32,
pub y: f32,
pub size: f32,
}
}
#[derive(Debug, Clone)]
pub struct Ship {
pub name: String,
pub sprite: String,
pub size: f32,
pub engines: Vec<Engine>,
}
#[derive(Debug, Clone)]
pub struct Engine {
pub pos: Point2<f32>,
pub size: f32,
}
impl Ship {
pub fn parse(value: toml::ShipRoot) -> Result<Self> {
return Ok(Self {
name: value.ship.name,
sprite: value.ship.sprite,
size: value.ship.size,
engines: value
.ship
.engines
.iter()
.map(|e| Engine {
pos: Point2 { x: e.x, y: e.y },
size: e.size,
})
.collect(),
});
}
}

View File

@ -2,13 +2,14 @@ use cgmath::Deg;
use std::time::Instant; use std::time::Instant;
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode}; use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
use super::{ship::ShipKind, Camera, InputStatus, Ship, System}; use super::{Camera, InputStatus, Ship, System};
use crate::{consts, content::Content, render::Sprite, render::Spriteable}; use crate::{consts, content::Content, render::Sprite, render::Spriteable};
pub struct Game { pub struct Game {
pub input: InputStatus, pub input: InputStatus,
pub last_update: Instant, pub last_update: Instant,
pub player: Ship, pub player: Ship,
pub test: Ship,
pub system: System, pub system: System,
pub camera: Camera, pub camera: Camera,
paused: bool, paused: bool,
@ -20,7 +21,7 @@ impl Game {
Game { Game {
last_update: Instant::now(), last_update: Instant::now(),
input: InputStatus::new(), input: InputStatus::new(),
player: Ship::new(ShipKind::Gypsum, (0.0, 0.0).into()), player: Ship::new(&ct.ships[0], (0.0, 0.0).into()),
camera: Camera { camera: Camera {
pos: (0.0, 0.0).into(), pos: (0.0, 0.0).into(),
zoom: 500.0, zoom: 500.0,
@ -28,6 +29,7 @@ impl Game {
system: System::new(&ct.systems[0]), system: System::new(&ct.systems[0]),
paused: false, paused: false,
time_scale: 1.0, time_scale: 1.0,
test: Ship::new(&ct.ships[0], (100.0, 100.0).into()),
} }
} }
@ -55,6 +57,7 @@ impl Game {
pub fn update(&mut self) { pub fn update(&mut self) {
let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale; let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale;
self.player.engines_on = self.input.key_thrust;
if self.input.key_thrust { if self.input.key_thrust {
self.player.physicsbody.thrust(50.0 * t); self.player.physicsbody.thrust(50.0 * t);
} }
@ -84,11 +87,13 @@ impl Game {
sprites.append(&mut self.system.get_sprites()); sprites.append(&mut self.system.get_sprites());
sprites.push(self.player.get_sprite()); sprites.push(self.player.get_sprite());
sprites.push(self.test.get_sprite());
// Make sure sprites are drawn in the correct order // Make sure sprites are drawn in the correct order
// (note the reversed a, b in the comparator) // (note the reversed a, b in the comparator)
// //
// TODO: use a gpu depth buffer instead. // TODO: maybe use a gpu depth buffer instead?
// I've tried this, but it doesn't seem to work with transparent textures.
sprites.sort_by(|a, b| b.pos.z.total_cmp(&a.pos.z)); sprites.sort_by(|a, b| b.pos.z.total_cmp(&a.pos.z));
return sprites; return sprites;

View File

@ -1,49 +1,63 @@
use cgmath::Point2; use cgmath::{Deg, Point2, Point3};
use crate::{physics::PhysicsBody, render::Sprite, render::SpriteTexture, render::Spriteable}; use crate::{
content::{self, ship::Engine},
pub enum ShipKind { physics::PhysicsBody,
Gypsum, render::Sprite,
} render::SpriteTexture,
render::Spriteable,
impl ShipKind { };
fn sprite(&self) -> SpriteTexture {
let name = match self {
Self::Gypsum => "ship::gypsum",
};
return SpriteTexture(name.to_owned());
}
fn size(&self) -> f32 {
match self {
Self::Gypsum => 100.0,
}
}
}
pub struct Ship { pub struct Ship {
pub physicsbody: PhysicsBody, pub physicsbody: PhysicsBody,
kind: ShipKind, pub engines_on: bool,
sprite: SpriteTexture,
size: f32,
engines: Vec<Engine>,
} }
impl Ship { impl Ship {
pub fn new(kind: ShipKind, pos: Point2<f32>) -> Self { pub fn new(ct: &content::ship::Ship, pos: Point2<f32>) -> Self {
Ship { Ship {
physicsbody: PhysicsBody::new(pos), physicsbody: PhysicsBody::new(pos),
kind, sprite: SpriteTexture(ct.sprite.clone()),
size: ct.size,
engines: ct.engines.clone(),
engines_on: false,
} }
} }
} }
impl Spriteable for Ship { impl Spriteable for Ship {
fn get_sprite(&self) -> Sprite { fn get_sprite(&self) -> Sprite {
return Sprite { let engines = if self.engines_on {
pos: (self.physicsbody.pos.x, self.physicsbody.pos.y, 1.0).into(), Some(
texture: self.kind.sprite(), self.engines
angle: self.physicsbody.angle, .iter()
scale: 1.0, .map(|e| Sprite {
size: self.kind.size(), pos: Point3 {
x: e.pos.x,
y: e.pos.y,
z: 1.0,
},
texture: SpriteTexture("flare::ion".to_owned()),
angle: Deg(0.0),
size: e.size,
children: None,
})
.collect(),
)
} else {
None
}; };
Sprite {
pos: (self.physicsbody.pos.x, self.physicsbody.pos.y, 1.0).into(),
texture: self.sprite.clone(), // TODO: sprite texture should be easy to clone
angle: self.physicsbody.angle,
size: self.size,
children: engines,
}
} }
} }

View File

@ -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, render::Spriteable}; use crate::{consts, content, render::Sprite, render::SpriteTexture};
pub struct StarfieldStar { pub struct StarfieldStar {
/// Star coordinates, in world space. /// Star coordinates, in world space.

View File

@ -1,6 +1,6 @@
use cgmath::{Deg, Point3}; use cgmath::{Deg, Point3};
use crate::{render::Sprite, render::SpriteTexture, render::Spriteable}; use crate::{render::Sprite, render::SpriteTexture};
pub struct SystemObject { pub struct SystemObject {
pub sprite: SpriteTexture, pub sprite: SpriteTexture,
@ -9,14 +9,14 @@ pub struct SystemObject {
pub angle: Deg<f32>, pub angle: Deg<f32>,
} }
impl Spriteable for SystemObject { impl SystemObject {
fn get_sprite(&self) -> Sprite { pub(super) fn get_sprite(&self) -> Sprite {
return Sprite { return Sprite {
texture: self.sprite.clone(), texture: self.sprite.clone(),
scale: 1.0,
pos: self.pos, pos: self.pos,
angle: self.angle, angle: self.angle,
size: self.size, size: self.size,
children: None,
}; };
} }
} }

View File

@ -1,4 +1,5 @@
use crate::consts; use crate::consts;
use cgmath::Matrix4;
// We can draw at most this many sprites on the screen. // We can draw at most this many sprites on the screen.
// TODO: compile-time option // TODO: compile-time option
@ -16,3 +17,11 @@ 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";
#[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: Matrix4<f32> = Matrix4::new(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.5, 0.5,
0.0, 0.0, 0.0, 1.0,
);

View File

@ -1,12 +1,12 @@
use anyhow::Result; use anyhow::Result;
use bytemuck; use bytemuck;
use cgmath::{EuclideanSpace, Matrix2, Point2, Vector2, Vector3}; use cgmath::{Deg, EuclideanSpace, Matrix4, Point2, Vector2, Vector3};
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};
use super::{ use super::{
consts::{SPRITE_INSTANCE_LIMIT, STARFIELD_INSTANCE_LIMIT}, consts::{OPENGL_TO_WGPU_MATRIX, SPRITE_INSTANCE_LIMIT, STARFIELD_INSTANCE_LIMIT},
globaldata::{GlobalData, GlobalDataContent}, globaldata::{GlobalData, GlobalDataContent},
pipeline::PipelineBuilder, pipeline::PipelineBuilder,
texturearray::TextureArray, texturearray::TextureArray,
@ -15,6 +15,7 @@ use super::{
types::{SpriteInstance, StarfieldInstance, TexturedVertex}, types::{SpriteInstance, StarfieldInstance, TexturedVertex},
VertexBuffer, VertexBuffer,
}, },
Sprite,
}; };
use crate::{consts, game::Game}; use crate::{consts, game::Game};
@ -194,6 +195,138 @@ impl GPUState {
self.update_starfield_buffer(game) self.update_starfield_buffer(game)
} }
/// Create a SpriteInstance for s and add it to instances.
/// Also handles child sprites.
fn push_sprite(
&self,
game: &Game,
instances: &mut Vec<SpriteInstance>,
clip_ne: Point2<f32>,
clip_sw: Point2<f32>,
s: Sprite,
) {
// Position adjusted for parallax
// TODO: adjust parallax for zoom?
let pos: Point2<f32> = {
(Point2 {
x: s.pos.x,
y: s.pos.y,
} - game.camera.pos.to_vec())
/ s.pos.z
};
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.pos.z;
let width = height * texture.aspect;
// Don't draw (or compute matrices for)
// sprites that are off the screen
if pos.x < clip_ne.x - width
|| pos.y > clip_ne.y + height
|| pos.x > clip_sw.x + width
|| pos.y < clip_sw.y - height
{
return;
}
// TODO: clean up
let scale = height / game.camera.zoom;
// Note that our mesh starts centered at (0, 0).
// This is essential---we do not want scale and rotation
// changing our sprite's position!
// Apply sprite aspect ratio, preserving height.
// This must be done *before* rotation.
//
// We apply the provided scale here as well as a minor optimization
let sprite_aspect_and_scale =
Matrix4::from_nonuniform_scale(texture.aspect * scale, scale, 1.0);
// Apply rotation
let rotate = Matrix4::from_angle_z(s.angle);
// Apply screen aspect ratio, again preserving height.
// This must be done AFTER rotation... think about it!
let screen_aspect = Matrix4::from_nonuniform_scale(1.0 / self.window_aspect, 1.0, 1.0);
// After finishing all ops, translate.
// This must be done last, all other operations
// require us to be at (0, 0).
let translate = Matrix4::from_translation(Vector3 {
x: pos.x / game.camera.zoom / self.window_aspect,
y: pos.y / game.camera.zoom,
z: 0.0,
});
// Order matters!
// The rightmost matrix is applied first.
let t =
OPENGL_TO_WGPU_MATRIX * translate * screen_aspect * rotate * sprite_aspect_and_scale;
instances.push(SpriteInstance {
transform: t.into(),
texture_index: texture.index,
});
// Add children
if let Some(children) = s.children {
for c in children {
self.push_subsprite(game, instances, c, pos, s.angle);
}
}
}
/// Add a sprite's subsprite to instance.
/// Only called by push_sprite.
fn push_subsprite(
&self,
game: &Game,
instances: &mut Vec<SpriteInstance>,
s: Sprite,
parent_pos: Point2<f32>,
parent_angle: Deg<f32>,
) {
// TODO: clean up
if s.children.is_some() {
panic!("Child sprites must not have child sprites!")
}
let texture = self.texture_array.get_sprite_texture(s.texture);
let scale = s.size / (s.pos.z * game.camera.zoom);
let sprite_aspect_and_scale =
Matrix4::from_nonuniform_scale(texture.aspect * scale, scale, 1.0);
let rotate = Matrix4::from_angle_z(s.angle);
let screen_aspect = Matrix4::from_nonuniform_scale(1.0 / self.window_aspect, 1.0, 1.0);
let ptranslate = Matrix4::from_translation(Vector3 {
x: parent_pos.x / game.camera.zoom / self.window_aspect,
y: parent_pos.y / game.camera.zoom,
z: 0.0,
});
let protate = Matrix4::from_angle_z(parent_angle);
let translate = Matrix4::from_translation(Vector3 {
x: s.pos.x / game.camera.zoom / self.window_aspect,
y: s.pos.y / game.camera.zoom,
z: 0.0,
});
// Order matters!
// The rightmost matrix is applied first.
let t = OPENGL_TO_WGPU_MATRIX
* ptranslate * screen_aspect
* protate * translate
* rotate * sprite_aspect_and_scale;
instances.push(SpriteInstance {
transform: t.into(),
texture_index: texture.index,
});
}
/// Make a SpriteInstance for each of the game's visible sprites. /// Make a SpriteInstance for each of the game's visible sprites.
/// Will panic if SPRITE_INSTANCE_LIMIT is exceeded. /// Will panic if SPRITE_INSTANCE_LIMIT is exceeded.
/// ///
@ -207,45 +340,13 @@ impl GPUState {
let clip_sw = Point2::from((self.window_aspect, -1.0)) * game.camera.zoom; let clip_sw = Point2::from((self.window_aspect, -1.0)) * game.camera.zoom;
for s in game.get_sprites() { for s in game.get_sprites() {
// Compute post-parallax position and distance-adjusted scale. self.push_sprite(game, &mut instances, clip_ne, clip_sw, s);
// We do this here so we can check if a sprite is on the screen.
let pos: Point2<f32> = {
(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.pos.z;
let width = height * texture.aspect;
// Don't draw (or compute matrices for)
// sprites that are off the screen
if pos.x < clip_ne.x - width
|| pos.y > clip_ne.y + height
|| pos.x > clip_sw.x + width
|| pos.y < clip_sw.y - height
{
continue;
}
instances.push(SpriteInstance {
position: pos.into(),
aspect: texture.aspect,
rotation: Matrix2::from_angle(s.angle).into(),
size: height,
texture_index: texture.index,
})
} }
// Enforce sprite limit // Enforce sprite limit
if instances.len() as u64 > SPRITE_INSTANCE_LIMIT { if instances.len() as u64 > SPRITE_INSTANCE_LIMIT {
// TODO: no panic, handle this better. // TODO: no panic, handle this better.
unreachable!("Sprite limit exceeded!") panic!("Sprite limit exceeded!")
} }
return instances; return instances;

View File

@ -12,16 +12,3 @@ pub use sprite::{Sprite, Spriteable};
/// A handle to a sprite texture /// A handle to a sprite texture
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SpriteTexture(pub String); 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,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.5, 0.5,
0.0, 0.0, 0.0, 1.0,
);
*/

View File

@ -1,10 +1,9 @@
struct InstanceInput { struct InstanceInput {
@location(2) rotation_matrix_0: vec2<f32>, @location(2) transform_matrix_0: vec4<f32>,
@location(3) rotation_matrix_1: vec2<f32>, @location(3) transform_matrix_1: vec4<f32>,
@location(4) position: vec2<f32>, @location(4) transform_matrix_2: vec4<f32>,
@location(5) size: f32, @location(5) transform_matrix_3: vec4<f32>,
@location(6) aspect: f32, @location(6) texture_idx: u32,
@location(7) texture_idx: u32,
}; };
struct VertexInput { struct VertexInput {
@ -47,33 +46,15 @@ fn vertex_main(
instance: InstanceInput, instance: InstanceInput,
) -> VertexOutput { ) -> VertexOutput {
// Apply sprite aspect ratio & scale factor let transform = mat4x4<f32>(
// This must be done *before* rotation. instance.transform_matrix_0,
let scale = instance.size / global.camera_zoom.x; instance.transform_matrix_1,
var pos: vec2<f32> = vec2<f32>( instance.transform_matrix_2,
vertex.position.x * instance.aspect * scale, instance.transform_matrix_3,
vertex.position.y * scale
);
// Rotate
pos = mat2x2<f32>(
instance.rotation_matrix_0,
instance.rotation_matrix_1,
) * pos;
// Apply screen aspect ratio, again preserving height.
// This must be done AFTER rotation... think about it!
pos = pos / vec2<f32>(global.window_aspect.x, 1.0);
// Translate
pos = pos + (
// Don't forget to correct distance for screen aspect ratio too!
(instance.position / global.camera_zoom.x)
/ vec2<f32>(global.window_aspect.x, 1.0)
); );
var out: VertexOutput; var out: VertexOutput;
out.position = vec4<f32>(pos, 0.0, 1.0); out.position = transform * vec4<f32>(vertex.position, 1.0);
out.texture_coords = vertex.texture_coords; out.texture_coords = vertex.texture_coords;
out.index = instance.texture_idx; out.index = instance.texture_idx;
return out; return out;

View File

@ -13,13 +13,18 @@ pub struct Sprite {
/// given as height in world units. /// given as height in world units.
pub size: f32, pub size: f32,
/// Scale factor.
/// if this is 1, sprite height is exactly self.size.
pub scale: f32,
/// This sprite's rotation /// This sprite's rotation
/// (relative to north, measured ccw) /// (relative to north, measured ccw)
pub angle: Deg<f32>, pub angle: Deg<f32>,
/// Sprites that should be drawn relative to this sprite.
/// Coordinates of sprites in this array will be interpreted
/// as world units, relative to the center of this sprite,
/// before any rotation or scaling.
/// Children rotate with their parent sprite.
///
/// Note that child sprites may NOT have children.
pub children: Option<Vec<Sprite>>,
} }
pub trait Spriteable { pub trait Spriteable {

View File

@ -86,20 +86,9 @@ impl BufferObject for StarfieldInstance {
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct SpriteInstance { pub struct SpriteInstance {
/// Rotation matrix for this sprite /// Extra transformations this sprite
pub rotation: [[f32; 2]; 2], /// (rotation, etc)
pub transform: [[f32; 4]; 4],
/// 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 size: f32,
// Sprite aspect ratio (width / height)
pub aspect: f32,
// What texture to use for this sprite // What texture to use for this sprite
pub texture_index: u32, pub texture_index: u32,
@ -114,39 +103,31 @@ impl BufferObject for SpriteInstance {
// instance when the shader starts processing a new instance // instance when the shader starts processing a new instance
step_mode: wgpu::VertexStepMode::Instance, step_mode: wgpu::VertexStepMode::Instance,
attributes: &[ attributes: &[
// 2 arrays = 1 2x2 matrix // 4 arrays = 1 4x4 matrix
wgpu::VertexAttribute { wgpu::VertexAttribute {
offset: 0, offset: 0,
shader_location: 2, shader_location: 2,
format: wgpu::VertexFormat::Float32x2, format: wgpu::VertexFormat::Float32x4,
}, },
wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 2]>() as wgpu::BufferAddress,
shader_location: 3,
format: wgpu::VertexFormat::Float32x2,
},
// Position
wgpu::VertexAttribute { wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress, offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress,
shader_location: 3,
format: wgpu::VertexFormat::Float32x4,
},
wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress,
shader_location: 4, shader_location: 4,
format: wgpu::VertexFormat::Float32x2, format: wgpu::VertexFormat::Float32x4,
}, },
// Size
wgpu::VertexAttribute { wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 6]>() as wgpu::BufferAddress, offset: mem::size_of::<[f32; 12]>() as wgpu::BufferAddress,
shader_location: 5, shader_location: 5,
format: wgpu::VertexFormat::Float32, format: wgpu::VertexFormat::Float32x4,
},
// Aspect
wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 7]>() as wgpu::BufferAddress,
shader_location: 6,
format: wgpu::VertexFormat::Float32,
}, },
// Texture // Texture
wgpu::VertexAttribute { wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress, offset: mem::size_of::<[f32; 16]>() as wgpu::BufferAddress,
shader_location: 7, shader_location: 6,
format: wgpu::VertexFormat::Uint32, format: wgpu::VertexFormat::Uint32,
}, },
], ],