three_d/core/texture/
texture2d_array.rs

1use crate::core::texture::*;
2
3///
4/// A array of 2D color textures that can be rendered into.
5///
6/// **Note:** [DepthTest] is disabled if not also writing to a [DepthTarget].
7/// Use a [RenderTarget] to write to both color and depth.
8///
9pub struct Texture2DArray {
10    context: Context,
11    id: crate::context::Texture,
12    width: u32,
13    height: u32,
14    depth: u32,
15    number_of_mip_maps: u32,
16    data_byte_size: usize,
17}
18
19impl Texture2DArray {
20    ///
21    /// Creates a new texture array from the given [CpuTexture]s.
22    /// All of the cpu textures must contain data with the same [TextureDataType] and the same width and height.
23    ///
24    /// **Note:** Mip maps will not be generated for RGB16F and RGB32F format, even if `mip_map_filter` is specified.
25    ///
26    pub fn new(context: &Context, cpu_textures: &[&CpuTexture]) -> Self {
27        let cpu_texture = cpu_textures
28            .get(0)
29            .expect("Expect at least one texture in a texture array");
30        match &cpu_texture.data {
31            TextureData::RU8(_) => Self::new_with_data(
32                context,
33                cpu_texture,
34                &cpu_textures.iter().map(|t| ru8_data(t)).collect::<Vec<_>>(),
35            ),
36            TextureData::RgU8(_) => Self::new_with_data(
37                context,
38                cpu_texture,
39                &cpu_textures
40                    .iter()
41                    .map(|t| rgu8_data(t))
42                    .collect::<Vec<_>>(),
43            ),
44            TextureData::RgbU8(_) => Self::new_with_data(
45                context,
46                cpu_texture,
47                &cpu_textures
48                    .iter()
49                    .map(|t| rgbu8_data(t))
50                    .collect::<Vec<_>>(),
51            ),
52            TextureData::RgbaU8(_) => Self::new_with_data(
53                context,
54                cpu_texture,
55                &cpu_textures
56                    .iter()
57                    .map(|t| rgbau8_data(t))
58                    .collect::<Vec<_>>(),
59            ),
60            TextureData::RF16(_) => Self::new_with_data(
61                context,
62                cpu_texture,
63                &cpu_textures
64                    .iter()
65                    .map(|t| rf16_data(t))
66                    .collect::<Vec<_>>(),
67            ),
68            TextureData::RgF16(_) => Self::new_with_data(
69                context,
70                cpu_texture,
71                &cpu_textures
72                    .iter()
73                    .map(|t| rgf16_data(t))
74                    .collect::<Vec<_>>(),
75            ),
76            TextureData::RgbF16(_) => Self::new_with_data(
77                context,
78                cpu_texture,
79                &cpu_textures
80                    .iter()
81                    .map(|t| rgbf16_data(t))
82                    .collect::<Vec<_>>(),
83            ),
84            TextureData::RgbaF16(_) => Self::new_with_data(
85                context,
86                cpu_texture,
87                &cpu_textures
88                    .iter()
89                    .map(|t| rgbaf16_data(t))
90                    .collect::<Vec<_>>(),
91            ),
92            TextureData::RF32(_) => Self::new_with_data(
93                context,
94                cpu_texture,
95                &cpu_textures
96                    .iter()
97                    .map(|t| rf32_data(t))
98                    .collect::<Vec<_>>(),
99            ),
100            TextureData::RgF32(_) => Self::new_with_data(
101                context,
102                cpu_texture,
103                &cpu_textures
104                    .iter()
105                    .map(|t| rgf32_data(t))
106                    .collect::<Vec<_>>(),
107            ),
108            TextureData::RgbF32(_) => Self::new_with_data(
109                context,
110                cpu_texture,
111                &cpu_textures
112                    .iter()
113                    .map(|t| rgbf32_data(t))
114                    .collect::<Vec<_>>(),
115            ),
116            TextureData::RgbaF32(_) => Self::new_with_data(
117                context,
118                cpu_texture,
119                &cpu_textures
120                    .iter()
121                    .map(|t| rgbaf32_data(t))
122                    .collect::<Vec<_>>(),
123            ),
124        }
125    }
126
127    fn new_with_data<T: TextureDataType>(
128        context: &Context,
129        cpu_texture: &CpuTexture,
130        data: &[&[T]],
131    ) -> Self {
132        let mut texture = Self::new_empty::<T>(
133            context,
134            cpu_texture.width,
135            cpu_texture.height,
136            data.len() as u32,
137            cpu_texture.min_filter,
138            cpu_texture.mag_filter,
139            cpu_texture.mipmap,
140            cpu_texture.wrap_s,
141            cpu_texture.wrap_t,
142        );
143        texture.fill(data);
144        texture
145    }
146
147    ///
148    /// Creates a new array of 2D textures.
149    ///
150    /// **Note:** Mip maps will not be generated for RGB16F and RGB32F format, even if `mip_map_filter` is specified.
151    ///
152    pub fn new_empty<T: TextureDataType>(
153        context: &Context,
154        width: u32,
155        height: u32,
156        depth: u32,
157        min_filter: Interpolation,
158        mag_filter: Interpolation,
159        mipmap: Option<Mipmap>,
160        wrap_s: Wrapping,
161        wrap_t: Wrapping,
162    ) -> Self {
163        let id = generate(context);
164        let number_of_mip_maps = calculate_number_of_mip_maps::<T>(mipmap, width, height, None);
165        let texture = Self {
166            context: context.clone(),
167            id,
168            width,
169            height,
170            depth,
171            number_of_mip_maps,
172            data_byte_size: std::mem::size_of::<T>(),
173        };
174        texture.bind();
175        set_parameters(
176            context,
177            crate::context::TEXTURE_2D_ARRAY,
178            min_filter,
179            mag_filter,
180            if number_of_mip_maps == 1 {
181                None
182            } else {
183                mipmap
184            },
185            wrap_s,
186            wrap_t,
187            None,
188        );
189        unsafe {
190            context.tex_storage_3d(
191                crate::context::TEXTURE_2D_ARRAY,
192                number_of_mip_maps as i32,
193                T::internal_format(),
194                width as i32,
195                height as i32,
196                depth as i32,
197            );
198        }
199        texture.generate_mip_maps();
200        texture
201    }
202
203    ///
204    /// Fills the texture array with the given pixel data.
205    ///
206    /// # Panic
207    /// Will panic if the data does not correspond to the width, height, depth and format specified at construction.
208    /// It is therefore necessary to create a new texture if the texture size or format has changed.
209    ///
210    pub fn fill<T: TextureDataType>(&mut self, data: &[&[T]]) {
211        for (i, data) in data.iter().enumerate() {
212            self.fill_layer(i as u32, data);
213        }
214    }
215
216    ///
217    /// Fills the given layer in the texture array with the given pixel data.
218    ///
219    /// # Panic
220    /// Will panic if the layer number is bigger than the number of layers or if the data does not correspond to the width, height and format specified at construction.
221    /// It is therefore necessary to create a new texture if the texture size or format has changed.
222    ///
223    pub fn fill_layer<T: TextureDataType>(&mut self, layer: u32, data: &[T]) {
224        if layer >= self.depth {
225            panic!(
226                "cannot fill the layer {} with data, since there are only {} layers in the texture array",
227                layer, self.depth
228            )
229        }
230        check_data_length::<T>(self.width, self.height, 1, self.data_byte_size, data.len());
231        self.bind();
232        let mut data = (*data).to_owned();
233        flip_y(&mut data, self.width as usize, self.height as usize);
234        unsafe {
235            self.context.tex_sub_image_3d(
236                crate::context::TEXTURE_2D_ARRAY,
237                0,
238                0,
239                0,
240                layer as i32,
241                self.width as i32,
242                self.height as i32,
243                1,
244                format_from_data_type::<T>(),
245                T::data_type(),
246                crate::context::PixelUnpackData::Slice(to_byte_slice(&data)),
247            );
248        }
249        self.generate_mip_maps();
250    }
251
252    ///
253    /// Returns a [ColorTarget] which can be used to clear, write to and read from the given layers and mip level of this texture.
254    /// 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.
255    /// 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.
256    /// Otherwise, the given mip level is used and no mip maps are generated.
257    ///
258    /// **Note:** [DepthTest] is disabled if not also writing to a depth texture.
259    ///
260    pub fn as_color_target<'a>(
261        &'a mut self,
262        layers: &'a [u32],
263        mip_level: Option<u32>,
264    ) -> ColorTarget<'a> {
265        ColorTarget::new_texture_2d_array(&self.context, self, layers, mip_level)
266    }
267
268    /// The width of this texture.
269    pub fn width(&self) -> u32 {
270        self.width
271    }
272
273    /// The height of this texture.
274    pub fn height(&self) -> u32 {
275        self.height
276    }
277
278    /// The number of layers.
279    pub fn depth(&self) -> u32 {
280        self.depth
281    }
282
283    /// The number of mip maps of this texture.
284    pub fn number_of_mip_maps(&self) -> u32 {
285        self.number_of_mip_maps
286    }
287
288    pub(in crate::core) fn generate_mip_maps(&self) {
289        if self.number_of_mip_maps > 1 {
290            self.bind();
291            unsafe {
292                self.context
293                    .generate_mipmap(crate::context::TEXTURE_2D_ARRAY);
294            }
295        }
296    }
297
298    pub(in crate::core) fn bind_as_color_target(&self, layer: u32, channel: u32, mip_level: u32) {
299        unsafe {
300            self.context.framebuffer_texture_layer(
301                crate::context::DRAW_FRAMEBUFFER,
302                crate::context::COLOR_ATTACHMENT0 + channel,
303                Some(self.id),
304                mip_level as i32,
305                layer as i32,
306            );
307        }
308    }
309
310    pub(in crate::core) fn bind(&self) {
311        unsafe {
312            self.context
313                .bind_texture(crate::context::TEXTURE_2D_ARRAY, Some(self.id));
314        }
315    }
316}
317
318impl Drop for Texture2DArray {
319    fn drop(&mut self) {
320        unsafe {
321            self.context.delete_texture(self.id);
322        }
323    }
324}