Skip to content

Commit

Permalink
Add ImageSamplerDescriptor as an image loader setting (#9982)
Browse files Browse the repository at this point in the history
Closes #9946 

# Objective

Add a new type mirroring `wgpu::SamplerDescriptor` for
`ImageLoaderSettings` to control how a loaded image should be sampled.

Fix issues with texture sampler descriptors not being set when loading
gltf texture from URI.

## Solution

Add a new `ImageSamplerDescriptor` and its affiliated types that mirrors
`wgpu::SamplerDescriptor`, use it in the image loader settings.

---

## Changelog

### Added

- Added new types `ImageSamplerDescriptor`, `ImageAddressMode`,
`ImageFilterMode`, `ImageCompareFunction` and `ImageSamplerBorderColor`
that mirrors the corresponding wgpu types.
- `ImageLoaderSettings` now carries an `ImageSamplerDescriptor` field
that will be used to determine how the loaded image is sampled, and will
be serialized as part of the image assets `.meta` files.

### Changed

- `Image::from_buffer` now takes the sampler descriptor to use as an
additional parameter.

### Fixed

- Sampler descriptors are set for gltf textures loaded from URI.
  • Loading branch information
Kanabenki committed Oct 25, 2023
1 parent af37ab5 commit 756fb06
Show file tree
Hide file tree
Showing 5 changed files with 334 additions and 31 deletions.
15 changes: 9 additions & 6 deletions crates/bevy_core_pipeline/src/tonemapping/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,7 @@ pub fn get_lut_bind_group_layout_entries(bindings: [u32; 2]) -> [BindGroupLayout
// allow(dead_code) so it doesn't complain when the tonemapping_luts feature is disabled
#[allow(dead_code)]
fn setup_tonemapping_lut_image(bytes: &[u8], image_type: ImageType) -> Image {
let mut image =
Image::from_buffer(bytes, image_type, CompressedImageFormats::NONE, false).unwrap();

image.sampler_descriptor = bevy_render::texture::ImageSampler::Descriptor(SamplerDescriptor {
let image_sampler = bevy_render::texture::ImageSampler::Descriptor(SamplerDescriptor {
label: Some("Tonemapping LUT sampler"),
address_mode_u: AddressMode::ClampToEdge,
address_mode_v: AddressMode::ClampToEdge,
Expand All @@ -359,8 +356,14 @@ fn setup_tonemapping_lut_image(bytes: &[u8], image_type: ImageType) -> Image {
mipmap_filter: FilterMode::Linear,
..default()
});

image
Image::from_buffer(
bytes,
image_type,
CompressedImageFormats::NONE,
false,
image_sampler,
)
.unwrap()
}

pub fn lut_placeholder() -> Image {
Expand Down
60 changes: 38 additions & 22 deletions crates/bevy_gltf/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ use bevy_render::{
},
prelude::SpatialBundle,
primitives::Aabb,
render_resource::{AddressMode, Face, FilterMode, PrimitiveTopology, SamplerDescriptor},
render_resource::{Face, PrimitiveTopology},
texture::{
CompressedImageFormats, Image, ImageLoaderSettings, ImageSampler, ImageType, TextureError,
CompressedImageFormats, Image, ImageAddressMode, ImageFilterMode, ImageLoaderSettings,
ImageSampler, ImageSamplerDescriptor, ImageType, TextureError,
},
};
use bevy_scene::Scene;
Expand Down Expand Up @@ -256,9 +257,14 @@ async fn load_gltf<'a, 'b, 'c>(
) {
let handle = match texture {
ImageOrPath::Image { label, image } => load_context.add_labeled_asset(label, image),
ImageOrPath::Path { path, is_srgb } => {
ImageOrPath::Path {
path,
is_srgb,
sampler_descriptor,
} => {
load_context.load_with_settings(path, move |settings: &mut ImageLoaderSettings| {
settings.is_srgb = is_srgb;
settings.sampler_descriptor = sampler_descriptor;
})
}
};
Expand Down Expand Up @@ -667,18 +673,19 @@ async fn load_image<'a, 'b>(
supported_compressed_formats: CompressedImageFormats,
) -> Result<ImageOrPath, GltfError> {
let is_srgb = !linear_textures.contains(&gltf_texture.index());
let sampler_descriptor = texture_sampler(&gltf_texture);
match gltf_texture.source().source() {
gltf::image::Source::View { view, mime_type } => {
let start = view.offset();
let end = view.offset() + view.length();
let buffer = &buffer_data[view.buffer().index()][start..end];
let mut image = Image::from_buffer(
let image = Image::from_buffer(
buffer,
ImageType::MimeType(mime_type),
supported_compressed_formats,
is_srgb,
ImageSampler::Descriptor(sampler_descriptor.into()),
)?;
image.sampler_descriptor = ImageSampler::Descriptor(texture_sampler(&gltf_texture));
Ok(ImageOrPath::Image {
image,
label: texture_label(&gltf_texture),
Expand All @@ -698,6 +705,7 @@ async fn load_image<'a, 'b>(
mime_type.map(ImageType::MimeType).unwrap_or(image_type),
supported_compressed_formats,
is_srgb,
ImageSampler::Descriptor(sampler_descriptor.into()),
)?,
label: texture_label(&gltf_texture),
})
Expand All @@ -706,6 +714,7 @@ async fn load_image<'a, 'b>(
Ok(ImageOrPath::Path {
path: image_path,
is_srgb,
sampler_descriptor,
})
}
}
Expand Down Expand Up @@ -1110,56 +1119,56 @@ fn skin_label(skin: &gltf::Skin) -> String {
}

/// Extracts the texture sampler data from the glTF texture.
fn texture_sampler<'a>(texture: &gltf::Texture) -> SamplerDescriptor<'a> {
fn texture_sampler(texture: &gltf::Texture) -> ImageSamplerDescriptor {
let gltf_sampler = texture.sampler();

SamplerDescriptor {
ImageSamplerDescriptor {
address_mode_u: texture_address_mode(&gltf_sampler.wrap_s()),
address_mode_v: texture_address_mode(&gltf_sampler.wrap_t()),

mag_filter: gltf_sampler
.mag_filter()
.map(|mf| match mf {
MagFilter::Nearest => FilterMode::Nearest,
MagFilter::Linear => FilterMode::Linear,
MagFilter::Nearest => ImageFilterMode::Nearest,
MagFilter::Linear => ImageFilterMode::Linear,
})
.unwrap_or(SamplerDescriptor::default().mag_filter),
.unwrap_or(ImageSamplerDescriptor::default().mag_filter),

min_filter: gltf_sampler
.min_filter()
.map(|mf| match mf {
MinFilter::Nearest
| MinFilter::NearestMipmapNearest
| MinFilter::NearestMipmapLinear => FilterMode::Nearest,
| MinFilter::NearestMipmapLinear => ImageFilterMode::Nearest,
MinFilter::Linear
| MinFilter::LinearMipmapNearest
| MinFilter::LinearMipmapLinear => FilterMode::Linear,
| MinFilter::LinearMipmapLinear => ImageFilterMode::Linear,
})
.unwrap_or(SamplerDescriptor::default().min_filter),
.unwrap_or(ImageSamplerDescriptor::default().min_filter),

mipmap_filter: gltf_sampler
.min_filter()
.map(|mf| match mf {
MinFilter::Nearest
| MinFilter::Linear
| MinFilter::NearestMipmapNearest
| MinFilter::LinearMipmapNearest => FilterMode::Nearest,
| MinFilter::LinearMipmapNearest => ImageFilterMode::Nearest,
MinFilter::NearestMipmapLinear | MinFilter::LinearMipmapLinear => {
FilterMode::Linear
ImageFilterMode::Linear
}
})
.unwrap_or(SamplerDescriptor::default().mipmap_filter),
.unwrap_or(ImageSamplerDescriptor::default().mipmap_filter),

..Default::default()
}
}

/// Maps the texture address mode form glTF to wgpu.
fn texture_address_mode(gltf_address_mode: &gltf::texture::WrappingMode) -> AddressMode {
fn texture_address_mode(gltf_address_mode: &gltf::texture::WrappingMode) -> ImageAddressMode {
match gltf_address_mode {
WrappingMode::ClampToEdge => AddressMode::ClampToEdge,
WrappingMode::Repeat => AddressMode::Repeat,
WrappingMode::MirroredRepeat => AddressMode::MirrorRepeat,
WrappingMode::ClampToEdge => ImageAddressMode::ClampToEdge,
WrappingMode::Repeat => ImageAddressMode::Repeat,
WrappingMode::MirroredRepeat => ImageAddressMode::MirrorRepeat,
}
}

Expand Down Expand Up @@ -1280,8 +1289,15 @@ fn resolve_node_hierarchy(
}

enum ImageOrPath {
Image { image: Image, label: String },
Path { path: PathBuf, is_srgb: bool },
Image {
image: Image,
label: String,
},
Path {
path: PathBuf,
is_srgb: bool,
sampler_descriptor: ImageSamplerDescriptor,
},
}

struct DataUri<'a> {
Expand Down
10 changes: 9 additions & 1 deletion crates/bevy_render/src/texture/compressed_image_saver.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::texture::{Image, ImageFormat, ImageFormatSetting, ImageLoader, ImageLoaderSettings};
use crate::texture::{
Image, ImageFormat, ImageFormatSetting, ImageLoader, ImageLoaderSettings, ImageSampler,
ImageSamplerDescriptor,
};
use bevy_asset::saver::{AssetSaver, SavedAsset};
use futures_lite::{AsyncWriteExt, FutureExt};
use thiserror::Error;
Expand Down Expand Up @@ -30,6 +33,10 @@ impl AssetSaver for CompressedImageSaver {
compressor_params.set_basis_format(basis_universal::BasisTextureFormat::UASTC4x4);
compressor_params.set_generate_mipmaps(true);
let is_srgb = image.texture_descriptor.format.is_srgb();
let sampler_descriptor = match &image.sampler_descriptor {
ImageSampler::Default => ImageSamplerDescriptor::default(),
ImageSampler::Descriptor(sampler_descriptor) => sampler_descriptor.clone().into(),
};
let color_space = if is_srgb {
basis_universal::ColorSpace::Srgb
} else {
Expand All @@ -55,6 +62,7 @@ impl AssetSaver for CompressedImageSaver {
Ok(ImageLoaderSettings {
format: ImageFormatSetting::Format(ImageFormat::Basis),
is_srgb,
sampler_descriptor,
})
}
.boxed()
Expand Down
Loading

0 comments on commit 756fb06

Please sign in to comment.