beamterm_renderer/gl/renderer.rs
1use web_sys::HtmlCanvasElement;
2
3use crate::{
4 error::Error,
5 gl::{context::GlState, GL},
6 js,
7};
8
9/// Rendering context that provides access to WebGL state.
10pub(super) struct RenderContext<'a> {
11 pub gl: &'a web_sys::WebGl2RenderingContext,
12 pub state: &'a mut GlState,
13}
14
15/// High-level WebGL2 renderer for terminal-style applications.
16///
17/// The `Renderer` manages the WebGL2 rendering context, canvas, and provides
18/// a simplified interface for rendering drawable objects. It handles frame
19/// management, viewport setup, and coordinate system transformations.
20#[derive(Debug)]
21pub struct Renderer {
22 gl: web_sys::WebGl2RenderingContext,
23 canvas: web_sys::HtmlCanvasElement,
24 state: GlState,
25 canvas_padding_color: (f32, f32, f32),
26}
27
28impl Renderer {
29 /// Creates a new renderer by querying for a canvas element with the given ID.
30 ///
31 /// This method searches the DOM for a canvas element with the specified ID,
32 /// initializes a WebGL2 context, and sets up the renderer with orthographic
33 /// projection matching the canvas dimensions.
34 ///
35 /// # Parameters
36 /// * `canvas_id` - CSS selector for the canvas element (e.g., "canvas" or "#my-canvas")
37 ///
38 /// # Returns
39 /// * `Ok(Renderer)` - Successfully created renderer
40 /// * `Err(Error)` - Failed to find canvas, create WebGL context, or initialize renderer
41 ///
42 /// # Errors
43 /// * `Error::UnableToRetrieveCanvas` - Canvas element not found
44 /// * `Error::FailedToRetrieveWebGl2RenderingContext` - WebGL2 not supported or failed to initialize
45 pub fn create(canvas_id: &str) -> Result<Self, Error> {
46 let canvas = js::get_canvas_by_id(canvas_id)?;
47 Self::create_with_canvas(canvas)
48 }
49
50 /// Sets the background color for the canvas area outside the terminal grid.
51 ///
52 /// When the canvas dimensions don't align perfectly with the terminal cell grid,
53 /// there may be unused pixels around the edges. This color fills those padding
54 /// areas to maintain a consistent appearance.
55 pub fn canvas_padding_color(mut self, color: u32) -> Self {
56 let r = ((color >> 16) & 0xFF) as f32 / 255.0;
57 let g = ((color >> 8) & 0xFF) as f32 / 255.0;
58 let b = (color & 0xFF) as f32 / 255.0;
59 self.canvas_padding_color = (r, g, b);
60 self
61 }
62
63 /// Creates a new renderer from an existing HTML canvas element.
64 ///
65 /// This method takes ownership of an existing canvas element and initializes
66 /// the WebGL2 context and renderer state. Useful when you already have a
67 /// reference to the canvas element.
68 ///
69 /// # Parameters
70 /// * `canvas` - HTML canvas element to use for rendering
71 ///
72 /// # Returns
73 /// * `Ok(Renderer)` - Successfully created renderer
74 /// * `Err(Error)` - Failed to create WebGL context or initialize renderer
75 pub fn create_with_canvas(canvas: HtmlCanvasElement) -> Result<Self, Error> {
76 let (width, height) = (canvas.width(), canvas.height());
77
78 // initialize WebGL context
79 let gl = js::get_webgl2_context(&canvas)?;
80 let state = GlState::new(&gl);
81
82 let mut renderer = Self {
83 gl,
84 canvas,
85 state,
86 canvas_padding_color: (0.0, 0.0, 0.0),
87 };
88 renderer.resize(width as _, height as _);
89 Ok(renderer)
90 }
91
92 /// Resizes the canvas and updates the viewport.
93 ///
94 /// This method changes the canvas resolution and adjusts the WebGL viewport
95 /// to match. The projection matrix is automatically updated to maintain
96 /// proper coordinate mapping.
97 ///
98 /// # Parameters
99 /// * `width` - New canvas width in pixels
100 /// * `height` - New canvas height in pixels
101 pub fn resize(&mut self, width: i32, height: i32) {
102 self.canvas.set_width(width as u32);
103 self.canvas.set_height(height as u32);
104 self.state.viewport(&self.gl, 0, 0, width, height);
105 }
106
107 /// Clears the framebuffer with the specified color.
108 ///
109 /// Sets the clear color and clears both the color and depth buffers.
110 /// Color components should be in the range [0.0, 1.0].
111 ///
112 /// # Parameters
113 /// * `r` - Red component (0.0 to 1.0)
114 /// * `g` - Green component (0.0 to 1.0)
115 /// * `b` - Blue component (0.0 to 1.0)
116 pub fn clear(&mut self, r: f32, g: f32, b: f32) {
117 self.state.clear_color(&self.gl, r, g, b, 1.0);
118 self.gl.clear(GL::COLOR_BUFFER_BIT | GL::DEPTH_BUFFER_BIT);
119 }
120
121 /// Begins a new rendering frame.
122 pub fn begin_frame(&mut self) {
123 let (r, g, b) = self.canvas_padding_color;
124 self.clear(r, g, b);
125 }
126
127 /// Renders a drawable object.
128 ///
129 /// This method calls the drawable's prepare, draw, and cleanup methods
130 /// in sequence, providing it with a render context containing.
131 ///
132 /// # Parameters
133 /// * `drawable` - Object implementing the `Drawable` trait
134 #[allow(private_bounds)]
135 pub fn render(&mut self, drawable: &impl Drawable) {
136 let mut context = RenderContext { gl: &self.gl, state: &mut self.state };
137
138 drawable.prepare(&mut context);
139 drawable.draw(&mut context);
140 drawable.cleanup(&mut context);
141 }
142
143 /// Ends the current rendering frame.
144 ///
145 /// This method finalizes the frame rendering. In future versions, this
146 /// may handle buffer swapping or other post-rendering operations.
147 pub fn end_frame(&mut self) {
148 // swap buffers (todo)
149 }
150
151 /// Returns a reference to the WebGL2 rendering context.
152 pub fn gl(&self) -> &GL {
153 &self.gl
154 }
155
156 /// Returns a mutable reference to the WebGL2 rendering context.
157 pub fn canvas(&self) -> &HtmlCanvasElement {
158 &self.canvas
159 }
160
161 /// Returns the current canvas dimensions as a tuple.
162 ///
163 /// # Returns
164 /// Tuple containing (width, height) in pixels
165 pub fn canvas_size(&self) -> (i32, i32) {
166 (self.canvas.width() as i32, self.canvas.height() as i32)
167 }
168}
169
170/// Trait for objects that can be rendered by the renderer.
171pub(super) trait Drawable {
172 /// Prepares the object for rendering.
173 ///
174 /// This method should set up all necessary OpenGL state, bind shaders,
175 /// textures, and vertex data required for rendering.
176 ///
177 /// # Parameters
178 /// * `context` - Mutable reference to the render context
179 fn prepare(&self, context: &mut RenderContext);
180
181 /// Performs the actual rendering.
182 ///
183 /// This method should issue draw calls to render the object. All necessary
184 /// state should already be set up from the `prepare()` call.
185 ///
186 /// # Parameters
187 /// * `context` - Mutable reference to the render context
188 fn draw(&self, context: &mut RenderContext);
189
190 /// Cleans up after rendering.
191 ///
192 /// This method should restore OpenGL state and unbind any resources
193 /// that were bound during `prepare()`. This ensures proper cleanup
194 /// for subsequent rendering operations.
195 ///
196 /// # Parameters
197 /// * `context` - Mutable reference to the render context
198 fn cleanup(&self, context: &mut RenderContext);
199}