summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shaders/sprite.wgsl25
-rw-r--r--src/instance.rs46
-rw-r--r--src/lib.rs2
-rw-r--r--src/renderer.rs24
-rw-r--r--src/vertex.rs5
5 files changed, 96 insertions, 6 deletions
diff --git a/shaders/sprite.wgsl b/shaders/sprite.wgsl
index d126cae..b8ce42b 100644
--- a/shaders/sprite.wgsl
+++ b/shaders/sprite.wgsl
@@ -3,14 +3,35 @@ struct VertexInput {
@location(0) position: vec2<f32>
}
+struct InstanceInput {
+ @location(1) position: vec2<f32>,
+ @location(2) size: vec2<f32>,
+ @location(3) rotation: f32,
+ @location(4) z_layer: i32
+}
+
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>
}
@vertex
-fn vs_main(model: VertexInput) -> VertexOutput {
+fn vs_main(model: VertexInput, instance: InstanceInput) -> VertexOutput {
var out: VertexOutput;
- out.clip_position = vec4<f32>(model.position, 0.0, 1.0);
+
+ // rotate the sprite
+ let a = vec2<f32>(cos(instance.rotation), sin(instance.rotation));
+ let b = vec2<f32>(-a[1], a[0]);
+ let rotation = mat2x2<f32>(a, b);
+ let rotated = rotation * model.position;
+
+ // scale the sprite
+ let x = rotated[0] * instance.size[0];
+ let y = rotated[1] * instance.size[1];
+
+ // move the sprite
+ let position = vec2<f32>(x, y) + instance.position;
+
+ out.clip_position = vec4<f32>(position, f32(instance.z_layer), 1.0);
return out;
}
diff --git a/src/instance.rs b/src/instance.rs
new file mode 100644
index 0000000..6de0133
--- /dev/null
+++ b/src/instance.rs
@@ -0,0 +1,46 @@
+use std::mem::size_of;
+
+use bytemuck::{Pod, Zeroable};
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug, PartialEq, Pod, Zeroable)]
+pub struct Instance {
+ /// Position on the screen
+ pub position: [f32; 2],
+ /// Relative size
+ pub size: [f32; 2],
+ /// Rotation, in radians
+ pub rotation: f32,
+ /// z-index
+ pub z_index: i32,
+}
+
+impl Default for Instance {
+ fn default() -> Self {
+ Self {
+ position: [0.0; 2],
+ size: [1.0; 2],
+ rotation: 0.0,
+ z_index: 0,
+ }
+ }
+}
+
+impl Instance {
+ // whenever this is updated, please also update `sprite.wgsl`
+ const ATTRIBUTES: [wgpu::VertexAttribute; 4] =
+ wgpu::vertex_attr_array![1 => Float32x2, 2 => Float32x2, 3 => Float32, 4 => Sint32];
+
+ pub(crate) fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
+ // make sure these two don't conflict
+ debug_assert_eq!(
+ Self::ATTRIBUTES[0].shader_location as usize,
+ crate::Vertex::ATTRIBUTES.len()
+ );
+ wgpu::VertexBufferLayout {
+ array_stride: size_of::<Self>() as wgpu::BufferAddress,
+ step_mode: wgpu::VertexStepMode::Instance,
+ attributes: &Self::ATTRIBUTES,
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index fbc517b..15f64b3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,9 +5,11 @@
#![allow(clippy::module_name_repetitions)]
pub mod config;
+mod instance;
pub mod renderer;
mod vertex;
pub use config::RenderWindowConfig;
+pub(crate) use instance::Instance;
pub use renderer::Renderer;
pub(crate) use vertex::Vertex;
diff --git a/src/renderer.rs b/src/renderer.rs
index dc8ecb2..75e6d1e 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -1,6 +1,6 @@
use std::convert::TryInto;
-use crate::{vertex::SQUARE, RenderWindowConfig, Vertex};
+use crate::{vertex::SQUARE, Instance, RenderWindowConfig, Vertex};
use pollster::FutureExt;
use thiserror::Error;
use wgpu::{include_wgsl, util::DeviceExt};
@@ -44,6 +44,7 @@ pub struct Renderer {
render_pipeline: wgpu::RenderPipeline,
square_vertex_buffer: wgpu::Buffer,
square_vertices: u32,
+ instances: Vec<Instance>,
window: Window,
}
@@ -128,7 +129,7 @@ impl Renderer {
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
- buffers: &[Vertex::desc()],
+ buffers: &[Vertex::desc(), Instance::desc()],
},
// information about the fragment shader
fragment: Some(wgpu::FragmentState {
@@ -171,6 +172,8 @@ impl Renderer {
usage: wgpu::BufferUsages::VERTEX,
});
+ let instances = Vec::new();
+
Ok(Self {
surface,
device,
@@ -179,6 +182,7 @@ impl Renderer {
render_pipeline,
square_vertex_buffer,
square_vertices,
+ instances,
window,
})
}
@@ -216,6 +220,19 @@ impl Renderer {
label: Some("Render Encoder"),
});
+ let num_instances = self
+ .instances
+ .len()
+ .try_into()
+ .expect("expected less than 3 billion instances");
+ let instance_buffer = self
+ .device
+ .create_buffer_init(&wgpu::util::BufferInitDescriptor {
+ label: Some("Sprite Instance Buffer"),
+ contents: bytemuck::cast_slice(&self.instances),
+ usage: wgpu::BufferUsages::VERTEX,
+ });
+
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
@@ -232,7 +249,8 @@ impl Renderer {
render_pass.set_pipeline(&self.render_pipeline);
render_pass.set_vertex_buffer(0, self.square_vertex_buffer.slice(..));
- render_pass.draw(0..self.square_vertices, 0..1);
+ render_pass.set_vertex_buffer(1, instance_buffer.slice(..));
+ render_pass.draw(0..self.square_vertices, 0..num_instances);
}
// the encoder can't finish building the command buffer until the
// render pass is dropped
diff --git a/src/vertex.rs b/src/vertex.rs
index 7735801..6514fd3 100644
--- a/src/vertex.rs
+++ b/src/vertex.rs
@@ -2,6 +2,7 @@ use std::mem::size_of;
use bytemuck::{Pod, Zeroable};
+/// The vertices needed to form a square
pub const SQUARE: [Vertex; 4] = [
Vertex::new(-0.5, -0.5),
Vertex::new(0.5, -0.5),
@@ -22,7 +23,9 @@ impl Vertex {
}
impl Vertex {
- const ATTRIBUTES: [wgpu::VertexAttribute; 1] = wgpu::vertex_attr_array![0 => Float32x2];
+ // whenever this is updated, please also update `sprite.wgsl`
+ pub(crate) const ATTRIBUTES: [wgpu::VertexAttribute; 1] =
+ wgpu::vertex_attr_array![0 => Float32x2];
pub(crate) const fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
wgpu::VertexBufferLayout {