diff --git a/README.md b/README.md index 1fb7a46..f745248 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, wrap = 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/assets/dynamic_asset_arrays.asset_arrays.ron b/bevy_asset_loader/assets/dynamic_asset_arrays.asset_arrays.ron index d1b3db9..1d955c6 100644 --- a/bevy_asset_loader/assets/dynamic_asset_arrays.asset_arrays.ron +++ b/bevy_asset_loader/assets/dynamic_asset_arrays.asset_arrays.ron @@ -32,6 +32,7 @@ Image( path: "images/female_adventurer_sheet.png", sampler: Nearest, + wrap: Repeat, ), Image( path: "images/female_adventurer_sheet.png", diff --git a/bevy_asset_loader/assets/full_dynamic_collection.assets.ron b/bevy_asset_loader/assets/full_dynamic_collection.assets.ron index 1f10be2..c5a63d6 100644 --- a/bevy_asset_loader/assets/full_dynamic_collection.assets.ron +++ b/bevy_asset_loader/assets/full_dynamic_collection.assets.ron @@ -15,6 +15,11 @@ path: "images/tree.png", sampler: Nearest ), + "pixel_tree_repeat": Image ( + path: "images/tree.png", + sampler: Nearest, + wrap: Repeat + ), "array_texture": Image ( path: "images/array_texture.png", array_texture_layers: 4 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/full_collection.rs b/bevy_asset_loader/examples/full_collection.rs index f9c73e8..9797d83 100644 --- a/bevy_asset_loader/examples/full_collection.rs +++ b/bevy_asset_loader/examples/full_collection.rs @@ -35,7 +35,7 @@ struct MyAssets { // Image asset with sampler nearest (good for crisp pixel art) #[asset(path = "images/pixel_tree.png")] - #[asset(image(sampler = nearest))] + #[asset(image(sampler(filter = nearest)))] image_tree_nearest: Handle, // Array texture #[asset(path = "images/array_texture.png")] diff --git a/bevy_asset_loader/examples/full_dynamic_collection.rs b/bevy_asset_loader/examples/full_dynamic_collection.rs index ab8586b..797f83c 100644 --- a/bevy_asset_loader/examples/full_dynamic_collection.rs +++ b/bevy_asset_loader/examples/full_dynamic_collection.rs @@ -1,7 +1,7 @@ use bevy::app::AppExit; use bevy::asset::RecursiveDependencyLoadState; use bevy::prelude::*; -use bevy::render::texture::{ImageSampler, ImageSamplerDescriptor}; +use bevy::render::texture::{ImageAddressMode, ImageSampler, ImageSamplerDescriptor}; use bevy::utils::HashMap; use bevy_asset_loader::prelude::*; @@ -54,6 +54,9 @@ struct MyAssets { // Image asset with sampler nearest (good for crisp pixel art) #[asset(key = "pixel_tree")] image_tree_nearest: Handle, + // Image asset with sampler nearest and address mode repeat + #[asset(key = "pixel_tree_repeat")] + image_tree_nearest_repeat: Handle, // Array texture #[asset(key = "array_texture")] array_texture: Handle, @@ -144,12 +147,28 @@ fn expectations( .get(&assets.image_tree_nearest) .expect("Image should be added to its asset resource"); let ImageSampler::Descriptor(descriptor) = &image.sampler else { - panic!("Descriptor was not set to non default value nearest"); + panic!("Descriptor was not set to non default value"); }; assert_eq!( descriptor.as_wgpu(), ImageSamplerDescriptor::nearest().as_wgpu() ); + let image = images + .get(&assets.image_tree_nearest_repeat) + .expect("Image should be added to its asset resource"); + let ImageSampler::Descriptor(descriptor) = &image.sampler else { + panic!("Descriptor was not set to non default value"); + }; + assert_eq!( + descriptor.as_wgpu(), + ImageSamplerDescriptor { + address_mode_u: ImageAddressMode::Repeat, + address_mode_v: ImageAddressMode::Repeat, + address_mode_w: ImageAddressMode::Repeat, + ..ImageSamplerDescriptor::nearest() + } + .as_wgpu() + ); let image = images .get(&assets.array_texture) diff --git a/bevy_asset_loader/examples/image_asset.rs b/bevy_asset_loader/examples/image_asset.rs index bc5ad1b..6f3cfc3 100644 --- a/bevy_asset_loader/examples/image_asset.rs +++ b/bevy_asset_loader/examples/image_asset.rs @@ -1,7 +1,9 @@ +use bevy::math::Affine2; use bevy::prelude::*; use bevy_asset_loader::prelude::*; -/// This example demonstrates how you can set a different sampler for an [`Image`]. +/// This example demonstrates how you can set different samplers and wrap modes for +/// an [`Image`] asset. fn main() { App::new() .add_plugins(DefaultPlugins) @@ -18,38 +20,65 @@ 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/pixel_tree.png")] + #[asset(image(sampler(filter = nearest, wrap = repeat)))] + tree_nearest_repeat: Handle, + #[asset(path = "images/array_texture.png")] #[asset(image(array_texture_layers = 4))] array_texture: Handle, } -fn draw(mut commands: Commands, image_assets: Res) { - commands.spawn(Camera2dBundle { - projection: OrthographicProjection { - far: 1000., - near: -1000., - scale: 0.25, +fn draw( + mut commands: Commands, + image_assets: Res, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + commands.spawn(Camera3dBundle { + transform: Transform::from_xyz(0.0, 1.5, 4.0).looking_at(Vec3::ZERO, Vec3::Y), + camera: Camera { + order: 1, ..default() }, ..default() }); + commands.spawn(Camera2dBundle::default()); commands.spawn(SpriteBundle { texture: image_assets.tree_linear.clone(), - transform: Transform::from_translation(Vec3::new(-50., 0., 1.)), + transform: Transform::from_translation(Vec3::new(-50., 0., 1.)).with_scale(Vec3::splat(5.)), ..Default::default() }); commands.spawn(SpriteBundle { texture: image_assets.tree_nearest.clone(), - transform: Transform::from_translation(Vec3::new(50., 0., 1.)), + transform: Transform::from_translation(Vec3::new(50., 0., 1.)).with_scale(Vec3::splat(5.)), ..Default::default() }); + commands.spawn(PbrBundle { + mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), + material: materials.add(StandardMaterial { + base_color_texture: Some(image_assets.tree_nearest_repeat.clone()), + uv_transform: Affine2::from_scale(Vec2::new(2., 3.)), + ..default() + }), + transform: Transform::from_xyz(1.5, 0.0, 0.0), + ..default() + }); + commands.spawn(PointLightBundle { + point_light: PointLight { + shadows_enabled: true, + ..default() + }, + transform: Transform::from_xyz(4.0, 8.0, 4.0), + ..default() + }); } fn assert(images: Res, image_assets: Res>) { diff --git a/bevy_asset_loader/src/standard_dynamic_asset.rs b/bevy_asset_loader/src/standard_dynamic_asset.rs index a527cc4..55e3f84 100644 --- a/bevy_asset_loader/src/standard_dynamic_asset.rs +++ b/bevy_asset_loader/src/standard_dynamic_asset.rs @@ -1,6 +1,7 @@ use crate::dynamic_asset::{DynamicAsset, DynamicAssetType}; use crate::dynamic_asset::{DynamicAssetCollection, DynamicAssets}; use bevy::asset::{Asset, AssetServer, Assets, LoadedFolder, UntypedHandle}; +use bevy::ecs::change_detection::Res; use bevy::ecs::system::SystemState; use bevy::ecs::world::{Command, World}; use bevy::reflect::TypePath; @@ -11,12 +12,15 @@ use serde::{Deserialize, Serialize}; use bevy::math::UVec2; #[cfg(feature = "3d")] use bevy::pbr::StandardMaterial; -use bevy::prelude::{Res, ResMut}; #[cfg(feature = "2d")] use bevy::sprite::TextureAtlasLayout; #[cfg(any(feature = "3d", feature = "2d"))] -use bevy::render::texture::{Image, ImageSampler, ImageSamplerDescriptor}; +use bevy::ecs::change_detection::ResMut; +#[cfg(any(feature = "3d", feature = "2d"))] +use bevy::render::texture::{ + Image, ImageAddressMode, ImageFilterMode, ImageSampler, ImageSamplerDescriptor, +}; /// These asset variants can be loaded from configuration files. They will then replace /// a dynamic asset based on their keys. @@ -48,8 +52,11 @@ pub enum StandardDynamicAsset { /// Image file path path: String, /// Sampler - #[serde(with = "optional", skip_serializing_if = "Option::is_none", default)] - sampler: Option, + #[serde(default, skip_serializing_if = "is_default")] + sampler: ImageSamplerType, + /// Sampler + #[serde(default, skip_serializing_if = "is_default")] + wrap: ImageAddressModeType, /// array texture layers #[serde(with = "optional", skip_serializing_if = "Option::is_none", default)] array_texture_layers: Option, @@ -111,32 +118,49 @@ mod optional { } } +#[cfg(any(feature = "3d", feature = "2d"))] +fn is_default(value: &T) -> bool { + T::default() == *value +} + /// Define the image sampler to configure for an image asset #[cfg(any(feature = "3d", feature = "2d"))] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Default)] pub enum ImageSamplerType { /// See [`ImageSampler::nearest`] + #[default] Nearest, /// See [`ImageSampler::linear`] Linear, } #[cfg(any(feature = "3d", feature = "2d"))] -impl From for ImageSamplerDescriptor { - fn from(value: ImageSamplerType) -> Self { +impl From<&ImageSamplerType> for ImageFilterMode { + fn from(value: &ImageSamplerType) -> Self { match value { - ImageSamplerType::Nearest => ImageSamplerDescriptor::nearest(), - ImageSamplerType::Linear => ImageSamplerDescriptor::linear(), + ImageSamplerType::Nearest => ImageFilterMode::Nearest, + ImageSamplerType::Linear => ImageFilterMode::Linear, } } } +/// Define the image sampler address mode +#[cfg(any(feature = "3d", feature = "2d"))] +#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Default)] +pub enum ImageAddressModeType { + /// See [`ImageAddressMode::ClampToEdge`] + #[default] + ClampToEdge, + /// See [`ImageAddressMode::Repeat`] + Repeat, +} + #[cfg(any(feature = "3d", feature = "2d"))] -impl From for ImageSampler { - fn from(value: ImageSamplerType) -> Self { +impl From<&ImageAddressModeType> for ImageAddressMode { + fn from(value: &ImageAddressModeType) -> Self { match value { - ImageSamplerType::Nearest => ImageSampler::nearest(), - ImageSamplerType::Linear => ImageSampler::linear(), + ImageAddressModeType::ClampToEdge => ImageAddressMode::ClampToEdge, + ImageAddressModeType::Repeat => ImageAddressMode::Repeat, } } } @@ -179,15 +203,14 @@ impl DynamicAsset for StandardDynamicAsset { StandardDynamicAsset::Image { path, sampler, + wrap: address_mode, array_texture_layers, } => { let mut system_state = SystemState::<(ResMut>, Res)>::new(world); let (mut images, asset_server) = system_state.get_mut(world); let mut handle = asset_server.load(path); - if let Some(sampler) = sampler { - Self::update_image_sampler(&mut handle, &mut images, sampler); - } + Self::update_image_sampler(&mut handle, &mut images, sampler, address_mode); if let Some(layers) = array_texture_layers { let image = images .get_mut(&handle) @@ -273,10 +296,19 @@ impl StandardDynamicAsset { handle: &mut bevy::asset::Handle, images: &mut Assets, sampler_type: &ImageSamplerType, + address_mode: &ImageAddressModeType, ) { let image = images.get_mut(&*handle).unwrap(); + let configured_descriptor = ImageSamplerDescriptor { + address_mode_u: address_mode.into(), + address_mode_v: address_mode.into(), + address_mode_w: address_mode.into(), + mag_filter: sampler_type.into(), + min_filter: sampler_type.into(), + mipmap_filter: sampler_type.into(), + ..Default::default() + }; let is_different_sampler = if let ImageSampler::Descriptor(descriptor) = &image.sampler { - let configured_descriptor: ImageSamplerDescriptor = sampler_type.clone().into(); !descriptor.as_wgpu().eq(&configured_descriptor.as_wgpu()) } else { false @@ -284,10 +316,10 @@ impl StandardDynamicAsset { if is_different_sampler { let mut cloned_image = image.clone(); - cloned_image.sampler = sampler_type.clone().into(); + cloned_image.sampler = ImageSampler::Descriptor(configured_descriptor); *handle = images.add(cloned_image); } else { - image.sampler = sampler_type.clone().into(); + image.sampler = ImageSampler::Descriptor(configured_descriptor); } } } diff --git a/bevy_asset_loader_derive/src/assets.rs b/bevy_asset_loader_derive/src/assets.rs index 13f4d20..dafba20 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,29 @@ impl TryFrom for SamplerType { } } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub(crate) enum WrapMode { + Clamp, + Repeat, +} + +impl TryFrom for WrapMode { + type Error = &'static str; + fn try_from(value: String) -> Result { + match value.as_str() { + "clamp" => Ok(Self::Clamp), + "repeat" => Ok(Self::Repeat), + _ => Err("Value must be either `clamp` or `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 +143,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 +169,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 +570,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 +697,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 +962,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 +995,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 +1005,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 +1015,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 8eeea91..40aaa13 100644 --- a/bevy_asset_loader_derive/src/lib.rs +++ b/bevy_asset_loader_derive/src/lib.rs @@ -66,6 +66,15 @@ 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 WRAP: &'static str = "wrap"; +} + pub(crate) const COLLECTION_ATTRIBUTE: &str = "collection"; pub(crate) const PATHS_ATTRIBUTE: &str = "paths"; pub(crate) const TYPED_ATTRIBUTE: &str = "typed"; @@ -455,30 +464,95 @@ 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", + ), + ); + } + } + if path == SamplerAttribute::WRAP { + if let Expr::Path(ExprPath { + path, .. + }) = &named_value.value + { + let wrap_result = WrapMode::try_from( + path.get_ident() + .unwrap() + .to_string(), + ); + + if let Ok(wrap) = wrap_result { + builder.wrap = Some(wrap); + } else { + errors.push(ParseFieldError::UnknownAttribute( + named_value.value.clone().into_token_stream(), + )); + } + } else { + errors.push( + ParseFieldError::WrongAttributeType( + named_value.into_token_stream(), + "path", + ), + ); + } + } + } + Meta::List(_) | Meta::Path(_) => { + errors.push( + ParseFieldError::WrongAttributeType( + sampler_meta_list + .clone() + .into_token_stream(), + "name-value", + ), + ); + } } - } 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 +566,10 @@ fn parse_field(field: &Field) -> Result> { "u32", )); } + } else { + errors.push(ParseFieldError::UnknownAttributeType( + path.into_token_stream(), + )); } } _ => {