diff --git a/game/actor.ts b/game/actor.ts index c320e0c9..b2a5ca8c 100644 --- a/game/actor.ts +++ b/game/actor.ts @@ -1,7 +1,7 @@ import { load_texture } from "./assets"; import { btn, Button } from "./input"; import { Vec2, new_v2, vadd, vsub, vnorm, vmul } from "./vector"; -import { spr, circle, use_texture, Texture } from "./graphics"; +import { spr, use_texture, Texture } from "./graphics"; import { has_collision, Level } from "./level"; export interface ActorProps { @@ -191,7 +191,6 @@ export class Robo extends Actor { const frame = (anim.start + ((clock / anim.speed) % anim.length)) >> 0; spr(x, y, w, h, frame * w, 0, 32, 32); - // circle(this.props.position.x, this.props.position.y, 16, 1); } } } diff --git a/src/circle_shader.wgsl b/src/circle_shader.wgsl deleted file mode 100644 index 4b03cae3..00000000 --- a/src/circle_shader.wgsl +++ /dev/null @@ -1,110 +0,0 @@ -// ---------------------------------------------------------------------------- -// Vertex shader -// ---------------------------------------------------------------------------- - -struct VertexInput { - @location(0) position : vec3, - @location(1) tex_coords : vec2, -}; - -struct InstanceInput { - @location(5) center: vec2, - @location(6) radius: f32, - @location(7) stroke_width: f32, - @location(8) stroke_color: vec4, - @location(9) fill_color: vec4, -}; - -struct VertexOutput { - @builtin(position) clip_position : vec4, - @location(0) tex_coords : vec2, - @location(1) inner_r2: f32, - @location(2) stroke_color: vec4, - @location(3) fill_color: vec4, -}; - -@vertex fn vs_main(vertex : VertexInput, instance : InstanceInput)->VertexOutput { - var out : VertexOutput; - out.stroke_color = instance.stroke_color; - out.fill_color = instance.fill_color; - - // The circle's coordinate system goes from (-1,-1) to (1,1) but by - // convention we provide ourselves texture coordinates that go from (0,0) - // to (1,1). - out.tex_coords = (vertex.tex_coords * vec2f(2.0,2.0)) - vec2f(1.0,1.0); - - // Compute the squared radius of the inner circle, so we don't do it - // per-pixel. - // - // The radius of the inner circle goes from 0 (no inner circle) to 1 (no - // stroke), because the radius of the outer circle is implicitly 1 (the - // circle in the square we're rendering. - // - // (Honestly I don't even need to do this per-vertex, this is per-instance, - // I can pre-calculate this if I need this to be faster somehow.) - let delta = instance.radius - instance.stroke_width; //, 0, instance.radius); - let inner_radius = delta / instance.radius; - out.inner_r2 = inner_radius * inner_radius; - - let radius = vec2f(instance.radius, instance.radius); - let in_pos = instance.center + mix(-radius, radius, vec2f(vertex.position.x, vertex.position.y)); - - let position = adjust_for_resolution(in_pos); - out.clip_position = vec4f(position.x, position.y, vertex.position.z, 1.0); - return out; -} - -// ---------------------------------------------------------------------------- -// Fragment shader -// ---------------------------------------------------------------------------- - -@fragment fn fs_main(in : VertexOutput)->@location(0) vec4 { - let tc2 = in.tex_coords * in.tex_coords; - if (tc2.x + tc2.y <= in.inner_r2) { - return in.fill_color; - } else if (tc2.x + tc2.y <= 1.0) { - return in.stroke_color; - } else { - return vec4(0.0, 0.0, 0.0, 0.0); - } -} - -// ---------------------------------------------------------------------------- -// Resolution Handling -// ---------------------------------------------------------------------------- - -struct ScreenUniform { - resolution : vec2f, -}; -@group(0) @binding(0) // 1. - var screen : ScreenUniform; - -const RES = vec2f(320.0, 240.0); // The logical resolution of the screen. - -fn adjust_for_resolution(in_pos: vec2) -> vec2 { - // Adjust in_pos for the "resolution" of the screen. - let RES_AR = RES.x / RES.y; // The aspect ratio of the logical screen. - - // the actual resolution of the screen. - let screen_ar = screen.resolution.x / screen.resolution.y; - - // Compute the difference in resolution ... correctly? - // - // nudge is the amount to add to the logical resolution so that the pixels - // stay the same size but we respect the aspect ratio of the screen. (So - // there's more of them in either the x or y direction.) - var nudge = vec2f(0.0); - if (screen_ar > RES_AR) { - nudge.x = (RES.y * screen_ar) - RES.x; - } else { - nudge.y = (RES.x / screen_ar) - RES.y; - } - var new_logical_resolution = RES + nudge; - - // Now we can convert the incoming position to clip space, in the new screen. - let centered = in_pos + (nudge / 2.0); - var position = (2.0 * centered / new_logical_resolution) - 1.0; - position.y = -position.y; - - return position; -} diff --git a/src/graphics.ts b/src/graphics.ts index a422e78c..06ce66f3 100644 --- a/src/graphics.ts +++ b/src/graphics.ts @@ -52,18 +52,6 @@ export function spr( core.spr(x, y, w, h, sx, sy, sw, sh); } -/** - * Draw a circle. - * - * @param x - The x coordinate of the center of the circle. - * @param y - The y coordinate of the center of the circle. - * @param r - The radius of the circle. - * @param s - The stroke width of the circle. - */ -export function circle(x: number, y: number, r: number, s: number) { - core.circle(x, y, r, s); -} - export class Texture { #id: number; constructor(id: number) { diff --git a/src/lib.rs b/src/lib.rs index ada883a2..dc81230a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,7 +46,7 @@ impl Vertex { #[repr(C)] #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] -pub struct SpriteInstance { +struct SpriteInstance { src_top_left: [f32; 2], src_dims: [f32; 2], @@ -85,60 +85,6 @@ impl SpriteInstance { } } -#[repr(C)] -#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] -pub struct CircleInstance { - center: [f32; 2], - radius: f32, - stroke_width: f32, - stroke_color: [f32; 4], - fill_color: [f32; 4], -} - -impl CircleInstance { - fn desc() -> wgpu::VertexBufferLayout<'static> { - wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Instance, - attributes: &[ - wgpu::VertexAttribute { - offset: 0, - shader_location: 5, - format: wgpu::VertexFormat::Float32x2, - }, - wgpu::VertexAttribute { - offset: std::mem::size_of::<[f32; 2]>() as wgpu::BufferAddress, - shader_location: 6, - format: wgpu::VertexFormat::Float32, - }, - wgpu::VertexAttribute { - offset: (std::mem::size_of::<[f32; 2]>() + std::mem::size_of::()) - as wgpu::BufferAddress, - shader_location: 7, - format: wgpu::VertexFormat::Float32, - }, - wgpu::VertexAttribute { - offset: (std::mem::size_of::<[f32; 2]>() - + std::mem::size_of::() - + std::mem::size_of::()) - as wgpu::BufferAddress, - shader_location: 8, - format: wgpu::VertexFormat::Float32x4, - }, - wgpu::VertexAttribute { - offset: (std::mem::size_of::<[f32; 2]>() - + std::mem::size_of::() - + std::mem::size_of::() - + std::mem::size_of::<[f32; 4]>()) - as wgpu::BufferAddress, - shader_location: 9, - format: wgpu::VertexFormat::Float32x4, - }, - ], - } - } -} - #[repr(C)] #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] struct ScreenUniforms { @@ -255,65 +201,6 @@ const SPRITE_VERTICES: &[Vertex] = &[ }, ]; -struct VertexBufferPool { - buffers: Vec>, - next_free_buffer: usize, -} - -impl VertexBufferPool { - fn new() -> Self { - VertexBufferPool { - buffers: Vec::new(), - next_free_buffer: 0, - } - } - - fn clear(&mut self) { - self.next_free_buffer = 0; - } - - fn new_buffer(&mut self, device: &wgpu::Device) -> VertexBufferHandle { - if self.next_free_buffer >= self.buffers.len() { - let max_sprites: usize = 4096; - let buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: Some("Vertex Buffer"), - size: (max_sprites * std::mem::size_of::()).try_into().unwrap(), - mapped_at_creation: false, - usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - }); - self.buffers.push(VertexBuffer { - buffer, - capacity: max_sprites, - vec: Vec::with_capacity(max_sprites), - }); - } - - let index = self.next_free_buffer; - self.next_free_buffer += 1; - let vb = &mut self.buffers[index]; - vb.vec.clear(); - VertexBufferHandle { - index, - capacity: vb.capacity, - } - } - - fn get(&self, handle: &VertexBufferHandle) -> &VertexBuffer { - &self.buffers[handle.index] - } - - fn get_mut(&mut self, handle: &VertexBufferHandle) -> &mut VertexBuffer { - &mut self.buffers[handle.index] - } - - fn copy_instance_buffers(&mut self, queue: &wgpu::Queue) { - for i in 0..self.next_free_buffer { - let vb = &self.buffers[i]; - queue.write_buffer(&vb.buffer, 0, bytemuck::cast_slice(&vb.vec)); - } - } -} - struct State { surface: wgpu::Surface, device: wgpu::Device, @@ -321,18 +208,15 @@ struct State { config: wgpu::SurfaceConfiguration, size: winit::dpi::PhysicalSize, window: Window, + render_pipeline: wgpu::RenderPipeline, sprite_vertex_buffer: wgpu::Buffer, - - sprite_pipeline: wgpu::RenderPipeline, - sprite_instance_buffers: VertexBufferPool, + sprite_instance_buffers: Vec>, + next_free_sprite_instance_buffer: usize, sprite_bind_group_layout: wgpu::BindGroupLayout, sprite_textures: HashMap, - circle_pipeline: wgpu::RenderPipeline, - circle_instance_buffers: VertexBufferPool, - write_textures: HashMap, screen_uniform: ScreenUniforms, @@ -440,7 +324,7 @@ impl State { let sprite_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("Sprite Pipeline Layout"), - bind_group_layouts: &[&screen_uniform_bind_group_layout, &sprite_bind_group_layout], + bind_group_layouts: &[&sprite_bind_group_layout, &screen_uniform_bind_group_layout], push_constant_ranges: &[], }); @@ -482,56 +366,6 @@ impl State { multiview: None, }); - let circle_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("Circle Shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("circle_shader.wgsl").into()), - }); - - let circle_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Sprite Pipeline Layout"), - bind_group_layouts: &[&screen_uniform_bind_group_layout], - push_constant_ranges: &[], - }); - - let circle_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Circle Pipeline"), - layout: Some(&circle_pipeline_layout), - vertex: wgpu::VertexState { - module: &circle_shader, - entry_point: "vs_main", - buffers: &[Vertex::desc(), CircleInstance::desc()], - }, - fragment: Some(wgpu::FragmentState { - module: &circle_shader, - entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format: config.format, - blend: Some(wgpu::BlendState::ALPHA_BLENDING), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE - polygon_mode: wgpu::PolygonMode::Fill, - // Requires Features::DEPTH_CLIP_CONTROL - unclipped_depth: false, - // Requires Features::CONSERVATIVE_RASTERIZATION - conservative: false, - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - }); - let sprite_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Sprite Vertex Buffer"), contents: bytemuck::cast_slice(SPRITE_VERTICES), @@ -545,17 +379,14 @@ impl State { queue: hardware.queue, config, size, - sprite_pipeline, + render_pipeline: sprite_pipeline, sprite_vertex_buffer, - sprite_instance_buffers: VertexBufferPool::new(), + sprite_instance_buffers: Vec::new(), + next_free_sprite_instance_buffer: 0, sprite_bind_group_layout, sprite_textures: HashMap::new(), - - circle_pipeline, - circle_instance_buffers: VertexBufferPool::new(), - write_textures: HashMap::new(), screen_uniform, screen_uniform_buffer, @@ -591,9 +422,8 @@ impl State { fn render(&mut self, commands: Vec) -> Result<(), wgpu::SurfaceError> { let _span = span!("context render"); - // Reset instance buffers. - self.sprite_instance_buffers.clear(); - self.circle_instance_buffers.clear(); + // Reset vertex buffers. + self.next_free_sprite_instance_buffer = 0; let mut builder = FrameBuilder::new(self)?; for command in commands { @@ -673,23 +503,59 @@ impl State { self.write_textures.insert(id, texture); } - fn copy_instance_buffers(&mut self) { - self.sprite_instance_buffers - .copy_instance_buffers(&self.queue); - self.circle_instance_buffers - .copy_instance_buffers(&self.queue); - } -} + fn new_vertex_buffer(&mut self) -> VertexBufferHandle { + if self.next_free_sprite_instance_buffer >= self.sprite_instance_buffers.len() { + let max_sprites: usize = 4096; + let buffer = self.device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Sprite Instance Buffer"), + size: (max_sprites * std::mem::size_of::()) + .try_into() + .unwrap(), + mapped_at_creation: false, + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + }); + self.sprite_instance_buffers.push(VertexBuffer { + buffer, + capacity: max_sprites, + vec: Vec::with_capacity(max_sprites), + }); + } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -enum DrawMode { - Sprites, - Circles, + let index = self.next_free_sprite_instance_buffer; + self.next_free_sprite_instance_buffer += 1; + let vb = &mut self.sprite_instance_buffers[index]; + vb.vec.clear(); + VertexBufferHandle { + index, + capacity: vb.capacity, + } + } + + fn get_sprite_instance_buffer( + &self, + handle: &VertexBufferHandle, + ) -> &VertexBuffer { + &self.sprite_instance_buffers[handle.index] + } + + fn get_sprite_instance_buffer_mut( + &mut self, + handle: &VertexBufferHandle, + ) -> &mut VertexBuffer { + &mut self.sprite_instance_buffers[handle.index] + } + + fn copy_instance_buffers(&mut self) { + for i in 0..self.next_free_sprite_instance_buffer { + let vb = &self.sprite_instance_buffers[i]; + self.queue + .write_buffer(&vb.buffer, 0, bytemuck::cast_slice(&vb.vec)); + } + } } #[derive(Debug)] struct DrawCall { - mode: DrawMode, texture_id: Option, vertex_buffer: VertexBufferHandle, draw_start: u32, @@ -697,9 +563,8 @@ struct DrawCall { } impl DrawCall { - pub fn new(mode: DrawMode, vertex_buffer: VertexBufferHandle, draw_start: u32) -> Self { + pub fn new(vertex_buffer: VertexBufferHandle, draw_start: u32) -> Self { DrawCall { - mode, texture_id: None, vertex_buffer, draw_start, @@ -708,7 +573,7 @@ impl DrawCall { } pub fn new_at_buffer_tail(&self) -> Self { - DrawCall::new(self.mode, self.vertex_buffer.clone(), self.draw_end) + DrawCall::new(self.vertex_buffer.clone(), self.draw_end) } pub fn switch_textures(&self, id: u32) -> DrawCall { @@ -733,32 +598,18 @@ impl DrawCall { pub fn draw<'a>(&self, state: &'a State, pass: &mut wgpu::RenderPass<'a>) { if self.draw_end > self.draw_start { - let vb = match self.mode { - DrawMode::Sprites => { - match self.texture_id { - Some(id) => { - let bind_group = state.sprite_textures.get(&id).unwrap(); - pass.set_bind_group(1, bind_group, &[]); - } - None => (), - }; - - &state - .sprite_instance_buffers - .get(&self.vertex_buffer) - .buffer - } - - DrawMode::Circles => { - &state - .circle_instance_buffers - .get(&self.vertex_buffer) - .buffer - } + let texture_id = match self.texture_id { + Some(id) => id, + None => return, }; - pass.set_bind_group(0, &state.screen_uniform_bind_group, &[]); + + let bind_group = state.sprite_textures.get(&texture_id).unwrap(); + pass.set_bind_group(0, bind_group, &[]); + + let vb = state.get_sprite_instance_buffer(&self.vertex_buffer); + pass.set_bind_group(1, &state.screen_uniform_bind_group, &[]); pass.set_vertex_buffer(0, state.sprite_vertex_buffer.slice(..)); - pass.set_vertex_buffer(1, vb.slice(..)); + pass.set_vertex_buffer(1, vb.buffer.slice(..)); pass.draw( 0..SPRITE_VERTICES.len() as u32, @@ -775,7 +626,6 @@ struct FrameBuilder<'a> { encoder: wgpu::CommandEncoder, output: wgpu::SurfaceTexture, - mode: DrawMode, target: Rc, color: Option<[f64; 4]>, draw_calls: Vec, @@ -805,7 +655,6 @@ impl<'a> FrameBuilder<'a> { encoder, output, - mode: DrawMode::Sprites, target: last_view, color: None, draw_calls: Vec::new(), @@ -853,8 +702,7 @@ impl<'a> FrameBuilder<'a> { } } GraphicsCommand::Print(pc) => println!("{}", pc.text), - GraphicsCommand::Sprite(si) => self.push_sprite(si), - GraphicsCommand::Circle(ci) => self.push_circle(ci), + GraphicsCommand::Sprite(sc) => self.push_sprite(sc), GraphicsCommand::UseTexture(id) => self.use_texture(id), GraphicsCommand::EndFrame => self.flush(), @@ -881,19 +729,6 @@ impl<'a> FrameBuilder<'a> { self.target = target; } - fn new_instance_buffer(&mut self) -> VertexBufferHandle { - match self.mode { - DrawMode::Sprites => self - .state - .sprite_instance_buffers - .new_buffer(&self.state.device), - DrawMode::Circles => self - .state - .circle_instance_buffers - .new_buffer(&self.state.device), - } - } - fn use_texture(&mut self, texture_id: u32) { match self.draw_calls.last_mut() { Some(call) => match call.texture_id { @@ -913,67 +748,42 @@ impl<'a> FrameBuilder<'a> { } }, None => { - let mut call = DrawCall::new(self.mode, self.new_instance_buffer(), 0); + let mut call = DrawCall::new(self.state.new_vertex_buffer(), 0); call.texture_id = Some(texture_id); self.draw_calls.push(call); } } } - fn switch_mode(&mut self, mode: DrawMode) { - if self.mode != mode { - self.flush(); - self.draw_calls.clear(); - self.mode = mode; - } - } - fn get_sprite_instance_buffer(&mut self) -> &mut VertexBuffer { - self.switch_mode(DrawMode::Sprites); match self.draw_calls.last_mut() { Some(call) => match call.allocate_capacity(1) { - Some(vb) => return self.state.sprite_instance_buffers.get_mut(&vb), + Some(vb) => return self.state.get_sprite_instance_buffer_mut(&vb), None => {} }, None => {} }; - let mut call = DrawCall::new(self.mode, self.new_instance_buffer(), 0); + let mut call = DrawCall::new(self.state.new_vertex_buffer(), 0); let vb = call.allocate_capacity(1).unwrap(); self.draw_calls.push(call); - self.state.sprite_instance_buffers.get_mut(&vb) + self.state.get_sprite_instance_buffer_mut(&vb) } - fn push_sprite(&mut self, si: SpriteInstance) { + fn push_sprite(&mut self, sc: script::graphics::SpriteCommand) { let vertex_buffer = self.get_sprite_instance_buffer(); - vertex_buffer.vec.push(si); - } - - fn get_circle_instance_buffer(&mut self) -> &mut VertexBuffer { - self.switch_mode(DrawMode::Circles); - match self.draw_calls.last_mut() { - Some(call) => match call.allocate_capacity(1) { - Some(vb) => return self.state.circle_instance_buffers.get_mut(&vb), - None => {} - }, - None => {} - }; - - let mut call = DrawCall::new(self.mode, self.new_instance_buffer(), 0); - let vb = call.allocate_capacity(1).unwrap(); - self.draw_calls.push(call); - self.state.circle_instance_buffers.get_mut(&vb) - } - - fn push_circle(&mut self, ci: CircleInstance) { - let vertex_buffer = self.get_circle_instance_buffer(); - vertex_buffer.vec.push(ci); + vertex_buffer.vec.push(SpriteInstance { + src_top_left: [sc.u, sc.v], + src_dims: [sc.sw, sc.sh], + dest_top_left: [sc.x, sc.y], + dest_dims: [sc.w, sc.h], + }); } fn flush(&mut self) { let first_call = match self.draw_calls.last() { Some(call) => call.new_at_buffer_tail(), - None => DrawCall::new(self.mode, self.new_instance_buffer(), 0), + None => DrawCall::new(self.state.new_vertex_buffer(), 0), }; if self.draw_calls.len() > 0 { @@ -999,11 +809,7 @@ impl<'a> FrameBuilder<'a> { depth_stencil_attachment: None, }); - match self.mode { - DrawMode::Sprites => pass.set_pipeline(&self.state.sprite_pipeline), - DrawMode::Circles => pass.set_pipeline(&self.state.circle_pipeline), - } - + pass.set_pipeline(&self.state.render_pipeline); for call in &self.draw_calls { call.draw(&self.state, &mut pass); } diff --git a/src/script/graphics.rs b/src/script/graphics.rs index 7bbe137e..b55b5d93 100644 --- a/src/script/graphics.rs +++ b/src/script/graphics.rs @@ -15,6 +15,18 @@ pub struct ClearCommand { pub color: [f64; 4], } +#[derive(Debug)] +pub struct SpriteCommand { + pub x: f32, + pub y: f32, + pub w: f32, + pub h: f32, + pub u: f32, + pub v: f32, + pub sw: f32, + pub sh: f32, +} + #[derive(Debug)] pub struct CreateTextureCommand { pub id: u32, @@ -26,8 +38,7 @@ pub struct CreateTextureCommand { pub enum GraphicsCommand { Clear(ClearCommand), Print(PrintCommand), - Circle(crate::CircleInstance), - Sprite(crate::SpriteInstance), + Sprite(SpriteCommand), CreateTexture(CreateTextureCommand), CreateWritableTexture { id: u32, @@ -67,26 +78,16 @@ impl GraphicsImpl { } fn spr(&self, x: f32, y: f32, w: f32, h: f32, u: f32, v: f32, sw: f32, sh: f32) { - let _ = self - .sender - .send(GraphicsCommand::Sprite(crate::SpriteInstance { - src_top_left: [u, v], - src_dims: [sw, sh], - dest_top_left: [x, y], - dest_dims: [w, h], - })); - } - - fn circle(&self, x: f32, y: f32, r: f32, s: f32) { - let _ = self - .sender - .send(GraphicsCommand::Circle(crate::CircleInstance { - center: [x, y], - radius: r, - stroke_width: s, - stroke_color: [1.0, 0.0, 0.0, 1.0], - fill_color: [1.0, 1.0, 1.0, 1.0], - })); + let _ = self.sender.send(GraphicsCommand::Sprite(SpriteCommand { + x, + y, + w, + h, + u, + v, + sw, + sh, + })); } fn create_texture( @@ -182,15 +183,6 @@ impl GraphicsAPI { )?, )?; } - { - let gfx = gfx.clone(); - builder.export( - "circle", - ctx.new_fn(move |_: &ContextRef, x: f32, y: f32, r: f32, s: f32| { - gfx.circle(x, y, r, s) - })?, - )?; - } { let gfx = gfx.clone(); builder.export( diff --git a/src/sprite_shader.wgsl b/src/sprite_shader.wgsl index cb2a9a21..267aeb3c 100644 --- a/src/sprite_shader.wgsl +++ b/src/sprite_shader.wgsl @@ -1,6 +1,10 @@ -// ---------------------------------------------------------------------------- // Vertex shader -// ---------------------------------------------------------------------------- + +struct ScreenUniform { + resolution : vec2f, +}; +@group(1) @binding(0) // 1. + var screen : ScreenUniform; struct VertexInput { @location(0) position : vec3, @@ -19,44 +23,12 @@ struct VertexOutput { @location(0) tex_coords : vec2, }; +const RES = vec2f(320.0, 240.0); // The logical resolution of the screen. + @vertex fn vs_main(vertex : VertexInput, instance : InstanceInput)->VertexOutput { var out : VertexOutput; out.tex_coords = instance.src_top_left + (vertex.tex_coords * instance.src_dims); - let in_pos = instance.dest_top_left + (vec2f(vertex.position.x, vertex.position.y) * instance.dest_dims); - - let position = adjust_for_resolution(in_pos); - out.clip_position = vec4f(position.x, position.y, vertex.position.z, 1.0); - return out; -} - -// ---------------------------------------------------------------------------- -// Fragment shader -// ---------------------------------------------------------------------------- - -@group(1) @binding(0) var t_diffuse : texture_2d; -@group(1) @binding(1) var s_diffuse : sampler; - -@fragment fn fs_main(in : VertexOutput)->@location(0) vec4 { - let tc = vec2(u32(in.tex_coords.x), u32(in.tex_coords.y)); - return textureLoad(t_diffuse, tc, 0); -} - - -// ---------------------------------------------------------------------------- -// Resolution Handling -// ---------------------------------------------------------------------------- - -struct ScreenUniform { - resolution : vec2f, -}; -@group(0) @binding(0) // 1. - var screen : ScreenUniform; - -const RES = vec2f(320.0, 240.0); // The logical resolution of the screen. - -fn adjust_for_resolution(in_pos: vec2) -> vec2 { - // Adjust in_pos for the "resolution" of the screen. let RES_AR = RES.x / RES.y; // The aspect ratio of the logical screen. // the actual resolution of the screen. @@ -75,10 +47,22 @@ fn adjust_for_resolution(in_pos: vec2) -> vec2 { } var new_logical_resolution = RES + nudge; - // Now we can convert the incoming position to clip space, in the new screen. + // Now we can convert the incoming position to clip space, in the new screen. + let in_pos = instance.dest_top_left + (vec2f(vertex.position.x, vertex.position.y) * instance.dest_dims); let centered = in_pos + (nudge / 2.0); var position = (2.0 * centered / new_logical_resolution) - 1.0; position.y = -position.y; - - return position; + + out.clip_position = vec4f(position.x, position.y, vertex.position.z, 1.0); + return out; +} + +// Fragment shader.... + +@group(0) @binding(0) var t_diffuse : texture_2d; +@group(0) @binding(1) var s_diffuse : sampler; + +@fragment fn fs_main(in : VertexOutput)->@location(0) vec4 { + let tc = vec2(u32(in.tex_coords.x), u32(in.tex_coords.y)); + return textureLoad(t_diffuse, tc, 0); } diff --git a/types/graphics-core.d.ts b/types/graphics-core.d.ts index 3eaa5234..45cf9b2c 100644 --- a/types/graphics-core.d.ts +++ b/types/graphics-core.d.ts @@ -15,8 +15,6 @@ export function spr( sh: number ); -export function circle(x: number, y: number, r: number, s: number); - export function create_texture( buffer: ArrayBuffer, label: string | undefined