dzahui/simulation/drawable/
binder.rs

1// Internal dependencies
2use crate::Error;
3
4// External dependencies
5use gl::{ self, types::{GLfloat, GLsizei, GLsizeiptr, GLuint}};
6use std::{mem, os::raw::c_void, ptr};
7use ndarray::Array1;
8
9
10/// # General Information
11///
12/// An object that can be represented in CPU via a, ebo, vbo, vao and texture (the latter is not necessary).
13///
14pub(crate) trait Bindable {
15    /// Obtains binder associated to object. Getter.
16    fn get_binder(&self) -> Result<&Binder, Error>;
17
18    /// Obtain binder mutable reference.
19    fn get_mut_binder(&mut self) -> Result<&mut Binder, Error>;
20
21    /// Shortcut to binder setup function.
22    fn setup(&mut self) -> Result<(), Error> {
23        Ok(self.get_mut_binder()?.setup())
24    }
25
26    /// Shortcut to setup texture function.
27    fn setup_texture(&mut self) -> Result<(), Error> {
28        Ok(self.get_mut_binder()?.setup_texture())
29    }
30
31    /// Shortcut to bind all without texture function.
32    fn bind_all_no_texture(&self) -> Result<(), Error> {
33        Ok(self.get_binder()?.bind_all_no_texture())
34    }
35
36    /// Shortcut to bind all function.
37    fn bind_all(&self) -> Result<(), Error> {
38        Ok(self.get_binder()?.bind_all())
39    }
40
41    /// Shortcut to bind vao function
42    fn bind_vao(&self) -> Result<(), Error> {
43        Ok(self.get_binder()?.bind_vao())
44    }
45
46    /// Shortcut to bind texture
47    fn bind_texture(&self) -> Result<(), Error> {
48        Ok(self.get_binder()?.bind_texture())
49    }
50
51    /// Shortcut to unbind texture
52    fn unbind_texture(&self) -> Result<(), Error> {
53        Ok(self.get_binder()?.unbind_texture())
54    }
55}
56
57/// # General Information
58///
59/// All objects that can be drawn by OpenGL should implement a drawable trait. The main functions are
60/// setup and draw. Both which contain general implementations to setup drawable object in GPU and draw it respectively.
61///
62pub(crate) trait Drawable: Bindable {
63    /// Creates a way to obtain vertices from drawable object. Getter.
64    fn get_vertices(&self) -> Result<Array1<f32>, Error>;
65    /// Creates a way to obtain indices to draw vertices (and triangles). Getter.
66    fn get_indices(&self) -> Result<&Array1<u32>, Error>;
67    /// Creates a way to obtain order of object's dimensions. Getter.
68    fn get_max_length(&self) -> Result<f32, Error>;
69
70    /// # General Information
71    ///
72    /// Once an object with Drawable trait has been created it can be sent to gpu.
73    /// This function will send vertex and indices information to GPU to be drawn on screen.
74    /// There's a couple of steps that should never be skipped:
75    ///
76    /// - Object's binder has to have been initialized prior to this function call.
77    /// - Always bind object's binder's vao and/or texture.
78    /// - There's no need to bind ebo or vbo once vao is bound.
79    ///
80    /// # Parameters
81    ///
82    /// * `&self` - All information is stored inside the object an accesed through the getter methods above.
83    ///
84    fn send_to_gpu(&self) -> Result<(), Error> {
85        let vertices = self.get_vertices()?;
86        let indices = self.get_indices()?;
87
88        unsafe {
89            // Point to data, specify data length and how it should be drawn (static draw serves to only draw once).
90            gl::BufferData(
91                gl::ARRAY_BUFFER,
92                (vertices.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
93                &vertices[0] as *const f32 as *const c_void,
94                // Double casting to raw pointer. Equivalent to C's void type when used as pointer.
95                gl::DYNAMIC_DRAW,
96            );
97
98            // Point to data, specify data length and how it should be drawn
99            gl::BufferData(
100                gl::ELEMENT_ARRAY_BUFFER,
101                (indices.len() * mem::size_of::<GLuint>()) as GLsizeiptr,
102                &indices[0] as *const u32 as *const c_void,
103                gl::DYNAMIC_DRAW,
104            );
105
106            // How should coordinates be read.
107            // Reading starts at index 0.
108            // Each coordinate is composed of 3 values.
109            // No normalized coordinates.
110            // The next coordinate is located 3 values after the first index of the previous one.
111            // The offset to start reading coordinates (for position it's normally zero. It is used when having texture and/or color coordinates).
112            gl::VertexAttribPointer(
113                0,
114                3,
115                gl::FLOAT,
116                gl::FALSE,
117                (6 * mem::size_of::<GLfloat>()) as GLsizei,
118                ptr::null(),
119            );
120            // Enable vertex atributes giving vertex location (setup in vertex shader).
121            gl::EnableVertexAttribArray(0);
122
123            // Enable color visibility
124            gl::VertexAttribPointer(
125                1,
126                3,
127                gl::FLOAT,
128                gl::FALSE,
129                (6 * mem::size_of::<GLfloat>()) as GLsizei,
130                (3 * mem::size_of::<GLfloat>()) as *const c_void,
131            );
132            gl::EnableVertexAttribArray(1);
133        }
134        Ok(())
135    }
136
137    /// # General Information
138    ///
139    /// A simple call to glDrawElements in triangles mode. It assumes all information to be drawn has been sent and is stored in a single vbo, veo pair.
140    /// (Making multiple calls to draw is, in general, not a good idea, since it can really slow down a program reducing the FPS. When drawing
141    /// multiple objects, it's better to use the so called 'batch rendering').
142    ///
143    /// # Parameters
144    ///
145    /// * `&self` - A reference to the object which is attached to a binder and knows how to get the indices and indices length.
146    ///
147    fn draw(&self) -> Result<(), Error> {
148        let indices_len: i32 = self.get_indices()?.len() as i32;
149
150        // Draw only when window is created and inside loop
151        // Drawn as triangles
152        unsafe {
153            // Draw
154            // Comment to see the triangles filled instead of only the lines that form them.
155            gl::DrawElements(gl::TRIANGLES, indices_len, gl::UNSIGNED_INT, ptr::null());
156        }
157
158        Ok(())
159    }
160}
161
162/// # General Information
163///
164/// Variables asocciated with GPU and drawable object(s). Assigned by OpenGL. Should always be mutable.
165///
166/// # Fields
167///
168/// * `vbo` (Vertex Buffer Object) -  Vertices Generated by Mesh.
169/// * `vao` (Vertex Array Object) - Binds vertices and it's configuration with OpenGL.
170/// * `ebo` (Element Buffer Object) - Indices to draw vertices.
171/// * `texture` - Texture in 2D to use over object to draw.
172///
173#[derive(Debug, PartialEq, Eq)]
174pub(crate) struct Binder {
175    pub(crate) vbo: u32,
176    pub(crate) vao: u32,
177    pub(crate) ebo: u32,
178    pub(crate) texture: u32,
179}
180
181impl Binder {
182    /// Simple new function. Generates new instance of Binder.
183    pub(crate) fn new() -> Binder {
184        Binder {
185            vbo: 0,
186            vao: 0,
187            ebo: 0,
188            texture: 0,
189        }
190    }
191
192    /// # General Information
193    ///
194    /// sets up binder's variables with GPU. Should always be used after instance of window has set up OpenGL context. Never binds texture.
195    ///
196    /// # Parameters
197    ///
198    /// * `&mut self` - OpenGL changes the values of instance fields effectively setting up linkage beetween vao and vbo and ebo. Texture has to be set up later since not
199    /// all drawings have it.
200    ///
201    pub(crate) fn setup(&mut self) {
202        unsafe {
203            // Create VAO
204            gl::GenVertexArrays(1, &mut self.vao);
205            // Bind Vertex Array Object first
206            // Since it is bound first, it binds to the EBO and VBO (because they are the only ones being bound after it)
207            gl::BindVertexArray(self.vao);
208
209            // Generates a VBO in GPU
210            gl::GenBuffers(1, &mut self.vbo);
211            // Generates a EBO in GPU
212            gl::GenBuffers(1, &mut self.ebo);
213            // Bind VBO
214            gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
215            // BInd VAO
216            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
217        }
218    }
219
220    /// # General Information
221    ///
222    /// Binds binder's vao to use.
223    ///
224    /// # Parameters
225    ///
226    /// * `&self` - Instance does not need to be mutable since it's already setup.
227    ///
228    pub(crate) fn bind_vao(&self) {
229        unsafe {
230            gl::BindVertexArray(self.vao);
231        }
232    }
233
234    /// # General Information
235    ///
236    /// Binds binder's vbo to use.
237    ///
238    /// # Parameters
239    ///
240    /// * `&self` - Instance does not need to be mutable since it's already setup.
241    ///
242    pub(crate) fn bind_vbo(&self) {
243        unsafe {
244            gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
245        }
246    }
247
248    /// # General Information
249    ///
250    /// Binds binder's ebo to use.
251    ///
252    /// # Parameters
253    ///
254    /// * `&self` - Instance does not need to be mutable since it's already setup.
255    ///
256    pub(crate) fn bind_ebo(&self) {
257        unsafe {
258            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
259        }
260    }
261
262    /// # General Information
263    ///
264    /// Binds vao, ebo and vbo. Never binds texture.
265    ///
266    /// # Parameters
267    ///
268    /// * `&self` - Instance does not need to be mutable since it's already setup.
269    ///
270    #[allow(dead_code)]
271    pub(crate) fn bind_all_no_texture(&self) {
272        self.bind_vao();
273        self.bind_vbo();
274        self.bind_ebo();
275    }
276
277    /// # General Information
278    ///
279    /// Binds a 2D texture providing a standar way to scale up and down (mipmap), and enabling blending so that alpha channel is respected.
280    /// When enabling blending, it's necessary to change how it occurs: If alpha channel is to be respected, the incoming pixels have to be alpha transparent,
281    /// while the pixes already present have to be (1-alpha) transparent.
282    ///
283    /// # Parameters
284    ///
285    /// * `&mut self` - OpenGL changes values of instance field texture effectively linking current texture to use.
286    ///
287    pub(crate) fn setup_texture(&mut self) {
288        unsafe {
289            // generate and bind texture
290            gl::GenTextures(1, &mut self.texture);
291            gl::BindTexture(gl::TEXTURE_2D, self.texture);
292        }
293    }
294
295    /// # General Information
296    ///
297    /// Binds binder's texture to use.
298    ///
299    /// # Parameters
300    ///
301    /// * `&self` - Instance does not need to be mutable since it's already setup.
302    ///
303    pub(crate) fn bind_texture(&self) {
304        unsafe {
305            gl::BindTexture(gl::TEXTURE_2D, self.texture);
306        }
307    }
308
309    /// # General information
310    ///
311    /// Unbind texture when needed. Since not all objects posess a texture, this has to be done sometimes.
312    ///
313    pub(crate) fn unbind_texture(&self) {
314        unsafe {
315            gl::BindTexture(gl::TEXTURE_2D, 0);
316        }
317    }
318
319    /// # General Information
320    ///
321    /// Binds vao, ebo, vbo and texture.
322    ///
323    /// # Parameters
324    ///
325    /// * `&self` - Instance does not need to be mutable since it's already setup.
326    ///
327    pub(crate) fn bind_all(&self) {
328        self.bind_vao();
329        self.bind_vbo();
330        self.bind_ebo();
331        self.bind_texture();
332    }
333}