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