Compare commits
3 commits
fd7e19e529
...
aa90cea4a3
| Author | SHA1 | Date | |
|---|---|---|---|
| aa90cea4a3 | |||
| c7903382a0 | |||
| af12dccd5d |
6 changed files with 309 additions and 85 deletions
|
|
@ -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)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
144
src/lib.rs
144
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::<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(())
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 })
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue