resource, render: load in texture sampler from gltf and use them in the renderer

This commit is contained in:
SeanOMik 2024-03-08 11:04:38 -05:00 committed by SeanOMik
parent b941fa2fe0
commit 5331cfc2c4
6 changed files with 276 additions and 34 deletions

View File

@ -93,21 +93,21 @@ async fn main() {
//let diffuse_texture = resman.request::<Texture>("assets/happy-tree.png").unwrap();
//let antique_camera_model = resman.request::<Model>("assets/AntiqueCamera.glb").unwrap();
//let cube_model = resman.request::<Model>("assets/cube-texture-bin.glb").unwrap();
let cube_gltf = resman.request::<Gltf>("assets/texture-sep/texture-sep.gltf").unwrap();
/* let cube_gltf = resman.request::<Gltf>("assets/texture-sep/texture-sep.gltf").unwrap();
let crate_gltf = resman.request::<Gltf>("assets/crate/crate.gltf").unwrap();
let separate_gltf = resman.request::<Gltf>("assets/pos-testing/separate-nodes-two-cubes.glb").unwrap();
drop(resman);
let separate_gltf = resman.request::<Gltf>("assets/pos-testing/child-node-cubes.glb").unwrap(); */
//drop(resman);
let cube_mesh = &cube_gltf.data_ref()
/* let cube_mesh = &cube_gltf.data_ref()
.unwrap().meshes[0];
let crate_mesh = &crate_gltf.data_ref()
.unwrap().meshes[0];
let separate_scene = &separate_gltf.data_ref()
.unwrap().scenes[0];
.unwrap().scenes[0]; */
/* let sponza_model = resman.request::<Gltf>("assets/sponza/Sponza.gltf").unwrap();
let sponza_model = resman.request::<Gltf>("assets/sponza/Sponza.gltf").unwrap();
drop(resman);
let sponza_scene = &sponza_model.data_ref()
@ -116,12 +116,12 @@ async fn main() {
world.spawn((
sponza_scene.clone(),
Transform::from_xyz(0.0, 0.0, 0.0),
)); */
));
world.spawn((
/* world.spawn((
separate_scene.clone(),
Transform::from_xyz(0.0, -5.0, -10.0),
));
)); */
/* {
let cube_tran = Transform::from_xyz(-5.9026427, -1.8953488, -10.0);
@ -146,7 +146,7 @@ async fn main() {
specular: 1.3,
},
light_tran,
cube_mesh.clone(),
//cube_mesh.clone(),
));
}

View File

@ -1,5 +1,7 @@
use std::sync::Arc;
use lyra_resource::{ResHandle, Texture};
use super::texture::RenderTexture;
pub struct MaterialSpecular {
@ -9,13 +11,18 @@ pub struct MaterialSpecular {
pub color_texture: Option<RenderTexture>,
}
fn texture_to_render(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: &Arc<wgpu::BindGroupLayout>, i: &Option<ResHandle<Texture>>) -> Option<RenderTexture> {
if let Some(tex) = i {
RenderTexture::from_resource(device, queue, bg_layout.clone(), tex, None).ok()
} else {
None
}
}
impl MaterialSpecular {
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Specular) -> Self {
let tex = value.texture.as_ref().and_then(|t| t.data_ref())
.map(|i| RenderTexture::from_image(device, queue, bg_layout.clone(), &i.image, None).unwrap());
let color_tex = value.color_texture.as_ref().and_then(|t| t.data_ref())
.map(|i| RenderTexture::from_image(device, queue, bg_layout, &i.image, None).unwrap());
let tex = texture_to_render(device, queue, &bg_layout, &value.texture);
let color_tex = texture_to_render(device, queue, &bg_layout, &value.color_texture);
Self {
factor: value.factor,
@ -39,8 +46,7 @@ pub struct Material {
impl Material {
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Material) -> Self {
let diffuse_texture = value.base_color_texture.as_ref().and_then(|t| t.data_ref())
.map(|i| RenderTexture::from_image(device, queue, bg_layout.clone(), &i.image, None).unwrap());
let diffuse_texture = texture_to_render(device, queue, &bg_layout, &value.base_color_texture);
let specular = value.specular.as_ref().map(|s| MaterialSpecular::from_resource(device, queue, bg_layout.clone(), s));

View File

@ -1,7 +1,7 @@
use std::sync::Arc;
use image::GenericImageView;
use lyra_resource::{ResHandle, Texture};
use lyra_resource::{FilterMode, ResHandle, Texture, WrappingMode};
use super::render_buffer::BindGroupPair;
@ -134,12 +134,101 @@ impl RenderTexture {
})
}
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, texture_res: &ResHandle<Texture>, label: Option<&str>) -> anyhow::Result<Self> {
let texture_ref = texture_res.data_ref().unwrap();
let img = texture_ref.image.data_ref().unwrap();
let rgba = img.to_rgba8();
let dimensions = img.dimensions();
let size = wgpu::Extent3d {
width: dimensions.0,
height: dimensions.1,
depth_or_array_layers: 1,
};
let texture = device.create_texture(
&wgpu::TextureDescriptor {
label,
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
}
);
queue.write_texture(
wgpu::ImageCopyTexture {
aspect: wgpu::TextureAspect::All,
texture: &texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
},
&rgba,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: std::num::NonZeroU32::new(4 * dimensions.0),
rows_per_image: std::num::NonZeroU32::new(dimensions.1),
},
size,
);
// convert resource sampler into wgpu sampler
let sampler_desc = match &texture_ref.sampler {
Some(sampler) => {
let magf = res_filter_to_wgpu(sampler.mag_filter.unwrap_or(FilterMode::Linear));
let minf = res_filter_to_wgpu(sampler.min_filter.unwrap_or(FilterMode::Nearest));
let mipf = res_filter_to_wgpu(sampler.mipmap_filter.unwrap_or(FilterMode::Nearest));
let wrap_u = res_wrap_to_wgpu(sampler.wrap_u);
let wrap_v = res_wrap_to_wgpu(sampler.wrap_v);
let wrap_w = res_wrap_to_wgpu(sampler.wrap_w);
wgpu::SamplerDescriptor {
address_mode_u: wrap_u,
address_mode_v: wrap_v,
address_mode_w: wrap_w,
mag_filter: magf,
min_filter: minf,
mipmap_filter: mipf,
..Default::default()
}
}
None => wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
},
};
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler(
&sampler_desc
);
let bgp = Self::create_bind_group_pair(device, bg_layout, &view, &sampler);
Ok(Self {
inner_texture: texture,
view,
sampler,
bindgroup_pair: Some(bgp),
})
}
/// Updates the texture on the gpu with the provided texture.
///
/// # Panics
/// Panics if `texture` is not loaded
pub fn update_texture(&mut self, _device: &wgpu::Device, queue: &wgpu::Queue, texture: &ResHandle<Texture>) {
let texture = &texture.data_ref().unwrap().image;
let texture = &texture.data_ref().unwrap();
let rgba = texture.to_rgba8();
let dimensions = texture.dimensions();
let size = wgpu::Extent3d {
@ -215,4 +304,21 @@ impl RenderTexture {
pub fn bind_group(&self) -> &wgpu::BindGroup {
&self.bindgroup_pair.as_ref().unwrap().bindgroup
}
}
/// Convert [`lyra_resource::WrappingMode`] to [`wgpu::AddressMode`]
fn res_wrap_to_wgpu(wmode: WrappingMode) -> wgpu::AddressMode {
match wmode {
WrappingMode::ClampToEdge => wgpu::AddressMode::ClampToEdge,
WrappingMode::MirroredRepeat => wgpu::AddressMode::MirrorRepeat,
WrappingMode::Repeat => wgpu::AddressMode::Repeat,
}
}
/// Convert [`lyra_resource::FilterMode`] to [`wgpu::FilterMode`]
fn res_filter_to_wgpu(fmode: FilterMode) -> wgpu::FilterMode {
match fmode {
FilterMode::Nearest => wgpu::FilterMode::Nearest,
FilterMode::Linear => wgpu::FilterMode::Linear,
}
}

View File

@ -1,6 +1,8 @@
use std::{collections::hash_map::DefaultHasher, hash::{Hash, Hasher}};
use crate::{Texture, ResHandle, util};
use gltf::texture::{MagFilter, MinFilter};
use crate::{util, FilterMode, Image, ResHandle, Texture, TextureSampler, WrappingMode};
use super::loader::GltfLoadContext;
/// PBR metallic roughness
@ -248,15 +250,33 @@ impl Material {
fn load_texture(context: &mut GltfLoadContext, texture_info: gltf::texture::Info<'_>) -> ResHandle<Texture> {
// TODO: texture_info.tex_coord()
let tex = texture_info.texture();
// TODO: tex.sampler()
let img = tex.source();
let src = img.source();
let buf = Material::read_source(context, src).unwrap();
let buflen = buf.len();
let mime_type = infer::get(&buf).expect("Failed to get file type").mime_type();
let mime_type = infer::get(&buf).expect("Failed to get image type").mime_type();
context.resource_manager.load_bytes::<Texture>(&uuid::Uuid::new_v4().to_string(), mime_type,
buf, 0, buflen).unwrap()
let tex_img = context.resource_manager.load_bytes::<Image>(&uuid::Uuid::new_v4().to_string(), mime_type,
buf, 0, buflen).unwrap();
let samp = tex.sampler();
let samp = TextureSampler {
mag_filter: samp.mag_filter().map(FilterMode::from_mag_filter),
min_filter: samp.min_filter().map(FilterMode::from_min_filter),
mipmap_filter: samp.min_filter().and_then(FilterMode::mipmap_filter),
wrap_u: samp.wrap_s().into(),
wrap_v: samp.wrap_t().into(),
wrap_w: WrappingMode::ClampToEdge,
};
let handler = ResHandle::with_data("", Texture {
image: tex_img,
sampler: Some(samp),
});
context.resource_manager.store_uuid(handler.clone());
handler
}
/// Load the Material from a gltf::Material.
@ -296,4 +316,55 @@ impl Material {
specular,
}
}
}
impl From<gltf::texture::MagFilter> for FilterMode {
fn from(value: gltf::texture::MagFilter) -> Self {
match value {
gltf::texture::MagFilter::Nearest => Self::Nearest,
gltf::texture::MagFilter::Linear => Self::Linear,
}
}
}
impl FilterMode {
/// Get the MinFilter mode and the mipmap filter mode from gltf MinFilter
pub fn from_min_filter(minf: MinFilter) -> FilterMode {
match minf {
MinFilter::Nearest => FilterMode::Nearest,
MinFilter::Linear => FilterMode::Linear,
MinFilter::NearestMipmapNearest => FilterMode::Nearest,
MinFilter::LinearMipmapNearest => FilterMode::Linear,
MinFilter::NearestMipmapLinear => FilterMode::Nearest,
MinFilter::LinearMipmapLinear => FilterMode::Linear,
}
}
pub fn from_mag_filter(magf: MagFilter) -> FilterMode {
match magf {
MagFilter::Nearest => FilterMode::Nearest,
MagFilter::Linear => FilterMode::Linear,
}
}
pub fn mipmap_filter(minf: MinFilter) -> Option<FilterMode> {
match minf {
MinFilter::Nearest => None,
MinFilter::Linear => None,
MinFilter::NearestMipmapNearest => Some(FilterMode::Nearest),
MinFilter::LinearMipmapNearest => Some(FilterMode::Nearest),
MinFilter::NearestMipmapLinear => Some(FilterMode::Linear),
MinFilter::LinearMipmapLinear => Some(FilterMode::Linear),
}
}
}
impl From<gltf::texture::WrappingMode> for WrappingMode {
fn from(value: gltf::texture::WrappingMode) -> Self {
match value {
gltf::texture::WrappingMode::ClampToEdge => Self::ClampToEdge,
gltf::texture::WrappingMode::MirroredRepeat => Self::MirroredRepeat,
gltf::texture::WrappingMode::Repeat => Self::Repeat,
}
}
}

View File

@ -3,7 +3,7 @@ use std::{fs::File, sync::Arc, io::Read};
use image::ImageError;
use tracing::{debug, trace};
use crate::{resource_manager::ResourceStorage, texture::Texture, resource::ResHandle, ResourceManager};
use crate::{resource::ResHandle, resource_manager::ResourceStorage, Image, ResourceManager};
use super::{LoaderError, ResourceLoader};
@ -13,7 +13,7 @@ impl From<ImageError> for LoaderError {
}
}
/// A struct that implements the `ResourceLoader` trait used for loading textures.
/// A struct that implements the `ResourceLoader` trait used for loading images.
#[derive(Default)]
pub struct ImageLoader;
@ -60,10 +60,8 @@ impl ResourceLoader for ImageLoader {
ImageError::IoError(e) => LoaderError::IoError(e),
_ => LoaderError::DecodingError(e.into()),
})?;
let texture = Texture {
image,
};
let res = ResHandle::with_data(path, texture);
let image = Image::from(image);
let res = ResHandle::with_data(path, image);
Ok(Arc::new(res))
}
@ -76,10 +74,8 @@ impl ResourceLoader for ImageLoader {
ImageError::IoError(e) => LoaderError::IoError(e),
_ => LoaderError::DecodingError(e.into()),
})?;
let texture = Texture {
image,
};
let res = ResHandle::with_data(&uuid::Uuid::new_v4().to_string(), texture);
let image = Image::from(image);
let res = ResHandle::with_data(&uuid::Uuid::new_v4().to_string(), image);
debug!("Finished loading image of {} bytes", length);

View File

@ -1,10 +1,73 @@
use std::ops::{Deref, DerefMut};
//pub use gltf::texture::{MagFilter, MinFilter, WrappingMode};
use image::DynamicImage;
use crate::ResHandle;
/// The filter mode of the sampler.
///
/// This is used for minification, magnification, and mipmap filters
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum FilterMode {
Nearest,
Linear,
}
/// The wrapping mode of the Texture coordinates
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum WrappingMode {
ClampToEdge,
MirroredRepeat,
Repeat,
}
/// The descriptor of the sampler for a Texture.
#[derive(Clone)]
pub struct TextureSampler {
pub mag_filter: Option<FilterMode>,
pub min_filter: Option<FilterMode>,
pub mipmap_filter: Option<FilterMode>,
pub wrap_u: WrappingMode,
pub wrap_v: WrappingMode,
pub wrap_w: WrappingMode,
}
#[derive(Clone)]
pub struct Image(DynamicImage);
impl Deref for Image {
type Target = DynamicImage;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Image {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<DynamicImage> for Image {
fn from(value: DynamicImage) -> Self {
Self(value)
}
}
#[derive(Clone)]
pub struct Texture {
pub image: DynamicImage,
pub image: ResHandle<Image>,
pub sampler: Option<TextureSampler>,
}
impl Texture {
/// Create a texture from an image.
pub fn from_image(image: ResHandle<Image>) -> Self {
Self {
image,
sampler: None,
}
}
}