diff --git a/README.md b/README.md index 1fb7a46..4818f8f 100644 --- a/README.md +++ b/README.md @@ -245,12 +245,16 @@ use bevy_asset_loader::asset_collection::AssetCollection; #[derive(AssetCollection, Resource)] struct ImageAssets { #[asset(path = "images/pixel_tree.png")] - #[asset(image(sampler = linear))] + #[asset(image(sampler(filter = linear)))] tree_linear: Handle, #[asset(path = "images/pixel_tree.png")] - #[asset(image(sampler = nearest))] + #[asset(image(sampler(filter = nearest)))] tree_nearest: Handle, + + #[asset(path = "images/pixel_tree.png")] + #[asset(image(sampler(filter = linear, repeat)))] + tree_linear_repeat: Handle, } ``` @@ -260,11 +264,18 @@ The corresponding dynamic asset would be ({ "tree_nearest": Image ( path: "images/tree.png", - sampler: Nearest + filter: Nearest, + wrap: Clamp ), "tree_linear": Image ( path: "images/tree.png", - sampler: Linear + filter: Linear, + wrap: Clamp + ), + "tree_linear_repeat": Image ( + path: "images/tree.png", + filter: Linear, + wrap: Repeat ), }) ``` diff --git a/bevy_asset_loader/examples/atlas_from_grid.rs b/bevy_asset_loader/examples/atlas_from_grid.rs index 3864f35..366fc42 100644 --- a/bevy_asset_loader/examples/atlas_from_grid.rs +++ b/bevy_asset_loader/examples/atlas_from_grid.rs @@ -29,7 +29,7 @@ struct MyAssets { #[asset(texture_atlas_layout(tile_size_x = 96, tile_size_y = 99, columns = 8, rows = 1))] female_adventurer_layout: Handle, // you can configure the sampler for the sprite sheet image - #[asset(image(sampler = nearest))] + #[asset(image(sampler(filter = nearest)))] #[asset(path = "images/female_adventurer_sheet.png")] female_adventurer: Handle, } diff --git a/bevy_asset_loader/examples/image_asset.rs b/bevy_asset_loader/examples/image_asset.rs index bc5ad1b..f909795 100644 --- a/bevy_asset_loader/examples/image_asset.rs +++ b/bevy_asset_loader/examples/image_asset.rs @@ -18,11 +18,11 @@ fn main() { #[derive(AssetCollection, Resource)] struct ImageAssets { #[asset(path = "images/pixel_tree.png")] - #[asset(image(sampler = linear))] + #[asset(image(sampler(filter = linear)))] tree_linear: Handle, #[asset(path = "images/pixel_tree.png")] - #[asset(image(sampler = nearest))] + #[asset(image(sampler(filter = nearest)))] tree_nearest: Handle, #[asset(path = "images/array_texture.png")] diff --git a/bevy_asset_loader_derive/src/assets.rs b/bevy_asset_loader_derive/src/assets.rs index 13f4d20..da889ac 100644 --- a/bevy_asset_loader_derive/src/assets.rs +++ b/bevy_asset_loader_derive/src/assets.rs @@ -1,6 +1,7 @@ use crate::{ParseFieldError, TextureAtlasAttribute}; use proc_macro2::{Ident, TokenStream}; use quote::quote; +use syn::{spanned::Spanned, Lit, LitStr}; #[derive(PartialEq, Debug)] pub(crate) struct TextureAtlasLayoutAssetField { @@ -16,12 +17,12 @@ pub(crate) struct TextureAtlasLayoutAssetField { } #[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub(crate) enum SamplerType { +pub(crate) enum FilterType { Linear, Nearest, } -impl TryFrom for SamplerType { +impl TryFrom for FilterType { type Error = &'static str; fn try_from(value: String) -> Result { match value.as_str() { @@ -32,11 +33,20 @@ impl TryFrom for SamplerType { } } +#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] +pub(crate) enum WrapMode { + #[default] + Clamp, + #[allow(dead_code)] + Repeat, +} + #[derive(PartialEq, Debug)] pub(crate) struct ImageAssetField { pub field_ident: Ident, pub asset_path: String, - pub sampler: Option, + pub filter: Option, + pub wrap: Option, pub array_texture_layers: Option, } @@ -124,18 +134,19 @@ impl AssetField { let field_ident = image.field_ident.clone(); let asset_path = image.asset_path.clone(); let layers = image.array_texture_layers.unwrap_or_default(); - let sampler = match image.sampler { - Some(SamplerType::Linear) | None => quote!(ImageSampler::linear()), - Some(SamplerType::Nearest) => quote!(ImageSampler::nearest()), + let filter = match image.filter { + Some(FilterType::Linear) | None => quote!(ImageFilterMode::Linear), + Some(FilterType::Nearest) => quote!(ImageFilterMode::Nearest), }; - let descriptor = match image.sampler { - Some(SamplerType::Linear) | None => quote!(ImageSamplerDescriptor::linear()), - Some(SamplerType::Nearest) => quote!(ImageSamplerDescriptor::nearest()), + let wrap = match image.wrap { + Some(WrapMode::Clamp) | None => quote!(ImageAddressMode::ClampToEdge), + Some(WrapMode::Repeat) => quote!(ImageAddressMode::Repeat), }; - let is_sampler_set = image.sampler.is_some(); + let is_sampler_set = image.filter.is_some() || image.wrap.is_some(); + let label = Lit::Str(LitStr::new(&field_ident.to_string(), token_stream.span())); quote!(#token_stream #field_ident : { - use bevy::render::texture::{ImageSampler, ImageSamplerDescriptor}; + use bevy::render::texture::{ImageAddressMode, ImageFilterMode, ImageSampler, ImageSamplerDescriptor}; let mut system_state = ::bevy::ecs::system::SystemState::<( ResMut<::bevy::prelude::Assets<::bevy::prelude::Image>>, Res<::bevy::prelude::AssetServer>, @@ -149,19 +160,30 @@ impl AssetField { image.reinterpret_stacked_2d_as_array(#layers); } + let this_descriptor = ImageSamplerDescriptor { + label: Some(#label.to_string()), + address_mode_u: #wrap, + address_mode_v: #wrap, + address_mode_w: #wrap, + mag_filter: #filter, + min_filter: #filter, + mipmap_filter: #filter, + ..::std::default::Default::default() + }; + if (#is_sampler_set) { let is_different_sampler = if let ImageSampler::Descriptor(descriptor) = &image.sampler { - !descriptor.as_wgpu().eq(&#descriptor.as_wgpu()) + !descriptor.as_wgpu().eq(&this_descriptor.as_wgpu()) } else { - false + true }; if is_different_sampler { let mut cloned_image = image.clone(); - cloned_image.sampler = #sampler; + cloned_image.sampler = ImageSampler::Descriptor(this_descriptor); handle = images.add(cloned_image); } else { - image.sampler = #sampler; + image.sampler = ImageSampler::Default; } } @@ -539,7 +561,8 @@ pub(crate) struct AssetBuilder { pub padding_y: Option, pub offset_x: Option, pub offset_y: Option, - pub sampler: Option, + pub filter: Option, + pub wrap: Option, pub array_texture_layers: Option, } @@ -665,11 +688,12 @@ impl AssetBuilder { self.is_mapped.into(), )); } - if self.sampler.is_some() || self.array_texture_layers.is_some() { + if self.filter.is_some() || self.array_texture_layers.is_some() { return Ok(AssetField::Image(ImageAssetField { field_ident: self.field_ident.unwrap(), asset_path: self.asset_path.unwrap(), - sampler: self.sampler, + filter: self.filter, + wrap: self.wrap, array_texture_layers: self.array_texture_layers, })); } @@ -929,14 +953,16 @@ mod test { let builder_linear = AssetBuilder { field_ident: Some(Ident::new("test", Span::call_site())), asset_path: Some("some/image.png".to_owned()), - sampler: Some(SamplerType::Linear), + filter: Some(FilterType::Linear), + wrap: None, ..Default::default() }; let builder_nearest = AssetBuilder { field_ident: Some(Ident::new("test", Span::call_site())), asset_path: Some("some/image.png".to_owned()), - sampler: Some(SamplerType::Nearest), + filter: Some(FilterType::Nearest), + wrap: None, ..Default::default() }; @@ -960,7 +986,8 @@ mod test { AssetField::Image(ImageAssetField { field_ident: Ident::new("test", Span::call_site()), asset_path: "some/image.png".to_owned(), - sampler: Some(SamplerType::Linear), + filter: Some(FilterType::Linear), + wrap: None, array_texture_layers: None }) ); @@ -969,7 +996,8 @@ mod test { AssetField::Image(ImageAssetField { field_ident: Ident::new("test", Span::call_site()), asset_path: "some/image.png".to_owned(), - sampler: Some(SamplerType::Nearest), + filter: Some(FilterType::Nearest), + wrap: None, array_texture_layers: None }) ); @@ -978,7 +1006,8 @@ mod test { AssetField::Image(ImageAssetField { field_ident: Ident::new("test", Span::call_site()), asset_path: "some/image.png".to_owned(), - sampler: None, + filter: None, + wrap: None, array_texture_layers: Some(42) }) ); diff --git a/bevy_asset_loader_derive/src/lib.rs b/bevy_asset_loader_derive/src/lib.rs index d649399..f09be4d 100644 --- a/bevy_asset_loader_derive/src/lib.rs +++ b/bevy_asset_loader_derive/src/lib.rs @@ -66,6 +66,17 @@ impl ImageAttribute { pub const LAYERS: &'static str = "array_texture_layers"; } +#[allow(dead_code)] +pub(crate) struct SamplerAttribute; +impl SamplerAttribute { + #[allow(dead_code)] + pub const FILTER: &'static str = "filter"; + #[allow(dead_code)] + pub const CLAMP: &'static str = "clamp"; + #[allow(dead_code)] + pub const REPEAT: &'static str = "repeat"; +} + pub(crate) const COLLECTION_ATTRIBUTE: &str = "collection"; pub(crate) const PATHS_ATTRIBUTE: &str = "paths"; pub(crate) const TYPED_ATTRIBUTE: &str = "typed"; @@ -455,30 +466,82 @@ fn parse_field(field: &Field) -> Result> { .parse_args_with(Punctuated::::parse_terminated); for attribute in image_meta_list.unwrap() { match attribute { - Meta::NameValue(named_value) => { - let path = named_value.path.get_ident().unwrap().clone(); + Meta::List(meta_list) => { + let path = meta_list.path.get_ident().unwrap().clone(); if path == ImageAttribute::SAMPLER { - if let Expr::Path(ExprPath { path, .. }) = - &named_value.value - { - let sampler_result = SamplerType::try_from( - path.get_ident().unwrap().to_string(), - ); + let sampler_meta_list = meta_list + .parse_args_with( + Punctuated::::parse_terminated, + ) + .unwrap(); + for attribute in &sampler_meta_list { + match attribute { + Meta::NameValue(named_value) => { + let path = named_value + .path + .get_ident() + .unwrap() + .clone(); + if path == SamplerAttribute::FILTER { + if let Expr::Path(ExprPath { + path, .. + }) = &named_value.value + { + let filter_result = + FilterType::try_from( + path.get_ident() + .unwrap() + .to_string(), + ); - if let Ok(sampler) = sampler_result { - builder.sampler = Some(sampler); - } else { - errors.push(ParseFieldError::UnknownAttribute( - named_value.value.into_token_stream(), - )); + if let Ok(filter) = filter_result { + builder.filter = Some(filter); + } else { + errors.push(ParseFieldError::UnknownAttribute( + named_value.value.clone().into_token_stream(), + )); + } + } else { + errors.push( + ParseFieldError::WrongAttributeType( + named_value.into_token_stream(), + "path", + ), + ); + } + } + } + Meta::Path(path) => { + let path = path.get_ident().unwrap().clone(); + if path == SamplerAttribute::CLAMP { + builder.wrap = Some(WrapMode::Clamp); + } else if path == SamplerAttribute::REPEAT { + builder.wrap = Some(WrapMode::Repeat); + } else { + errors.push( + ParseFieldError::UnknownAttribute( + path.into_token_stream(), + ), + ); + } + } + Meta::List(_) => { + errors.push( + ParseFieldError::WrongAttributeType( + sampler_meta_list + .clone() + .into_token_stream(), + "name-value or path", + ), + ); + } } - } else { - errors.push(ParseFieldError::WrongAttributeType( - named_value.into_token_stream(), - "path", - )); } - } else if path == ImageAttribute::LAYERS { + } + } + Meta::NameValue(named_value) => { + let path = named_value.path.get_ident().unwrap().clone(); + if path == ImageAttribute::LAYERS { if let Expr::Lit(ExprLit { lit: Lit::Int(layers), .. @@ -492,6 +555,10 @@ fn parse_field(field: &Field) -> Result> { "u32", )); } + } else { + errors.push(ParseFieldError::UnknownAttributeType( + path.into_token_stream(), + )); } } _ => {