Compare commits

...

3 commits

Author SHA1 Message Date
aa90cea4a3 [oden] lol sprite API 2023-06-23 06:25:45 -07:00
c7903382a0 [oden-js] Convert Option<> 2023-06-23 05:48:26 -07:00
af12dccd5d [oden-js] Functions up to 8 args 2023-06-23 05:42:05 -07:00
6 changed files with 309 additions and 85 deletions

View file

@ -104,3 +104,14 @@ impl TryFromValue for Value {
Ok(value.dup(ctx))
}
}
impl<T: TryFromValue> TryFromValue for Option<T> {
#[inline]
fn try_from_value(value: &ValueRef, ctx: &ContextRef) -> Result<Self> {
if value.is_undefined() {
Ok(None)
} else {
Ok(Some(T::try_from_value(value, ctx)?))
}
}
}

View file

@ -48,11 +48,11 @@ where
}
}
impl<R, A, F> RustFunction<PhantomData<(&R, &A, &F)>> for F
impl<R, A, FN> RustFunction<PhantomData<(&R, &A, &FN)>> for FN
where
R: IntoRustFunctionResult,
A: TryFromValue,
F: Fn(&ContextRef, A) -> R + Sized,
FN: Fn(&ContextRef, A) -> R + Sized,
{
fn argument_count() -> usize {
1
@ -72,12 +72,12 @@ where
}
}
impl<R, A, B, F> RustFunction<PhantomData<(&R, &A, &B, &F)>> for F
impl<R, A, B, FN> RustFunction<PhantomData<(&R, &A, &B, &FN)>> for FN
where
R: IntoRustFunctionResult,
A: TryFromValue,
B: TryFromValue,
F: Fn(&ContextRef, A, B) -> R + Sized,
FN: Fn(&ContextRef, A, B) -> R + Sized,
{
fn argument_count() -> usize {
2
@ -98,13 +98,13 @@ where
}
}
impl<R, A, B, C, F> RustFunction<PhantomData<(&R, &A, &B, &C, &F)>> for F
impl<R, A, B, C, FN> RustFunction<PhantomData<(&R, &A, &B, &C, &FN)>> for FN
where
R: IntoRustFunctionResult,
A: TryFromValue,
B: TryFromValue,
C: TryFromValue,
F: Fn(&ContextRef, A, B, C) -> R + Sized,
FN: Fn(&ContextRef, A, B, C) -> R + Sized,
{
fn argument_count() -> usize {
3
@ -126,14 +126,14 @@ where
}
}
impl<R, A, B, C, D, F> RustFunction<PhantomData<(&R, &A, &B, &C, &D, &F)>> for F
impl<R, A, B, C, D, FN> RustFunction<PhantomData<(&R, &A, &B, &C, &D, &FN)>> for FN
where
R: IntoRustFunctionResult,
A: TryFromValue,
B: TryFromValue,
C: TryFromValue,
D: TryFromValue,
F: Fn(&ContextRef, A, B, C, D) -> R + Sized,
FN: Fn(&ContextRef, A, B, C, D) -> R + Sized,
{
fn argument_count() -> usize {
4
@ -156,7 +156,7 @@ where
}
}
impl<R, A, B, C, D, E, F> RustFunction<PhantomData<(&R, &A, &B, &C, &D, &E, &F)>> for F
impl<R, A, B, C, D, E, FN> RustFunction<PhantomData<(&R, &A, &B, &C, &D, &E, &FN)>> for FN
where
R: IntoRustFunctionResult,
A: TryFromValue,
@ -164,7 +164,7 @@ where
C: TryFromValue,
D: TryFromValue,
E: TryFromValue,
F: Fn(&ContextRef, A, B, C, D, E) -> R + Sized,
FN: Fn(&ContextRef, A, B, C, D, E) -> R + Sized,
{
fn argument_count() -> usize {
5
@ -187,3 +187,113 @@ where
res.into_res(context)
}
}
impl<R, A, B, C, D, E, F, FN> RustFunction<PhantomData<(&R, &A, &B, &C, &D, &E, &F, &FN)>> for FN
where
R: IntoRustFunctionResult,
A: TryFromValue,
B: TryFromValue,
C: TryFromValue,
D: TryFromValue,
E: TryFromValue,
F: TryFromValue,
FN: Fn(&ContextRef, A, B, C, D, E, F) -> R + Sized,
{
fn argument_count() -> usize {
6
}
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
if args.len() != Self::argument_count() {
return Err(Error::ArgumentCountMismatch {
expected: Self::argument_count(),
received: args.len(),
});
}
let va = A::try_from_value(args[0], &context)?;
let vb = B::try_from_value(args[1], &context)?;
let vc = C::try_from_value(args[2], &context)?;
let vd = D::try_from_value(args[3], &context)?;
let ve = E::try_from_value(args[4], &context)?;
let vf = F::try_from_value(args[4], &context)?;
let res = self(context, va, vb, vc, vd, ve, vf);
res.into_res(context)
}
}
impl<R, A, B, C, D, E, F, G, FN> RustFunction<PhantomData<(&R, &A, &B, &C, &D, &E, &F, &G, &FN)>>
for FN
where
R: IntoRustFunctionResult,
A: TryFromValue,
B: TryFromValue,
C: TryFromValue,
D: TryFromValue,
E: TryFromValue,
F: TryFromValue,
G: TryFromValue,
FN: Fn(&ContextRef, A, B, C, D, E, F, G) -> R + Sized,
{
fn argument_count() -> usize {
7
}
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
if args.len() != Self::argument_count() {
return Err(Error::ArgumentCountMismatch {
expected: Self::argument_count(),
received: args.len(),
});
}
let va = A::try_from_value(args[0], &context)?;
let vb = B::try_from_value(args[1], &context)?;
let vc = C::try_from_value(args[2], &context)?;
let vd = D::try_from_value(args[3], &context)?;
let ve = E::try_from_value(args[4], &context)?;
let vf = F::try_from_value(args[4], &context)?;
let vg = G::try_from_value(args[4], &context)?;
let res = self(context, va, vb, vc, vd, ve, vf, vg);
res.into_res(context)
}
}
impl<R, A, B, C, D, E, F, G, H, FN>
RustFunction<PhantomData<(&R, &A, &B, &C, &D, &E, &F, &G, &H, &FN)>> for FN
where
R: IntoRustFunctionResult,
A: TryFromValue,
B: TryFromValue,
C: TryFromValue,
D: TryFromValue,
E: TryFromValue,
F: TryFromValue,
G: TryFromValue,
H: TryFromValue,
FN: Fn(&ContextRef, A, B, C, D, E, F, G, H) -> R + Sized,
{
fn argument_count() -> usize {
8
}
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
if args.len() != Self::argument_count() {
return Err(Error::ArgumentCountMismatch {
expected: Self::argument_count(),
received: args.len(),
});
}
let va = A::try_from_value(args[0], &context)?;
let vb = B::try_from_value(args[1], &context)?;
let vc = C::try_from_value(args[2], &context)?;
let vd = D::try_from_value(args[3], &context)?;
let ve = E::try_from_value(args[4], &context)?;
let vf = F::try_from_value(args[4], &context)?;
let vg = G::try_from_value(args[4], &context)?;
let vh = H::try_from_value(args[4], &context)?;
let res = self(context, va, vb, vc, vd, ve, vf, vg, vh);
res.into_res(context)
}
}

View file

@ -128,3 +128,13 @@ impl<T: Class> TryIntoValue for T {
self.into_value(ctx)
}
}
impl<T: TryIntoValue> TryIntoValue for Option<T> {
#[inline]
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
match self {
None => Ok(ctx.undefined()),
Some(v) => v.try_into_value(ctx),
}
}
}

View file

@ -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::<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 {
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(())

View file

@ -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);
}

View file

@ -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<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 {
@ -64,18 +91,43 @@ pub struct GraphicsAPI {
impl GraphicsAPI {
pub fn define(ctx: &ContextRef, sender: Sender<GraphicsCommand>) -> oden_js::Result<Self> {
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<f32>,
sh: Option<f32>| {
gfx.spr_fn(ctx, x, y, w, h, u, v, sw, sh)
},
)?,
)?;
}
builder.build("graphics")?;
Ok(GraphicsAPI { gfx })
}