1use glam::f32::{Mat4, Vec3};
2use js_sys::Uint8Array;
3use wasm_bindgen::JsCast;
4use wasm_bindgen::JsValue;
5use wasm_bindgen::prelude::*;
6use wasm_bindgen_futures::spawn_local;
7use web_sys::{
8 Element, HtmlCanvasElement, WebGl2RenderingContext, WebGlBuffer, WebGlProgram, WebGlShader,
9 WebGlUniformLocation, WebGlVertexArrayObject, console, window,
10};
11mod init;
12mod shaders;
13
14const PI: f32 = 3.14159265358979323846;
15#[derive(Clone, Debug)]
20pub struct Mesh {
21 pub vertices: Vec<f32>,
22 pub indices: Vec<u16>,
23}
24
25impl Mesh {
26 pub fn new(vertices: Vec<f32>, indices: Vec<u16>) -> Self {
28 Mesh { vertices, indices }
29 }
30
31 pub fn vertex_count(&self) -> usize {
33 self.vertices.len()
35 }
36
37 pub fn index_count(&self) -> usize {
39 self.indices.len()
40 }
41}
42
43pub fn cube() -> Mesh {
51 let mut verts: Vec<f32> = Vec::with_capacity(24 * 8);
53 let scale = 10000.0; let mut push_v = |pos: [f32; 3], norm: [f32; 3], uv: [f32; 2]| {
55 verts.push(pos[0] * scale);
56 verts.push(pos[1] * scale);
57 verts.push(pos[2] * scale);
58 verts.push(norm[0]);
59 verts.push(norm[1]);
60 verts.push(norm[2]);
61 verts.push(uv[0]);
62 verts.push(uv[1]);
63 };
64
65 let n = [0.0, 0.0, 1.0];
69 push_v([-1.0, -1.0, 1.0], n, [0.0, 0.0]);
70 push_v([1.0, -1.0, 1.0], n, [1.0, 0.0]);
71 push_v([1.0, 1.0, 1.0], n, [1.0, 1.0]);
72 push_v([-1.0, 1.0, 1.0], n, [0.0, 1.0]);
73
74 let n = [0.0, 0.0, -1.0];
76 push_v([1.0, -1.0, -1.0], n, [0.0, 0.0]);
77 push_v([-1.0, -1.0, -1.0], n, [1.0, 0.0]);
78 push_v([-1.0, 1.0, -1.0], n, [1.0, 1.0]);
79 push_v([1.0, 1.0, -1.0], n, [0.0, 1.0]);
80
81 let n = [0.0, 1.0, 0.0];
83 push_v([-1.0, 1.0, 1.0], n, [0.0, 0.0]);
84 push_v([1.0, 1.0, 1.0], n, [1.0, 0.0]);
85 push_v([1.0, 1.0, -1.0], n, [1.0, 1.0]);
86 push_v([-1.0, 1.0, -1.0], n, [0.0, 1.0]);
87
88 let n = [0.0, -1.0, 0.0];
90 push_v([-1.0, -1.0, -1.0], n, [0.0, 0.0]);
91 push_v([1.0, -1.0, -1.0], n, [1.0, 0.0]);
92 push_v([1.0, -1.0, 1.0], n, [1.0, 1.0]);
93 push_v([-1.0, -1.0, 1.0], n, [0.0, 1.0]);
94
95 let n = [1.0, 0.0, 0.0];
97 push_v([1.0, -1.0, 1.0], n, [0.0, 0.0]);
98 push_v([1.0, -1.0, -1.0], n, [1.0, 0.0]);
99 push_v([1.0, 1.0, -1.0], n, [1.0, 1.0]);
100 push_v([1.0, 1.0, 1.0], n, [0.0, 1.0]);
101
102 let n = [-1.0, 0.0, 0.0];
104 push_v([-1.0, -1.0, -1.0], n, [0.0, 0.0]);
105 push_v([-1.0, -1.0, 1.0], n, [1.0, 0.0]);
106 push_v([-1.0, 1.0, 1.0], n, [1.0, 1.0]);
107 push_v([-1.0, 1.0, -1.0], n, [0.0, 1.0]);
108
109 let mut indices: Vec<u16> = Vec::with_capacity(36);
111 for face in 0..6u16 {
112 let base = face * 4;
113 indices.push(base + 0);
114 indices.push(base + 1);
115 indices.push(base + 2);
116 indices.push(base + 0);
117 indices.push(base + 2);
118 indices.push(base + 3);
119 }
120
121 Mesh::new(verts, indices)
122}
123
124#[wasm_bindgen]
125pub struct FastLayer {
126 canvas_id: String,
127 context: WebGl2RenderingContext,
128 program: Option<WebGlProgram>,
129 model: Option<Mesh>,
130 vao: Option<WebGlVertexArrayObject>,
131 vbo: Option<WebGlBuffer>,
132 ebo: Option<WebGlBuffer>,
133}
134
135#[wasm_bindgen]
136impl FastLayer {
137 pub fn new(canvas_id: &str) -> FastLayer {
138 let document = web_sys::window().unwrap().document().unwrap();
139 let canvas = document.get_element_by_id(canvas_id).unwrap();
140 let canvas: web_sys::HtmlCanvasElement = canvas
141 .dyn_into::<web_sys::HtmlCanvasElement>()
142 .map_err(|_| ())
143 .unwrap();
144 let context: WebGl2RenderingContext = canvas
145 .get_context("webgl2")
146 .unwrap()
147 .unwrap()
148 .dyn_into()
149 .unwrap();
150
151 context.enable(WebGl2RenderingContext::DEPTH_TEST);
153
154 FastLayer {
155 canvas_id: canvas_id.to_string(),
156 context,
157 program: None,
158 model: None,
159 vao: None,
160 vbo: None,
161 ebo: None,
162 }
163 }
164
165 pub fn compile(&mut self) {
166 let vertex_source = shaders::VERTEX_SOURCE;
167
168 let fragment_source = shaders::FRAGMENT_SOURCE;
169
170 let vertex_shader = self
171 .context
172 .create_shader(WebGl2RenderingContext::VERTEX_SHADER)
173 .unwrap();
174 self.context.shader_source(&vertex_shader, vertex_source);
175 self.context.compile_shader(&vertex_shader);
176
177 if !self
178 .context
179 .get_shader_parameter(&vertex_shader, WebGl2RenderingContext::COMPILE_STATUS)
180 .as_bool()
181 .unwrap_or(false)
182 {
183 let info = self
184 .context
185 .get_shader_info_log(&vertex_shader)
186 .unwrap_or_else(|| "Unknown error".to_string());
187 panic!("Could not compile vertex shader. \n\n{}", info);
188 }
189
190 let fragment_shader = self
191 .context
192 .create_shader(WebGl2RenderingContext::FRAGMENT_SHADER)
193 .unwrap();
194 self.context
195 .shader_source(&fragment_shader, fragment_source);
196 self.context.compile_shader(&fragment_shader);
197
198 if !self
199 .context
200 .get_shader_parameter(&fragment_shader, WebGl2RenderingContext::COMPILE_STATUS)
201 .as_bool()
202 .unwrap_or(false)
203 {
204 let info = self
205 .context
206 .get_shader_info_log(&fragment_shader)
207 .unwrap_or_else(|| "Unknown error".to_string());
208 panic!("Could not compile fragment shader. \n\n{}", info);
209 }
210
211 let program = self.context.create_program().unwrap();
212 self.context.attach_shader(&program, &vertex_shader);
213 self.context.attach_shader(&program, &fragment_shader);
214 self.context.link_program(&program);
215
216 if !self
217 .context
218 .get_program_parameter(&program, WebGl2RenderingContext::LINK_STATUS)
219 .as_bool()
220 .unwrap_or(false)
221 {
222 let info = self
223 .context
224 .get_program_info_log(&program)
225 .unwrap_or_else(|| "Unknown error".to_string());
226 panic!("Could not link WebGL program. \n\n{}", info);
227 }
228
229 self.program = Some(program);
230
231 if let Some(mesh) = &self.model {
233 let vao = self.context.create_vertex_array();
235 self.context.bind_vertex_array(vao.as_ref());
236
237 let vbo = self.context.create_buffer();
239 self.context
240 .bind_buffer(WebGl2RenderingContext::ARRAY_BUFFER, vbo.as_ref());
241 unsafe {
243 let vert_array = js_sys::Float32Array::view(&mesh.vertices);
244 self.context.buffer_data_with_array_buffer_view(
245 WebGl2RenderingContext::ARRAY_BUFFER,
246 &vert_array,
247 WebGl2RenderingContext::STATIC_DRAW,
248 );
249 }
250
251 let ebo = self.context.create_buffer();
253 self.context
254 .bind_buffer(WebGl2RenderingContext::ELEMENT_ARRAY_BUFFER, ebo.as_ref());
255 unsafe {
256 let idx_array = js_sys::Uint16Array::view(&mesh.indices);
257 self.context.buffer_data_with_array_buffer_view(
258 WebGl2RenderingContext::ELEMENT_ARRAY_BUFFER,
259 &idx_array,
260 WebGl2RenderingContext::STATIC_DRAW,
261 );
262 }
263
264 if let Some(ref prog) = self.program {
267 let pos_loc = self.context.get_attrib_location(prog, "a_position");
269 if pos_loc >= 0 {
270 let idx: u32 = pos_loc as u32;
271 self.context.enable_vertex_attrib_array(idx);
272 self.context.vertex_attrib_pointer_with_i32(
274 idx,
275 3,
276 WebGl2RenderingContext::FLOAT,
277 false,
278 (8 * std::mem::size_of::<f32>()) as i32,
279 0,
280 );
281 }
282 let normal_loc = self.context.get_attrib_location(prog, "a_normal");
284 if normal_loc >= 0 {
285 let idx: u32 = normal_loc as u32;
286 self.context.enable_vertex_attrib_array(idx);
287 self.context.vertex_attrib_pointer_with_i32(
289 idx,
290 3,
291 WebGl2RenderingContext::FLOAT,
292 false,
293 (8 * std::mem::size_of::<f32>()) as i32,
294 (3 * std::mem::size_of::<f32>()) as i32,
295 );
296 }
297 let uv_loc = self.context.get_attrib_location(prog, "a_uv");
299 if uv_loc >= 0 {
300 let idx: u32 = uv_loc as u32;
301 self.context.enable_vertex_attrib_array(idx);
302 self.context.vertex_attrib_pointer_with_i32(
304 idx,
305 2,
306 WebGl2RenderingContext::FLOAT,
307 false,
308 (8 * std::mem::size_of::<f32>()) as i32,
309 (6 * std::mem::size_of::<f32>()) as i32,
310 );
311 }
312 }
313
314 self.context.bind_vertex_array(None);
316
317 self.vao = vao;
319 self.vbo = vbo;
320 self.ebo = ebo;
321 }
322 }
323
324 pub fn draw(&self, matrix: &[f32]) {
325 if matrix.len() != 16 {
326 panic!("matrix must be 16 length")
327 }
328 assert!(self.program.is_some(), "No compiled program available");
330 assert!(self.model.is_some(), "No model available");
331 assert!(self.vao.is_some(), "No VAO available");
332
333 let prog = self.program.as_ref().unwrap();
342 self.context.use_program(Some(prog));
343 let location = self.context.get_uniform_location(prog, "u_matrix");
344
345 if let Some(loc) = location {
346 self.context
402 .uniform_matrix4fv_with_f32_array(Some(&loc), false, &matrix);
403 }
404
405 let model_location = self.context.get_uniform_location(prog, "u_model_matrix");
406 if let Some(loc) = model_location {
407 let model: [f32; 16] = [
410 2.4981121214570498e-8,
411 -3.05930501345358e-24,
412 -0.0,
413 -0.0,
414 -1.8732840461706885e-40,
415 -1.52965250672679e-24,
416 2.4981121214570498e-8,
417 0.0,
418 3.05930501345358e-24,
419 2.4981121214570498e-8,
420 1.52965250672679e-24,
421 0.0,
422 0.5,
423 0.5,
424 0.0,
425 1.0,
426 ];
427 self.context
430 .uniform_matrix4fv_with_f32_array(Some(&loc), false, &model);
431 }
432
433 let mesh = self.model.as_ref().unwrap();
435 self.context.bind_vertex_array(self.vao.as_ref());
436 let count = mesh.index_count() as i32;
437 self.context.draw_elements_with_i32(
438 WebGl2RenderingContext::TRIANGLES,
439 count,
440 WebGl2RenderingContext::UNSIGNED_SHORT,
441 0,
442 );
443 self.context.bind_vertex_array(None);
444 }
445
446 pub fn load_model_from_url(&mut self, url: &str) {
447 self.model = Some(cube());
448 return;
449 let url = url.to_string();
450 spawn_local(async move {
451 let window = match web_sys::window() {
452 Some(w) => w,
453 None => {
454 console::log_1(&JsValue::from_str("No window available"));
455 return;
456 }
457 };
458
459 let fetch_promise = window.fetch_with_str(&url);
460 match wasm_bindgen_futures::JsFuture::from(fetch_promise).await {
461 Ok(resp_value) => {
462 let resp: web_sys::Response = resp_value.dyn_into().unwrap();
463 if !resp.ok() {
464 console::log_1(&JsValue::from_str(&format!(
465 "Failed to fetch {}: HTTP {}",
466 url,
467 resp.status()
468 )));
469 return;
470 }
471
472 match resp.array_buffer() {
473 Ok(ab_promise) => {
474 match wasm_bindgen_futures::JsFuture::from(ab_promise).await {
475 Ok(array_buffer) => {
476 let u8_array = Uint8Array::new(&array_buffer);
477 let size = u8_array.length();
478 console::log_1(&JsValue::from_str(&format!(
479 "Loaded model size: {}",
480 size
481 )));
482 }
483 Err(err) => {
484 console::log_1(&JsValue::from_str(&format!(
485 "Failed to read array_buffer: {:?}",
486 err
487 )));
488 }
489 }
490 }
491 Err(err) => {
492 console::log_1(&JsValue::from_str(&format!(
493 "Failed to call array_buffer(): {:?}",
494 err
495 )));
496 }
497 }
498 }
499 Err(err) => {
500 console::log_1(&JsValue::from_str(&format!("Fetch error: {:?}", err)));
501 }
502 }
503 });
504 }
505}