maia_wasm/render/engine/
vao.rs

1use crate::array_view::ArrayView;
2use std::rc::Rc;
3use wasm_bindgen::prelude::*;
4use web_sys::{WebGl2RenderingContext, WebGlBuffer, WebGlProgram, WebGlVertexArrayObject};
5
6use super::RenderEngine;
7
8/// WebGL2 VAO builder.
9///
10/// This builder object is used to create a new Vertex Array Object or modify an
11/// existing one.
12pub struct VaoBuilder<'a> {
13    engine: &'a mut RenderEngine,
14    vao: Rc<WebGlVertexArrayObject>,
15}
16
17impl VaoBuilder<'_> {
18    pub(super) fn new(engine: &mut RenderEngine) -> Result<VaoBuilder<'_>, JsValue> {
19        let vao = Rc::new(
20            engine
21                .gl
22                .create_vertex_array()
23                .ok_or("failed to create VAO")?,
24        );
25        Ok(Self::modify_vao(engine, vao))
26    }
27
28    pub(super) fn modify_vao(
29        engine: &mut RenderEngine,
30        vao: Rc<WebGlVertexArrayObject>,
31    ) -> VaoBuilder<'_> {
32        engine.bind_vertex_array(&vao);
33        VaoBuilder { engine, vao }
34    }
35
36    /// Adds or modifies an array buffer to the VAO.
37    ///
38    /// This function creates a WebGL2 buffer, fills it with the array
39    /// `contents`, and associates it with the VAO being built or modified,
40    /// associating it to a given `attribute` in a WebGL2 `program`.
41    pub fn create_array_buffer<T: ArrayView>(
42        self,
43        program: &WebGlProgram,
44        attribute: &str,
45        size: i32,
46        contents: &[T],
47    ) -> Result<Self, JsValue> {
48        let attribute_location = match self.engine.gl.get_attrib_location(program, attribute) {
49            x if x >= 0 => Ok(x as u32),
50            _ => Err("failed to get attribute location"),
51        }?;
52        self.engine
53            .gl
54            .enable_vertex_attrib_array(attribute_location);
55        self.create_and_fill_buffer(WebGl2RenderingContext::ARRAY_BUFFER, contents)?;
56        let normalized = false;
57        let stride = 0;
58        let offset = 0;
59        self.engine.gl.vertex_attrib_pointer_with_i32(
60            attribute_location,
61            size,
62            T::GL_TYPE,
63            normalized,
64            stride,
65            offset,
66        );
67        Ok(self)
68    }
69
70    /// Adds or modifies an element array buffer to the VAO.
71    ///
72    /// This function creates a WebGL2 buffer, fills it with the array
73    /// `contents`, and associates it with the VAO as an element array buffer.
74    pub fn create_element_array_buffer(self, contents: &[u16]) -> Result<Self, JsValue> {
75        self.create_and_fill_buffer(WebGl2RenderingContext::ELEMENT_ARRAY_BUFFER, contents)?;
76        Ok(self)
77    }
78
79    fn create_and_fill_buffer<T: ArrayView>(
80        &self,
81        target: u32,
82        contents: &[T],
83    ) -> Result<WebGlBuffer, JsValue> {
84        let buffer = self
85            .engine
86            .gl
87            .create_buffer()
88            .ok_or("failed to create_buffer")?;
89        self.engine.gl.bind_buffer(target, Some(&buffer));
90        unsafe {
91            let view = T::view(contents);
92            self.engine.gl.buffer_data_with_array_buffer_view(
93                target,
94                &view,
95                WebGl2RenderingContext::STATIC_DRAW,
96            );
97        }
98        Ok(buffer)
99    }
100
101    /// Builds the VAO.
102    ///
103    /// Finishes the construction of the VAO, returning the VAO object.
104    pub fn build(self) -> Rc<WebGlVertexArrayObject> {
105        self.vao
106    }
107}