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