three_d/core/texture/
texture2d.rs

1use crate::core::texture::*;
2
3///
4/// A 2D texture, basically an image that is transferred to the GPU.
5///
6pub struct Texture2D {
7    context: Context,
8    id: crate::context::Texture,
9    width: u32,
10    height: u32,
11    number_of_mip_maps: u32,
12    data_byte_size: usize,
13}
14
15impl Texture2D {
16    ///
17    /// Construcs a new texture with the given data.
18    ///
19    /// **Note:** Mip maps will not be generated for RGB16F and RGB32F format, even if `mip_map_filter` is specified.
20    ///
21    pub fn new(context: &Context, cpu_texture: &CpuTexture) -> Self {
22        match cpu_texture.data {
23            TextureData::RU8(ref data) => Self::new_with_data(context, cpu_texture, data),
24            TextureData::RgU8(ref data) => Self::new_with_data(context, cpu_texture, data),
25            TextureData::RgbU8(ref data) => Self::new_with_data(context, cpu_texture, data),
26            TextureData::RgbaU8(ref data) => Self::new_with_data(context, cpu_texture, data),
27            TextureData::RF16(ref data) => Self::new_with_data(context, cpu_texture, data),
28            TextureData::RgF16(ref data) => Self::new_with_data(context, cpu_texture, data),
29            TextureData::RgbF16(ref data) => Self::new_with_data(context, cpu_texture, data),
30            TextureData::RgbaF16(ref data) => Self::new_with_data(context, cpu_texture, data),
31            TextureData::RF32(ref data) => Self::new_with_data(context, cpu_texture, data),
32            TextureData::RgF32(ref data) => Self::new_with_data(context, cpu_texture, data),
33            TextureData::RgbF32(ref data) => Self::new_with_data(context, cpu_texture, data),
34            TextureData::RgbaF32(ref data) => Self::new_with_data(context, cpu_texture, data),
35        }
36    }
37
38    fn new_with_data<T: TextureDataType>(
39        context: &Context,
40        cpu_texture: &CpuTexture,
41        data: &[T],
42    ) -> Self {
43        let mut texture = Self::new_empty::<T>(
44            context,
45            cpu_texture.width,
46            cpu_texture.height,
47            cpu_texture.min_filter,
48            cpu_texture.mag_filter,
49            cpu_texture.mipmap,
50            cpu_texture.wrap_s,
51            cpu_texture.wrap_t,
52        );
53        texture.fill(data);
54        texture
55    }
56
57    ///
58    /// Constructs a new empty 2D texture with the given parameters.
59    /// The format is determined by the generic [TextureDataType] parameter
60    /// (for example, if [u8; 4] is specified, the format is RGBA and the data type is byte).
61    ///
62    /// **Note:** Mip maps will not be generated for RGB16F and RGB32F format, even if `mip_map_filter` is specified.
63    ///
64    pub fn new_empty<T: TextureDataType>(
65        context: &Context,
66        width: u32,
67        height: u32,
68        min_filter: Interpolation,
69        mag_filter: Interpolation,
70        mipmap: Option<Mipmap>,
71        wrap_s: Wrapping,
72        wrap_t: Wrapping,
73    ) -> Self {
74        let id = generate(context);
75        let number_of_mip_maps = calculate_number_of_mip_maps::<T>(mipmap, width, height, None);
76        let texture = Self {
77            context: context.clone(),
78            id,
79            width,
80            height,
81            number_of_mip_maps,
82            data_byte_size: std::mem::size_of::<T>(),
83        };
84        texture.bind();
85        set_parameters(
86            context,
87            crate::context::TEXTURE_2D,
88            min_filter,
89            mag_filter,
90            if number_of_mip_maps == 1 {
91                None
92            } else {
93                mipmap
94            },
95            wrap_s,
96            wrap_t,
97            None,
98        );
99        unsafe {
100            context.tex_storage_2d(
101                crate::context::TEXTURE_2D,
102                number_of_mip_maps as i32,
103                T::internal_format(),
104                width as i32,
105                height as i32,
106            );
107        }
108        texture.generate_mip_maps();
109        texture
110    }
111
112    ///
113    /// Fills this texture with the given data.
114    ///
115    /// # Panic
116    /// Will panic if the length of the data does not correspond to the width, height and format specified at construction.
117    /// It is therefore necessary to create a new texture if the texture size or format has changed.
118    ///
119    pub fn fill<T: TextureDataType>(&mut self, data: &[T]) {
120        check_data_length::<T>(self.width, self.height, 1, self.data_byte_size, data.len());
121        self.bind();
122        let mut data = data.to_owned();
123        flip_y(&mut data, self.width as usize, self.height as usize);
124        unsafe {
125            self.context.tex_sub_image_2d(
126                crate::context::TEXTURE_2D,
127                0,
128                0,
129                0,
130                self.width as i32,
131                self.height as i32,
132                format_from_data_type::<T>(),
133                T::data_type(),
134                crate::context::PixelUnpackData::Slice(to_byte_slice(&data)),
135            );
136        }
137        self.generate_mip_maps();
138    }
139
140    ///
141    /// Returns a [ColorTarget] which can be used to clear, write to and read from the given mip level of this texture.
142    /// Combine this together with a [DepthTarget] with [RenderTarget::new] to be able to write to both a depth and color target at the same time.
143    /// If `None` is specified as the mip level, the 0 level mip level is used and mip maps are generated after a write operation if a mip map filter is specified.
144    /// Otherwise, the given mip level is used and no mip maps are generated.
145    ///
146    /// **Note:** [DepthTest] is disabled if not also writing to a depth texture.
147    ///
148    pub fn as_color_target(&mut self, mip_level: Option<u32>) -> ColorTarget<'_> {
149        ColorTarget::new_texture2d(&self.context, self, mip_level)
150    }
151
152    /// The width of this texture.
153    pub fn width(&self) -> u32 {
154        self.width
155    }
156
157    /// The height of this texture.
158    pub fn height(&self) -> u32 {
159        self.height
160    }
161
162    /// The number of mip maps of this texture.
163    pub fn number_of_mip_maps(&self) -> u32 {
164        self.number_of_mip_maps
165    }
166
167    pub(crate) fn generate_mip_maps(&self) {
168        if self.number_of_mip_maps > 1 {
169            self.bind();
170            unsafe {
171                self.context.generate_mipmap(crate::context::TEXTURE_2D);
172            }
173        }
174    }
175
176    pub(in crate::core) fn bind_as_color_target(&self, channel: u32, mip_level: u32) {
177        unsafe {
178            self.context.framebuffer_texture_2d(
179                crate::context::FRAMEBUFFER,
180                crate::context::COLOR_ATTACHMENT0 + channel,
181                crate::context::TEXTURE_2D,
182                Some(self.id),
183                mip_level as i32,
184            );
185        }
186    }
187    pub(in crate::core) fn bind(&self) {
188        unsafe {
189            self.context
190                .bind_texture(crate::context::TEXTURE_2D, Some(self.id));
191        }
192    }
193}
194
195impl Drop for Texture2D {
196    fn drop(&mut self) {
197        unsafe {
198            self.context.delete_texture(self.id);
199        }
200    }
201}