From b75597c140e9b4758a1ad803044dda96b403e1a5 Mon Sep 17 00:00:00 2001 From: natrixaeria Date: Fri, 14 Jun 2019 00:13:15 +0200 Subject: Wrap WebGl2 --- webhogg/wasm/src/context/graphics.rs | 19 +++++++++++-------- webhogg/wasm/src/context/mod.rs | 1 + webhogg/wasm/src/context/webgl.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 webhogg/wasm/src/context/webgl.rs diff --git a/webhogg/wasm/src/context/graphics.rs b/webhogg/wasm/src/context/graphics.rs index b94441d..fa37e39 100644 --- a/webhogg/wasm/src/context/graphics.rs +++ b/webhogg/wasm/src/context/graphics.rs @@ -1,10 +1,12 @@ use log::*; use crate::error::WasmError; use wasm_bindgen::JsCast; -use web_sys::WebGl2RenderingContext as Gl; +use web_sys::WebGl2RenderingContext as GlContext; + +use super::webgl::{Color4, WebGl2}; pub struct GraphicsContext { - gl: Gl, + gl: WebGl2, frame_nr: u64, } @@ -16,26 +18,27 @@ impl GraphicsContext { .ok_or_else(|| WasmError::WebGl2ContextCreation( format!("context cration failed: getContext returned nothing")))?; let context = context - .dyn_into::() + .dyn_into::() .map_err(|_| WasmError::WebGl2ContextCreation( format!("context object is not a context")))?; + + let gl = WebGl2::from_context(context); Ok(Self { - gl: context, - frame_nr: 0, + gl, frame_nr: 0, }) } pub fn update(&mut self) -> Result<(), WasmError> { let light = 0.5; + let speed = 60.0; - let a = (self.frame_nr as f32) / 60.0; + let a = (self.frame_nr as f32) / speed; let a = f32::abs(f32::sin(a)); let b = f32::abs(f32::cos(a)); let (a, b) = (a * light, b * light); - self.gl.clear_color(a, light - a, b, 1.0); - self.gl.clear(Gl::COLOR_BUFFER_BIT); + self.gl.clear(Color4::new(a, light - a, b, 1.0)); self.frame_nr += 1; diff --git a/webhogg/wasm/src/context/mod.rs b/webhogg/wasm/src/context/mod.rs index 3e8261b..a581047 100644 --- a/webhogg/wasm/src/context/mod.rs +++ b/webhogg/wasm/src/context/mod.rs @@ -1,3 +1,4 @@ +mod webgl; pub mod graphics; pub mod logic; diff --git a/webhogg/wasm/src/context/webgl.rs b/webhogg/wasm/src/context/webgl.rs new file mode 100644 index 0000000..abecc6e --- /dev/null +++ b/webhogg/wasm/src/context/webgl.rs @@ -0,0 +1,26 @@ +use web_sys::WebGl2RenderingContext as GlContext; + +pub struct Color4(f32, f32, f32, f32); + +impl Color4 { + pub fn new(r: f32, g: f32, b: f32, a: f32) -> Color4 { + Color4(r, g, b, a) + } +} + +pub struct WebGl2 { + gl: GlContext, +} + +impl WebGl2 { + pub fn from_context(context: GlContext) -> Self { + WebGl2 { + gl: context, + } + } + + pub fn clear(&self, color: Color4) { + self.gl.clear_color(color.0, color.1, color.2, color.3); + self.gl.clear(GlContext::COLOR_BUFFER_BIT); + } +} -- cgit v1.2.3 From 4fd207e78452a9e282ef65fc9c3eaf8b19115956 Mon Sep 17 00:00:00 2001 From: natrixaeria Date: Fri, 14 Jun 2019 00:18:35 +0200 Subject: Fill all screen space with the canvas --- webhogg/wasm/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webhogg/wasm/index.html b/webhogg/wasm/index.html index 315d9cb..350c58d 100644 --- a/webhogg/wasm/index.html +++ b/webhogg/wasm/index.html @@ -4,8 +4,8 @@ webhogg - - + + your browser is incompetent -- cgit v1.2.3 From 438825a7ce98a0cd455ff0adebd6d3cf8d3209be Mon Sep 17 00:00:00 2001 From: natrixaeria Date: Fri, 14 Jun 2019 17:20:01 +0200 Subject: Draw a rectangle --- webhogg/wasm/Cargo.toml | 6 +- webhogg/wasm/src/context/graphics.rs | 40 ++++++++++++- webhogg/wasm/src/context/main.fs | 9 +++ webhogg/wasm/src/context/main.vs | 9 +++ webhogg/wasm/src/context/mod.rs | 1 + webhogg/wasm/src/context/shader.rs | 34 +++++++++++ webhogg/wasm/src/context/webgl.rs | 112 ++++++++++++++++++++++++++++++++++- webhogg/wasm/src/error.rs | 6 ++ webhogg/wasm/src/graphics.rs | 5 +- 9 files changed, 215 insertions(+), 7 deletions(-) create mode 100644 webhogg/wasm/src/context/main.fs create mode 100644 webhogg/wasm/src/context/main.vs create mode 100644 webhogg/wasm/src/context/shader.rs diff --git a/webhogg/wasm/Cargo.toml b/webhogg/wasm/Cargo.toml index 2d69f41..8f44389 100644 --- a/webhogg/wasm/Cargo.toml +++ b/webhogg/wasm/Cargo.toml @@ -24,5 +24,9 @@ js-sys = "0.3" version = "0.3" features = [ "OffscreenCanvas", - "WebGl2RenderingContext" + "WebGl2RenderingContext", + "WebGlShader", + "WebGlProgram", + "WebGlBuffer", + "WebGlVertexArrayObject" ] diff --git a/webhogg/wasm/src/context/graphics.rs b/webhogg/wasm/src/context/graphics.rs index fa37e39..6af35a2 100644 --- a/webhogg/wasm/src/context/graphics.rs +++ b/webhogg/wasm/src/context/graphics.rs @@ -1,13 +1,18 @@ -use log::*; use crate::error::WasmError; use wasm_bindgen::JsCast; use web_sys::WebGl2RenderingContext as GlContext; -use super::webgl::{Color4, WebGl2}; +use super::webgl; +use super::webgl::{Color4, ShaderType, WebGl2}; +use super::shader::{MAIN_VERTEX_SHADER, MAIN_FRAGMENT_SHADER}; +use super::shader::ShaderProgram; pub struct GraphicsContext { gl: WebGl2, frame_nr: u64, + shader: ShaderProgram, + vao: webgl::WebGlVertexArrayObject, + buffer: webgl::WebGlBuffer, } impl GraphicsContext { @@ -23,9 +28,33 @@ impl GraphicsContext { format!("context object is not a context")))?; let gl = WebGl2::from_context(context); + let shader = ShaderProgram::from_sources(&gl, &[ + (ShaderType::Vertex, MAIN_VERTEX_SHADER.to_string()), + (ShaderType::Fragment, MAIN_FRAGMENT_SHADER.to_string()), + ])?; + + let vao = gl.create_vertex_array() + .map_err(|_| WasmError::WebGlBuffer( + format!("glGenVertexArrays failed")))?; + gl.bind_vertex_array(&vao); + + let buffer = gl.create_buffer() + .map_err(|_| WasmError::WebGlBuffer( + format!("glCreateBuffer failed")))?; + gl.bind_array_buffer(&buffer); + gl.array_buffer_data_f32(&[ + 0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0, + 0.0, 1.0, + 1.0, 0.0, + ]); + gl.enable_vertex_attrib_array(0); Ok(Self { gl, frame_nr: 0, + shader, vao, buffer }) } @@ -38,7 +67,12 @@ impl GraphicsContext { let b = f32::abs(f32::cos(a)); let (a, b) = (a * light, b * light); - self.gl.clear(Color4::new(a, light - a, b, 1.0)); + self.gl.set_viewport(); + self.gl.clear(&Color4::new(a, light - a, b, 1.0)); + + self.shader.run(&self.gl); + self.gl.vertex_attrib_f32_pointer(0, 2); + self.gl.draw_triangle_arrays(6); self.frame_nr += 1; diff --git a/webhogg/wasm/src/context/main.fs b/webhogg/wasm/src/context/main.fs new file mode 100644 index 0000000..971d2db --- /dev/null +++ b/webhogg/wasm/src/context/main.fs @@ -0,0 +1,9 @@ +#version 300 es + +precision highp float; + +out vec4 color; + +void main() { + color = vec4(0.0, 1.0, 1.0, 1.0); +} diff --git a/webhogg/wasm/src/context/main.vs b/webhogg/wasm/src/context/main.vs new file mode 100644 index 0000000..18df0a0 --- /dev/null +++ b/webhogg/wasm/src/context/main.vs @@ -0,0 +1,9 @@ +#version 300 es + +precision highp float; + +layout(location=0) in vec2 position; + +void main() { + gl_Position = vec4(position, 0.0, 0.0); +} diff --git a/webhogg/wasm/src/context/mod.rs b/webhogg/wasm/src/context/mod.rs index a581047..09902ce 100644 --- a/webhogg/wasm/src/context/mod.rs +++ b/webhogg/wasm/src/context/mod.rs @@ -1,3 +1,4 @@ +mod shader; mod webgl; pub mod graphics; pub mod logic; diff --git a/webhogg/wasm/src/context/shader.rs b/webhogg/wasm/src/context/shader.rs new file mode 100644 index 0000000..9ccb9fc --- /dev/null +++ b/webhogg/wasm/src/context/shader.rs @@ -0,0 +1,34 @@ +use crate::error::WasmError; +use super::webgl; +use super::webgl::{WebGl2, ShaderType}; + +pub const MAIN_VERTEX_SHADER: &str = include_str!("main.vs"); +pub const MAIN_FRAGMENT_SHADER: &str = include_str!("main.fs"); + +pub struct ShaderProgram { + program: webgl::WebGlProgram, +} + +impl ShaderProgram { + pub fn from_sources(gl: &WebGl2, sources: &[(ShaderType, String)]) -> Result { + let program = gl.create_program() + .map_err(|_| WasmError::Shader(format!("glCreateProgram failed ({})", gl.get_error())))?; + for (shader_type, source) in sources { + let shader = gl.create_shader(shader_type) + .map_err(|_| WasmError::Shader(format!("glCreateShader failed ({})", gl.get_error())))?; + gl.shader_source(&shader, source); + gl.compile_shader(&shader) + .map_err(|e| WasmError::Shader(format!("compile error in {} shader: {}", shader_type, e)))?; + gl.attach_shader(&program, &shader) + } + gl.link_program(&program) + .map_err(|e| WasmError::Shader(format!("linker error in program: {}", e)))?; + Ok(Self { + program + }) + } + + pub fn run(&self, gl: &WebGl2) { + gl.use_program(&self.program) + } +} diff --git a/webhogg/wasm/src/context/webgl.rs b/webhogg/wasm/src/context/webgl.rs index abecc6e..8883835 100644 --- a/webhogg/wasm/src/context/webgl.rs +++ b/webhogg/wasm/src/context/webgl.rs @@ -1,13 +1,50 @@ -use web_sys::WebGl2RenderingContext as GlContext; +pub use web_sys::{ + WebGl2RenderingContext as GlContext, + WebGlProgram, WebGlShader, + WebGlBuffer, WebGlVertexArrayObject, +}; +use wasm_bindgen::prelude::*; +use std::fmt::Display; pub struct Color4(f32, f32, f32, f32); +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_namespace=Float32Array, js_name=of, variadic)] + fn _create_f32_buffer(args: &[f32]) -> js_sys::Float32Array; +} + +#[derive(Debug)] +pub enum ShaderType { + Vertex, + Fragment, +} + +impl Display for ShaderType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl ShaderType { + pub fn to_id(&self) -> u32 { + match self { + ShaderType::Vertex => GlContext::VERTEX_SHADER, + ShaderType::Fragment => GlContext::FRAGMENT_SHADER, + } + } +} + impl Color4 { pub fn new(r: f32, g: f32, b: f32, a: f32) -> Color4 { Color4(r, g, b, a) } } +pub fn create_f32_buffer(buffer: &[f32]) -> js_sys::Float32Array { + _create_f32_buffer(buffer) +} + pub struct WebGl2 { gl: GlContext, } @@ -19,8 +56,79 @@ impl WebGl2 { } } - pub fn clear(&self, color: Color4) { + pub fn create_program(&self) -> Result { + self.gl.create_program().ok_or(()) + } + + pub fn create_shader(&self, shader_type: &ShaderType) -> Result { + self.gl.create_shader(shader_type.to_id()).ok_or(()) + } + + pub fn get_error(&self) -> u32 { self.gl.get_error() } + pub fn shader_source(&self, id: &WebGlShader, source: &str) { self.gl.shader_source(id, source) } + pub fn compile_shader(&self, id: &WebGlShader) -> Result<(), String> { + self.gl.compile_shader(id); + if self.gl.get_shader_parameter(id, GlContext::COMPILE_STATUS) == JsValue::FALSE { + Err(self.gl.get_shader_info_log(id) + .unwrap_or("/could not retrieve program information/".to_string())) + } else { Ok(()) } + } + pub fn link_program(&self, id: &WebGlProgram) -> Result<(), String> { + self.gl.link_program(id); + if self.gl.get_program_parameter(id, GlContext::LINK_STATUS) == JsValue::FALSE { + Err(self.gl.get_program_info_log(id) + .unwrap_or("/could not retrieve program information/".to_string())) + } else { Ok(()) } + } + pub fn attach_shader(&self, program: &WebGlProgram, shader: &WebGlShader) { + self.gl.attach_shader(program, shader) + } + + pub fn clear(&self, color: &Color4) { self.gl.clear_color(color.0, color.1, color.2, color.3); self.gl.clear(GlContext::COLOR_BUFFER_BIT); } + + pub fn set_viewport(&self) { + self.gl.viewport(0, 0, self.gl.drawing_buffer_width(), self.gl.drawing_buffer_height()); + } + + pub fn create_buffer(&self) -> Result { + self.gl.create_buffer().ok_or(()) + } + + pub fn bind_array_buffer(&self, buffer: &WebGlBuffer) { + self.gl.bind_buffer(GlContext::ARRAY_BUFFER, Some(buffer)) + } + pub fn unbind_array_buffer(&self) { self.gl.bind_buffer(GlContext::ARRAY_BUFFER, None) } + + pub fn array_buffer_data_f32(&self, data: &[f32]) { + self.gl.buffer_data_with_opt_array_buffer( + GlContext::ARRAY_BUFFER, + Some(&create_f32_buffer(data).buffer()), + GlContext::STATIC_DRAW) + } + + pub fn create_vertex_array(&self) -> Result { + self.gl.create_vertex_array().ok_or(()) + } + pub fn bind_vertex_array(&self, array: &WebGlVertexArrayObject) { + self.gl.bind_vertex_array(Some(array)) + } + pub fn unbind_vertex_array(&self) { self.gl.bind_vertex_array(None) } + pub fn vertex_attrib_f32_pointer(&self, location: u32, dim: i32) { + self.gl.vertex_attrib_pointer_with_i32(location, dim, GlContext::FLOAT, false, 0, 0) + } + + pub fn draw_triangle_arrays(&self, count: i32) { + self.gl.draw_arrays(GlContext::TRIANGLES, 0, count) + } + + pub fn enable_vertex_attrib_array(&self, location: u32) { + self.gl.enable_vertex_attrib_array(location) + } + + pub fn use_program(&self, program: &WebGlProgram) { + self.gl.use_program(Some(program)) + } } diff --git a/webhogg/wasm/src/error.rs b/webhogg/wasm/src/error.rs index 1c6ec27..fbb6bf8 100644 --- a/webhogg/wasm/src/error.rs +++ b/webhogg/wasm/src/error.rs @@ -3,6 +3,8 @@ use std::error::Error; #[derive(Debug)] pub enum WasmError { WebGl2ContextCreation(String), + Shader(String), + WebGlBuffer(String), } impl std::fmt::Display for WasmError { @@ -15,6 +17,8 @@ impl Error for WasmError { fn description(&self) -> &str { match self { WasmError::WebGl2ContextCreation(msg) => msg, + WasmError::Shader(msg) => msg, + WasmError::WebGlBuffer(msg) => msg, } } @@ -25,6 +29,8 @@ impl WasmError { pub fn name(&self) -> &str { match self { WasmError::WebGl2ContextCreation(_) => "WebGl2ContextCreationError", + WasmError::Shader(_) => "ShaderError", + WasmError::WebGlBuffer(_) => "WebGlBufferError", } } } diff --git a/webhogg/wasm/src/graphics.rs b/webhogg/wasm/src/graphics.rs index c4902ae..219c652 100644 --- a/webhogg/wasm/src/graphics.rs +++ b/webhogg/wasm/src/graphics.rs @@ -10,7 +10,10 @@ pub fn start_graphics(canvas: web_sys::OffscreenCanvas) { match context::graphics::GraphicsContext::from_canvas(canvas) { Ok(ctx) => context::set_graphics(ctx), - Err(e) => error!("graphics {}", e) + Err(e) => { + error!("graphics {}", e); + panic!() + } } } -- cgit v1.2.3 From a4532a3f034850c9fe8e26cc210bda618136dcbf Mon Sep 17 00:00:00 2001 From: natrixaeria Date: Fri, 14 Jun 2019 17:28:36 +0200 Subject: Adapt canvas rendering resolution to screen size --- webhogg/wasm/index.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/webhogg/wasm/index.html b/webhogg/wasm/index.html index 350c58d..a992390 100644 --- a/webhogg/wasm/index.html +++ b/webhogg/wasm/index.html @@ -8,6 +8,11 @@ your browser is incompetent + -- cgit v1.2.3