1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
// Internal dependencies
use crate::Error;

// External dependencies
use gl::{ self, types::{GLfloat, GLsizei, GLsizeiptr, GLuint}};
use std::{mem, os::raw::c_void, ptr};
use ndarray::Array1;


/// # General Information
///
/// An object that can be represented in CPU via a, ebo, vbo, vao and texture (the latter is not necessary).
///
pub(crate) trait Bindable {
    /// Obtains binder associated to object. Getter.
    fn get_binder(&self) -> Result<&Binder, Error>;

    /// Obtain binder mutable reference.
    fn get_mut_binder(&mut self) -> Result<&mut Binder, Error>;

    /// Shortcut to binder setup function.
    fn setup(&mut self) -> Result<(), Error> {
        Ok(self.get_mut_binder()?.setup())
    }

    /// Shortcut to setup texture function.
    fn setup_texture(&mut self) -> Result<(), Error> {
        Ok(self.get_mut_binder()?.setup_texture())
    }

    /// Shortcut to bind all without texture function.
    fn bind_all_no_texture(&self) -> Result<(), Error> {
        Ok(self.get_binder()?.bind_all_no_texture())
    }

    /// Shortcut to bind all function.
    fn bind_all(&self) -> Result<(), Error> {
        Ok(self.get_binder()?.bind_all())
    }

    /// Shortcut to bind vao function
    fn bind_vao(&self) -> Result<(), Error> {
        Ok(self.get_binder()?.bind_vao())
    }

    /// Shortcut to bind texture
    fn bind_texture(&self) -> Result<(), Error> {
        Ok(self.get_binder()?.bind_texture())
    }

    /// Shortcut to unbind texture
    fn unbind_texture(&self) -> Result<(), Error> {
        Ok(self.get_binder()?.unbind_texture())
    }
}

/// # General Information
///
/// All objects that can be drawn by OpenGL should implement a drawable trait. The main functions are
/// setup and draw. Both which contain general implementations to setup drawable object in GPU and draw it respectively.
///
pub(crate) trait Drawable: Bindable {
    /// Creates a way to obtain vertices from drawable object. Getter.
    fn get_vertices(&self) -> Result<Array1<f32>, Error>;
    /// Creates a way to obtain indices to draw vertices (and triangles). Getter.
    fn get_indices(&self) -> Result<&Array1<u32>, Error>;
    /// Creates a way to obtain order of object's dimensions. Getter.
    fn get_max_length(&self) -> Result<f32, Error>;

    /// # General Information
    ///
    /// Once an object with Drawable trait has been created it can be sent to gpu.
    /// This function will send vertex and indices information to GPU to be drawn on screen.
    /// There's a couple of steps that should never be skipped:
    ///
    /// - Object's binder has to have been initialized prior to this function call.
    /// - Always bind object's binder's vao and/or texture.
    /// - There's no need to bind ebo or vbo once vao is bound.
    ///
    /// # Parameters
    ///
    /// * `&self` - All information is stored inside the object an accesed through the getter methods above.
    ///
    fn send_to_gpu(&self) -> Result<(), Error> {
        let vertices = self.get_vertices()?;
        let indices = self.get_indices()?;

        unsafe {
            // Point to data, specify data length and how it should be drawn (static draw serves to only draw once).
            gl::BufferData(
                gl::ARRAY_BUFFER,
                (vertices.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
                &vertices[0] as *const f32 as *const c_void,
                // Double casting to raw pointer. Equivalent to C's void type when used as pointer.
                gl::DYNAMIC_DRAW,
            );

            // Point to data, specify data length and how it should be drawn
            gl::BufferData(
                gl::ELEMENT_ARRAY_BUFFER,
                (indices.len() * mem::size_of::<GLuint>()) as GLsizeiptr,
                &indices[0] as *const u32 as *const c_void,
                gl::DYNAMIC_DRAW,
            );

            // How should coordinates be read.
            // Reading starts at index 0.
            // Each coordinate is composed of 3 values.
            // No normalized coordinates.
            // The next coordinate is located 3 values after the first index of the previous one.
            // The offset to start reading coordinates (for position it's normally zero. It is used when having texture and/or color coordinates).
            gl::VertexAttribPointer(
                0,
                3,
                gl::FLOAT,
                gl::FALSE,
                (6 * mem::size_of::<GLfloat>()) as GLsizei,
                ptr::null(),
            );
            // Enable vertex atributes giving vertex location (setup in vertex shader).
            gl::EnableVertexAttribArray(0);

            // Enable color visibility
            gl::VertexAttribPointer(
                1,
                3,
                gl::FLOAT,
                gl::FALSE,
                (6 * mem::size_of::<GLfloat>()) as GLsizei,
                (3 * mem::size_of::<GLfloat>()) as *const c_void,
            );
            gl::EnableVertexAttribArray(1);
        }
        Ok(())
    }

    /// # General Information
    ///
    /// 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.
    /// (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
    /// multiple objects, it's better to use the so called 'batch rendering').
    ///
    /// # Parameters
    ///
    /// * `&self` - A reference to the object which is attached to a binder and knows how to get the indices and indices length.
    ///
    fn draw(&self) -> Result<(), Error> {
        let indices_len: i32 = self.get_indices()?.len() as i32;

        // Draw only when window is created and inside loop
        // Drawn as triangles
        unsafe {
            // Draw
            // Comment to see the triangles filled instead of only the lines that form them.
            gl::DrawElements(gl::TRIANGLES, indices_len, gl::UNSIGNED_INT, ptr::null());
        }

        Ok(())
    }
}

/// # General Information
///
/// Variables asocciated with GPU and drawable object(s). Assigned by OpenGL. Should always be mutable.
///
/// # Fields
///
/// * `vbo` (Vertex Buffer Object) -  Vertices Generated by Mesh.
/// * `vao` (Vertex Array Object) - Binds vertices and it's configuration with OpenGL.
/// * `ebo` (Element Buffer Object) - Indices to draw vertices.
/// * `texture` - Texture in 2D to use over object to draw.
///
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct Binder {
    pub(crate) vbo: u32,
    pub(crate) vao: u32,
    pub(crate) ebo: u32,
    pub(crate) texture: u32,
}

impl Binder {
    /// Simple new function. Generates new instance of Binder.
    pub(crate) fn new() -> Binder {
        Binder {
            vbo: 0,
            vao: 0,
            ebo: 0,
            texture: 0,
        }
    }

