1use super::{Error, Result, traits, };
2use wasm_bindgen::JsCast;
3
4pub struct Renderer {
6 context: web_sys::WebGl2RenderingContext,
7 canvas: web_sys::HtmlCanvasElement,
8 renderables: Vec<(String, Box<dyn traits::Renderable>)>,
9}
10
11impl Renderer {
12 pub fn with_renderable(&mut self, id: String, renderable: Option<Box<dyn traits::Renderable>>) -> Result<()> {
13 if let Some(r) = renderable {
14 if let Some(i) = self.renderables.iter().position(|(s, _)| s.eq(&id)) {
15 self.renderables[i] = (id, r);
16 } else {
17 self.renderables.push((id, r));
18 }
19 } else {
20 self.renderables.retain(|(s, _)| s.ne(&id));
21 }
22
23 Ok(())
24 }
25
26 pub fn new(canvas: web_sys::HtmlCanvasElement) -> Result<Self> {
27 let context = canvas
28 .get_context("webgl2")
29 .map_err(|e| { Error::UnsupportedOperation(format!("Failed to get WebGL 2.0 context: {:?}", e)) })?
30 .ok_or_else(|| Error::UnsupportedOperation("WebGL 2.0 context is null".to_string()))?
31 .dyn_into::<web_sys::WebGl2RenderingContext>()
32 .map_err(|_| Error::UnsupportedOperation("Failed to cast to WebGL 2.0 context".to_string()))?;
33
34 let renderer = Renderer {
35 context,
36 canvas,
37 renderables: Vec::new(),
38 };
39
40 renderer.resize()?;
42
43 Ok(renderer)
44 }
45
46 pub fn render<C: traits::Camera>(&self, camera: &C) -> Result<()> {
47 self.context.viewport(0, 0, self.canvas.width() as i32, self.canvas.height() as i32);
48 for (_, r) in self.renderables.iter() {
55 r.render(&self.context, camera.as_f32_array()?)?;
56 }
57 Ok(())
58 }
59
60 pub fn resize(&self) -> Result<()> {
62 let window = web_sys::window()
63 .ok_or_else(|| Error::UnsupportedOperation("Window object not available".to_string()))?;
64
65 let dpr = window.device_pixel_ratio();
66 let rect = self.canvas.get_bounding_client_rect();
67
68 self.canvas.set_width((rect.width() * dpr) as u32);
69 self.canvas.set_height((rect.height() * dpr) as u32);
70
71 self.context.viewport(0, 0, self.canvas.width() as i32, self.canvas.height() as i32);
72
73 Ok(())
74 }
75
76 pub fn resize_and_render<C: traits::Camera>(&self, camera: &C) -> Result<()> {
77 self.resize()?;
78 self.render(camera)?;
79 Ok(())
80 }
81}
82