Skip to main content

engine/
texture.rs

1/**-----------------------------------------------
2*!  Texture loading and management.
3*?  Defines the Texture struct and methods
4*?  for loading/creating a single GPU texture.
5*-----------------------------------------------**/
6use image::GenericImageView;
7
8//? A loaded GPU texture created from image bytes or a DynamicImage.
9pub struct Texture {
10    pub texture: wgpu::Texture,
11    pub view: wgpu::TextureView,
12    pub sampler: wgpu::Sampler,
13    pub width: u32,
14    pub height: u32,
15}
16
17//? Various constructors for creating textures from different
18//? sources (bytes, DynamicImage, or a white pixel).
19impl Texture {
20    pub fn from_bytes(
21        device: &wgpu::Device,
22        queue: &wgpu::Queue,
23        bytes: &[u8],
24        label: Option<&str>,
25    ) -> Result<Self, image::ImageError> {
26        let img = image::load_from_memory(bytes)?;
27        Self::from_image(device, queue, &img, label)
28    }
29
30    pub fn from_image(
31        device: &wgpu::Device,
32        queue: &wgpu::Queue,
33        img: &image::DynamicImage,
34        label: Option<&str>,
35    ) -> Result<Self, image::ImageError> {
36        let rgba = img.to_rgba8();
37        let dimensions = img.dimensions();
38
39        let size = wgpu::Extent3d {
40            width: dimensions.0,
41            height: dimensions.1,
42            depth_or_array_layers: 1,
43        };
44
45        //* We create the texture with COPY_DST usage so we can write
46        //* the pixel data, and TEXTURE_BINDING so it can be sampled in shaders.
47
48        let texture = device.create_texture(&wgpu::TextureDescriptor {
49            label,
50            size,
51            mip_level_count: 1,
52            sample_count: 1,
53            dimension: wgpu::TextureDimension::D2,
54            format: wgpu::TextureFormat::Rgba8UnormSrgb,
55            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
56            view_formats: &[],
57        });
58
59        queue.write_texture(
60            wgpu::TexelCopyTextureInfo {
61                texture: &texture,
62                mip_level: 0,
63                origin: wgpu::Origin3d::ZERO,
64                aspect: wgpu::TextureAspect::All,
65            },
66            &rgba,
67            wgpu::TexelCopyBufferLayout {
68                offset: 0,
69                bytes_per_row: Some(4 * dimensions.0),
70                rows_per_image: Some(dimensions.1),
71            },
72            size,
73        );
74
75        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
76        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
77            address_mode_u: wgpu::AddressMode::ClampToEdge,
78            address_mode_v: wgpu::AddressMode::ClampToEdge,
79            mag_filter: wgpu::FilterMode::Nearest,
80            min_filter: wgpu::FilterMode::Nearest,
81            mipmap_filter: wgpu::FilterMode::Nearest,
82            ..Default::default()
83        });
84
85        Ok(Self {
86            texture,
87            view,
88            sampler,
89            width: dimensions.0,
90            height: dimensions.1,
91        })
92    }
93
94    //? Create a 1x1 white pixel texture (tint-able).
95    pub fn white_pixel(device: &wgpu::Device, queue: &wgpu::Queue) -> Self {
96        use wgpu::util::DeviceExt;
97
98        let texture = device.create_texture_with_data(
99            queue,
100            &wgpu::TextureDescriptor {
101                label: Some("White Pixel Texture"),
102                size: wgpu::Extent3d {
103                    width: 1,
104                    height: 1,
105                    depth_or_array_layers: 1,
106                },
107                mip_level_count: 1,
108                sample_count: 1,
109                dimension: wgpu::TextureDimension::D2,
110                format: wgpu::TextureFormat::Rgba8UnormSrgb,
111                usage: wgpu::TextureUsages::TEXTURE_BINDING,
112                view_formats: &[],
113            },
114            wgpu::util::TextureDataOrder::LayerMajor,
115            &[255, 255, 255, 255],
116        );
117
118        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
119        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
120            address_mode_u: wgpu::AddressMode::ClampToEdge,
121            address_mode_v: wgpu::AddressMode::ClampToEdge,
122            mag_filter: wgpu::FilterMode::Nearest,
123            min_filter: wgpu::FilterMode::Nearest,
124            ..Default::default()
125        });
126
127        Self {
128            texture,
129            view,
130            sampler,
131            width: 1,
132            height: 1,
133        }
134    }
135}