[oden] lol sprite API
This commit is contained in:
parent
c7903382a0
commit
aa90cea4a3
3 changed files with 168 additions and 75 deletions
144
src/lib.rs
144
src/lib.rs
|
|
@ -1,5 +1,4 @@
|
||||||
use bytemuck;
|
use bytemuck;
|
||||||
use wgpu::util::DeviceExt;
|
|
||||||
use winit::{
|
use winit::{
|
||||||
event::*,
|
event::*,
|
||||||
event_loop::{ControlFlow, EventLoop},
|
event_loop::{ControlFlow, EventLoop},
|
||||||
|
|
@ -8,13 +7,13 @@ use winit::{
|
||||||
};
|
};
|
||||||
|
|
||||||
mod script;
|
mod script;
|
||||||
use script::graphics::{ClearCommand, GraphicsCommand, PrintCommand};
|
use script::graphics::GraphicsCommand;
|
||||||
mod texture;
|
mod texture;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
position: [f32; 3],
|
position: [f32; 3], // TODO: Why do I pass in a Z here?
|
||||||
tex_coords: [f32; 2],
|
tex_coords: [f32; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,8 +73,7 @@ struct State {
|
||||||
render_pipeline: wgpu::RenderPipeline,
|
render_pipeline: wgpu::RenderPipeline,
|
||||||
|
|
||||||
vertex_buffer: wgpu::Buffer,
|
vertex_buffer: wgpu::Buffer,
|
||||||
index_buffer: wgpu::Buffer,
|
max_vertices: usize,
|
||||||
num_indices: u32, // Indices in index_buffer
|
|
||||||
|
|
||||||
diffuse_bind_group: wgpu::BindGroup,
|
diffuse_bind_group: wgpu::BindGroup,
|
||||||
|
|
||||||
|
|
@ -248,17 +246,15 @@ impl State {
|
||||||
multiview: None,
|
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"),
|
label: Some("Vertex Buffer"),
|
||||||
contents: bytemuck::cast_slice(VERTICES),
|
size: (max_vertices * std::mem::size_of::<Vertex>())
|
||||||
usage: wgpu::BufferUsages::VERTEX,
|
.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 {
|
Self {
|
||||||
window,
|
window,
|
||||||
|
|
@ -269,8 +265,7 @@ impl State {
|
||||||
size,
|
size,
|
||||||
render_pipeline,
|
render_pipeline,
|
||||||
vertex_buffer,
|
vertex_buffer,
|
||||||
index_buffer,
|
max_vertices,
|
||||||
num_indices,
|
|
||||||
diffuse_bind_group,
|
diffuse_bind_group,
|
||||||
|
|
||||||
mouse_x: 0.0,
|
mouse_x: 0.0,
|
||||||
|
|
@ -302,11 +297,6 @@ impl State {
|
||||||
let view = output
|
let view = output
|
||||||
.texture
|
.texture
|
||||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
let mut encoder = self
|
|
||||||
.device
|
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
|
||||||
label: Some("Render Encoder"),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Group the commands into passes.
|
// Group the commands into passes.
|
||||||
struct Pass {
|
struct Pass {
|
||||||
|
|
@ -316,8 +306,8 @@ impl State {
|
||||||
let mut passes = Vec::new();
|
let mut passes = Vec::new();
|
||||||
for command in commands {
|
for command in commands {
|
||||||
match command {
|
match command {
|
||||||
GraphicsCommand::Clear(ClearCommand { color }) => passes.push(Pass {
|
GraphicsCommand::Clear(cc) => passes.push(Pass {
|
||||||
color: Some(color),
|
color: Some(cc.color),
|
||||||
commands: Vec::new(),
|
commands: Vec::new(),
|
||||||
}),
|
}),
|
||||||
GraphicsCommand::EndFrame => (),
|
GraphicsCommand::EndFrame => (),
|
||||||
|
|
@ -331,39 +321,91 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut vertices = Vec::new();
|
||||||
for pass in passes {
|
for pass in passes {
|
||||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
// TODO: It would be great if we could use multiple passes in a
|
||||||
label: Some("Render Pass"),
|
// single encoder but right now because of the dyanmic
|
||||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
// nature of vertices we can't, I think? Because
|
||||||
view: &view,
|
// queue.write_buffer doesn't actually happen until we call
|
||||||
resolve_target: None,
|
// submit...
|
||||||
ops: wgpu::Operations {
|
let mut encoder = self
|
||||||
load: if let Some([r, g, b, a]) = pass.color {
|
.device
|
||||||
wgpu::LoadOp::Clear(wgpu::Color {
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
r, //0.1,
|
label: Some("Render Encoder"),
|
||||||
g, //0.2,
|
});
|
||||||
b,
|
|
||||||
a,
|
{
|
||||||
})
|
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
} else {
|
label: Some("Render Pass"),
|
||||||
wgpu::LoadOp::Load
|
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 {
|
vertices.clear();
|
||||||
match command {
|
for command in pass.commands {
|
||||||
GraphicsCommand::Print(PrintCommand { text }) => {
|
match command {
|
||||||
println!("{}", text);
|
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);
|
// 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();
|
output.present();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { cls, print } from "graphics";
|
import { cls, print, spr } from "graphics";
|
||||||
|
|
||||||
export function init() {
|
export function init() {
|
||||||
print("Hello world!");
|
print("Hello world!");
|
||||||
|
|
@ -8,4 +8,5 @@ export function update() {}
|
||||||
|
|
||||||
export function draw() {
|
export function draw() {
|
||||||
cls(0.1, 0.2, 0.3);
|
cls(0.1, 0.2, 0.3);
|
||||||
|
spr(0, 0, 0.5, 0.5, 0, 0, 0.5, 0.5);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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::mpsc::Sender;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -10,9 +10,21 @@ pub struct ClearCommand {
|
||||||
pub color: [f64; 4],
|
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 {
|
pub enum GraphicsCommand {
|
||||||
Clear(ClearCommand),
|
Clear(ClearCommand),
|
||||||
Print(PrintCommand),
|
Print(PrintCommand),
|
||||||
|
Sprite(SpriteCommand),
|
||||||
EndFrame,
|
EndFrame,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,22 +51,37 @@ impl GraphicsImpl {
|
||||||
Ok(Value::undefined(ctx))
|
Ok(Value::undefined(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cls_fn(&self, ctx: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
fn cls_fn(&self, ctx: &ContextRef, r: f64, g: f64, b: f64) -> 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)?;
|
|
||||||
|
|
||||||
let _ = self.sender.send(GraphicsCommand::Clear(ClearCommand {
|
let _ = self.sender.send(GraphicsCommand::Clear(ClearCommand {
|
||||||
color: [r, g, b, 1.0],
|
color: [r, g, b, 1.0],
|
||||||
}));
|
}));
|
||||||
Ok(Value::undefined(ctx))
|
Ok(Value::undefined(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spr_fn(
|
||||||
|
&self,
|
||||||
|
ctx: &ContextRef,
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
w: f32,
|
||||||
|
h: f32,
|
||||||
|
u: f32,
|
||||||
|
v: f32,
|
||||||
|
sw: Option<f32>,
|
||||||
|
sh: Option<f32>,
|
||||||
|
) -> 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 {
|
pub struct GraphicsAPI {
|
||||||
|
|
@ -64,18 +91,43 @@ pub struct GraphicsAPI {
|
||||||
impl GraphicsAPI {
|
impl GraphicsAPI {
|
||||||
pub fn define(ctx: &ContextRef, sender: Sender<GraphicsCommand>) -> oden_js::Result<Self> {
|
pub fn define(ctx: &ContextRef, sender: Sender<GraphicsCommand>) -> oden_js::Result<Self> {
|
||||||
let gfx = Arc::new(GraphicsImpl::new(sender));
|
let gfx = Arc::new(GraphicsImpl::new(sender));
|
||||||
let gfx_a = gfx.clone();
|
let mut builder = module::NativeModuleBuilder::new(ctx);
|
||||||
let gfx_b = gfx.clone();
|
{
|
||||||
module::NativeModuleBuilder::new(ctx)
|
let gfx = gfx.clone();
|
||||||
.export(
|
builder.export(
|
||||||
"print",
|
"print",
|
||||||
ctx.new_dynamic_fn(move |ctx, _, args| gfx_a.print_fn(ctx, args))?,
|
ctx.new_dynamic_fn(move |ctx, _, args| gfx.print_fn(ctx, args))?,
|
||||||
)?
|
)?;
|
||||||
.export(
|
}
|
||||||
|
{
|
||||||
|
let gfx = gfx.clone();
|
||||||
|
builder.export(
|
||||||
"cls",
|
"cls",
|
||||||
ctx.new_dynamic_fn(move |ctx, _, args| gfx_b.cls_fn(ctx, args))?,
|
ctx.new_fn(move |ctx: &ContextRef, r: f64, g: f64, b: f64| {
|
||||||
)?
|
gfx.cls_fn(ctx, r, g, b)
|
||||||
.build("graphics")?;
|
})?,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
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<f32>,
|
||||||
|
sh: Option<f32>| {
|
||||||
|
gfx.spr_fn(ctx, x, y, w, h, u, v, sw, sh)
|
||||||
|
},
|
||||||
|
)?,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
builder.build("graphics")?;
|
||||||
Ok(GraphicsAPI { gfx })
|
Ok(GraphicsAPI { gfx })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue