1use wde_logger::prelude::*;
2
3use bevy::{
4 asset::{AssetLoader, LoadContext, io::Reader},
5 ecs::system::lifetimeless::SRes,
6 prelude::*
7};
8use image::GenericImageView;
9use serde::{Deserialize, Serialize};
10use std::io::{Error, ErrorKind};
11use thiserror::Error;
12
13use crate::core::RenderInstance;
14
15use super::asset::{PrepareAssetError, RenderAsset};
16
17pub use wde_wgpu::texture::{
19 DEPTH_FORMAT, FilterMode, SWAPCHAIN_FORMAT, TextureFormat, TextureUsages
20};
21
22#[derive(Asset, TypePath, Clone, Debug)]
26pub struct Texture {
27 pub label: String,
28 pub size: (u32, u32),
30
31 pub format: TextureFormat,
33 pub usages: TextureUsages,
35
36 pub sample_count: u32,
38 pub layer_count: u32,
40 pub mip_level_count: u32,
42 pub filterable: bool,
44
45 pub data: Vec<u8>
47}
48impl Default for Texture {
49 fn default() -> Self {
50 Texture {
51 label: "Unknown Texture".to_string(),
52 size: (1, 1),
53 format: TextureFormat::Rgba8Unorm,
54 usages: TextureUsages::TEXTURE_BINDING,
55 sample_count: 1,
56 layer_count: 1,
57 mip_level_count: 1,
58 filterable: true,
59 data: Vec::new()
60 }
61 }
62}
63
64pub struct GpuTexture {
66 pub label: String,
68 pub texture: wde_wgpu::texture::Texture
70}
71impl RenderAsset for GpuTexture {
72 type SourceAsset = Texture;
73 type Params = SRes<RenderInstance>;
74
75 fn prepare(
76 asset: Self::SourceAsset,
77 render_instance: &mut bevy::ecs::system::SystemParamItem<Self::Params>
78 ) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
79 trace!(asset.label, "Preparing GPU texture asset.");
80
81 let render_instance = render_instance.0.as_ref().read().unwrap();
82
83 let texture = wde_wgpu::texture::Texture::new(
85 &render_instance,
86 &asset.label,
87 (asset.size.0, asset.size.1),
88 asset.format,
89 asset.usages,
90 asset.sample_count,
91 asset.layer_count,
92 asset.mip_level_count,
93 asset.filterable
94 );
95
96 if !asset.data.is_empty() {
98 texture.copy_from_buffer(&render_instance, asset.format, &asset.data);
99 }
100 Ok(GpuTexture {
101 label: asset.label,
102 texture
103 })
104 }
105
106 fn label(&self) -> &str {
107 &self.label
108 }
109}
110
111#[derive(Serialize, Deserialize)]
113pub struct TextureLoaderSettings {
114 pub label: String,
116 pub format: TextureFormat,
118 pub usages: TextureUsages
120}
121impl Default for TextureLoaderSettings {
122 fn default() -> Self {
123 Self {
124 label: "Unknown Texture".to_string(),
125 format: TextureFormat::Rgba8Unorm,
126 usages: TextureUsages::TEXTURE_BINDING
127 }
128 }
129}
130
131#[derive(Debug, Error)]
132pub(crate) enum TextureLoaderError {
133 #[error("Could not load texture: {0}")]
134 Io(#[from] std::io::Error)
135}
136#[derive(Default, TypePath)]
137pub(crate) struct TextureLoader;
138impl AssetLoader for TextureLoader {
139 type Asset = Texture;
140 type Settings = TextureLoaderSettings;
141 type Error = TextureLoaderError;
142
143 async fn load(
144 &self,
145 reader: &mut dyn Reader,
146 settings: &TextureLoaderSettings,
147 load_context: &mut LoadContext<'_>
148 ) -> Result<Self::Asset, Self::Error> {
149 debug!("Loading texture {}.", load_context.path());
150
151 let mut bytes = Vec::new();
153 reader.read_to_end(&mut bytes).await?;
154
155 let image = match image::load_from_memory(&bytes) {
157 Ok(image) => image,
158 Err(err) => {
159 error!("Could not load texture: {}", err);
160 return Err(TextureLoaderError::Io(Error::new(
161 ErrorKind::InvalidData,
162 err
163 )));
164 }
165 };
166 let size = image.dimensions();
167
168 let format_properties = get_format_properties(settings.format).unwrap();
170 let data = match format_properties.0 {
171 8 => from_channels(&image.into_rgba8(), format_properties.1),
172 16 => bytemuck::cast_slice(&from_channels(&image.into_rgba16(), format_properties.1))
173 .to_vec(),
174 21 => bytemuck::cast_slice(&from_channels(&image.into_rgba32f(), format_properties.1))
175 .to_vec(),
176 _ => unreachable!()
177 };
178
179 Ok(Texture {
180 label: settings.label.clone(),
181 format: settings.format,
182 usages: settings.usages,
183 size,
184 sample_count: 1,
185 layer_count: 1,
186 mip_level_count: 1,
187 filterable: true,
188 data
189 })
190 }
191
192 fn extensions(&self) -> &[&str] {
193 &["png", "jpg"]
194 }
195}
196
197fn get_format_properties(texture_format: TextureFormat) -> Option<(u32, u32)> {
203 match texture_format {
204 TextureFormat::R8Unorm
205 | TextureFormat::R8Uint
206 | TextureFormat::R8Snorm
207 | TextureFormat::R8Sint => Some((8, 1)),
208 TextureFormat::R16Unorm
209 | TextureFormat::R16Uint
210 | TextureFormat::R16Snorm
211 | TextureFormat::R16Sint
212 | TextureFormat::R16Float => Some((16, 1)),
213 TextureFormat::R32Uint | TextureFormat::R32Sint | TextureFormat::R32Float => Some((32, 1)),
214 TextureFormat::Rg8Unorm
215 | TextureFormat::Rg8Uint
216 | TextureFormat::Rg8Snorm
217 | TextureFormat::Rg8Sint => Some((8, 2)),
218 TextureFormat::Rg16Unorm
219 | TextureFormat::Rg16Uint
220 | TextureFormat::Rg16Snorm
221 | TextureFormat::Rg16Sint
222 | TextureFormat::Rg16Float => Some((16, 2)),
223 TextureFormat::Rg32Uint | TextureFormat::Rg32Sint | TextureFormat::Rg32Float => {
224 Some((32, 2))
225 }
226 TextureFormat::Rgba8Unorm
227 | TextureFormat::Rgba8UnormSrgb
228 | TextureFormat::Rgba8Uint
229 | TextureFormat::Rgba8Snorm
230 | TextureFormat::Rgba8Sint => Some((8, 4)),
231 TextureFormat::Rgba16Unorm
232 | TextureFormat::Rgba16Uint
233 | TextureFormat::Rgba16Snorm
234 | TextureFormat::Rgba16Sint
235 | TextureFormat::Rgba16Float => Some((16, 4)),
236 TextureFormat::Rgba32Uint | TextureFormat::Rgba32Sint | TextureFormat::Rgba32Float => {
237 Some((32, 4))
238 }
239 _ => None
240 }
241}
242
243fn from_channels<T: Clone + Copy + bytemuck::NoUninit + bytemuck::Pod>(
245 data: &[T],
246 channels: u32
247) -> Vec<T> {
248 let inv_channels = [4, 3, 2, 1];
249 if channels == 4 {
250 return data.to_vec();
251 }
252 let inv_channel = inv_channels[channels as usize - 1];
253
254 let mut buffer: Vec<T> = Vec::with_capacity(data.len() / inv_channel as usize);
256 for i in 0..data.len() / inv_channel as usize {
257 buffer.push(data[i * inv_channel as usize]);
258 }
259 buffer
260}