diff options
author | Dennis Kobert <d-kobert@web.de> | 2019-06-21 23:52:07 +0200 |
---|---|---|
committer | Dennis Kobert <d-kobert@web.de> | 2019-06-21 23:52:07 +0200 |
commit | 4e0037169db5d0c7d824debedb5513b69676506a (patch) | |
tree | 82c56547fa70b499627236efa66c3bf7e5411ead | |
parent | 36c89240a87ecb826cf09bc7b3069aa636c9f2f1 (diff) | |
parent | 031f63755aada2f1b51eb945fda2a18ad0d24aad (diff) |
Merge branch 'wasm' into game_server_refactor
-rw-r--r-- | webhogg/wasm/Cargo.toml | 14 | ||||
-rw-r--r-- | webhogg/wasm/index.html | 11 | ||||
-rw-r--r-- | webhogg/wasm/pkg/main.js | 38 | ||||
-rw-r--r-- | webhogg/wasm/pkg/worker-graphics.js | 1 | ||||
-rw-r--r-- | webhogg/wasm/pkg/worker.js | 17 | ||||
-rw-r--r-- | webhogg/wasm/src/context/graphics.rs | 81 | ||||
-rw-r--r-- | webhogg/wasm/src/context/logic.rs | 11 | ||||
-rw-r--r-- | webhogg/wasm/src/context/main.fs | 9 | ||||
-rw-r--r-- | webhogg/wasm/src/context/main.vs | 9 | ||||
-rw-r--r-- | webhogg/wasm/src/context/mod.rs | 26 | ||||
-rw-r--r-- | webhogg/wasm/src/context/shader.rs | 34 | ||||
-rw-r--r-- | webhogg/wasm/src/context/webgl.rs | 134 | ||||
-rw-r--r-- | webhogg/wasm/src/error.rs | 36 | ||||
-rw-r--r-- | webhogg/wasm/src/graphics.rs | 25 | ||||
-rw-r--r-- | webhogg/wasm/src/lib.rs | 13 | ||||
-rw-r--r-- | webhogg/wasm/src/logger.rs | 33 | ||||
-rw-r--r-- | webhogg/wasm/src/logic.rs | 19 |
17 files changed, 498 insertions, 13 deletions
diff --git a/webhogg/wasm/Cargo.toml b/webhogg/wasm/Cargo.toml index 7978928..8f44389 100644 --- a/webhogg/wasm/Cargo.toml +++ b/webhogg/wasm/Cargo.toml @@ -16,3 +16,17 @@ lto = true [dependencies] wasm-bindgen = "0.2" +log = "0.4" +fern = "0.5" +js-sys = "0.3" + +[dependencies.web-sys] +version = "0.3" +features = [ + "OffscreenCanvas", + "WebGl2RenderingContext", + "WebGlShader", + "WebGlProgram", + "WebGlBuffer", + "WebGlVertexArrayObject" +] diff --git a/webhogg/wasm/index.html b/webhogg/wasm/index.html index eae1cc2..a992390 100644 --- a/webhogg/wasm/index.html +++ b/webhogg/wasm/index.html @@ -4,8 +4,15 @@ <meta charset='utf-8'/> <title>webhogg</title> </head> - <body> - <canvas id='c'>your browser is incompetent</canvas> + <body style='margin: 0; width: 100%; height: 100%; posisiton: fixed; display: block;'> + <canvas id='c' style='width: 100%; height: 100%; position: fixed; display: block; overflow: hidden;'> + your browser is incompetent + </canvas> + <script> + let canvas = document.getElementById('c'); + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + </script> <script src='pkg/main.js'></script> </body> </html> diff --git a/webhogg/wasm/pkg/main.js b/webhogg/wasm/pkg/main.js index eec740d..90b3956 100644 --- a/webhogg/wasm/pkg/main.js +++ b/webhogg/wasm/pkg/main.js @@ -1,9 +1,39 @@ +workers = []; + +function exit() { + for (var worker of workers) { + worker.terminate(); + } + console.clear(); +} + async function main() { let fetchingSource = fetch('bin/webhogg-wasm.wasm'); + + let canvasElement = document.getElementById('c'); + let offCanvas = canvasElement.transferControlToOffscreen(); + let fetchedSource = await fetchingSource; - let source = await fetchedSource.text(); - //alert(source) - let workerGraphics = new Worker('pkg/worker-graphics.js'); -} + source = await fetchedSource.arrayBuffer(); + const modules = [ + { type: 'graphics', + source: source, + canvas: offCanvas, + dt: 16 }, + { type: 'logic', + source: source, + canvas: [], + dt: 10000 }, + ]; + for (var module of modules) { + let worker = new Worker('pkg/worker.js'); + if (module.type === 'graphics') { + worker.postMessage(module, [module.canvas]); + } else { + worker.postMessage(module); + } + workers.push(worker); + } +} main(); diff --git a/webhogg/wasm/pkg/worker-graphics.js b/webhogg/wasm/pkg/worker-graphics.js deleted file mode 100644 index 8360014..0000000 --- a/webhogg/wasm/pkg/worker-graphics.js +++ /dev/null @@ -1 +0,0 @@ -console.log('lelel'); diff --git a/webhogg/wasm/pkg/worker.js b/webhogg/wasm/pkg/worker.js new file mode 100644 index 0000000..780ea6f --- /dev/null +++ b/webhogg/wasm/pkg/worker.js @@ -0,0 +1,17 @@ +let data = null; + +onmessage = function (e) { + data = e.data; + + importScripts('../bin/webhogg-wasm.js'); + wasm_bindgen(data.source).then(ctx => { + if (data.type === 'graphics') { + wasm_bindgen.start_graphics(data.canvas); + setInterval(wasm_bindgen.loop_graphics, data.dt); + } else if (data.type === 'logic') { + wasm_bindgen.start_logic(); + setInterval(wasm_bindgen.loop_logic, data.dt); + } + + }); +} diff --git a/webhogg/wasm/src/context/graphics.rs b/webhogg/wasm/src/context/graphics.rs new file mode 100644 index 0000000..6af35a2 --- /dev/null +++ b/webhogg/wasm/src/context/graphics.rs @@ -0,0 +1,81 @@ +use crate::error::WasmError; +use wasm_bindgen::JsCast; +use web_sys::WebGl2RenderingContext as GlContext; + +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 { + pub fn from_canvas(canvas: web_sys::OffscreenCanvas) -> Result<Self, WasmError> { + let context = canvas.get_context("webgl2") + .map_err(|_| WasmError::WebGl2ContextCreation( + format!("context cration failed: getContext returned an exception")))? + .ok_or_else(|| WasmError::WebGl2ContextCreation( + format!("context cration failed: getContext returned nothing")))?; + let context = context + .dyn_into::<GlContext>() + .map_err(|_| WasmError::WebGl2ContextCreation( + 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 + }) + } + + pub fn update(&mut self) -> Result<(), WasmError> { + let light = 0.5; + let speed = 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.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; + + Ok(()) + } +} diff --git a/webhogg/wasm/src/context/logic.rs b/webhogg/wasm/src/context/logic.rs new file mode 100644 index 0000000..71dfea4 --- /dev/null +++ b/webhogg/wasm/src/context/logic.rs @@ -0,0 +1,11 @@ +use crate::error::WasmError; + +pub struct LogicContext { +} + +impl LogicContext { + pub fn new() -> Result<Self, WasmError> { + Ok(Self { + }) + } +} 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 new file mode 100644 index 0000000..09902ce --- /dev/null +++ b/webhogg/wasm/src/context/mod.rs @@ -0,0 +1,26 @@ +mod shader; +mod webgl; +pub mod graphics; +pub mod logic; + +use graphics::GraphicsContext; +use logic::LogicContext; + +static mut GTX: Option<GraphicsContext> = None; +static mut LTX: Option<LogicContext> = None; + +pub fn get_graphics() -> &'static mut GraphicsContext { + unsafe { GTX.as_mut().unwrap() } +} + +pub fn get_logic() -> &'static mut LogicContext { + unsafe { LTX.as_mut().unwrap() } +} + +pub fn set_graphics(gtx: GraphicsContext) { + unsafe { GTX = Some(gtx) } +} + +pub fn set_logic(ltx: LogicContext) { + unsafe { LTX = Some(ltx) } +} 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<Self, WasmError> { + 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 new file mode 100644 index 0000000..8883835 --- /dev/null +++ b/webhogg/wasm/src/context/webgl.rs @@ -0,0 +1,134 @@ +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, +} + +impl WebGl2 { + pub fn from_context(context: GlContext) -> Self { + WebGl2 { + gl: context, + } + } + + pub fn create_program(&self) -> Result<WebGlProgram, ()> { + self.gl.create_program().ok_or(()) + } + + pub fn create_shader(&self, shader_type: &ShaderType) -> Result<WebGlShader, ()> { + 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<WebGlBuffer, ()> { + 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<WebGlVertexArrayObject, ()> { + 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 new file mode 100644 index 0000000..fbb6bf8 --- /dev/null +++ b/webhogg/wasm/src/error.rs @@ -0,0 +1,36 @@ +use std::error::Error; + +#[derive(Debug)] +pub enum WasmError { + WebGl2ContextCreation(String), + Shader(String), + WebGlBuffer(String), +} + +impl std::fmt::Display for WasmError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!(f, "{}: {}", self.name(), self.description()) + } +} + +impl Error for WasmError { + fn description(&self) -> &str { + match self { + WasmError::WebGl2ContextCreation(msg) => msg, + WasmError::Shader(msg) => msg, + WasmError::WebGlBuffer(msg) => msg, + } + } + + fn source(&self) -> Option<&'static dyn Error> { None } +} + +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 new file mode 100644 index 0000000..219c652 --- /dev/null +++ b/webhogg/wasm/src/graphics.rs @@ -0,0 +1,25 @@ +use wasm_bindgen::prelude::*; +use log::*; +use crate::*; + +#[wasm_bindgen] +pub fn start_graphics(canvas: web_sys::OffscreenCanvas) { + logger::init_logger(); + info!("graphics: wasm entry-point reached"); + //debug!("js value is?: undefined: {}", canvas.is_undefined()); + + match context::graphics::GraphicsContext::from_canvas(canvas) { + Ok(ctx) => context::set_graphics(ctx), + Err(e) => { + error!("graphics {}", e); + panic!() + } + } +} + +#[wasm_bindgen] +pub fn loop_graphics() { + context::get_graphics().update() + .map_err(|e| error!("gaphics loop {}", e)) + .unwrap(); +} diff --git a/webhogg/wasm/src/lib.rs b/webhogg/wasm/src/lib.rs index cb334fb..7aa4e86 100644 --- a/webhogg/wasm/src/lib.rs +++ b/webhogg/wasm/src/lib.rs @@ -1,8 +1,9 @@ -use wasm_bindgen::prelude::*; +mod logger; +pub mod error; +pub mod context; -#[wasm_bindgen] -pub fn enrty() { -} +pub mod logic; +pub mod graphics; -fn main() { -} +pub use logic::*; +pub use graphics::*; diff --git a/webhogg/wasm/src/logger.rs b/webhogg/wasm/src/logger.rs new file mode 100644 index 0000000..96c4cbf --- /dev/null +++ b/webhogg/wasm/src/logger.rs @@ -0,0 +1,33 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_namespace=console, js_name=log)] + fn __console_log_colored2(f: &str, c1: &str, c2: &str); +} + +fn log(rec: &log::Record) { + __console_log_colored2(&format!("{}", rec.args()), + &format!("color: {}", match rec.level() { + log::Level::Trace => "violet", + log::Level::Debug => "blue", + log::Level::Info => "green", + log::Level::Warn => "orange", + log::Level::Error => "red" + }), ""); +} + +pub fn init_logger() { + fern::Dispatch::new().format(|out, message, record|{ + out.finish(format_args!( + "%c{}%c {} > {}", + record.level(), + record.target(), + message + ) + ) + }) + .level(log::LevelFilter::Debug) + .chain(fern::Output::call(log)) + .apply().unwrap(); +} diff --git a/webhogg/wasm/src/logic.rs b/webhogg/wasm/src/logic.rs new file mode 100644 index 0000000..14272d9 --- /dev/null +++ b/webhogg/wasm/src/logic.rs @@ -0,0 +1,19 @@ +use wasm_bindgen::prelude::*; +use log::*; +use crate::*; + +#[wasm_bindgen] +pub fn start_logic() { + logger::init_logger(); + info!("logic: wasm entry-point reached"); + + match context::logic::LogicContext::new() { + Ok(ctx) => context::set_logic(ctx), + Err(e) => error!("logic {}", e) + } +} + +#[wasm_bindgen] +pub fn loop_logic() { + debug!("logic: loopin'"); +} |