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
19pub 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 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 bitmap: true,
50 }
51 }
52}
53
54pub 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 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 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 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 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}