Skip to content

component discovery #7256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/bevy_core_pipeline/src/bloom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{core_2d, core_3d, fullscreen_vertex_shader::fullscreen_shader_vertex
use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, HandleUntyped};
use bevy_ecs::{
pairs_with,
prelude::{Component, Entity},
query::{QueryItem, QueryState, With},
system::{Commands, Query, Res, ResMut, Resource},
Expand All @@ -27,7 +28,6 @@ use bevy_render::{
use bevy_utils::tracing::info_span;
use bevy_utils::HashMap;
use std::num::NonZeroU32;

const BLOOM_SHADER_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 929599476923908);

Expand Down Expand Up @@ -114,6 +114,7 @@ impl Plugin for BloomPlugin {
///
/// See also <https://en.wikipedia.org/wiki/Bloom_(shader_effect)>.
#[derive(Component, Reflect, Clone)]
#[pairs_with(Camera)]
pub struct BloomSettings {
/// Baseline of the threshold curve (default: 1.0).
///
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_core_pipeline/src/core_3d/camera_3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ impl ExtractComponent for Camera3d {
}
}

/// For components that can be added alongside the bundle, see [`PairsWithCamera`](bevy_render::camera::PairsWithCamera).
#[derive(Bundle)]
pub struct Camera3dBundle {
pub camera: Camera,
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_core_pipeline/src/fxaa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{core_2d, core_3d, fullscreen_vertex_shader::fullscreen_shader_vertex
use bevy_app::prelude::*;
use bevy_asset::{load_internal_asset, HandleUntyped};
use bevy_derive::Deref;
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_ecs::{pairs_with, prelude::*, query::QueryItem};
use bevy_reflect::TypeUuid;
use bevy_render::{
extract_component::{ExtractComponent, ExtractComponentPlugin},
Expand Down Expand Up @@ -41,6 +41,7 @@ impl Sensitivity {
}

#[derive(Component, Clone)]
#[pairs_with(Camera)]
pub struct Fxaa {
/// Enable render passes for FXAA.
pub enabled: bool,
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_ecs/macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.9.0" }
syn = "1.0"
quote = "1.0"
proc-macro2 = "1.0"
once_cell = "1.17.0"
11 changes: 11 additions & 0 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ extern crate proc_macro;

mod component;
mod fetch;
mod pairs;

use crate::fetch::derive_world_query_impl;
use bevy_macro_utils::{derive_label, get_named_struct_fields, BevyManifest};
Expand Down Expand Up @@ -578,3 +579,13 @@ pub fn derive_resource(input: TokenStream) -> TokenStream {
pub fn derive_component(input: TokenStream) -> TokenStream {
component::derive_component(input)
}

#[proc_macro_derive(PairsWithOthers)]
pub fn derive_pairs_with_others(input: TokenStream) -> TokenStream {
pairs::derive_pairs_with_others(input)
}

#[proc_macro_attribute]
pub fn pairs_with(attr: TokenStream, item: TokenStream) -> TokenStream {
pairs::derive_pairs_with(attr, item)
}
81 changes: 81 additions & 0 deletions crates/bevy_ecs/macros/src/pairs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use std::collections::HashMap;

use bevy_macro_utils::BevyManifest;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, AttributeArgs, DeriveInput, Error};

static CRATE_LOOKUP: once_cell::sync::Lazy<
HashMap<&'static str, (&'static str, Option<&'static str>)>,
> = once_cell::sync::Lazy::new(|| HashMap::from([("Camera", ("bevy_render", Some("camera")))]));

pub fn derive_pairs_with_others(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
#[allow(unused)]
let component_name = &ast.ident;
let trait_name = format_ident!("PairsWith{}", ast.ident);

TokenStream::from(quote! {
/// Lists component types that can be added to entities with the given component.
/// Note: in the bevy project this will only show components included in with engine. If
/// your project uses plugins that add to this list, they will be visible in the
/// bevy documentation in your project tree.
pub trait #trait_name : Component {}
})
}

pub fn derive_pairs_with(attrs: TokenStream, mut item: TokenStream) -> TokenStream {
let attrs = parse_macro_input!(attrs as AttributeArgs);

let mut pairs_with = None;
for meta in attrs {
use syn::NestedMeta::{Lit, Meta};
match meta {
Meta(syn::Meta::Path(path)) => {
if pairs_with.is_some() {
return Error::new_spanned(
path,
"multiple parse_with attributes not supported",
)
.into_compile_error()
.into();
}
pairs_with = Some(path.get_ident().unwrap().to_owned());
}
Lit(tok) => {
return Error::new_spanned(tok, "unexpected token in parse_with attribute")
.into_compile_error()
.into();
}
Meta(tok) => {
return Error::new_spanned(tok, "unexpected token in parse_with attribute")
.into_compile_error()
.into();
}
}
}

let pairs_with = pairs_with.unwrap();
let pairs_with_string = pairs_with.to_string();
let (crate_path, subcrate) = CRATE_LOOKUP.get(pairs_with_string.as_str()).unwrap();
let crate_path = BevyManifest::get_path_direct(crate_path);
let crate_path = match subcrate {
Some(subcrate) => {
let subcrate: syn::Path = syn::parse(subcrate.parse::<TokenStream>().unwrap()).unwrap();
quote! { #crate_path::#subcrate }
}
None => {
quote! { crate_path }
}
};
let trait_name = format_ident!("PairsWith{}", pairs_with);

let ast = item.clone();
let ast = parse_macro_input!(ast as DeriveInput);
let component_name = &ast.ident;

item.extend(TokenStream::from(quote! {
impl #crate_path::#trait_name for #component_name {}
}));
item
}
1 change: 1 addition & 0 deletions crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub mod prelude {
}

pub use bevy_ecs_macros::all_tuples;
pub use bevy_ecs_macros::{pairs_with, PairsWithOthers};

#[cfg(test)]
mod tests {
Expand Down
5 changes: 4 additions & 1 deletion crates/bevy_render/src/camera/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{
};
use bevy_asset::{AssetEvent, Assets, Handle};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::PairsWithOthers;
use bevy_ecs::{
change_detection::DetectChanges,
component::Component,
Expand Down Expand Up @@ -81,7 +82,9 @@ pub struct ComputedCameraValues {
///
/// Adding a camera is typically done by adding a bundle, either the `Camera2dBundle` or the
/// `Camera3dBundle`.
#[derive(Component, Debug, Reflect, FromReflect, Clone)]
///
/// For components that can be added to the camera, see [`PairsWithCamera`].
#[derive(Component, Debug, Reflect, FromReflect, Clone, PairsWithOthers)]
#[reflect(Component)]
pub struct Camera {
/// If set, this camera will render to the given [`Viewport`] rectangle within the configured [`RenderTarget`].
Expand Down