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