Skip to main content

optic_render/handles/
texture.rs

1use optic_core::{ImgFilter, ImgFormat, ImgWrap, Size2D};
2
3use crate::GL;
4
5/// A handle to an OpenGL 2D texture object.
6///
7/// Stores the GL texture ID, size, pixel format, filtering, and wrap mode.
8/// Created by [`create_texture`] or via [`TextureFile::ship`](crate::asset::TextureFile::ship).
9#[derive(Clone, Debug)]
10pub struct Texture2D {
11    pub id: u32,
12    pub size: Size2D,
13    pub fmt: ImgFormat,
14    pub filter: ImgFilter,
15    pub wrap: ImgWrap,
16}
17
18impl Texture2D {
19    /// Creates a new texture handle from a raw GL texture ID.
20    pub fn new(
21        id: u32,
22        size: Size2D,
23        fmt: ImgFormat,
24        filter: ImgFilter,
25        wrap: ImgWrap,
26    ) -> Self {
27        Self { id, size, fmt, filter, wrap }
28    }
29
30    /// Returns the texture dimensions.
31    pub fn size(&self) -> Size2D { self.size }
32    /// Returns the current wrap mode.
33    pub fn wrap(&self) -> ImgWrap { self.wrap }
34    /// Overrides the stored wrap mode.
35    pub fn set_wrap(&mut self, wrap: ImgWrap) { self.wrap = wrap; }
36    /// Returns the current filter mode.
37    pub fn filter(&self) -> ImgFilter { self.filter }
38    /// Overrides the stored filter mode.
39    pub fn set_filter(&mut self, filter: ImgFilter) { self.filter = filter; }
40
41    /// Deletes the underlying OpenGL texture.
42    pub fn delete(self) { delete_texture(self.id); }
43}
44
45/// Creates a new OpenGL 2D texture from raw pixel data.
46///
47/// Generates mipmaps automatically. The texture is left bound to texture unit 0
48/// after creation.
49pub fn create_texture(
50    bytes: &[u8],
51    size: Size2D,
52    fmt: &ImgFormat,
53    filter: &ImgFilter,
54    wrap: &ImgWrap,
55) -> u32 {
56    let mut id = 0u32;
57    unsafe {
58        gl::GenTextures(1, &mut id);
59        GL::bind_texture_at(id, 0);
60
61        let wrap_gl = match wrap {
62            ImgWrap::Repeat => gl::REPEAT,
63            ImgWrap::Extend => gl::CLAMP_TO_EDGE,
64            ImgWrap::Clip => gl::CLAMP_TO_BORDER,
65        };
66        let (min_fil, mag_fil) = match filter {
67            ImgFilter::Closest => (gl::NEAREST_MIPMAP_NEAREST as i32, gl::NEAREST as i32),
68            ImgFilter::Linear => (gl::LINEAR_MIPMAP_LINEAR as i32, gl::LINEAR as i32),
69        };
70
71        gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, wrap_gl as i32);
72        gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, wrap_gl as i32);
73        gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, min_fil);
74        gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, mag_fil);
75
76        let (base, sized, pix_type) = match fmt {
77            ImgFormat::R(bd) => match bd {
78                32 => (gl::RED, gl::R32F, gl::FLOAT),
79                16 => (gl::RED, gl::R16, gl::UNSIGNED_SHORT),
80                _  => (gl::RED, gl::R8, gl::UNSIGNED_BYTE),
81            },
82            ImgFormat::RG(bd) => match bd {
83                32 => (gl::RG, gl::RG32F, gl::FLOAT),
84                16 => (gl::RG, gl::RG16, gl::UNSIGNED_SHORT),
85                _  => (gl::RG, gl::RG8, gl::UNSIGNED_BYTE),
86            },
87            ImgFormat::RGB(bd) => match bd {
88                32 => (gl::RGB, gl::RGB32F, gl::FLOAT),
89                16 => (gl::RGB, gl::RGB16, gl::UNSIGNED_SHORT),
90                _  => (gl::RGB, gl::RGB8, gl::UNSIGNED_BYTE),
91            },
92            ImgFormat::RGBA(bd) => match bd {
93                32 => (gl::RGBA, gl::RGBA32F, gl::FLOAT),
94                16 => (gl::RGBA, gl::RGBA16, gl::UNSIGNED_SHORT),
95                _  => (gl::RGBA, gl::RGBA8, gl::UNSIGNED_BYTE),
96            },
97        };
98
99        gl::TexImage2D(
100            gl::TEXTURE_2D,
101            0,
102            sized as i32,
103            size.w as i32,
104            size.h as i32,
105            0,
106            base,
107            pix_type,
108            bytes.as_ptr() as *const std::ffi::c_void,
109        );
110        gl::GenerateMipmap(gl::TEXTURE_2D);
111        GL::unbind_texture();
112    }
113    id
114}
115
116/// Deletes an OpenGL 2D texture by its ID.
117pub fn delete_texture(id: u32) {
118    unsafe { gl::DeleteTextures(1, &id); }
119}