Skip to main content

three_d/core/texture/
texture3d.rs

1use crate::core::texture::*;
2///
3/// A 3D color texture.
4///
5pub struct Texture3D {
6    context: Context,
7    id: crate::context::Texture,
8    width: u32,
9    height: u32,
10    depth: u32,
11    number_of_mip_maps: u32,
12    data_byte_size: usize,
13}
14
15impl Texture3D {
16    ///
17    /// Construcs a new 3D 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: &CpuTexture3D) -> 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: &CpuTexture3D,
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.depth,
48            cpu_texture.min_filter,
49            cpu_texture.mag_filter,
50            cpu_texture.mipmap,
51            cpu_texture.wrap_s,
52            cpu_texture.wrap_t,
53            cpu_texture.wrap_r,
54        );
55        texture.fill(data);
56        texture
57    }
58
59    ///
60    /// Creates a new empty 3D color texture.
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        depth: u32,
69        min_filter: Interpolation,
70        mag_filter: Interpolation,
71        mipmap: Option<Mipmap>,
72        wrap_s: Wrapping,
73        wrap_t: Wrapping,
74        wrap_r: Wrapping,
75    ) -> Self {
76        unsafe {
77            Self::new_unchecked::<T>(
78                context,
79                width,
80                height,
81                depth,
82                min_filter,
83                mag_filter,
84                mipmap,
85                wrap_s,
86                wrap_t,
87                wrap_r,
88                |texture| {
89                    context.tex_storage_3d(
90                        crate::context::TEXTURE_3D,
91                        texture.number_of_mip_maps() as i32,
92                        T::internal_format(),
93                        width as i32,
94                        height as i32,
95                        depth as i32,
96                    )
97                },
98            )
99        }
100    }
101
102    ///
103    /// Fills this texture with the given data and generate mip maps if specified at construction.
104    ///
105    /// # Panic
106    /// Will panic if the length of the data does not correspond to the width, height, depth and format specified at construction.
107    /// It is therefore necessary to create a new texture if the texture size or format has changed.
108    ///
109    pub fn fill<T: TextureDataType>(&self, data: &[T]) {
110        check_data_length::<T>(
111            self.width,
112            self.height,
113            self.depth,
114            self.data_byte_size,
115            data.len(),
116        );
117        self.bind();
118        unsafe {
119            self.context.tex_sub_image_3d(
120                crate::context::TEXTURE_3D,
121                0,
122                0,
123                0,
124                0,
125                self.width as i32,
126                self.height as i32,
127                self.depth as i32,
128                format_from_data_type::<T>(),
129                T::data_type(),
130                crate::context::PixelUnpackData::Slice(Some(to_byte_slice(data))),
131            );
132        }
133        self.generate_mip_maps();
134    }
135
136    /// The width of this texture.
137    pub fn width(&self) -> u32 {
138        self.width
139    }
140
141    /// The height of this texture.
142    pub fn height(&self) -> u32 {
143        self.height
144    }
145
146    /// The depth of this texture.
147    pub fn depth(&self) -> u32 {
148        self.depth
149    }
150
151    /// The number of mip maps of this texture.
152    pub fn number_of_mip_maps(&self) -> u32 {
153        self.number_of_mip_maps
154    }
155
156    fn generate_mip_maps(&self) {
157        if self.number_of_mip_maps > 1 {
158            self.bind();
159            unsafe {
160                self.context.generate_mipmap(crate::context::TEXTURE_3D);
161            }
162        }
163    }
164    pub(in crate::core) fn bind(&self) {
165        unsafe {
166            self.context
167                .bind_texture(crate::context::TEXTURE_3D, Some(self.id));
168        }
169    }
170
171    ///
172    /// Creates a new texture where it is up to the caller to allocate and transfer data to the GPU
173    /// using low-level context calls inside the callback.
174    /// This function binds the texture and sets the parameters before calling the callback and generates mip maps afterwards.
175    ///
176    /// # Safety
177    ///
178    /// This function is unsafe and should only be used in special cases,
179    /// for example when you have an uncommon source of data or the data is in a special format like sRGB.
180    ///
181    pub unsafe fn new_unchecked<T: TextureDataType>(
182        context: &Context,
183        width: u32,
184        height: u32,
185        depth: u32,
186        min_filter: Interpolation,
187        mag_filter: Interpolation,
188        mipmap: Option<Mipmap>,
189        wrap_s: Wrapping,
190        wrap_t: Wrapping,
191        wrap_r: Wrapping,
192        callback: impl FnOnce(&Self),
193    ) -> Self {
194        let id = generate(context);
195        let number_of_mip_maps =
196            calculate_number_of_mip_maps::<T>(mipmap, width, height, Some(depth));
197        let texture = Self {
198            context: context.clone(),
199            id,
200            width,
201            height,
202            depth,
203            number_of_mip_maps,
204            data_byte_size: std::mem::size_of::<T>(),
205        };
206        texture.bind();
207        set_parameters(
208            context,
209            crate::context::TEXTURE_3D,
210            min_filter,
211            mag_filter,
212            if number_of_mip_maps == 1 {
213                None
214            } else {
215                mipmap
216            },
217            wrap_s,
218            wrap_t,
219            Some(wrap_r),
220        );
221        callback(&texture);
222        texture.generate_mip_maps();
223        texture
224    }
225}
226
227impl Drop for Texture3D {
228    fn drop(&mut self) {
229        unsafe {
230            self.context.delete_texture(self.id);
231        }
232    }
233}