Skip to main content

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            .first()
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 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        unsafe {
164            Self::new_unchecked::<T>(
165                context,
166                width,
167                height,
168                depth,
169                min_filter,
170                mag_filter,
171                mipmap,
172                wrap_s,
173                wrap_t,
174                |texture| {
175                    context.tex_storage_3d(
176                        crate::context::TEXTURE_2D_ARRAY,
177                        texture.number_of_mip_maps() as i32,
178                        T::internal_format(),
179                        width as i32,
180                        height as i32,
181                        depth as i32,
182                    )
183                },
184            )
185        }
186    }
187
188    ///
189    /// Fills the texture array with the given pixel data and generate mip maps if specified at construction.
190    ///
191    /// # Panic
192    /// Will panic if the data does not correspond to the width, height, depth and format specified at construction.
193    /// It is therefore necessary to create a new texture if the texture size or format has changed.
194    ///
195    pub fn fill<T: TextureDataType>(&self, data: &[&[T]]) {
196        for (i, data) in data.iter().enumerate() {
197            self.fill_layer(i as u32, data);
198        }
199    }
200
201    ///
202    /// Fills the given layer in the texture array with the given pixel data and generate mip maps if specified at construction.
203    ///
204    /// # Panic
205    /// 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.
206    /// It is therefore necessary to create a new texture if the texture size or format has changed.
207    ///
208    pub fn fill_layer<T: TextureDataType>(&self, layer: u32, data: &[T]) {
209        if layer >= self.depth {
210            panic!(
211                "cannot fill the layer {} with data, since there are only {} layers in the texture array",
212                layer, self.depth
213            )
214        }
215        check_data_length::<T>(self.width, self.height, 1, self.data_byte_size, data.len());
216        self.bind();
217        let mut data = (*data).to_owned();
218        flip_y(&mut data, self.width as usize, self.height as usize);
219        unsafe {
220            self.context.tex_sub_image_3d(
221                crate::context::TEXTURE_2D_ARRAY,
222                0,
223                0,
224                0,
225                layer as i32,
226                self.width as i32,
227                self.height as i32,
228                1,
229                format_from_data_type::<T>(),
230                T::data_type(),
231                crate::context::PixelUnpackData::Slice(Some(to_byte_slice(&data))),
232            );
233        }
234        self.generate_mip_maps();
235    }
236
237    ///
238    /// Returns a [ColorTarget] which can be used to clear, write to and read from the given layers and mip level of this texture.
239    /// 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.
240    /// 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.
241    /// Otherwise, the given mip level is used and no mip maps are generated.
242    ///
243    /// **Note:** [DepthTest] is disabled if not also writing to a depth texture.
244    ///
245    pub fn as_color_target<'a>(
246        &'a self,
247        layers: &'a [u32],
248        mip_level: Option<u32>,
249    ) -> ColorTarget<'a> {
250        ColorTarget::new_texture_2d_array(&self.context, self, layers, mip_level)
251    }
252
253    /// The width of this texture.
254    pub fn width(&self) -> u32 {
255        self.width
256    }
257
258    /// The height of this texture.
259    pub fn height(&self) -> u32 {
260        self.height
261    }
262
263    /// The number of layers.
264    pub fn depth(&self) -> u32 {
265        self.depth
266    }
267
268    /// The number of mip maps of this texture.
269    pub fn number_of_mip_maps(&self) -> u32 {
270        self.number_of_mip_maps
271    }
272
273    pub(in crate::core) fn generate_mip_maps(&self) {
274        if self.number_of_mip_maps > 1 {
275            self.bind();
276            unsafe {
277                self.context
278                    .generate_mipmap(crate::context::TEXTURE_2D_ARRAY);
279            }
280        }
281    }
282
283    pub(in crate::core) fn bind_as_color_target(&self, layer: u32, channel: u32, mip_level: u32) {
284        unsafe {
285            self.context.framebuffer_texture_layer(
286                crate::context::DRAW_FRAMEBUFFER,
287                crate::context::COLOR_ATTACHMENT0 + channel,
288                Some(self.id),
289                mip_level as i32,
290                layer as i32,
291            );
292        }
293    }
294
295    pub(in crate::core) fn bind(&self) {
296        unsafe {
297            self.context
298                .bind_texture(crate::context::TEXTURE_2D_ARRAY, Some(self.id));
299        }
300    }
301
302    ///
303    /// Creates a new texture where it is up to the caller to allocate and transfer data to the GPU
304    /// using low-level context calls inside the callback.
305    /// This function binds the texture and sets the parameters before calling the callback and generates mip maps afterwards.
306    ///
307    /// # Safety
308    ///
309    /// This function is unsafe and should only be used in special cases,
310    /// for example when you have an uncommon source of data or the data is in a special format like sRGB.
311    ///
312    pub unsafe fn new_unchecked<T: TextureDataType>(
313        context: &Context,
314        width: u32,
315        height: u32,
316        depth: u32,
317        min_filter: Interpolation,
318        mag_filter: Interpolation,
319        mipmap: Option<Mipmap>,
320        wrap_s: Wrapping,
321        wrap_t: Wrapping,
322        callback: impl FnOnce(&Self),
323    ) -> Self {
324        let id = generate(context);
325        let number_of_mip_maps = calculate_number_of_mip_maps::<T>(mipmap, width, height, None);
326        let texture = Self {
327            context: context.clone(),
328            id,
329            width,
330            height,
331            depth,
332            number_of_mip_maps,
333            data_byte_size: std::mem::size_of::<T>(),
334        };
335        texture.bind();
336        set_parameters(
337            context,
338            crate::context::TEXTURE_2D_ARRAY,
339            min_filter,
340            mag_filter,
341            if number_of_mip_maps == 1 {
342                None
343            } else {
344                mipmap
345            },
346            wrap_s,
347            wrap_t,
348            None,
349        );
350        callback(&texture);
351        texture.generate_mip_maps();
352        texture
353    }
354}
355
356impl Drop for Texture2DArray {
357    fn drop(&mut self) {
358        unsafe {
359            self.context.delete_texture(self.id);
360        }
361    }
362}