diff --git a/src/lib.rs b/src/lib.rs index a21e5470..42d6346e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,4 @@ use bytemuck; -use wgpu::util::DeviceExt; use winit::{ event::*, event_loop::{ControlFlow, EventLoop}, @@ -8,13 +7,13 @@ use winit::{ }; mod script; -use script::graphics::{ClearCommand, GraphicsCommand, PrintCommand}; +use script::graphics::GraphicsCommand; mod texture; #[repr(C)] #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] struct Vertex { - position: [f32; 3], + position: [f32; 3], // TODO: Why do I pass in a Z here? tex_coords: [f32; 2], } @@ -74,8 +73,7 @@ struct State { render_pipeline: wgpu::RenderPipeline, vertex_buffer: wgpu::Buffer, - index_buffer: wgpu::Buffer, - num_indices: u32, // Indices in index_buffer + max_vertices: usize, diffuse_bind_group: wgpu::BindGroup, @@ -248,17 +246,15 @@ impl State { multiview: None, }); - let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + let max_vertices: usize = 4096; + let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor { label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(VERTICES), - usage: wgpu::BufferUsages::VERTEX, + size: (max_vertices * std::mem::size_of::()) + .try_into() + .unwrap(), + mapped_at_creation: false, + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, }); - let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Index Buffer"), - contents: bytemuck::cast_slice(INDICES), - usage: wgpu::BufferUsages::INDEX, - }); - let num_indices = INDICES.len() as u32; Self { window, @@ -269,8 +265,7 @@ impl State { size, render_pipeline, vertex_buffer, - index_buffer, - num_indices, + max_vertices, diffuse_bind_group, mouse_x: 0.0, @@ -302,11 +297,6 @@ impl State { let view = output .texture .create_view(&wgpu::TextureViewDescriptor::default()); - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Render Encoder"), - }); // Group the commands into passes. struct Pass { @@ -316,8 +306,8 @@ impl State { let mut passes = Vec::new(); for command in commands { match command { - GraphicsCommand::Clear(ClearCommand { color }) => passes.push(Pass { - color: Some(color), + GraphicsCommand::Clear(cc) => passes.push(Pass { + color: Some(cc.color), commands: Vec::new(), }), GraphicsCommand::EndFrame => (), @@ -331,39 +321,91 @@ impl State { } } + let mut vertices = Vec::new(); for pass in passes { - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Render Pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: if let Some([r, g, b, a]) = pass.color { - wgpu::LoadOp::Clear(wgpu::Color { - r, //0.1, - g, //0.2, - b, - a, - }) - } else { - wgpu::LoadOp::Load + // TODO: It would be great if we could use multiple passes in a + // single encoder but right now because of the dyanmic + // nature of vertices we can't, I think? Because + // queue.write_buffer doesn't actually happen until we call + // submit... + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), + }); + + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: if let Some([r, g, b, a]) = pass.color { + wgpu::LoadOp::Clear(wgpu::Color { + r, //0.1, + g, //0.2, + b, + a, + }) + } else { + wgpu::LoadOp::Load + }, + store: true, }, - store: true, - }, - })], - depth_stencil_attachment: None, - }); + })], + depth_stencil_attachment: None, + }); - for command in pass.commands { - match command { - GraphicsCommand::Print(PrintCommand { text }) => { - println!("{}", text); + vertices.clear(); + for command in pass.commands { + match command { + GraphicsCommand::Print(pc) => { + println!("{}", pc.text); + } + GraphicsCommand::Sprite(sc) => { + vertices.push(Vertex { + position: [sc.x, sc.y, 0.0], + tex_coords: [sc.u, sc.v], + }); + vertices.push(Vertex { + position: [sc.x + sc.w, sc.y, 0.0], + tex_coords: [sc.u + sc.sw, sc.v], + }); + vertices.push(Vertex { + position: [sc.x, sc.y + sc.h, 0.0], + tex_coords: [sc.u, sc.v], + }); + vertices.push(Vertex { + position: [sc.x, sc.y + sc.h, 0.0], + tex_coords: [sc.u, sc.v], + }); + vertices.push(Vertex { + position: [sc.x + sc.w, sc.y, 0.0], + tex_coords: [sc.u, sc.v], + }); + vertices.push(Vertex { + position: [sc.x + sc.w, sc.y + sc.h, 0.0], + tex_coords: [sc.u, sc.v], + }); + } + + GraphicsCommand::Clear(_) => (), // Already handled + GraphicsCommand::EndFrame => (), // Should never appear } - - GraphicsCommand::Clear(_) => (), // Already handled - GraphicsCommand::EndFrame => (), // Should never appear } + + assert!(vertices.len() < self.max_vertices); // ! + self.queue + .write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&vertices)); + render_pass.set_pipeline(&self.render_pipeline); + render_pass.set_bind_group(0, &self.diffuse_bind_group, &[]); + render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); + render_pass.draw(0..(vertices.len() as u32), 0..1); } + + // Submit will accept anything that implements IntoIter + self.queue.submit(std::iter::once(encoder.finish())); } // { @@ -397,8 +439,6 @@ impl State { // render_pass.draw_indexed(0..self.num_indices, 0, 0..1); // } - // Submit will accept anything that implements IntoIter - self.queue.submit(std::iter::once(encoder.finish())); output.present(); Ok(()) diff --git a/src/main.js b/src/main.js index 7bc0a8ba..8b21b959 100644 --- a/src/main.js +++ b/src/main.js @@ -1,4 +1,4 @@ -import { cls, print } from "graphics"; +import { cls, print, spr } from "graphics"; export function init() { print("Hello world!"); @@ -8,4 +8,5 @@ export function update() {} export function draw() { cls(0.1, 0.2, 0.3); + spr(0, 0, 0.5, 0.5, 0, 0, 0.5, 0.5); } diff --git a/src/script/graphics.rs b/src/script/graphics.rs index ee3e9f69..4ad1560a 100644 --- a/src/script/graphics.rs +++ b/src/script/graphics.rs @@ -1,4 +1,4 @@ -use oden_js::{module, ContextRef, Error, Value, ValueRef, ValueResult}; +use oden_js::{module, ContextRef, Value, ValueRef, ValueResult}; use std::sync::mpsc::Sender; use std::sync::Arc; @@ -10,9 +10,21 @@ pub struct ClearCommand { pub color: [f64; 4], } +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, +} + pub enum GraphicsCommand { Clear(ClearCommand), Print(PrintCommand), + Sprite(SpriteCommand), EndFrame, } @@ -39,22 +51,37 @@ impl GraphicsImpl { Ok(Value::undefined(ctx)) } - fn cls_fn(&self, ctx: &ContextRef, args: &[&ValueRef]) -> ValueResult { - if args.len() != 3 { - return Err(Error::ArgumentCountMismatch { - expected: 3, - received: args.len(), - }); - } - let r = args[0].to_float64(&ctx)?; - let g = args[1].to_float64(&ctx)?; - let b = args[1].to_float64(&ctx)?; - + fn cls_fn(&self, ctx: &ContextRef, r: f64, g: f64, b: f64) -> ValueResult { let _ = self.sender.send(GraphicsCommand::Clear(ClearCommand { color: [r, g, b, 1.0], })); Ok(Value::undefined(ctx)) } + + fn spr_fn( + &self, + ctx: &ContextRef, + x: f32, + y: f32, + w: f32, + h: f32, + u: f32, + v: f32, + sw: Option, + sh: Option, + ) -> ValueResult { + let _ = self.sender.send(GraphicsCommand::Sprite(SpriteCommand { + x, + y, + w, + h, + u, + v, + sw: sw.unwrap_or(w), + sh: sh.unwrap_or(h), + })); + Ok(Value::undefined(ctx)) + } } pub struct GraphicsAPI { @@ -64,18 +91,43 @@ pub struct GraphicsAPI { impl GraphicsAPI { pub fn define(ctx: &ContextRef, sender: Sender) -> oden_js::Result { let gfx = Arc::new(GraphicsImpl::new(sender)); - let gfx_a = gfx.clone(); - let gfx_b = gfx.clone(); - module::NativeModuleBuilder::new(ctx) - .export( + let mut builder = module::NativeModuleBuilder::new(ctx); + { + let gfx = gfx.clone(); + builder.export( "print", - ctx.new_dynamic_fn(move |ctx, _, args| gfx_a.print_fn(ctx, args))?, - )? - .export( + ctx.new_dynamic_fn(move |ctx, _, args| gfx.print_fn(ctx, args))?, + )?; + } + { + let gfx = gfx.clone(); + builder.export( "cls", - ctx.new_dynamic_fn(move |ctx, _, args| gfx_b.cls_fn(ctx, args))?, - )? - .build("graphics")?; + ctx.new_fn(move |ctx: &ContextRef, r: f64, g: f64, b: f64| { + gfx.cls_fn(ctx, r, g, b) + })?, + )?; + } + { + let gfx = gfx.clone(); + builder.export( + "spr", + ctx.new_fn( + move |ctx: &ContextRef, + x: f32, + y: f32, + w: f32, + h: f32, + u: f32, + v: f32, + sw: Option, + sh: Option| { + gfx.spr_fn(ctx, x, y, w, h, u, v, sw, sh) + }, + )?, + )?; + } + builder.build("graphics")?; Ok(GraphicsAPI { gfx }) }