easy_opengl/
textures.rs

1use std::ffi::c_void;
2use std::ffi::CString;
3
4#[derive(Copy, Clone)]
5pub enum TextureParam {
6    Linear = gl::LINEAR as isize,
7    ClampToEdge = gl::CLAMP_TO_EDGE as isize,
8    Nearest = gl::NEAREST as isize,
9    Repeat = gl::REPEAT as isize,
10}
11
12#[derive(Clone, Copy)]
13pub enum TextureFormat {
14    Rgba = gl::RGBA as isize,
15    Rgb = gl::RGB as isize,
16    Red = gl::RED as isize,
17}
18
19// pub enum PixelDataType {
20//     I8 = gl::BYTE as isize,
21//     U8 = gl::UNSIGNED_BYTE as isize,
22//     I32 = gl::INT as isize,
23//     U32 = gl::UNSIGNED_INT as isize,
24//     F32 = gl::FLOAT as isize,
25// }
26
27pub struct TextureConfig {
28    min_filter: TextureParam,
29    mag_filter: TextureParam,
30    wrap_s: TextureParam,
31    wrap_t: TextureParam,
32
33    format: TextureFormat,
34    internal_format: TextureFormat,
35    // pixel_data_type: PixelDataType,
36    bitmap: bool,
37}
38
39impl TextureConfig {
40    pub fn new() -> Self {
41        Self {
42            min_filter: TextureParam::Nearest,
43            mag_filter: TextureParam::Linear,
44            wrap_s: TextureParam::Repeat,
45            wrap_t: TextureParam::Repeat,
46            format: TextureFormat::Rgb,
47            internal_format: TextureFormat::Rgba,
48            // pixel_data_type: PixelDataType::U8,
49            bitmap: true,
50        }
51    }
52}
53
54/// A abstract representation of a 2D texture
55///  # Example
56/// ``` Rust
57/// let mut texture1 = Texture2D::new();
58/// texture1.load_from_file("./src/a.png", TextureConfig::new());
59/// texture1.send_data(30, 30, 1, 1, 0xFF000000 as ptr); // Set a red pixel on x: 30, y: 30
60///
61/// let data = vec![...];
62/// let texture2 = Texture2D::new();
63/// texture2.gen_texture(TextureConfig::new());
64/// texture2.send_data(0, 0, 100, 200, data as ptr);
65///
66/// let texture3 = Texture2D::new();
67/// texture3.load_from_memory(data as ptr, TextureConfig::new());
68/// ```
69pub struct Texture2D {
70    pub id: u32,
71    pub width: u32,
72    pub height: u32,
73    pub config: Option<TextureConfig>,
74}
75
76impl Texture2D {
77    pub fn new() -> Self {
78        Self {
79            id: 0,
80            width: 0,
81            height: 0,
82            config: None,
83        }
84    }
85    // Its function allow to generate and allocate a texture to send data later
86    pub fn gen_texture(&mut self, config: TextureConfig) {
87        if self.config.is_some() {
88            println!("Texture already created");
89            return;
90        }
91
92        self.config = Some(config);
93        let config = self.config.as_mut().unwrap();
94        unsafe {
95            gl::GenTextures(1, &mut self.id);
96            gl::BindTexture(gl::TEXTURE_2D, self.id);
97            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, config.wrap_s as i32);
98            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, config.wrap_t as i32);
99            gl::TexParameteri(
100                gl::TEXTURE_2D,
101                gl::TEXTURE_MIN_FILTER,
102                config.min_filter as i32,
103            );
104            gl::TexParameteri(
105                gl::TEXTURE_2D,
106                gl::TEXTURE_MAG_FILTER,
107                config.mag_filter as i32,
108            );
109        }
110    }
111
112    // Send data on a already allocated texture with the config of the generated texture
113    pub fn send_data(
114        &self,
115        xoffset: u32,
116        yoffset: u32,
117        width: u32,
118        height: u32,
119        data: *const c_void,
120    ) {
121        if self.config.is_none() {
122            println!("A texture needs to be created first");
123            return;
124        }
125
126        let config = self.config.as_ref().unwrap();
127
128        unsafe {
129            gl::TexSubImage2D(
130                gl::TEXTURE_2D,
131                0,
132                xoffset as i32,
133                yoffset as i32,
134                width as i32,
135                height as i32,
136                config.format as u32,
137                gl::UNSIGNED_BYTE,
138                data,
139            );
140        }
141    }
142
143    // Generate and allocate a texture with the given data
144    pub fn load_from_memory(
145        &mut self,
146        width: u32,
147        height: u32,
148        data: *const c_void,
149        config: TextureConfig,
150    ) {
151        if self.config.is_some() {
152            println!("Texture already created");
153            return;
154        }
155
156        self.config = Some(config);
157        let config = self.config.as_mut().unwrap();
158
159        unsafe {
160            gl::GenTextures(1, &mut self.id);
161            gl::BindTexture(gl::TEXTURE_2D, self.id);
162            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, config.wrap_s as i32);
163            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, config.wrap_t as i32);
164            gl::TexParameteri(
165                gl::TEXTURE_2D,
166                gl::TEXTURE_MIN_FILTER,
167                config.min_filter as i32,
168            );
169            gl::TexParameteri(
170                gl::TEXTURE_2D,
171                gl::TEXTURE_MAG_FILTER,
172                config.mag_filter as i32,
173            );
174
175            gl::TexImage2D(
176                gl::TEXTURE_2D,
177                0,
178                config.internal_format as i32,
179                width as i32,
180                height as i32,
181                0,
182                config.format as u32,
183                gl::UNSIGNED_BYTE,
184                data,
185            );
186        }
187    }
188
189    // Generate and allocate a texture with given file path
190    pub fn load_from_file(&mut self, filepath: &str, config: TextureConfig) {
191        if self.config.is_some() {
192            println!("Texture already created");
193            return;
194        }
195
196        self.config = Some(config);
197        let config = self.config.as_mut().unwrap();
198
199        unsafe {
200            let c_str_filename = CString::new(filepath.as_bytes()).unwrap();
201            stb_image::stb_image::bindgen::stbi_set_flip_vertically_on_load(1);
202            let mut width = 0;
203            let mut height = 0;
204            let mut channels = 0;
205            let data = stb_image::stb_image::bindgen::stbi_load(
206                c_str_filename.as_ptr(),
207                &mut width,
208                &mut height,
209                &mut channels,
210                0,
211            );
212
213            if data.is_null() {
214                panic!("Fail to load texture {}", filepath);
215            }
216
217            if channels == 1 {
218                config.format = TextureFormat::Red;
219            } else if channels == 3 {
220                config.format = TextureFormat::Rgb;
221            } else {
222                config.format = TextureFormat::Rgba;
223            }
224
225            gl::GenTextures(1, &mut self.id);
226            gl::BindTexture(gl::TEXTURE_2D, self.id);
227            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, config.wrap_s as i32);
228            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, config.wrap_t as i32);
229            gl::TexParameteri(
230                gl::TEXTURE_2D,
231                gl::TEXTURE_MIN_FILTER,
232                config.min_filter as i32,
233            );
234            gl::TexParameteri(
235                gl::TEXTURE_2D,
236                gl::TEXTURE_MAG_FILTER,
237                config.mag_filter as i32,
238            );
239
240            gl::TexImage2D(
241                gl::TEXTURE_2D,
242                0,
243                config.internal_format as i32,
244                width,
245                height,
246                0,
247                config.format as u32,
248                gl::UNSIGNED_BYTE,
249                data as *const c_void,
250            );
251
252            if config.bitmap {
253                gl::GenerateMipmap(gl::TEXTURE_2D);
254            }
255        }
256    }
257
258    pub fn bind(&self) {
259        unsafe {
260            gl::BindTexture(gl::TEXTURE_2D, self.id);
261        }
262    }
263}
264
265impl Drop for Texture2D {
266    fn drop(&mut self) {
267        unsafe {
268            gl::DeleteTextures(1, &self.id);
269        }
270    }
271}