j_webgl/
renderer.rs

1use super::{Error, Result, traits, };
2use wasm_bindgen::JsCast;
3
4/// Renderer for WebGL 2.0 rendering operations
5pub 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        // Resize the canvas to match its CSS size, accounting for device pixel ratio
41        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      /*
49      use traits::Camera;
50      let camera = structs::camera::Basic::new(
51        self.canvas.client_width() as f32, self.canvas.client_height() as f32,
52      )?;
53      */
54      for (_, r) in self.renderables.iter() {
55        r.render(&self.context, camera.as_f32_array()?)?;
56      }
57      Ok(())
58    }
59
60    /// Resizes the canvas to match its CSS size, accounting for device pixel ratio
61    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