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
use fere_common::*;
use gl::types::*;
use image::{io::Reader as IReader, DynamicImage};
use std::io::{BufRead, Seek};

#[derive(Debug)]
pub struct TextureData {
    pub name: String,
    pub data: Vec<u8>,
    pub size: IVec2,
    pub channel: u8,
}

pub fn import<T: BufRead + Seek>(name: &str, source: T) -> Result<TextureData, String> {
    let source = IReader::new(source).with_guessed_format().unwrap();
    let result = source.decode().map_err(|e| format!("{:?}", e))?;
    match result {
        DynamicImage::ImageRgb8(img) => {
            let size = IVec2::new(img.dimensions().0 as i32, img.dimensions().1 as i32);
            let data = img.into_raw();
            assert_eq!(data.len(), (size.x * size.y * 3) as usize);
            Ok(TextureData {
                name: name.to_owned(),
                data,
                size,
                channel: 3,
            })
        }
        DynamicImage::ImageRgba8(img) => {
            let size = IVec2::new(img.dimensions().0 as i32, img.dimensions().1 as i32);
            let data = img.into_raw();
            assert_eq!(data.len(), (size.x * size.y * 4) as usize);
            Ok(TextureData {
                name: name.to_owned(),
                data,
                size,
                channel: 4,
            })
        }
        _ => Err("Unsupported image format".to_owned()),
    }
}

#[derive(Debug)]
pub struct Texture {
    pub name: String,
    // If it's not from the particular file, then None
    pub path: Option<String>,
    pub size: IVec2,

    // CPU things - will be purged from memory after buffer
    data: Option<TextureData>,

    // GPU things - will exist only after buffer
    tex: GLuint,
}

impl Texture {
    pub fn new(path: Option<String>, data: TextureData) -> Self {
        Texture {
            name: data.name.clone(),
            path,
            size: data.size,
            data: Some(data),
            tex: 0,
        }
    }

    pub fn buffer(&mut self) {
        unsafe {
            gl::GenTextures(1, &mut self.tex);
            gl::BindTexture(gl::TEXTURE_2D, self.tex);

            let data = self.data.take().unwrap();
            let format = match data.channel {
                1 => gl::RED,
                3 => gl::RGB,
                4 => gl::RGBA,
                _ => panic!("Invalid Image channel set"),
            };
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0,
                format as i32,
                self.size.x,
                self.size.y,
                0,
                format,
                gl::UNSIGNED_BYTE,
                data.data.as_ptr().cast(),
            );

            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
        }
    }

    pub fn bind(&self) {
        debug_assert!(self.data.is_none(), "bind() on an unbufferd texure");
        unsafe { gl::BindTexture(gl::TEXTURE_2D, self.tex) }
    }

    pub fn bind_at(&self, index: u32) {
        debug_assert!(self.data.is_none(), "bind() on an unbufferd texure");
        unsafe {
            gl::ActiveTexture(gl::TEXTURE0 + index);
            gl::BindTexture(gl::TEXTURE_2D, self.tex);
        }
    }

    pub fn bind_or_buffer(&mut self) {
        if self.data.is_some() {
            self.buffer();
        }
        unsafe { gl::BindTexture(gl::TEXTURE_2D, self.tex) }
    }

    pub fn get_raw(&self) -> GLuint {
        self.tex
    }
}

impl Drop for Texture {
    fn drop(&mut self) {
        if self.data.is_none() {
            unsafe {
                gl::DeleteTextures(1, &self.tex);
            }
        }
    }
}