Skip to main content

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 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        unsafe {
75            Self::new_unchecked::<T>(
76                context,
77                width,
78                height,
79                min_filter,
80                mag_filter,
81                mipmap,
82                wrap_s,
83                wrap_t,
84                |texture| {
85                    context.tex_storage_2d(
86                        crate::context::TEXTURE_2D,
87                        texture.number_of_mip_maps() as i32,
88                        T::internal_format(),
89                        width as i32,
90                        height as i32,
91                    );
92                },
93            )
94        }
95    }
96
97    ///
98    /// Fills this texture with the given data and generate mip maps if specified at construction.
99    ///
100    /// # Panic
101    /// Will panic if the length of the data does not correspond to the width, height and format specified at construction.
102    /// It is therefore necessary to create a new texture if the texture size or format has changed.
103    ///
104    pub fn fill<T: TextureDataType>(&self, data: &[T]) {
105        check_data_length::<T>(self.width, self.height, 1, self.data_byte_size, data.len());
106        self.bind();
107        let mut data = data.to_owned();
108        flip_y(&mut data, self.width as usize, self.height as usize);
109        unsafe {
110            self.context.tex_sub_image_2d(
111                crate::context::TEXTURE_2D,
112                0,
113                0,
114                0,
115                self.width as i32,
116                self.height as i32,
117                format_from_data_type::<T>(),
118                T::data_type(),
119                crate::context::PixelUnpackData::Slice(Some(to_byte_slice(&data))),
120            );
121        }
122        self.generate_mip_maps();
123    }
124
125    ///
126    /// Returns a [ColorTarget] which can be used to clear, write to and read from the given mip level of this texture.
127    /// 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.
128    /// 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.
129    /// Otherwise, the given mip level is used and no mip maps are generated.
130    ///
131    /// **Note:** [DepthTest] is disabled if not also writing to a depth texture.
132    ///
133    pub fn as_color_target(&self, mip_level: Option<u32>) -> ColorTarget<'_> {
134        ColorTarget::new_texture2d(&self.context, self, mip_level)
135    }
136
137    /// The width of this texture.
138    pub fn width(&self) -> u32 {
139        self.width
140    }
141
142    /// The height of this texture.
143    pub fn height(&self) -> u32 {
144        self.height
145    }
146
147    /// The number of mip maps of this texture.
148    pub fn number_of_mip_maps(&self) -> u32 {
149        self.number_of_mip_maps
150    }
151
152    pub(crate) fn generate_mip_maps(&self) {
153        if self.number_of_mip_maps > 1 {
154            self.bind();
155            unsafe {
156                self.context.generate_mipmap(crate::context::TEXTURE_2D);
157            }
158        }
159    }
160
161    pub(in crate::core) fn bind_as_color_target(&self, channel: u32, mip_level: u32) {
162        unsafe {
163            self.context.framebuffer_texture_2d(
164                crate::context::FRAMEBUFFER,
165                crate::context::COLOR_ATTACHMENT0 + channel,
166                crate::context::TEXTURE_2D,
167                Some(self.id),
168                mip_level as i32,
169            );
170        }
171    }
172    pub(in crate::core) fn bind(&self) {
173        unsafe {
174            self.context
175                .bind_texture(crate::context::TEXTURE_2D, Some(self.id));
176        }
177    }
178
179    ///
180    /// Creates a new texture where it is up to the caller to allocate and transfer data to the GPU
181    /// using low-level context calls inside the callback.
182    /// This function binds the texture and sets the parameters before calling the callback and generates mip maps afterwards.
183    ///
184    /// # Safety
185    ///
186    /// This function is unsafe and should only be used in special cases,
187    /// for example when you have an uncommon source of data or the data is in a special format like sRGB.
188    ///
189    pub unsafe fn new_unchecked<T: TextureDataType>(
190        context: &Context,
191        width: u32,
192        height: u32,
193        min_filter: Interpolation,
194        mag_filter: Interpolation,
195        mipmap: Option<Mipmap>,
196        wrap_s: Wrapping,
197        wrap_t: Wrapping,
198        callback: impl FnOnce(&Self),
199    ) -> Self {
200        let id = generate(context);
201        let number_of_mip_maps = calculate_number_of_mip_maps::<T>(mipmap, width, height, None);
202        let texture = Self {
203            context: context.clone(),
204            id,
205            width,
206            height,
207            number_of_mip_maps,
208            data_byte_size: std::mem::size_of::<T>(),
209        };
210        texture.bind();
211        set_parameters(
212            context,
213            crate::context::TEXTURE_2D,
214            min_filter,
215            mag_filter,
216            if number_of_mip_maps == 1 {
217                None
218            } else {
219                mipmap
220            },
221            wrap_s,
222            wrap_t,
223            None,
224        );
225        callback(&texture);
226        texture.generate_mip_maps();
227        texture
228    }
229}
230
231impl Drop for Texture2D {
232    fn drop(&mut self) {
233        unsafe {
234            self.context.delete_texture(self.id);
235        }
236    }
237}