176 lines
4.4 KiB
Rust
176 lines
4.4 KiB
Rust
use oden_js::{module, ContextRef, Error, Result, Value};
|
|
use std::sync::atomic::{AtomicU32, Ordering};
|
|
use std::sync::mpsc::Sender;
|
|
use std::sync::Arc;
|
|
|
|
#[derive(Debug)]
|
|
pub struct PrintCommand {
|
|
pub text: String,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
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,
|
|
pub image: image::DynamicImage,
|
|
pub label: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum GraphicsCommand {
|
|
Clear(ClearCommand),
|
|
Print(PrintCommand),
|
|
Sprite(SpriteCommand),
|
|
CreateTexture(CreateTextureCommand),
|
|
UseTexture(u32),
|
|
EndFrame,
|
|
}
|
|
|
|
struct GraphicsImpl {
|
|
next_texture_id: AtomicU32,
|
|
sender: Sender<GraphicsCommand>,
|
|
}
|
|
|
|
impl GraphicsImpl {
|
|
pub fn new(sender: Sender<GraphicsCommand>) -> Self {
|
|
GraphicsImpl {
|
|
sender,
|
|
next_texture_id: AtomicU32::new(0),
|
|
}
|
|
}
|
|
|
|
fn print(&self, text: String) -> () {
|
|
let _ = self
|
|
.sender
|
|
.send(GraphicsCommand::Print(PrintCommand { text }));
|
|
}
|
|
|
|
fn cls(&self, r: f64, g: f64, b: f64) -> () {
|
|
let _ = self.sender.send(GraphicsCommand::Clear(ClearCommand {
|
|
color: [r, g, b, 1.0],
|
|
}));
|
|
}
|
|
|
|
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(SpriteCommand {
|
|
x,
|
|
y,
|
|
w,
|
|
h,
|
|
u,
|
|
v,
|
|
sw,
|
|
sh,
|
|
}));
|
|
}
|
|
|
|
fn create_texture(
|
|
&self,
|
|
context: &ContextRef,
|
|
buffer: Value,
|
|
label: Option<String>,
|
|
) -> Result<u32> {
|
|
let bytes = buffer.get_array_buffer(context)?;
|
|
let image = match image::load_from_memory(&bytes) {
|
|
Ok(i) => i,
|
|
Err(e) => return Err(Error::RustFunctionError(format!("{e}"))),
|
|
};
|
|
|
|
let id = self.next_texture_id.fetch_add(1, Ordering::SeqCst);
|
|
let _ = self
|
|
.sender
|
|
.send(GraphicsCommand::CreateTexture(CreateTextureCommand {
|
|
id,
|
|
image,
|
|
label,
|
|
}));
|
|
|
|
Ok(id)
|
|
}
|
|
|
|
fn use_texture(&self, id: u32) {
|
|
let _ = self.sender.send(GraphicsCommand::UseTexture(id));
|
|
}
|
|
}
|
|
|
|
pub struct GraphicsAPI {
|
|
gfx: Arc<GraphicsImpl>,
|
|
}
|
|
|
|
impl GraphicsAPI {
|
|
pub fn define(ctx: &ContextRef, sender: Sender<GraphicsCommand>) -> oden_js::Result<Self> {
|
|
let gfx = Arc::new(GraphicsImpl::new(sender));
|
|
let mut builder = module::native::NativeModuleBuilder::new(ctx);
|
|
{
|
|
let gfx = gfx.clone();
|
|
builder.export(
|
|
"print",
|
|
ctx.new_fn(move |_: &ContextRef, t: String| gfx.print(t))?,
|
|
)?;
|
|
}
|
|
{
|
|
let gfx = gfx.clone();
|
|
builder.export(
|
|
"cls",
|
|
ctx.new_fn(move |_: &ContextRef, r: f64, g: f64, b: f64| gfx.cls(r, g, b))?,
|
|
)?;
|
|
}
|
|
{
|
|
let gfx = gfx.clone();
|
|
builder.export(
|
|
"spr",
|
|
ctx.new_fn(
|
|
move |_: &ContextRef,
|
|
x: f32,
|
|
y: f32,
|
|
w: f32,
|
|
h: f32,
|
|
u: f32,
|
|
v: f32,
|
|
sw: f32,
|
|
sh: f32| gfx.spr(x, y, w, h, u, v, sw, sh),
|
|
)?,
|
|
)?;
|
|
}
|
|
{
|
|
let gfx = gfx.clone();
|
|
builder.export(
|
|
"use_texture",
|
|
ctx.new_fn(move |_: &ContextRef, id: u32| gfx.use_texture(id))?,
|
|
)?;
|
|
}
|
|
{
|
|
let gfx = gfx.clone();
|
|
builder.export(
|
|
"create_texture",
|
|
ctx.new_fn(
|
|
move |c: &ContextRef, buffer: Value, label: Option<String>| {
|
|
gfx.create_texture(c, buffer, label)
|
|
},
|
|
)?,
|
|
)?;
|
|
}
|
|
builder.build("graphics-core")?;
|
|
Ok(GraphicsAPI { gfx })
|
|
}
|
|
|
|
pub fn end_frame(&self) {
|
|
let _ = self.gfx.sender.send(GraphicsCommand::EndFrame);
|
|
}
|
|
}
|