fere_resources/
texture.rs

1use fere_common::*;
2use gl::types::*;
3use image::{io::Reader as IReader, DynamicImage};
4use std::io::{BufRead, Seek};
5
6#[derive(Debug)]
7pub struct TextureData {
8    pub name: String,
9    pub data: Vec<u8>,
10    pub size: IVec2,
11    pub channel: u8,
12}
13
14pub fn import<T: BufRead + Seek>(name: &str, source: T) -> Result<TextureData, String> {
15    let source = IReader::new(source).with_guessed_format().unwrap();
16    let result = source.decode().map_err(|e| format!("{:?}", e))?;
17    match result {
18        DynamicImage::ImageRgb8(img) => {
19            let size = IVec2::new(img.dimensions().0 as i32, img.dimensions().1 as i32);
20            let data = img.into_raw();
21            assert_eq!(data.len(), (size.x * size.y * 3) as usize);
22            Ok(TextureData {
23                name: name.to_owned(),
24                data,
25                size,
26                channel: 3,
27            })
28        }
29        DynamicImage::ImageRgba8(img) => {
30            let size = IVec2::new(img.dimensions().0 as i32, img.dimensions().1 as i32);
31            let data = img.into_raw();
32            assert_eq!(data.len(), (size.x * size.y * 4) as usize);
33            Ok(TextureData {
34                name: name.to_owned(),
35                data,
36                size,
37                channel: 4,
38            })
39        }
40        _ => Err("Unsupported image format".to_owned()),
41    }
42}
43
44#[derive(Debug)]
45pub struct Texture {
46    pub name: String,
47    // If it's not from the particular file, then None
48    pub path: Option<String>,
49    pub size: IVec2,
50
51    // CPU things - will be purged from memory after buffer
52    data: Option<TextureData>,
53
54    // GPU things - will exist only after buffer
55    tex: GLuint,
56}
57
58impl Texture {
59    pub fn new(path: Option<String>, data: TextureData) -> Self {
60        Texture {
61            name: data.name.clone(),
62            path,
63            size: data.size,
64            data: Some(data),
65            tex: 0,
66        }
67    }
68
69    pub fn buffer(&mut self) {
70        unsafe {
71            gl::GenTextures(1, &mut self.tex);
72            gl::BindTexture(gl::TEXTURE_2D, self.tex);
73
74            let data = self.data.take().unwrap();
75            let format = match data.channel {
76                1 => gl::RED,
77                3 => gl::RGB,
78                4 => gl::RGBA,
79                _ => panic!("Invalid Image channel set"),
80            };
81            gl::TexImage2D(
82                gl::TEXTURE_2D,
83                0,
84                format as i32,
85                self.size.x,
86                self.size.y,
87                0,
88                format,
89                gl::UNSIGNED_BYTE,
90                data.data.as_ptr().cast(),
91            );
92
93            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
94            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
95            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
96            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
97        }
98    }
99
100    pub fn bind(&self) {
101        debug_assert!(self.data.is_none(), "bind() on an unbufferd texure");
102        unsafe { gl::BindTexture(gl::TEXTURE_2D, self.tex) }
103    }
104
105    pub fn bind_at(&self, index: u32) {
106        debug_assert!(self.data.is_none(), "bind() on an unbufferd texure");
107        unsafe {
108            gl::ActiveTexture(gl::TEXTURE0 + index);
109            gl::BindTexture(gl::TEXTURE_2D, self.tex);
110        }
111    }
112
113    pub fn bind_or_buffer(&mut self) {
114        if self.data.is_some() {
115            self.buffer();
116        }
117        unsafe { gl::BindTexture(gl::TEXTURE_2D, self.tex) }
118    }
119
120    pub fn get_raw(&self) -> GLuint {
121        self.tex
122    }
123}
124
125impl Drop for Texture {
126    fn drop(&mut self) {
127        if self.data.is_none() {
128            unsafe {
129                gl::DeleteTextures(1, &self.tex);
130            }
131        }
132    }
133}