Skip to content

Commit 756fb06

Browse files
authored
Add ImageSamplerDescriptor as an image loader setting (#9982)
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.
1 parent af37ab5 commit 756fb06

File tree

5 files changed

+334
-31
lines changed

5 files changed

+334
-31
lines changed

crates/bevy_core_pipeline/src/tonemapping/mod.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -346,10 +346,7 @@ pub fn get_lut_bind_group_layout_entries(bindings: [u32; 2]) -> [BindGroupLayout
346346
// allow(dead_code) so it doesn't complain when the tonemapping_luts feature is disabled
347347
#[allow(dead_code)]
348348
fn setup_tonemapping_lut_image(bytes: &[u8], image_type: ImageType) -> Image {
349-
let mut image =
350-
Image::from_buffer(bytes, image_type, CompressedImageFormats::NONE, false).unwrap();
351-
352-
image.sampler_descriptor = bevy_render::texture::ImageSampler::Descriptor(SamplerDescriptor {
349+
let image_sampler = bevy_render::texture::ImageSampler::Descriptor(SamplerDescriptor {
353350
label: Some("Tonemapping LUT sampler"),
354351
address_mode_u: AddressMode::ClampToEdge,
355352
address_mode_v: AddressMode::ClampToEdge,
@@ -359,8 +356,14 @@ fn setup_tonemapping_lut_image(bytes: &[u8], image_type: ImageType) -> Image {
359356
mipmap_filter: FilterMode::Linear,
360357
..default()
361358
});
362-
363-
image
359+
Image::from_buffer(
360+
bytes,
361+
image_type,
362+
CompressedImageFormats::NONE,
363+
false,
364+
image_sampler,
365+
)
366+
.unwrap()
364367
}
365368

366369
pub fn lut_placeholder() -> Image {

crates/bevy_gltf/src/loader.rs

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ use bevy_render::{
2222
},
2323
prelude::SpatialBundle,
2424
primitives::Aabb,
25-
render_resource::{AddressMode, Face, FilterMode, PrimitiveTopology, SamplerDescriptor},
25+
render_resource::{Face, PrimitiveTopology},
2626
texture::{
27-
CompressedImageFormats, Image, ImageLoaderSettings, ImageSampler, ImageType, TextureError,
27+
CompressedImageFormats, Image, ImageAddressMode, ImageFilterMode, ImageLoaderSettings,
28+
ImageSampler, ImageSamplerDescriptor, ImageType, TextureError,
2829
},
2930
};
3031
use bevy_scene::Scene;
@@ -256,9 +257,14 @@ async fn load_gltf<'a, 'b, 'c>(
256257
) {
257258
let handle = match texture {
258259
ImageOrPath::Image { label, image } => load_context.add_labeled_asset(label, image),
259-
ImageOrPath::Path { path, is_srgb } => {
260+
ImageOrPath::Path {
261+
path,
262+
is_srgb,
263+
sampler_descriptor,
264+
} => {
260265
load_context.load_with_settings(path, move |settings: &mut ImageLoaderSettings| {
261266
settings.is_srgb = is_srgb;
267+
settings.sampler_descriptor = sampler_descriptor;
262268
})
263269
}
264270
};
@@ -667,18 +673,19 @@ async fn load_image<'a, 'b>(
667673
supported_compressed_formats: CompressedImageFormats,
668674
) -> Result<ImageOrPath, GltfError> {
669675
let is_srgb = !linear_textures.contains(&gltf_texture.index());
676+
let sampler_descriptor = texture_sampler(&gltf_texture);
670677
match gltf_texture.source().source() {
671678
gltf::image::Source::View { view, mime_type } => {
672679
let start = view.offset();
673680
let end = view.offset() + view.length();
674681
let buffer = &buffer_data[view.buffer().index()][start..end];
675-
let mut image = Image::from_buffer(
682+
let image = Image::from_buffer(
676683
buffer,
677684
ImageType::MimeType(mime_type),
678685
supported_compressed_formats,
679686
is_srgb,
687+
ImageSampler::Descriptor(sampler_descriptor.into()),
680688
)?;
681-
image.sampler_descriptor = ImageSampler::Descriptor(texture_sampler(&gltf_texture));
682689
Ok(ImageOrPath::Image {
683690
image,
684691
label: texture_label(&gltf_texture),
@@ -698,6 +705,7 @@ async fn load_image<'a, 'b>(
698705
mime_type.map(ImageType::MimeType).unwrap_or(image_type),
699706
supported_compressed_formats,
700707
is_srgb,
708+
ImageSampler::Descriptor(sampler_descriptor.into()),
701709
)?,
702710
label: texture_label(&gltf_texture),
703711
})
@@ -706,6 +714,7 @@ async fn load_image<'a, 'b>(
706714
Ok(ImageOrPath::Path {
707715
path: image_path,
708716
is_srgb,
717+
sampler_descriptor,
709718
})
710719
}
711720
}
@@ -1110,56 +1119,56 @@ fn skin_label(skin: &gltf::Skin) -> String {
11101119
}
11111120

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

1116-
SamplerDescriptor {
1125+
ImageSamplerDescriptor {
11171126
address_mode_u: texture_address_mode(&gltf_sampler.wrap_s()),
11181127
address_mode_v: texture_address_mode(&gltf_sampler.wrap_t()),
11191128

11201129
mag_filter: gltf_sampler
11211130
.mag_filter()
11221131
.map(|mf| match mf {
1123-
MagFilter::Nearest => FilterMode::Nearest,
1124-
MagFilter::Linear => FilterMode::Linear,
1132+
MagFilter::Nearest => ImageFilterMode::Nearest,
1133+
MagFilter::Linear => ImageFilterMode::Linear,
11251134
})
1126-
.unwrap_or(SamplerDescriptor::default().mag_filter),
1135+
.unwrap_or(ImageSamplerDescriptor::default().mag_filter),
11271136

11281137
min_filter: gltf_sampler
11291138
.min_filter()
11301139
.map(|mf| match mf {
11311140
MinFilter::Nearest
11321141
| MinFilter::NearestMipmapNearest
1133-
| MinFilter::NearestMipmapLinear => FilterMode::Nearest,
1142+
| MinFilter::NearestMipmapLinear => ImageFilterMode::Nearest,
11341143
MinFilter::Linear
11351144
| MinFilter::LinearMipmapNearest
1136-
| MinFilter::LinearMipmapLinear => FilterMode::Linear,
1145+
| MinFilter::LinearMipmapLinear => ImageFilterMode::Linear,
11371146
})
1138-
.unwrap_or(SamplerDescriptor::default().min_filter),
1147+
.unwrap_or(ImageSamplerDescriptor::default().min_filter),
11391148

11401149
mipmap_filter: gltf_sampler
11411150
.min_filter()
11421151
.map(|mf| match mf {
11431152
MinFilter::Nearest
11441153
| MinFilter::Linear
11451154
| MinFilter::NearestMipmapNearest
1146-
| MinFilter::LinearMipmapNearest => FilterMode::Nearest,
1155+
| MinFilter::LinearMipmapNearest => ImageFilterMode::Nearest,
11471156
MinFilter::NearestMipmapLinear | MinFilter::LinearMipmapLinear => {
1148-
FilterMode::Linear
1157+
ImageFilterMode::Linear
11491158
}
11501159
})
1151-
.unwrap_or(SamplerDescriptor::default().mipmap_filter),
1160+
.unwrap_or(ImageSamplerDescriptor::default().mipmap_filter),
11521161

11531162
..Default::default()
11541163
}
11551164
}
11561165

11571166
/// Maps the texture address mode form glTF to wgpu.
1158-
fn texture_address_mode(gltf_address_mode: &gltf::texture::WrappingMode) -> AddressMode {
1167+
fn texture_address_mode(gltf_address_mode: &gltf::texture::WrappingMode) -> ImageAddressMode {
11591168
match gltf_address_mode {
1160-
WrappingMode::ClampToEdge => AddressMode::ClampToEdge,
1161-
WrappingMode::Repeat => AddressMode::Repeat,
1162-
WrappingMode::MirroredRepeat => AddressMode::MirrorRepeat,
1169+
WrappingMode::ClampToEdge => ImageAddressMode::ClampToEdge,
1170+
WrappingMode::Repeat => ImageAddressMode::Repeat,
1171+
WrappingMode::MirroredRepeat => ImageAddressMode::MirrorRepeat,
11631172
}
11641173
}
11651174

@@ -1280,8 +1289,15 @@ fn resolve_node_hierarchy(
12801289
}
12811290

12821291
enum ImageOrPath {
1283-
Image { image: Image, label: String },
1284-
Path { path: PathBuf, is_srgb: bool },
1292+
Image {
1293+
image: Image,
1294+
label: String,
1295+
},
1296+
Path {
1297+
path: PathBuf,
1298+
is_srgb: bool,
1299+
sampler_descriptor: ImageSamplerDescriptor,
1300+
},
12851301
}
12861302

12871303
struct DataUri<'a> {

crates/bevy_render/src/texture/compressed_image_saver.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::texture::{Image, ImageFormat, ImageFormatSetting, ImageLoader, ImageLoaderSettings};
1+
use crate::texture::{
2+
Image, ImageFormat, ImageFormatSetting, ImageLoader, ImageLoaderSettings, ImageSampler,
3+
ImageSamplerDescriptor,
4+
};
25
use bevy_asset::saver::{AssetSaver, SavedAsset};
36
use futures_lite::{AsyncWriteExt, FutureExt};
47
use thiserror::Error;
@@ -30,6 +33,10 @@ impl AssetSaver for CompressedImageSaver {
3033
compressor_params.set_basis_format(basis_universal::BasisTextureFormat::UASTC4x4);
3134
compressor_params.set_generate_mipmaps(true);
3235
let is_srgb = image.texture_descriptor.format.is_srgb();
36+
let sampler_descriptor = match &image.sampler_descriptor {
37+
ImageSampler::Default => ImageSamplerDescriptor::default(),
38+
ImageSampler::Descriptor(sampler_descriptor) => sampler_descriptor.clone().into(),
39+
};
3340
let color_space = if is_srgb {
3441
basis_universal::ColorSpace::Srgb
3542
} else {
@@ -55,6 +62,7 @@ impl AssetSaver for CompressedImageSaver {
5562
Ok(ImageLoaderSettings {
5663
format: ImageFormatSetting::Format(ImageFormat::Basis),
5764
is_srgb,
65+
sampler_descriptor,
5866
})
5967
}
6068
.boxed()

0 commit comments

Comments
 (0)