|
|
@ -1,23 +1,33 @@ |
|
|
|
#![allow(dead_code)] |
|
|
|
|
|
|
|
use { |
|
|
|
js_sys::WebAssembly, |
|
|
|
wasm_bindgen::{prelude::*, JsCast}, |
|
|
|
web_sys::{WebGlBuffer, WebGlProgram, WebGlRenderingContext, WebGlShader}, |
|
|
|
web_sys::{WebGlBuffer, WebGlProgram, WebGlRenderingContext, WebGlShader, WebGlTexture}, |
|
|
|
}; |
|
|
|
|
|
|
|
use emgui::Frame; |
|
|
|
|
|
|
|
type Gl = WebGlRenderingContext; |
|
|
|
|
|
|
|
#[wasm_bindgen] |
|
|
|
pub struct Painter { |
|
|
|
canvas: web_sys::HtmlCanvasElement, |
|
|
|
gl: WebGlRenderingContext, |
|
|
|
texture: WebGlTexture, |
|
|
|
program: WebGlProgram, |
|
|
|
index_buffer: WebGlBuffer, |
|
|
|
pos_buffer: WebGlBuffer, |
|
|
|
tc_buffer: WebGlBuffer, |
|
|
|
color_buffer: WebGlBuffer, |
|
|
|
tex_size: (u16, u16), |
|
|
|
} |
|
|
|
|
|
|
|
impl Painter { |
|
|
|
pub fn new(canvas_id: &str) -> Result<Painter, JsValue> { |
|
|
|
pub fn new( |
|
|
|
canvas_id: &str, |
|
|
|
(tex_width, tex_height, pixels): (u16, u16, &[u8]), |
|
|
|
) -> Result<Painter, JsValue> { |
|
|
|
let document = web_sys::window().unwrap().document().unwrap(); |
|
|
|
let canvas = document.get_element_by_id(canvas_id).unwrap(); |
|
|
|
let canvas: web_sys::HtmlCanvasElement = canvas.dyn_into::<web_sys::HtmlCanvasElement>()?; |
|
|
@ -27,19 +37,47 @@ impl Painter { |
|
|
|
.unwrap() |
|
|
|
.dyn_into::<WebGlRenderingContext>()?; |
|
|
|
|
|
|
|
gl.enable(WebGlRenderingContext::BLEND); |
|
|
|
gl.blend_func( |
|
|
|
WebGlRenderingContext::SRC_ALPHA, |
|
|
|
WebGlRenderingContext::ONE_MINUS_SRC_ALPHA, |
|
|
|
); |
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
|
|
let texture = gl.create_texture().unwrap(); |
|
|
|
gl.bind_texture(Gl::TEXTURE_2D, Some(&texture)); |
|
|
|
|
|
|
|
// TODO: remove once https://github.com/rustwasm/wasm-bindgen/issues/1005 is fixed.
|
|
|
|
let mut pixels: Vec<_> = pixels.iter().cloned().collect(); |
|
|
|
|
|
|
|
let level = 0; |
|
|
|
let internal_format = Gl::ALPHA; |
|
|
|
let border = 0; |
|
|
|
let src_format = Gl::ALPHA; |
|
|
|
let src_type = Gl::UNSIGNED_BYTE; |
|
|
|
gl.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array( |
|
|
|
Gl::TEXTURE_2D, |
|
|
|
level, |
|
|
|
internal_format as i32, |
|
|
|
tex_width as i32, |
|
|
|
tex_height as i32, |
|
|
|
border, |
|
|
|
src_format, |
|
|
|
src_type, |
|
|
|
Some(&mut pixels), |
|
|
|
) |
|
|
|
.unwrap(); |
|
|
|
gl.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_WRAP_S, Gl::CLAMP_TO_EDGE as i32); |
|
|
|
gl.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_WRAP_T, Gl::CLAMP_TO_EDGE as i32); |
|
|
|
gl.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_MIN_FILTER, Gl::NEAREST as i32); |
|
|
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
|
|
let vert_shader = compile_shader( |
|
|
|
&gl, |
|
|
|
WebGlRenderingContext::VERTEX_SHADER, |
|
|
|
Gl::VERTEX_SHADER, |
|
|
|
r#" |
|
|
|
uniform vec2 u_screen_size; |
|
|
|
uniform vec2 u_tex_size; |
|
|
|
attribute vec2 a_pos; |
|
|
|
attribute vec2 a_tc; |
|
|
|
attribute vec4 a_color; |
|
|
|
varying vec2 v_tc; |
|
|
|
varying vec4 v_color; |
|
|
|
void main() { |
|
|
|
gl_Position = vec4( |
|
|
@ -47,34 +85,41 @@ impl Painter { |
|
|
|
1.0 - 2.0 * a_pos.y / u_screen_size.y, |
|
|
|
0.0, |
|
|
|
1.0); |
|
|
|
// v_color = vec4(1.0, 0.0, 0.0, 0.5);
|
|
|
|
v_tc = a_tc / u_tex_size; |
|
|
|
v_color = a_color; |
|
|
|
} |
|
|
|
"#, |
|
|
|
)?; |
|
|
|
let frag_shader = compile_shader( |
|
|
|
&gl, |
|
|
|
WebGlRenderingContext::FRAGMENT_SHADER, |
|
|
|
Gl::FRAGMENT_SHADER, |
|
|
|
r#" |
|
|
|
uniform sampler2D u_sampler; |
|
|
|
precision highp float; |
|
|
|
varying vec2 v_tc; |
|
|
|
varying vec4 v_color; |
|
|
|
void main() { |
|
|
|
gl_FragColor = v_color; |
|
|
|
gl_FragColor *= texture2D(u_sampler, v_tc).a; |
|
|
|
} |
|
|
|
"#, |
|
|
|
)?; |
|
|
|
let program = link_program(&gl, [vert_shader, frag_shader].iter())?; |
|
|
|
let index_buffer = gl.create_buffer().ok_or("failed to create index_buffer")?; |
|
|
|
let pos_buffer = gl.create_buffer().ok_or("failed to create pos_buffer")?; |
|
|
|
let tc_buffer = gl.create_buffer().ok_or("failed to create tc_buffer")?; |
|
|
|
let color_buffer = gl.create_buffer().ok_or("failed to create color_buffer")?; |
|
|
|
|
|
|
|
Ok(Painter { |
|
|
|
canvas, |
|
|
|
gl, |
|
|
|
texture, |
|
|
|
program, |
|
|
|
index_buffer, |
|
|
|
pos_buffer, |
|
|
|
tc_buffer, |
|
|
|
color_buffer, |
|
|
|
tex_size: (tex_width, tex_height), |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
@ -83,19 +128,26 @@ impl Painter { |
|
|
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
|
|
gl.enable(Gl::BLEND); |
|
|
|
gl.blend_func(Gl::SRC_ALPHA, Gl::ONE_MINUS_SRC_ALPHA); |
|
|
|
gl.use_program(Some(&self.program)); |
|
|
|
gl.active_texture(Gl::TEXTURE0); |
|
|
|
gl.bind_texture(Gl::TEXTURE_2D, Some(&self.texture)); |
|
|
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
|
|
let indices: Vec<u16> = frame.indices.iter().map(|idx| *idx as u16).collect(); |
|
|
|
|
|
|
|
let mut positions = Vec::with_capacity(2 * frame.vertices.len()); |
|
|
|
let mut positions: Vec<f32> = Vec::with_capacity(2 * frame.vertices.len()); |
|
|
|
let mut tex_coords: Vec<u16> = Vec::with_capacity(2 * frame.vertices.len()); |
|
|
|
for v in &frame.vertices { |
|
|
|
positions.push(v.x); |
|
|
|
positions.push(v.y); |
|
|
|
tex_coords.push(v.u); |
|
|
|
tex_coords.push(v.v); |
|
|
|
} |
|
|
|
|
|
|
|
let mut colors = Vec::with_capacity(4 * frame.vertices.len()); |
|
|
|
let mut colors: Vec<u8> = Vec::with_capacity(4 * frame.vertices.len()); |
|
|
|
for v in &frame.vertices { |
|
|
|
colors.push(v.color.r); |
|
|
|
colors.push(v.color.g); |
|
|
@ -112,14 +164,11 @@ impl Painter { |
|
|
|
let indices_array = js_sys::Int16Array::new(&indices_memory_buffer) |
|
|
|
.subarray(indices_ptr, indices_ptr + indices.len() as u32); |
|
|
|
|
|
|
|
gl.bind_buffer( |
|
|
|
WebGlRenderingContext::ELEMENT_ARRAY_BUFFER, |
|
|
|
Some(&self.index_buffer), |
|
|
|
); |
|
|
|
gl.bind_buffer(Gl::ELEMENT_ARRAY_BUFFER, Some(&self.index_buffer)); |
|
|
|
gl.buffer_data_with_array_buffer_view( |
|
|
|
WebGlRenderingContext::ELEMENT_ARRAY_BUFFER, |
|
|
|
Gl::ELEMENT_ARRAY_BUFFER, |
|
|
|
&indices_array, |
|
|
|
WebGlRenderingContext::STATIC_DRAW, // TODO: STREAM ?
|
|
|
|
Gl::STREAM_DRAW, |
|
|
|
); |
|
|
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
@ -131,29 +180,47 @@ impl Painter { |
|
|
|
let pos_array = js_sys::Float32Array::new(&pos_memory_buffer) |
|
|
|
.subarray(pos_ptr, pos_ptr + positions.len() as u32); |
|
|
|
|
|
|
|
gl.bind_buffer(WebGlRenderingContext::ARRAY_BUFFER, Some(&self.pos_buffer)); |
|
|
|
gl.buffer_data_with_array_buffer_view( |
|
|
|
WebGlRenderingContext::ARRAY_BUFFER, |
|
|
|
&pos_array, |
|
|
|
WebGlRenderingContext::STATIC_DRAW, // TODO: STREAM ?
|
|
|
|
); |
|
|
|
gl.bind_buffer(Gl::ARRAY_BUFFER, Some(&self.pos_buffer)); |
|
|
|
gl.buffer_data_with_array_buffer_view(Gl::ARRAY_BUFFER, &pos_array, Gl::STREAM_DRAW); |
|
|
|
|
|
|
|
let a_pos_loc = gl.get_attrib_location(&self.program, "a_pos"); |
|
|
|
assert!(a_pos_loc >= 0); |
|
|
|
let a_pos_loc = a_pos_loc as u32; |
|
|
|
|
|
|
|
let normalize = false; |
|
|
|
let stride = 0; |
|
|
|
let offset = 0; |
|
|
|
gl.vertex_attrib_pointer_with_i32(a_pos_loc, 2, Gl::FLOAT, normalize, stride, offset); |
|
|
|
gl.enable_vertex_attrib_array(a_pos_loc); |
|
|
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
|
|
let tc_memory_buffer = wasm_bindgen::memory() |
|
|
|
.dyn_into::<WebAssembly::Memory>()? |
|
|
|
.buffer(); |
|
|
|
let tc_ptr = tex_coords.as_ptr() as u32 / 2; |
|
|
|
let tc_array = js_sys::Uint16Array::new(&tc_memory_buffer) |
|
|
|
.subarray(tc_ptr, tc_ptr + tex_coords.len() as u32); |
|
|
|
|
|
|
|
gl.bind_buffer(Gl::ARRAY_BUFFER, Some(&self.tc_buffer)); |
|
|
|
gl.buffer_data_with_array_buffer_view(Gl::ARRAY_BUFFER, &tc_array, Gl::STREAM_DRAW); |
|
|
|
|
|
|
|
let a_tc_loc = gl.get_attrib_location(&self.program, "a_tc"); |
|
|
|
assert!(a_tc_loc >= 0); |
|
|
|
let a_tc_loc = a_tc_loc as u32; |
|
|
|
|
|
|
|
let normalize = false; |
|
|
|
let stride = 0; |
|
|
|
let offset = 0; |
|
|
|
gl.vertex_attrib_pointer_with_i32( |
|
|
|
a_pos_loc, |
|
|
|
a_tc_loc, |
|
|
|
2, |
|
|
|
WebGlRenderingContext::FLOAT, |
|
|
|
Gl::UNSIGNED_SHORT, |
|
|
|
normalize, |
|
|
|
stride, |
|
|
|
offset, |
|
|
|
); |
|
|
|
gl.enable_vertex_attrib_array(a_pos_loc); |
|
|
|
gl.enable_vertex_attrib_array(a_tc_loc); |
|
|
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
|
@ -164,15 +231,8 @@ impl Painter { |
|
|
|
let colors_array = js_sys::Uint8Array::new(&colors_memory_buffer) |
|
|
|
.subarray(colors_ptr, colors_ptr + colors.len() as u32); |
|
|
|
|
|
|
|
gl.bind_buffer( |
|
|
|
WebGlRenderingContext::ARRAY_BUFFER, |
|
|
|
Some(&self.color_buffer), |
|
|
|
); |
|
|
|
gl.buffer_data_with_array_buffer_view( |
|
|
|
WebGlRenderingContext::ARRAY_BUFFER, |
|
|
|
&colors_array, |
|
|
|
WebGlRenderingContext::STATIC_DRAW, // TODO: STREAM ?
|
|
|
|
); |
|
|
|
gl.bind_buffer(Gl::ARRAY_BUFFER, Some(&self.color_buffer)); |
|
|
|
gl.buffer_data_with_array_buffer_view(Gl::ARRAY_BUFFER, &colors_array, Gl::STREAM_DRAW); |
|
|
|
|
|
|
|
let a_color_loc = gl.get_attrib_location(&self.program, "a_color"); |
|
|
|
assert!(a_color_loc >= 0); |
|
|
@ -184,7 +244,7 @@ impl Painter { |
|
|
|
gl.vertex_attrib_pointer_with_i32( |
|
|
|
a_color_loc, |
|
|
|
4, |
|
|
|
WebGlRenderingContext::UNSIGNED_BYTE, |
|
|
|
Gl::UNSIGNED_BYTE, |
|
|
|
normalize, |
|
|
|
stride, |
|
|
|
offset, |
|
|
@ -201,17 +261,23 @@ impl Painter { |
|
|
|
self.canvas.width() as f32, |
|
|
|
self.canvas.height() as f32, |
|
|
|
); |
|
|
|
// gl.uniform2f(Some(&u_screen_size_loc), 4.0, 1.0);
|
|
|
|
|
|
|
|
let u_tex_size_loc = gl |
|
|
|
.get_uniform_location(&self.program, "u_tex_size") |
|
|
|
.unwrap(); |
|
|
|
gl.uniform2f( |
|
|
|
Some(&u_tex_size_loc), |
|
|
|
self.tex_size.0 as f32, |
|
|
|
self.tex_size.1 as f32, |
|
|
|
); |
|
|
|
|
|
|
|
let u_sampler_loc = gl.get_uniform_location(&self.program, "u_sampler").unwrap(); |
|
|
|
gl.uniform1i(Some(&u_sampler_loc), 0); |
|
|
|
|
|
|
|
gl.clear_color(0.05, 0.05, 0.05, 1.0); |
|
|
|
gl.clear(WebGlRenderingContext::COLOR_BUFFER_BIT); |
|
|
|
gl.clear(Gl::COLOR_BUFFER_BIT); |
|
|
|
|
|
|
|
gl.draw_elements_with_i32( |
|
|
|
WebGlRenderingContext::TRIANGLE_STRIP, |
|
|
|
indices.len() as i32, |
|
|
|
WebGlRenderingContext::UNSIGNED_SHORT, |
|
|
|
0, |
|
|
|
); |
|
|
|
gl.draw_elements_with_i32(Gl::TRIANGLES, indices.len() as i32, Gl::UNSIGNED_SHORT, 0); |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
} |
|
|
@ -229,7 +295,7 @@ fn compile_shader( |
|
|
|
gl.compile_shader(&shader); |
|
|
|
|
|
|
|
if gl |
|
|
|
.get_shader_parameter(&shader, WebGlRenderingContext::COMPILE_STATUS) |
|
|
|
.get_shader_parameter(&shader, Gl::COMPILE_STATUS) |
|
|
|
.as_bool() |
|
|
|
.unwrap_or(false) |
|
|
|
{ |
|
|
@ -254,7 +320,7 @@ fn link_program<'a, T: IntoIterator<Item = &'a WebGlShader>>( |
|
|
|
gl.link_program(&program); |
|
|
|
|
|
|
|
if gl |
|
|
|
.get_program_parameter(&program, WebGlRenderingContext::LINK_STATUS) |
|
|
|
.get_program_parameter(&program, Gl::LINK_STATUS) |
|
|
|
.as_bool() |
|
|
|
.unwrap_or(false) |
|
|
|
{ |
|
|
|