use crate::engine_handle::Engine;
use crate::resource::{self, InProgressResource, ResourceId, ResourceType};
use crate::vectors::Vec2;
use crate::{layouts, ERROR_TEXTURE_DATA};
use image::{GenericImageView, ImageError};
use std::fmt::Display;
use std::io::Error;
use std::path::Path;
pub struct Texture {
pub(crate) _view: wgpu::TextureView,
pub(crate) bind_group: wgpu::BindGroup,
pub(crate) size: Vec2<f32>,
}
impl Texture {
pub fn new<P>(engine: &mut Engine, path: P) -> ResourceId<Texture>
where
P: AsRef<Path>,
{
let typed_id = resource::generate_id::<Texture>();
let id = typed_id.get_id();
let path = path.as_ref();
let ip_resource = InProgressResource::new(path, id, ResourceType::Image);
resource::start_load(engine, path, &ip_resource);
engine.add_in_progress_resource(ip_resource);
typed_id
}
pub fn from_btyes(
engine: &mut Engine,
label: Option<&str>,
bytes: &[u8],
) -> ResourceId<Texture> {
let img = image::load_from_memory(bytes)
.map(|img| Self::from_image(engine, img, label))
.unwrap_or_else(|e| {
log::warn!("{}, occured loading default", e);
Self::default(engine)
});
let typed_id = resource::generate_id::<Texture>();
engine.resource_manager.insert_texture(typed_id, img);
typed_id
}
pub(crate) fn from_resource_data(
engine: &Engine,
label: Option<&str>,
bytes: &[u8],
) -> Result<Self, TextureError> {
let img = image::load_from_memory(bytes)?;
Ok(Self::from_image(engine, img, label))
}
pub(crate) fn new_direct(
view: wgpu::TextureView,
bind_group: wgpu::BindGroup,
size: Vec2<f32>,
) -> Self {
Self {
_view: view,
bind_group,
size,
}
}
pub(crate) fn default(engine: &Engine) -> Self {
let image = image::load_from_memory(ERROR_TEXTURE_DATA).unwrap();
Self::from_image(engine, image, Some("Error Texture"))
}
fn from_image(engine: &Engine, img: image::DynamicImage, label: Option<&str>) -> Self {
let wgpu = engine.get_wgpu();
let diffuse_rgba = img.to_rgba8();
let (width, height) = img.dimensions();
let texture_size = wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
};
let texture = wgpu.device.create_texture(&wgpu::TextureDescriptor {
size: texture_size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
view_formats: &[],
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
label,
});
wgpu.queue.write_texture(
wgpu::ImageCopyTextureBase {
texture: &texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
&diffuse_rgba,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(4 * width),
rows_per_image: Some(height),
},
texture_size,
);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let bind_group_layout = layouts::create_texture_layout(&wgpu.device);
let bind_group = wgpu.device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(engine.get_texture_sampler()),
},
],
label: Some("diffuse_bind_group"),
});
let size = Vec2 {
x: width as f32,
y: height as f32,
};
Self {
_view: view,
bind_group,
size,
}
}
}
#[derive(Debug)]
pub(crate) enum TextureError {
IoError(Error),
ImageError(ImageError),
}
impl From<Error> for TextureError {
fn from(value: Error) -> TextureError {
Self::IoError(value)
}
}
impl From<ImageError> for TextureError {
fn from(value: ImageError) -> Self {
Self::ImageError(value)
}
}
impl std::error::Error for TextureError {}
impl Display for TextureError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::IoError(e) => write!(f, "{}", e),
Self::ImageError(e) => write!(f, "{}", e),
}
}
}