    /// # General Information
    ///
    /// sets up binder's variables with GPU. Should always be used after instance of window has set up OpenGL context. Never binds texture.
    ///
    /// # Parameters
    ///
    /// * `&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
    /// all drawings have it.
    ///
    pub(crate) fn setup(&mut self) {
        unsafe {
            // Create VAO
            gl::GenVertexArrays(1, &mut self.vao);
            // Bind Vertex Array Object first
            // Since it is bound first, it binds to the EBO and VBO (because they are the only ones being bound after it)
            gl::BindVertexArray(self.vao);

            // Generates a VBO in GPU
            gl::GenBuffers(1, &mut self.vbo);
            // Generates a EBO in GPU
            gl::GenBuffers(1, &mut self.ebo);
            // Bind VBO
            gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
            // BInd VAO
            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
        }
    }

    /// # General Information
    ///
    /// Binds binder's vao to use.
    ///
    /// # Parameters
    ///
    /// * `&self` - Instance does not need to be mutable since it's already setup.
    ///
    pub(crate) fn bind_vao(&self) {
        unsafe {
            gl::BindVertexArray(self.vao);
        }
    }

    /// # General Information
    ///
    /// Binds binder's vbo to use.
    ///
    /// # Parameters
    ///
    /// * `&self` - Instance does not need to be mutable since it's already setup.
    ///
    pub(crate) fn bind_vbo(&self) {
        unsafe {
            gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
        }
    }

    /// # General Information
    ///
    /// Binds binder's ebo to use.
    ///
    /// # Parameters
    ///
    /// * `&self` - Instance does not need to be mutable since it's already setup.
    ///
    pub(crate) fn bind_ebo(&self) {
        unsafe {
            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
        }
    }

    /// # General Information
    ///
    /// Binds vao, ebo and vbo. Never binds texture.
    ///
    /// # Parameters
    ///
    /// * `&self` - Instance does not need to be mutable since it's already setup.
    ///
    #[allow(dead_code)]
    pub(crate) fn bind_all_no_texture(&self) {
        self.bind_vao();
        self.bind_vbo();
        self.bind_ebo();
    }

    /// # General Information
    ///
    /// Binds a 2D texture providing a standar way to scale up and down (mipmap), and enabling blending so that alpha channel is respected.
    /// 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,
    /// while the pixes already present have to be (1-alpha) transparent.
    ///
    /// # Parameters
    ///
    /// * `&mut self` - OpenGL changes values of instance field texture effectively linking current texture to use.
    ///
    pub(crate) fn setup_texture(&mut self) {
        unsafe {
            // generate and bind texture
            gl::GenTextures(1, &mut self.texture);
            gl::BindTexture(gl::TEXTURE_2D, self.texture);
        }
    }

    /// # General Information
    ///
    /// Binds binder's texture to use.
    ///
    /// # Parameters
    ///
    /// * `&self` - Instance does not need to be mutable since it's already setup.
    ///
    pub(crate) fn bind_texture(&self) {
        unsafe {
            gl::BindTexture(gl::TEXTURE_2D, self.texture);
        }
    }

    /// # General information
    ///
    /// Unbind texture when needed. Since not all objects posess a texture, this has to be done sometimes.
    ///
    pub(crate) fn unbind_texture(&self) {
        unsafe {
            gl::BindTexture(gl::TEXTURE_2D, 0);
        }
    }

    /// # General Information
    ///
    /// Binds vao, ebo, vbo and texture.
    ///
    /// # Parameters
    ///
    /// * `&self` - Instance does not need to be mutable since it's already setup.
    ///
    pub(crate) fn bind_all(&self) {
        self.bind_vao();
        self.bind_vbo();
        self.bind_ebo();
        self.bind_texture();
    }
}