Skip to content
Draft
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
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3032,6 +3032,10 @@ description = "Demonstrates how to use BSN to compose scenes"
category = "Scene"
wasm = true

[[example]]
name = "dynamic_bsn"
path = "examples/scene/dynamic_bsn.rs"

# Shaders
[[package.metadata.example_category]]
name = "Shaders"
Expand Down
31 changes: 31 additions & 0 deletions assets/scenes/example.bsn
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Example BSN file.

#Root
bevy_transform::components::transform::Transform
bevy_camera::visibility::Visibility::Visible
bevy_ecs::hierarchy::Children [
bevy_camera::components::Camera3d
bevy_transform::components::transform::Transform {
translation: glam::Vec3 { x: 0.7, y: 0.7, z: 1.0 },
rotation: glam::Quat { x: -0.15037778, y: 0.2968788, z: 0.04740241, w: 0.941808 },
}
bevy_camera::visibility::Visibility::Visible
bevy_light::probe::EnvironmentMapLight {
diffuse_map: "environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2",
specular_map: "environment_maps/pisa_specular_rgb9e5_zstd.ktx2",
intensity: 250.0,
},

bevy_transform::components::transform::Transform
// Embed an asset as a handle:
bevy_scene::components::SceneRoot("models/FlightHelmet/FlightHelmet.gltf#Scene0"),

bevy_light::directional_light::DirectionalLight {
shadow_maps_enabled: true,
}
// A template:
@bevy_light::cascade::CascadeShadowConfigBuilder {
num_cascades: 1,
maximum_distance: 1.6,
}
]
8 changes: 5 additions & 3 deletions crates/bevy_asset/src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use crate::{
ErasedAssetIndex, ReflectHandle, UntypedAssetId,
};
use alloc::sync::Arc;
use bevy_ecs::template::{FromTemplate, SpecializeFromTemplate, Template, TemplateContext};
use bevy_ecs::{reflect::{ReflectFromTemplate, ReflectTemplate}, template::{FromTemplate, SpecializeFromTemplate, Template, TemplateContext}};
use bevy_platform::collections::Equivalent;
use bevy_reflect::{Reflect, TypePath};
use bevy_reflect::{FromReflect, Reflect, TypePath, prelude::ReflectDefault};
use core::{
any::TypeId,
hash::{Hash, Hasher},
Expand Down Expand Up @@ -130,7 +130,7 @@ impl core::fmt::Debug for StrongHandle {
///
/// [`Handle::Strong`], via [`StrongHandle`] also provides access to useful [`Asset`] metadata, such as the [`AssetPath`] (if it exists).
#[derive(Reflect)]
#[reflect(Debug, Hash, PartialEq, Clone, Handle)]
#[reflect(Debug, Hash, PartialEq, Clone, Handle, FromTemplate)]
pub enum Handle<A: Asset> {
/// A "strong" reference to a live (or loading) [`Asset`]. If a [`Handle`] is [`Handle::Strong`], the [`Asset`] will be kept
/// alive until the [`Handle`] is dropped. Strong handles also provide access to additional asset metadata.
Expand Down Expand Up @@ -208,6 +208,8 @@ impl<T: Asset> FromTemplate for Handle<T> {
type Template = HandleTemplate<T>;
}

#[derive(Reflect)]
#[reflect(Default, Template)]
pub enum HandleTemplate<T: Asset> {
Path(AssetPath<'static>),
Handle(Handle<T>),
Expand Down
2 changes: 0 additions & 2 deletions crates/bevy_ecs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ std = [
"arrayvec/std",
"log/std",
"bevy_platform/std",
"downcast-rs/std",
]

## `critical-section` provides the building blocks for synchronization primitives
Expand Down Expand Up @@ -127,7 +126,6 @@ log = { version = "0.4", default-features = false }
bumpalo = "3"
subsecond = { version = "0.7.0-rc.0", optional = true }
slotmap = { version = "1.0.7", default-features = false }
downcast-rs = { version = "2", default-features = false }

concurrent-queue = { version = "2.5.0", default-features = false }
[target.'cfg(not(all(target_has_atomic = "8", target_has_atomic = "16", target_has_atomic = "32", target_has_atomic = "64", target_has_atomic = "ptr")))'.dependencies]
Expand Down
26 changes: 26 additions & 0 deletions crates/bevy_ecs/macros/src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use syn::{

const TEMPLATE_DEFAULT_ATTRIBUTE: &str = "default";
const TEMPLATE_ATTRIBUTE: &str = "template";
const TEMPLATE_REFLECT: &str = "reflect";

pub(crate) fn derive_from_template(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
Expand All @@ -21,6 +22,27 @@ pub(crate) fn derive_from_template(input: TokenStream) -> TokenStream {
let is_pub = matches!(ast.vis, syn::Visibility::Public(_));
let maybe_pub = if is_pub { quote!(pub) } else { quote!() };

let should_make_template_reflectable = ast
.attrs
.iter()
.filter(|attr| attr.path().is_ident(&TEMPLATE_ATTRIBUTE))
.any(|template_attr| {
let mut found = false;
let _ = template_attr.parse_nested_meta(|meta| {
found = found || meta.path.is_ident(TEMPLATE_REFLECT);
Ok(())
});
found
});
let maybe_reflect = if should_make_template_reflectable {
quote! {
#[derive(Reflect)]
#[reflect(Default, Template)]
}
} else {
quote! {}
};

let template = match &ast.data {
Data::Struct(data_struct) => {
let result = match struct_impl(&data_struct.fields, &bevy_ecs, false) {
Expand All @@ -38,6 +60,7 @@ pub(crate) fn derive_from_template(input: TokenStream) -> TokenStream {
Fields::Named(_) => {
quote! {
#[allow(missing_docs)]
#maybe_reflect
#maybe_pub struct #template_ident #impl_generics #where_clause {
#(#template_fields,)*
}
Expand Down Expand Up @@ -69,6 +92,7 @@ pub(crate) fn derive_from_template(input: TokenStream) -> TokenStream {
Fields::Unnamed(_) => {
quote! {
#[allow(missing_docs)]
#maybe_reflect
#maybe_pub struct #template_ident #impl_generics (
#(#template_fields,)*
) #where_clause;
Expand Down Expand Up @@ -100,6 +124,7 @@ pub(crate) fn derive_from_template(input: TokenStream) -> TokenStream {
Fields::Unit => {
quote! {
#[allow(missing_docs)]
#maybe_reflect
#maybe_pub struct #template_ident;

impl #impl_generics #bevy_ecs::template::Template for #template_ident #type_generics #where_clause {
Expand Down Expand Up @@ -269,6 +294,7 @@ pub(crate) fn derive_from_template(input: TokenStream) -> TokenStream {

quote! {
#[allow(missing_docs)]
#maybe_reflect
#maybe_pub enum #template_ident #type_generics #where_clause {
#(#variant_definitions,)*
}
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_ecs/src/reflect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod from_world;
mod map_entities;
mod message;
mod resource;
mod template;

use bevy_utils::prelude::DebugName;
pub use bundle::{ReflectBundle, ReflectBundleFns};
Expand All @@ -29,6 +30,7 @@ pub use from_world::{ReflectFromWorld, ReflectFromWorldFns};
pub use map_entities::ReflectMapEntities;
pub use message::{ReflectMessage, ReflectMessageFns};
pub use resource::ReflectResource;
pub use template::{ReflectFromTemplate, ReflectTemplate};

/// A [`Resource`] storing [`TypeRegistry`] for
/// type registrations relevant to a whole app.
Expand Down
60 changes: 60 additions & 0 deletions crates/bevy_ecs/src/reflect/template.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//! Definitions for `FromTemplate` and `Template` reflection.

use alloc::boxed::Box;
use core::any::TypeId;

use bevy_reflect::{FromType, Reflect};
use derive_more::{Deref, DerefMut};

use crate::{
error::BevyError,
prelude::{FromTemplate, Template},
template::TemplateContext,
};

#[derive(Clone, Deref, DerefMut)]
pub struct ReflectFromTemplate(pub ReflectFromTemplateData);

#[derive(Clone, Deref, DerefMut)]
pub struct ReflectTemplate(pub ReflectTemplateData);

#[derive(Clone)]
pub struct ReflectFromTemplateData {
pub template_type_id: TypeId,
}

#[derive(Clone)]
pub struct ReflectTemplateData {
pub build_template:
fn(&dyn Reflect, &mut TemplateContext) -> Result<Box<dyn Reflect>, BevyError>,
}

impl<T> FromType<T> for ReflectFromTemplate
where
T: FromTemplate,
T::Template: 'static,
<T::Template as Template>::Output: Reflect,
{
fn from_type() -> Self {
ReflectFromTemplate(ReflectFromTemplateData {
template_type_id: TypeId::of::<T::Template>(),
})
}
}

impl<T> FromType<T> for ReflectTemplate
where
T: Template + 'static,
<T as Template>::Output: Reflect,
{
fn from_type() -> Self {
ReflectTemplate(ReflectTemplateData {
build_template: |this, context| {
let Some(this) = this.downcast_ref::<T>() else {
return Err("Unexpected `build_template` receiver type".into());
};
Ok(Box::new(<T as Template>::build_template(this, context)?))
},
})
}
}
21 changes: 17 additions & 4 deletions crates/bevy_ecs/src/template.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Functionality that relates to the [`Template`] trait.

pub use bevy_ecs_macros::FromTemplate;
use bevy_reflect::PartialReflect;

use crate::{
bundle::Bundle,
Expand All @@ -10,7 +11,9 @@ use crate::{
world::{EntityWorldMut, Mut, World},
};
use alloc::{boxed::Box, vec, vec::Vec};
use downcast_rs::{impl_downcast, Downcast};
use bevy_platform::collections::hash_map::Entry;
use bevy_utils::TypeIdMap;
use core::any::{Any, TypeId};
use variadics_please::all_tuples;

/// A [`Template`] is something that, given a spawn context (target [`Entity`], [`World`], etc), can produce a [`Template::Output`].
Expand Down Expand Up @@ -388,15 +391,17 @@ impl FromTemplate for Entity {
}

/// A type-erased, object-safe, downcastable version of [`Template`].
pub trait ErasedTemplate: Downcast + Send + Sync {
pub trait ErasedTemplate: Send + Sync {
/// Applies this template to the given `entity`.
fn apply(&self, context: &mut TemplateContext) -> Result<(), BevyError>;

/// Clones this template. See [`Clone`].
fn clone_template(&self) -> Box<dyn ErasedTemplate>;
}

impl_downcast!(ErasedTemplate);
fn as_any_mut(&mut self) -> &mut dyn Any;

fn try_as_partial_reflect_mut(&mut self) -> Option<&mut dyn PartialReflect>;
}

impl<T: Template<Output: Bundle> + Send + Sync + 'static> ErasedTemplate for T {
fn apply(&self, context: &mut TemplateContext) -> Result<(), BevyError> {
Expand All @@ -408,6 +413,14 @@ impl<T: Template<Output: Bundle> + Send + Sync + 'static> ErasedTemplate for T {
fn clone_template(&self) -> Box<dyn ErasedTemplate> {
Box::new(Template::clone_template(self))
}

fn as_any_mut(&mut self) -> &mut dyn Any {
self
}

fn try_as_partial_reflect_mut(&mut self) -> Option<&mut dyn PartialReflect> {
None
}
}

/// A [`Template`] driven by a function that returns an output. This is used to create "free floating" templates without
Expand Down
24 changes: 23 additions & 1 deletion crates/bevy_light/src/cascade.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! Provides shadow cascade configuration and construction helpers.

use bevy_camera::{Camera, Projection};
use bevy_ecs::{entity::EntityHashMap, prelude::*};
use bevy_ecs::{
entity::EntityHashMap, prelude::*, reflect::ReflectTemplate, template::TemplateContext,
};
use bevy_math::{ops, Mat4, Vec3A, Vec4};
use bevy_reflect::prelude::*;
use bevy_transform::components::GlobalTransform;
Expand Down Expand Up @@ -56,6 +58,8 @@ fn calculate_cascade_bounds(
}

/// Builder for [`CascadeShadowConfig`].
#[derive(Reflect)]
#[reflect(Default, Template)]
pub struct CascadeShadowConfigBuilder {
/// The number of shadow cascades.
/// More cascades increases shadow quality by mitigating perspective aliasing - a phenomenon where areas
Expand Down Expand Up @@ -163,6 +167,24 @@ impl From<CascadeShadowConfigBuilder> for CascadeShadowConfig {
}
}

impl Template for CascadeShadowConfigBuilder {
type Output = CascadeShadowConfig;

fn build_template(&self, _: &mut TemplateContext) -> Result<Self::Output> {
Ok(self.build())
}

fn clone_template(&self) -> Self {
Self {
num_cascades: self.num_cascades,
minimum_distance: self.minimum_distance,
maximum_distance: self.maximum_distance,
first_cascade_far_bound: self.first_cascade_far_bound,
overlap_proportion: self.overlap_proportion,
}
}
}

/// A [`DirectionalLight`]'s per-view list of [`Cascade`]s.
#[derive(Component, Clone, Debug, Default, Reflect)]
#[reflect(Component, Debug, Default, Clone)]
Expand Down
16 changes: 9 additions & 7 deletions crates/bevy_light/src/probe.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use bevy_asset::{Assets, Handle, RenderAssetUsages};
use bevy_camera::visibility::{self, ViewVisibility, Visibility, VisibilityClass};
use bevy_color::{Color, ColorToComponents, Srgba};
use bevy_ecs::prelude::*;
use bevy_ecs::{
prelude::*,
reflect::{ReflectFromTemplate, ReflectTemplate},
};
use bevy_image::Image;
use bevy_math::{Quat, UVec2, Vec3};
use bevy_reflect::prelude::*;
Expand Down Expand Up @@ -101,8 +104,9 @@ impl LightProbe {
/// area in space.
///
/// See `bevy_pbr::environment_map` for detailed information.
#[derive(Clone, Component, Reflect)]
#[reflect(Component, Default, Clone)]
#[derive(Clone, Component, Reflect, FromTemplate)]
#[reflect(Component, Clone, FromTemplate)]
#[template(reflect)]
pub struct EnvironmentMapLight {
/// The blurry image that represents diffuse radiance surrounding a region.
pub diffuse_map: Handle<Image>,
Expand Down Expand Up @@ -159,7 +163,7 @@ impl EnvironmentMapLight {
Self {
diffuse_map: handle.clone(),
specular_map: handle,
..Default::default()
..EnvironmentMapLight::default()
}
}

Expand Down Expand Up @@ -200,10 +204,8 @@ impl EnvironmentMapLight {
)
}
}
}

impl Default for EnvironmentMapLight {
fn default() -> Self {
pub fn default() -> Self {
EnvironmentMapLight {
diffuse_map: Handle::default(),
specular_map: Handle::default(),
Expand Down
Loading